feat: numbered tab selection for launching projects into separate grid tabs

Space cycles tab number (1→2→...→9→off), digit keys 1-9 assign directly.
Each number gets a unique color in the picker brackets. On launch, projects
are grouped by tab number into separate grid tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-28 10:57:32 +00:00
parent 9e424c192a
commit f5dd49d870
6 changed files with 112 additions and 44 deletions

View File

@@ -15,9 +15,10 @@ export async function doLaunch() {
return
}
const items: { path: string; name: string; sessionId?: string; targetBranch?: string }[] = []
type LaunchItem = { path: string; name: string; tabNum: number; sessionId?: string; targetBranch?: string }
const items: LaunchItem[] = []
for (const path of app.selectedProjects) {
for (const [path, tabNum] of app.selectedProjects) {
const project = app.projects.find(p => p.path === path)
if (!project) continue
const targetBranch = app.selectedBranches.get(path)
@@ -27,7 +28,7 @@ export async function doLaunch() {
project.sessionCount = project.sessions.length
}
const lastSessionId = project.sessions[0]?.id
items.push({ path, name: project.name, sessionId: lastSessionId, targetBranch: needsBranch ? targetBranch : undefined })
items.push({ path, name: project.name, tabNum, sessionId: lastSessionId, targetBranch: needsBranch ? targetBranch : undefined })
}
for (const project of app.projects) {
@@ -36,41 +37,58 @@ export async function doLaunch() {
if (app.selectedSessions.has(session.id)) {
const targetBranch = app.selectedBranches.get(project.path)
const needsBranch = targetBranch && targetBranch !== project.branch
items.push({ path: project.path, name: project.name, sessionId: session.id, targetBranch: needsBranch ? targetBranch : undefined })
// Sessions without explicit tab number go to tab 1
items.push({ path: project.path, name: project.name, tabNum: 1, sessionId: session.id, targetBranch: needsBranch ? targetBranch : undefined })
}
}
}
if (items.length === 0) return
// Determine target tab: use active grid tab or create a new one
let targetTabId: number
if (app.viewMode === "grid" && app.directGrid && app.gridTabs.length > 0) {
targetTabId = app.directGrid.activeTabId
} else {
targetTabId = createNewGridTab()
// Group items by tab number
const byTab = new Map<number, LaunchItem[]>()
for (const item of items) {
if (!byTab.has(item.tabNum)) byTab.set(item.tabNum, [])
byTab.get(item.tabNum)!.push(item)
}
ensureGridView()
const termW = process.stdout.columns || 120
const termH = process.stdout.rows || 40
const totalPanes = items.length + (app.directGrid?.getTabPaneCount(targetTabId) || 0)
const cols = totalPanes <= 1 ? 1 : totalPanes <= 2 ? 2 : totalPanes <= 4 ? 2 : 3
const rows = Math.ceil(totalPanes / cols)
const paneW = Math.max(Math.floor(termW / cols) - 2, 20)
const paneH = Math.max(Math.floor((termH - 2) / rows) - 4, 6)
// Launch each tab group into its own grid tab
for (const [tabNum, tabItems] of byTab) {
// Find existing grid tab for this number, or create one
let targetTabId: number
const existingTab = app.gridTabs.find(t => t.name === `Tab ${tabNum}`)
if (existingTab) {
targetTabId = existingTab.id
} else {
targetTabId = createNewGridTab()
// Rename to match the picker tab number
const tab = app.gridTabs.find(t => t.id === targetTabId)
if (tab) tab.name = `Tab ${tabNum}`
}
for (const item of items) {
const session = await createSession({
projectPath: item.path,
projectName: item.name,
sessionId: item.sessionId,
targetBranch: item.targetBranch,
width: paneW,
height: paneH,
})
await app.directGrid!.addPane(session, targetTabId)
const termW = process.stdout.columns || 120
const termH = process.stdout.rows || 40
const totalPanes = tabItems.length + (app.directGrid?.getTabPaneCount(targetTabId) || 0)
const cols = totalPanes <= 1 ? 1 : totalPanes <= 2 ? 2 : totalPanes <= 4 ? 2 : 3
const rows = Math.ceil(totalPanes / cols)
const paneW = Math.max(Math.floor(termW / cols) - 2, 20)
const paneH = Math.max(Math.floor((termH - 2) / rows) - 4, 6)
for (const item of tabItems) {
const session = await createSession({
projectPath: item.path,
projectName: item.name,
sessionId: item.sessionId,
targetBranch: item.targetBranch,
width: paneW,
height: paneH,
})
await app.directGrid!.addPane(session, targetTabId)
}
switchToGridTab(targetTabId)
}
app.selectedProjects.clear()