Compare commits

...
Author SHA1 Message Date
jlongster 2ac763869b fix(tui): keep preview tabs when starting a new session 2026-09-04 22:16:35 +00:00
3 changed files with 77 additions and 1 deletions
@@ -188,6 +188,7 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
() => config.tabs.scope,
],
([routed]) => {
if (route.data.type === "home") setPreview(undefined)
if (!routed || routed === "dummy") return
const sessionID = root(routed)
const permanent = promotedSession === sessionID
+49 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test"
import { EmbeddedTerminalRenderable } from "@opentui/core"
import { EmbeddedTerminalRenderable, TextAttributes } from "@opentui/core"
import { createTestRenderer } from "@opentui/core/testing"
import { Effect, FileSystem } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
@@ -800,6 +800,54 @@ test("session startup prompt is submitted exactly once", async () => {
}
})
test.each(["/new", "/clear", "shortcut"])("%s makes the temporary session tab permanent", async (command) => {
await using state = await tmpdir()
const session = {
id: "ses_preview",
title: "Keep me",
projectID: "project",
location: { directory },
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
time: { created: 0, updated: 0 },
}
await using setup = await createAppFixture({
state: state.path,
args: { sessionID: session.id },
config: { animations: false, tabs: { enabled: true }, keybinds: { "session.new": "f6" } },
fetch: (url) => {
if (url.pathname === `/api/session/${session.id}`) return json({ data: session })
if (/^\/api\/session\/ses_preview\/(message|inbox|permission)$/.test(url.pathname))
return json({ data: [], cursor: {} })
return undefined
},
})
const title = () =>
setup
.captureSpans()
.lines.flatMap((line) => line.spans)
.find((span) => span.text.includes(session.title))
await setup.waitForFrame((frame) => frame.includes(session.title))
expect(title()!.attributes & TextAttributes.ITALIC).toBe(TextAttributes.ITALIC)
if (command === "shortcut") setup.mockInput.pressKey("F6")
if (command !== "shortcut") {
await setup.mockInput.typeText(command)
await setup.waitForFrame((frame) => frame.includes("New session"))
setup.mockInput.pressEnter()
}
await setup.waitForFrame(
(frame) => frame.includes("New session") && !frame.includes(command) && frame.includes(session.title),
)
expect(title()).toBeDefined()
expect(title()!.attributes & TextAttributes.ITALIC).toBe(0)
await setup.mockInput.typeText("New draft")
await setup.waitForFrame((frame) => frame.includes("New draft"))
setup.mockInput.pressKey("u", { ctrl: true })
await setup.waitForFrame((frame) => !frame.includes("New draft"))
})
test.each([false, true])("uses the resolved launch directory for new prompts (fallback: %s)", async (fallback) => {
await using state = await tmpdir()
const target = fallback ? directory : process.cwd()
@@ -332,6 +332,33 @@ test.each([undefined, true, false])("replaces previews with legacy experiment se
}
})
test.each([
["home", "preview"],
["home", "permanent"],
["add", "preview"],
["add", "permanent"],
])("opening a new session via %s keeps the preview when %s is selected", async (action, selected) => {
const setup = await renderSessionTabs("preview", { persisted: ["permanent"] })
try {
await wait(() => setup.tabs.tabs().length === 2 && setup.tabs.isPreview("preview"))
setup.tabs.select(selected)
if (action === "add") setup.tabs.add()
if (action === "home") setup.route.navigate({ type: "home" })
expect(setup.tabs.newTab()).toBe(true)
expect(setup.tabs.isPreview("preview")).toBe(false)
expect(setup.tabs.tabs().map((tab) => tab.sessionID)).toEqual(["permanent", "preview"])
setup.route.navigate({ type: "session", sessionID: "next-preview" })
await wait(() => setup.tabs.tabs().some((tab) => tab.sessionID === "next-preview"))
expect(setup.tabs.isPreview("next-preview")).toBe(true)
expect(setup.tabs.tabs().map((tab) => tab.sessionID)).toEqual(["permanent", "preview", "next-preview"])
} finally {
await setup.destroy()
}
})
test("server-wide prompt admissions do not promote a local session preview", async () => {
const setup = await renderSessionTabs("preview")