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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
)}`
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user