Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
35e51b5
feat(tui): add persistent terminal workspaces
jlongster Aug 25, 2026
2d32f64
feat(tui): move terminal selection into composer tabs
jlongster Aug 25, 2026
e86a458
fix(tui): close composer when terminal gains focus
jlongster Aug 25, 2026
e27c531
refactor(tui): remove terminal pane title overlay
jlongster Aug 25, 2026
50c3ead
fix(tui): reset composer to subagents for prompt picker
jlongster Aug 25, 2026
5879f91
fix(tui): highlight only the visible terminal
jlongster Aug 25, 2026
4848f9e
feat(tui): fade the first two terminal rows
jlongster Aug 25, 2026
232c272
refactor(tui): remove terminal row fade overlays
jlongster Aug 26, 2026
bc15a09
feat(tui): fade terminal rows into default background
jlongster Aug 26, 2026
b8a32ec
refactor(tui): remove terminal row fade
jlongster Aug 26, 2026
e8b01e5
feat(tui): make persistent terminals opt-in
jlongster Aug 26, 2026
6fa9c62
fix(tui): independently toggle sidebar and terminal panes
jlongster Aug 26, 2026
4295baf
fix(tui): animate right pane closed
jlongster Aug 26, 2026
fe5f1a7
fix(tui): report terminal loading failures clearly
jlongster Aug 26, 2026
260de36
fix(tui): preserve preview tabs when restoring selected sessions
jlongster Aug 26, 2026
da1628c
refactor(tui): remove pane width animation
jlongster Aug 26, 2026
b3d684f
refactor(tui): simplify terminal settings
jlongster Aug 26, 2026
1aa18f4
refactor(tui): simplify right pane selection
jlongster Aug 26, 2026
a631349
refactor(tui): remove session tab restoration changes
jlongster Aug 26, 2026
fec340f
chore: update persistent PTY daemon to 0.1.9
jlongster Aug 26, 2026
7d6c792
fix(tui): avoid highlighting new terminal without an active pane
jlongster Aug 26, 2026
bf5fec4
refactor(tui): keep sidebar sizing unchanged
jlongster Aug 26, 2026
833d7fe
refactor(tui): simplify terminal pane state and composition
jlongster Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(tui): remove session tab restoration changes
  • Loading branch information
jlongster committed Aug 26, 2026
commit a6313493956b0a10f03d64c523958e77af00b35c
5 changes: 0 additions & 5 deletions packages/tui/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -646,11 +646,6 @@ function App(props: { pair?: DialogPairCredentials }) {
sessionID: args.sessionID,
prompt: startupPrompt,
})
} else if (!args.sessionID && !args.continue && !args.fork && !startup.initialRoute && sessionTabs.enabled()) {
const sessionID = sessionTabs.selected()
if (sessionID && sessionTabs.tabs().some((tab) => tab.sessionID === sessionID)) {
route.navigate({ type: "session", sessionID })
}
}
})
})
Expand Down
36 changes: 6 additions & 30 deletions packages/tui/src/context/session-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {

type TabsState = {
tabs: SessionTab[]
selectedSessionID?: string
// Kept empty for rollback compatibility with clients that still read this field.
unread: Record<string, unknown>
}
Expand Down Expand Up @@ -142,18 +141,13 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
const members = data.session.family(session)
return members.length > 0 ? members : [session]
}
const normalize = (value: TabsState) => {
const tabs = value.tabs.reduce<SessionTab[]>((tabs, tab) => {
const normalize = (value: TabsState) => ({
tabs: value.tabs.reduce<SessionTab[]>((tabs, tab) => {
const sessionID = root(tab.sessionID)
return openSessionTab(tabs, { sessionID, title: title(sessionID, tab.title) })
}, [])
const selected = value.selectedSessionID && root(value.selectedSessionID)
return {
tabs,
...(selected && tabs.some((tab) => tab.sessionID === selected) ? { selectedSessionID: selected } : {}),
unread: {},
}
}
}, []),
unread: {},
})
const current = () => (route.data.type === "session" ? root(route.data.sessionID) : undefined)
const newTab = createMemo((open = false) => {
if (route.data.type === "home") return true
Expand Down Expand Up @@ -204,14 +198,7 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
promotedSession = undefined
cancelledTabs.delete(sessionID)
history = recordSessionTabHistory(history, sessionID)
if (state().tabs.some((tab) => tab.sessionID === sessionID)) {
if (state().selectedSessionID !== sessionID) {
update((draft) => {
if (!cancelledTabs.has(sessionID)) draft.selectedSessionID = sessionID
})
}
return
}
if (state().tabs.some((tab) => tab.sessionID === sessionID)) return
const fallback = newTab() ? NEW_SESSION_TAB_TITLE : undefined
const temporary = previews() && !permanent
const replaced = temporary ? previewID() : undefined
Expand All @@ -227,12 +214,10 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
const index = draft.tabs.findIndex((item) => item.sessionID === replaced)
if (index !== -1) {
draft.tabs[index] = tab
draft.selectedSessionID = sessionID
return
}
}
draft.tabs = openSessionTab(draft.tabs, tab)
draft.selectedSessionID = sessionID
})
},
),
Expand Down Expand Up @@ -282,8 +267,6 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
const next = normalize(draft)
draft.tabs = next.tabs
draft.unread = next.unread
if (next.selectedSessionID) draft.selectedSessionID = next.selectedSessionID
else delete draft.selectedSessionID
})
})

Expand Down Expand Up @@ -375,10 +358,6 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
history = previous.history
update((draft) => {
draft.tabs = closeSessionTab(draft.tabs, target).tabs
if (draft.selectedSessionID === target) {
if (next) draft.selectedSessionID = next
else delete draft.selectedSessionID
}
})
setPromptPulses((pulses) => {
if (pulses[target] === undefined) return pulses
Expand All @@ -401,9 +380,6 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
return newTab()
},
current,
selected() {
return state().selectedSessionID
},
status,
scrollAnchor(sessionID: string) {
const target = root(sessionID)
Expand Down
20 changes: 0 additions & 20 deletions packages/tui/test/context/session-tabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,32 +559,12 @@ test("stores session tabs for the current working directory by default", async (
expect(stored.global).toEqual({ tabs: [], unread: {} })
expect(Object.keys(stored.cwd)).toEqual([directory])
expect(stored.cwd[directory].tabs.map((tab: { sessionID: string }) => tab.sessionID)).toEqual(["first"])
expect(stored.cwd[directory].selectedSessionID).toBe("first")
expect(stored.cwd[directory].unread).toEqual({})
} finally {
await setup.destroy()
}
})

test("persists selection when returning to an already open session tab", async () => {
const setup = await renderSessionTabs("first", { persisted: ["first", "second"] })

try {
await wait(() => setup.tabs.selected() === "first")
setup.tabs.select("second")
await wait(() => setup.tabs.selected() === "second")
setup.tabs.select("first")
await wait(() => setup.tabs.selected() === "first")
await setup.flush()

const stored = await Bun.file(path.join(setup.state, "test", "tui", "tabs.json")).json()
expect(stored.cwd[directory].selectedSessionID).toBe("first")
expect(stored.cwd[directory].tabs.map((tab: { sessionID: string }) => tab.sessionID)).toEqual(["first", "second"])
} finally {
await setup.destroy()
}
})

test("keeps scroll anchors for open session tabs", async () => {
const setup = await renderSessionTabs("first")

Expand Down
Loading