From bc8a0c1934c625528ac0f7254e251160a15d8deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:56:38 +0000 Subject: [PATCH] feat: toggle between picker and grid without closing panes Add pause()/resume() to DirectGridRenderer that detach/reattach frame listeners without killing captures or PTY sessions. switchToPicker now pauses the grid, switchToGrid resumes it. Panes keep running in the background while browsing the project list. Footer shows "t grid" hint when panes are active in the background. Co-Authored-By: Claude Opus 4.6 --- src/components/direct-grid.ts | 25 +++++++++++++++++++++++++ src/grid/view-switch.ts | 10 ++++++++-- src/input/handlers.ts | 2 +- src/ui/panels.ts | 5 +++-- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/direct-grid.ts b/src/components/direct-grid.ts index 84ec3a4..be7e035 100644 --- a/src/components/direct-grid.ts +++ b/src/components/direct-grid.ts @@ -89,6 +89,31 @@ export class DirectGridRenderer { this.writeRaw(SHOW_CURSOR) } + pause() { + this.running = false + if (this.titleTimer) { clearInterval(this.titleTimer); this.titleTimer = null } + // Detach frame listeners (stops rendering) but keep captures alive + for (const p of this.panes) p.directPane.detach() + } + + resume() { + this.running = true + this.writeRaw(HIDE_CURSOR + CLEAR) + // Reattach frame listeners and redraw + for (let i = 0; i < this.panes.length; i++) { + const p = this.panes[i] + const dp = p.directPane + const idx = i + dp.attach(p.session.name) + dp.onFrame = (lines) => { + if (!this.running) return + this.drawPane(idx, lines) + } + } + this.repositionAll() + this.titleTimer = setInterval(() => this.refreshTitles(), 1000) + } + // ─── Getters ─────────────────────────────────────────── get focusIndex() { return this._focusIndex } diff --git a/src/grid/view-switch.ts b/src/grid/view-switch.ts index 88024f1..85dd155 100644 --- a/src/grid/view-switch.ts +++ b/src/grid/view-switch.ts @@ -9,7 +9,8 @@ export function ensureGridView() { export function switchToGrid() { app.viewMode = "grid" - if (!app.directGrid) { + const isNew = !app.directGrid + if (isNew) { app.directGrid = new DirectGridRenderer(app.rawStdoutWrite) } @@ -20,7 +21,12 @@ export function switchToGrid() { app.rawStdoutWrite("\x1b[?1049h") app.rawStdoutWrite("\x1b[?1000h") app.rawStdoutWrite("\x1b[?1006h") - app.directGrid.start() + + if (isNew || app.directGrid.paneCount === 0) { + app.directGrid.start() + } else { + app.directGrid.resume() + } } export function resizeGridPanes() { diff --git a/src/input/handlers.ts b/src/input/handlers.ts index 40a1a2e..02cb383 100644 --- a/src/input/handlers.ts +++ b/src/input/handlers.ts @@ -345,7 +345,7 @@ export function switchToPicker() { app.viewMode = "picker" if (app.directGrid) { if (app.directGrid.selectMode) app.directGrid.exitSelectMode() - if (app.directGrid.paneCount > 0) app.directGrid.stop() + if (app.directGrid.paneCount > 0) app.directGrid.pause() } app.renderer.resume() process.stdin.removeAllListeners("data") diff --git a/src/ui/panels.ts b/src/ui/panels.ts index d412f66..c55d157 100644 --- a/src/ui/panels.ts +++ b/src/ui/panels.ts @@ -93,13 +93,14 @@ export function updateColumnHeaders() { } export function updateFooter() { + const gridHint = app.directGrid && app.directGrid.paneCount > 0 ? " │ t grid" : "" if (app.bottomPanelMode === "idle" && app.cachedIdleSessions.length > 0) { app.footerText.content = t` ${dim( - "↑↓ nav │ tab/shift-tab idle select │ enter focus │ i preview │ space select │ a all │ n none │ s sort │ q quit" + "↑↓ nav │ tab/shift-tab idle select │ enter focus │ i preview │ space select │ a all │ n none │ s sort │ q quit" + gridHint )}` } else { app.footerText.content = t` ${dim( - "↑↓ nav │ space select │ → expand │ ← collapse │ f folder │ g go to │ i idle │ a all │ n none │ s sort │ enter grid │ o external │ q quit" + "↑↓ nav │ space select │ → expand │ ← collapse │ f folder │ g go to │ i idle │ a all │ n none │ s sort │ enter grid │ o external │ q quit" + gridHint )}` } }