mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-31 15:43:34 +00:00
fix(tui): preserve tab context on home and close (#39421)
This commit is contained in:
parent
fe91698ed6
commit
1c8175a61a
3 changed files with 56 additions and 9 deletions
|
|
@ -75,15 +75,30 @@ export function adaptiveSessionTabLayout(
|
|||
) {
|
||||
if (tabs.length === 0) return { tabs: [], widths: [], before: 0, after: 0, start: 0, total: 0 }
|
||||
|
||||
const activeIndex = Math.max(
|
||||
0,
|
||||
tabs.findIndex((tab) => tab.sessionID === active),
|
||||
)
|
||||
const activeIndex = tabs.findIndex((tab) => tab.sessionID === active)
|
||||
const fit = (width: number) =>
|
||||
Math.min(tabs.length, Math.max(1, 1 + Math.floor((Math.max(0, width) - SESSION_TAB_WIDTH) / SESSION_TAB_MIN_WIDTH)))
|
||||
Math.min(
|
||||
tabs.length,
|
||||
Math.max(
|
||||
1,
|
||||
activeIndex === -1
|
||||
? Math.floor(Math.max(0, width) / SESSION_TAB_MIN_WIDTH)
|
||||
: 1 + Math.floor((Math.max(0, width) - SESSION_TAB_WIDTH) / SESSION_TAB_MIN_WIDTH),
|
||||
),
|
||||
)
|
||||
const solve = (count: number, start: number, attempts: number): { count: number; start: number } => {
|
||||
const boundedStart = Math.min(Math.max(0, start), tabs.length - count)
|
||||
const nextStart = Math.min(
|
||||
Math.max(0, activeIndex < start ? activeIndex : activeIndex >= start + count ? activeIndex - count + 1 : start),
|
||||
Math.max(
|
||||
0,
|
||||
activeIndex === -1
|
||||
? boundedStart
|
||||
: activeIndex < boundedStart
|
||||
? activeIndex
|
||||
: activeIndex >= boundedStart + count
|
||||
? activeIndex - count + 1
|
||||
: boundedStart,
|
||||
),
|
||||
tabs.length - count,
|
||||
)
|
||||
const markers =
|
||||
|
|
@ -103,7 +118,7 @@ export function adaptiveSessionTabLayout(
|
|||
)
|
||||
const roomy = contentWidth >= SESSION_TAB_WIDTH * visible.length
|
||||
const total = roomy ? Math.min(contentWidth, SESSION_TAB_MAX_WIDTH * visible.length) : contentWidth
|
||||
if (roomy) {
|
||||
if (roomy || activeIndex === -1) {
|
||||
const width = Math.floor(total / visible.length)
|
||||
const remainder = total - width * visible.length
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -202,11 +202,16 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
|
|||
const target = root(sessionID)
|
||||
const closed = closeSessionTab(store.tabs, target)
|
||||
if (closed.tabs.length === store.tabs.length) return
|
||||
const selected = navigate && current() === target
|
||||
const previous = selected
|
||||
? moveSessionTabHistory(recordSessionTabHistory(history, target), closed.tabs, target, -1)
|
||||
: { history, sessionID: undefined }
|
||||
const next = previous.sessionID ?? closed.next
|
||||
history = previous.history
|
||||
batch(() => {
|
||||
setStore("tabs", reconcile(closed.tabs))
|
||||
clearUnread(target)
|
||||
if (navigate && current() === target)
|
||||
route.navigate(closed.next ? { type: "session", sessionID: closed.next } : { type: "home" })
|
||||
if (selected) route.navigate(next ? { type: "session", sessionID: next } : { type: "home" })
|
||||
})
|
||||
save()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,15 @@ describe("session tabs", () => {
|
|||
expect(moveSessionTabHistory(history, tabs, undefined, -1).sessionID).toBe("b")
|
||||
})
|
||||
|
||||
test("returns to the previous selected open tab after closing the active tab", () => {
|
||||
const tabs = ["a", "b", "c"].map((sessionID) => ({ sessionID }))
|
||||
const history = ["a", "c"].reduce(recordSessionTabHistory, { entries: [], index: -1 })
|
||||
const closed = closeSessionTab(tabs, "b")
|
||||
const current = recordSessionTabHistory(history, "b")
|
||||
|
||||
expect(moveSessionTabHistory(current, closed.tabs, "b", -1).sessionID).toBe("c")
|
||||
})
|
||||
|
||||
test("reveals completion activity only after session work becomes idle", () => {
|
||||
expect(sessionTabComplete("activity", true)).toBe(false)
|
||||
expect(sessionTabComplete("activity", false)).toBe(true)
|
||||
|
|
@ -90,6 +99,24 @@ describe("session tabs", () => {
|
|||
expect(layout.widths.reduce((total, width) => total + width, 0)).toBe(76)
|
||||
})
|
||||
|
||||
test("does not reserve an active tab slot on the new session page", () => {
|
||||
const tabs = ["a", "b", "c", "d", "e"].map((sessionID) => ({ sessionID }))
|
||||
const layout = adaptiveSessionTabLayout(tabs, "dummy", 40)
|
||||
|
||||
expect(layout.tabs).toEqual(tabs)
|
||||
expect(layout.widths).toEqual([8, 8, 8, 8, 8])
|
||||
expect(layout.widths.reduce((total, width) => total + width, 0)).toBe(layout.total)
|
||||
})
|
||||
|
||||
test("keeps the visible tab window stable on the new session page", () => {
|
||||
const tabs = Array.from({ length: 10 }, (_, index) => ({ sessionID: String(index) }))
|
||||
const selected = adaptiveSessionTabLayout(tabs, "7", 70)
|
||||
const home = adaptiveSessionTabLayout(tabs, undefined, 70, selected.start)
|
||||
|
||||
expect(selected.start).toBeGreaterThan(0)
|
||||
expect(home.start).toBe(selected.start)
|
||||
})
|
||||
|
||||
test("only swaps old and new active width inside a sticky window", () => {
|
||||
const tabs = ["a", "b", "c", "d", "e", "f", "g"].map((sessionID) => ({ sessionID }))
|
||||
const before = adaptiveSessionTabLayout(tabs, "c", 76)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue