fix(sidebar): preserve host filters across stale UI updates (#25737)

This commit is contained in:
Neil
2026-10-05 18:17:51 -07:00
committed by GitHub
parent 9756daeced
commit 4ccfc166d5
7 changed files with 118 additions and 16 deletions
@@ -156,6 +156,8 @@ export function usePersistedUIWriter(): void {
sortBy: s.sortBy,
projectOrderBy: s.projectOrderBy,
showSleepingWorkspaces: s.showSleepingWorkspaces,
workspaceHostScope: s.workspaceHostScope,
visibleWorkspaceHostIds: s.visibleWorkspaceHostIds,
hideDefaultBranchWorkspace: s.hideDefaultBranchWorkspace,
hideAutomationGeneratedWorkspaces: s.hideAutomationGeneratedWorkspaces,
hideCliCreatedWorkspaces: s.hideCliCreatedWorkspaces,
@@ -270,6 +270,40 @@ describe('workspace view preferences: cross-client persistence (STA-5781)', () =
expect(after.hideCliCreatedWorkspaces).toBe(before.hideCliCreatedWorkspaces)
})
it('saves a revealed remote host after an unrelated older broadcast', async () => {
act(() => {
authority.set({ workspaceHostScope: 'local', visibleWorkspaceHostIds: ['local'] })
})
deliverBroadcasts()
act(() => {
authority.set({ sidebarWidth: 320 })
store.getState().setVisibleWorkspaceHostIds(['local', 'runtime:m4air'])
})
pendingBroadcasts.reverse()
deliverBroadcasts()
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
await flushDesktopDebounce()
deliverBroadcasts()
expect(authority.get().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
expect(authority.get().sidebarWidth).toBe(320)
})
it('saves All hosts after flipping back during a host-selection write', async () => {
holdAcks = true
act(() => store.getState().setWorkspaceHostScope('local'))
await flushDesktopDebounce()
act(() => store.getState().setWorkspaceHostScope('all'))
deliverBroadcasts()
expect(store.getState().visibleWorkspaceHostIds).toBeNull()
expect(store.getState().workspaceHostScope).toBe('all')
await resolveAcks()
holdAcks = false
await flushDesktopDebounce()
deliverBroadcasts()
expect(authority.get().visibleWorkspaceHostIds).toBeNull()
expect(authority.get().workspaceHostScope).toBe('all')
})
it('persists a left sidebar close across an unrelated sync and restores it on startup', async () => {
act(() => {
store.getState().toggleSidebar()
@@ -21,6 +21,8 @@ function makeBaseline(overrides: Partial<PersistedUIWriteBaseline> = {}): Persis
sortBy: 'recent',
projectOrderBy: 'manual',
showSleepingWorkspaces: true,
workspaceHostScope: 'all',
visibleWorkspaceHostIds: null,
hideDefaultBranchWorkspace: false,
hideAutomationGeneratedWorkspaces: false,
hideCliCreatedWorkspaces: false,
@@ -58,11 +60,13 @@ describe('PERSISTED_UI_WRITE_BASELINE_FIELDS', () => {
describe('diffPersistedUIWriteFields', () => {
it('is empty when values are equal even across fresh array/record identities', () => {
const a = makeBaseline({
visibleWorkspaceHostIds: ['local', 'runtime:m4air'],
filterRepoIds: ['r1', 'r2'],
showDotfilesByWorktree: { w1: true },
acknowledgedAgentsByPaneKey: { p1: 5 }
})
const b = makeBaseline({
visibleWorkspaceHostIds: ['local', 'runtime:m4air'],
filterRepoIds: ['r1', 'r2'],
showDotfilesByWorktree: { w1: true },
acknowledgedAgentsByPaneKey: { p1: 5 }
@@ -70,6 +74,15 @@ describe('diffPersistedUIWriteFields', () => {
expect(diffPersistedUIWriteFields(a, b)).toEqual({})
})
it('distinguishes All hosts from a selected host list', () => {
expect(
diffPersistedUIWriteFields(
makeBaseline({ visibleWorkspaceHostIds: null }),
makeBaseline({ visibleWorkspaceHostIds: ['local'] })
)
).toEqual({ visibleWorkspaceHostIds: null })
})
it('reports only the diverged fields, valued from the current mirror', () => {
const baseline = makeBaseline()
const current = makeBaseline({ showSleepingWorkspaces: false, filterRepoIds: ['r1'] })
@@ -21,6 +21,8 @@ export type PersistedUIWriteBaseline = {
sortBy: PersistedUIState['sortBy']
projectOrderBy: PersistedUIState['projectOrderBy']
showSleepingWorkspaces: boolean
workspaceHostScope: PersistedUIState['workspaceHostScope']
visibleWorkspaceHostIds: PersistedUIState['visibleWorkspaceHostIds']
hideDefaultBranchWorkspace: boolean
hideAutomationGeneratedWorkspaces: boolean
hideCliCreatedWorkspaces: boolean
@@ -52,6 +54,8 @@ const PERSISTED_UI_WRITE_BASELINE_FIELD_SET = {
sortBy: true,
projectOrderBy: true,
showSleepingWorkspaces: true,
workspaceHostScope: true,
visibleWorkspaceHostIds: true,
hideDefaultBranchWorkspace: true,
hideAutomationGeneratedWorkspaces: true,
hideCliCreatedWorkspaces: true,
@@ -104,6 +108,9 @@ function writeFieldEqual(field: keyof PersistedUIWriteBaseline, a: unknown, b: u
if (field === 'filterRepoIds') {
return stringArrayEqual(a as readonly string[], b as readonly string[])
}
if (field === 'visibleWorkspaceHostIds') {
return a === b || (Array.isArray(a) && Array.isArray(b) && stringArrayEqual(a, b))
}
if (
field === 'explorerDisplayRootByWorktree' ||
field === 'showDotfilesByWorktree' ||
@@ -0,0 +1,58 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createUIStore, makePersistedUI } from './ui-slice-test-harness'
beforeEach(() => {
vi.stubGlobal('window', { api: { ui: { set: vi.fn().mockResolvedValue(undefined) } } })
})
afterEach(() => vi.unstubAllGlobals())
describe('host visibility across saved UI broadcasts', () => {
it('keeps a newly revealed remote host when an older UI snapshot arrives', () => {
const store = createUIStore()
const previous = makePersistedUI({
workspaceHostScope: 'local',
visibleWorkspaceHostIds: ['local']
})
store.getState().hydratePersistedUI(previous)
store.getState().setVisibleWorkspaceHostIds(['local', 'runtime:m4air'])
store.getState().hydratePersistedUI(previous, 'sync')
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
})
it('keeps All hosts while the prior selection is being saved', () => {
const store = createUIStore()
const previous = makePersistedUI({ workspaceHostScope: 'all', visibleWorkspaceHostIds: null })
store.getState().hydratePersistedUI(previous)
const fields = ['workspaceHostScope', 'visibleWorkspaceHostIds'] as const
store.getState().setWorkspaceHostScope('local')
store.getState().notePersistedUIWriteStarted(fields)
store.getState().setWorkspaceHostScope('all')
store
.getState()
.hydratePersistedUI(
makePersistedUI({ workspaceHostScope: 'local', visibleWorkspaceHostIds: ['local'] }),
'sync'
)
expect(store.getState().workspaceHostScope).toBe('all')
expect(store.getState().visibleWorkspaceHostIds).toBeNull()
})
it('accepts another client’s host selection when there is no local edit', () => {
const store = createUIStore()
store.getState().hydratePersistedUI(makePersistedUI())
store.getState().hydratePersistedUI(
makePersistedUI({
workspaceHostScope: 'runtime:m4air',
visibleWorkspaceHostIds: ['runtime:m4air']
}),
'sync'
)
expect(store.getState().workspaceHostScope).toBe('runtime:m4air')
expect(store.getState().visibleWorkspaceHostIds).toEqual(['runtime:m4air'])
})
})
@@ -480,7 +480,7 @@ describe('createUISlice hydratePersistedUI', () => {
expect(setUI).not.toHaveBeenCalled()
})
it('persists workspace host scope changes', () => {
it('updates workspace host scope for the guarded UI writer', () => {
const setUI = vi.fn(() => Promise.resolve())
vi.stubGlobal('window', { api: { ui: { set: setUI } } })
const store = createUIStore()
@@ -489,13 +489,10 @@ describe('createUISlice hydratePersistedUI', () => {
expect(store.getState().workspaceHostScope).toBe('runtime:env-1')
expect(store.getState().visibleWorkspaceHostIds).toEqual(['runtime:env-1'])
expect(setUI).toHaveBeenCalledWith({
workspaceHostScope: 'runtime:env-1',
visibleWorkspaceHostIds: ['runtime:env-1']
})
expect(setUI).not.toHaveBeenCalled()
})
it('persists visible workspace host changes independently of focused host', () => {
it('updates host visibility independently of focused host for the guarded UI writer', () => {
const setUI = vi.fn(() => Promise.resolve())
vi.stubGlobal('window', { api: { ui: { set: setUI } } })
const store = createUIStore()
@@ -505,10 +502,7 @@ describe('createUISlice hydratePersistedUI', () => {
expect(store.getState().workspaceHostScope).toBe('runtime:env-1')
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:env-1'])
expect(setUI).toHaveBeenLastCalledWith({
workspaceHostScope: 'runtime:env-1',
visibleWorkspaceHostIds: ['local', 'runtime:env-1']
})
expect(setUI).not.toHaveBeenCalled()
})
it('persists workspace host order changes', () => {
@@ -67,9 +67,6 @@ export function createUiPreferenceActions(set: UISliceSet, get: UISliceGet): Par
const normalized = normalizeExecutionHostScope(scope)
const visibleWorkspaceHostIds = normalized === 'all' ? null : [normalized]
set({ workspaceHostScope: normalized, visibleWorkspaceHostIds })
window.api.ui
.set({ workspaceHostScope: normalized, visibleWorkspaceHostIds })
.catch(console.error)
},
visibleWorkspaceHostIds: null,
setVisibleWorkspaceHostIds: (ids) => {
@@ -82,9 +79,6 @@ export function createUiPreferenceActions(set: UISliceSet, get: UISliceGet): Par
workspaceHostScope = normalized[0]
}
set({ visibleWorkspaceHostIds: normalized, workspaceHostScope })
window.api.ui
.set({ visibleWorkspaceHostIds: normalized, workspaceHostScope })
.catch(console.error)
},
workspaceHostOrder: [],
setWorkspaceHostOrder: (ids) => {