mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
* Record chat tab opens, closes and host-listed chats in main (dark, layout-owner build only) A chat tab the window opens, closes, or adds and removes from a host listing of this machine's chats now reaches main as openAgentSessionTab, closeTabs or setTabProps under the window's ids, so the window's save no longer teaches main a chat tab. openAgentSessionTab takes the window's place, creation time and new split group, and the same open again changes nothing. Switch-off builds are unchanged. * test(layout): a paired server's chat tabs send nothing to main (dark) * fix(layout): keep a windowless host's chat-tab saves as they were; send synced chat opens in tab order (dark) Only a window's openAgentSessionTab (main marks it drawn on layout.apply) takes the new rules: a repeat changes nothing, a taken id is refused, and a workspace with no group takes the window's. A windowless host's own saves mint a fresh group and keep a chat whose id another tab holds, as before. A host listing's new chats are recorded in tab order, so each open's anchor already reached main. * refactor(layout): one rule set for a chat tab open; the windowless caller names only a group it holds (dark) Drops the per-sender drawn flag. The windowless save passes its remembered active group only when the workspace still holds it, so it mints a fresh group as before. Refusing a taken chat tab id is gated on the build switch: shipped builds still save the duplicate id.
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types'
|
||
import { withChatTabChanges, type ChatTabChange } from './agent-session-tab-layout'
|
||
|
||
const WT = 'repo-1::/tmp/worktree-a'
|
||
|
||
const EMPTY: WorkspaceSessionState = {
|
||
activeRepoId: 'repo-1',
|
||
activeWorktreeId: WT,
|
||
activeTabId: null,
|
||
tabsByWorktree: {},
|
||
terminalLayoutsByTabId: {}
|
||
}
|
||
|
||
function open(sessionId: string, tabId = `structured-agent-session-${sessionId}`): ChatTabChange {
|
||
return { open: { sessionId, agent: 'codex', tabId } }
|
||
}
|
||
|
||
function change(session: WorkspaceSessionState, changes: ChatTabChange[]): WorkspaceSessionState {
|
||
const next = withChatTabChanges('local', session, WT, changes)
|
||
if (!next) {
|
||
throw new Error('nothing changed')
|
||
}
|
||
return next
|
||
}
|
||
|
||
describe('a windowless host’s chat tab saves', () => {
|
||
it('open a chat in a freshly minted group once the last one closed', () => {
|
||
const first = change(EMPTY, [open('chat-a')])
|
||
const firstGroup = first.tabGroups?.[WT]?.[0]?.id
|
||
const closed = change(first, [{ close: 'chat-a' }])
|
||
// The workspace keeps no group, but its saved view still names the closed chat's group.
|
||
const remembering = {
|
||
...closed,
|
||
activeGroupIdByWorktree: { ...closed.activeGroupIdByWorktree, [WT]: firstGroup! }
|
||
}
|
||
expect(remembering.tabGroups?.[WT] ?? []).toEqual([])
|
||
const reopened = change(remembering, [open('chat-b')])
|
||
const groups = reopened.tabGroups?.[WT] ?? []
|
||
expect(firstGroup).toBeTruthy()
|
||
expect(groups).toHaveLength(1)
|
||
expect(groups[0]?.id).not.toBe(firstGroup)
|
||
})
|
||
|
||
// Preserved legacy behaviour: with the build switch off a duplicate chat tab id is still saved.
|
||
it('save a chat whose tab id another tab already holds, as they always have', () => {
|
||
const saved = change(EMPTY, [open('chat-a', 'structured-agent-session-shared')])
|
||
const both = change(saved, [open('chat-b', 'structured-agent-session-shared')])
|
||
expect(
|
||
(both.unifiedTabs?.[WT] ?? [])
|
||
.filter((tab) => tab.contentType === 'agent-session')
|
||
.map((tab) => tab.entityId)
|
||
.sort()
|
||
).toEqual(['chat-a', 'chat-b'])
|
||
})
|
||
})
|