mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(layout): tab reconcile and terminal reconnect stop writing tab structure (dark, layout-owner build only)
Main's frames draw the tab bar and rows: reconcile still returns its counts but writes nothing, and reconnect still advertises restored terminals but leaves the rows to main.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createTabsSliceMockApi } from '../tabs-slice-test-harness'
|
||||
import { createTestStore, makeTab } from '../store-test-helpers'
|
||||
|
||||
vi.mock('sonner', () => ({ toast: { info: vi.fn(), success: vi.fn(), error: vi.fn() } }))
|
||||
vi.mock('../../../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
|
||||
|
||||
createTabsSliceMockApi()
|
||||
|
||||
const WT = 'repo1::/tmp/feature'
|
||||
|
||||
// Main draws the tab bar from its frames; these shared paths must not write structure of their own.
|
||||
describe('shared tab paths in a layout-owner build', () => {
|
||||
let store: ReturnType<typeof createTestStore>
|
||||
|
||||
beforeEach(() => {
|
||||
store = createTestStore()
|
||||
})
|
||||
|
||||
it('reconcile counts what would render and leaves the tab bar to main', () => {
|
||||
const entry = {
|
||||
id: 'stale-terminal',
|
||||
entityId: 'stale-terminal',
|
||||
groupId: 'g-1',
|
||||
worktreeId: WT,
|
||||
contentType: 'terminal' as const,
|
||||
label: 'Terminal 1',
|
||||
customLabel: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 1
|
||||
}
|
||||
store.setState({
|
||||
unifiedTabsByWorktree: { [WT]: [entry] },
|
||||
groupsByWorktree: {
|
||||
[WT]: [{ id: 'g-1', worktreeId: WT, activeTabId: entry.id, tabOrder: [entry.id] }]
|
||||
},
|
||||
activeGroupIdByWorktree: { [WT]: 'g-1' },
|
||||
tabsByWorktree: { [WT]: [] }
|
||||
})
|
||||
const before = store.getState()
|
||||
|
||||
expect(store.getState().reconcileWorktreeTabModel(WT)).toEqual({
|
||||
renderableTabCount: 0,
|
||||
activeRenderableTabId: null
|
||||
})
|
||||
store.getState().reconcileWorktreeTabModels([WT])
|
||||
|
||||
expect(store.getState().unifiedTabsByWorktree).toBe(before.unifiedTabsByWorktree)
|
||||
expect(store.getState().groupsByWorktree).toBe(before.groupsByWorktree)
|
||||
})
|
||||
|
||||
it('reconnect advertises restored terminals without rewriting their rows', async () => {
|
||||
const rows = { [WT]: [makeTab({ id: 'tab-1', worktreeId: WT, ptyId: null })] }
|
||||
store.setState({
|
||||
tabsByWorktree: rows,
|
||||
terminalLayoutsByTabId: {},
|
||||
pendingReconnectWorktreeIds: [WT],
|
||||
pendingReconnectTabByWorktree: { [WT]: ['tab-1'] },
|
||||
pendingReconnectPtyIdByTabId: { 'tab-1': 'pty-1' }
|
||||
})
|
||||
|
||||
await store.getState().reconnectPersistedTerminals()
|
||||
|
||||
expect(store.getState().ptyIdsByTabId['tab-1']).toEqual(['pty-1'])
|
||||
expect(store.getState().tabsByWorktree).toBe(rows)
|
||||
expect(store.getState().workspaceSessionReady).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,7 @@ import { buildHydratedTabState } from '../tabs-hydration'
|
||||
import { projectWorktreeTabModelReconciliation } from './tabs-reconciliation'
|
||||
import { createWorktreeTabModelReconciliationBatch } from './tabs-reconciliation-batch'
|
||||
import type { AppState } from '../../types'
|
||||
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../../../../shared/layout-owner-switch'
|
||||
|
||||
function replaceWorkspaceRecordKeys<T>(
|
||||
current: Record<string, T>,
|
||||
@@ -59,6 +60,10 @@ export function createTabsSessionActions(
|
||||
if (worktreeIds.length === 0) {
|
||||
return
|
||||
}
|
||||
// Switch point: main's frames draw the tab bar, so reconcile writes no structure of its own.
|
||||
if (ORCA_LAYOUT_OWNER_IS_MAIN) {
|
||||
return
|
||||
}
|
||||
const patch = projectWorktreeTabModelReconciliations(get(), worktreeIds)
|
||||
if (Object.keys(patch).length > 0) {
|
||||
set(patch)
|
||||
@@ -67,7 +72,8 @@ export function createTabsSessionActions(
|
||||
|
||||
reconcileWorktreeTabModel: (worktreeId) => {
|
||||
const reconciliation = projectWorktreeTabModelReconciliation(get(), worktreeId)
|
||||
if (Object.keys(reconciliation.patch).length > 0) {
|
||||
// Switch point: the counts only; main's frames draw the tab bar.
|
||||
if (!ORCA_LAYOUT_OWNER_IS_MAIN && Object.keys(reconciliation.patch).length > 0) {
|
||||
set(reconciliation.patch)
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { TerminalTab } from '../../../../shared/terminal-tab-types'
|
||||
import { buildByIdIndex, buildWorktreeByIdIndex } from '../slices/worktree-by-id-index'
|
||||
import { getRepoIdFromWorktreeId } from '../../../../shared/worktree/id'
|
||||
import type { TerminalSlice, TerminalStoreGet, TerminalStoreSet } from './terminal-state'
|
||||
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../../../shared/layout-owner-switch'
|
||||
|
||||
export function createWorkspaceTerminalReconnectActions(
|
||||
set: TerminalStoreSet,
|
||||
@@ -117,7 +118,10 @@ export function createWorkspaceTerminalReconnectActions(
|
||||
return
|
||||
}
|
||||
set({
|
||||
...(reconnectedTabsByWorktree ? { tabsByWorktree: reconnectedTabsByWorktree } : {}),
|
||||
// Switch point: main's frames draw the rows, with main's own bindings.
|
||||
...(reconnectedTabsByWorktree && !ORCA_LAYOUT_OWNER_IS_MAIN
|
||||
? { tabsByWorktree: reconnectedTabsByWorktree }
|
||||
: {}),
|
||||
...(reconnectedPtyIdsByTabId ? { ptyIdsByTabId: reconnectedPtyIdsByTabId } : {}),
|
||||
workspaceSessionReady: true,
|
||||
pendingReconnectWorktreeIds: [],
|
||||
|
||||
Reference in New Issue
Block a user