From c7c249b4858d083cd772b71ea44a6e01838c4fbb Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sat, 7 Nov 2026 21:35:00 -0800 Subject: [PATCH] wip --- .../slices/ui/ui-slice-activity-actions.ts | 31 +------------------ .../workspace-attention-contract.ts | 2 -- .../workspace-attention-slice.ts | 10 ------ .../workspace-unread-explicit-read.test.ts | 14 ++++++--- 4 files changed, 11 insertions(+), 46 deletions(-) diff --git a/src/renderer/src/store/slices/ui/ui-slice-activity-actions.ts b/src/renderer/src/store/slices/ui/ui-slice-activity-actions.ts index a415bb52a3d..26a943f954c 100644 --- a/src/renderer/src/store/slices/ui/ui-slice-activity-actions.ts +++ b/src/renderer/src/store/slices/ui/ui-slice-activity-actions.ts @@ -1,7 +1,5 @@ import type { StoredAgentAttentionUnread } from '@/attention/agent-attention-contract' import { emitAgentSubjectReads } from '@/attention/agent-subject-read-actions' -import type { AppState } from '@/store/types' -import { paneKeyTabId } from '@/store/slices/workspace-attention/workspace-live-markers' import type { UISlice, UISliceGet, UISliceSet } from './ui-slice-contract' import { collectAcknowledgedAgentNotificationId, @@ -21,34 +19,11 @@ type ActivityActions = Pick< | 'clearManuallyUnreadTurns' > -/** Workspaces holding these panes: terminal tabs, structured chat tabs, or the agent's own record. */ -function workspaceIdsOfPaneKeys(s: AppState, paneKeys: readonly string[]): Set { - const ids = new Set() - for (const paneKey of paneKeys) { - const tabId = paneKeyTabId(paneKey) - for (const worktreeId of [ - resolvePaneKeyWorktreeIdFromTabs(s, paneKey), - s.agentStatusByPaneKey?.[paneKey]?.worktreeId, - s.retainedAgentsByPaneKey?.[paneKey]?.worktreeId, - ...Object.entries(s.unifiedTabsByWorktree ?? {}) - .filter(([, tabs]) => tabs.some((tab) => tab.id === tabId)) - .map(([worktreeId]) => worktreeId) - ]) { - if (worktreeId) { - ids.add(worktreeId) - } - } - } - return ids -} - -export function createUiActivityActions(set: UISliceSet, get: UISliceGet): ActivityActions { +export function createUiActivityActions(set: UISliceSet, _get: UISliceGet): ActivityActions { return { acknowledgedAgentsByPaneKey: {}, acknowledgeAgents: (paneKeys, reads, intent) => { emitAgentSubjectReads(paneKeys, reads, intent) - const readWorkspaceIds = - intent === 'explicit' ? workspaceIdsOfPaneKeys(get(), paneKeys) : null const notificationIdsToDismiss = new Set() set((s) => { if (paneKeys.length === 0) { @@ -116,10 +91,6 @@ export function createUiActivityActions(set: UISliceSet, get: UISliceGet): Activ ...(nextManual ? { manuallyUnreadTurnsByPaneKey: nextManual } : {}) } }) - // Explicit reads skip the on-screen scan that would otherwise lower the workspace flag. - if (readWorkspaceIds && readWorkspaceIds.size > 0) { - get().settleWorkspacesAfterExplicitRead(readWorkspaceIds) - } // Dismiss main’s announced subjects; rebuilt IDs cover restart-empty notification records. if (paneKeys.length > 0 && typeof window !== 'undefined') { void window.api?.notifications?.dismiss?.([...notificationIdsToDismiss], paneKeys) diff --git a/src/renderer/src/store/slices/workspace-attention/workspace-attention-contract.ts b/src/renderer/src/store/slices/workspace-attention/workspace-attention-contract.ts index c124cb945aa..da2f2e03e91 100644 --- a/src/renderer/src/store/slices/workspace-attention/workspace-attention-contract.ts +++ b/src/renderer/src/store/slices/workspace-attention/workspace-attention-contract.ts @@ -43,6 +43,4 @@ export type WorkspaceAttentionSlice = { isUnread: boolean, executionHostId: ExecutionHostId ) => void - /** An explicit read elsewhere (Activity, dashboard, a jump) lowers flags left with nothing unread behind them. */ - settleWorkspacesAfterExplicitRead: (worktreeIds: Iterable) => void } diff --git a/src/renderer/src/store/slices/workspace-attention/workspace-attention-slice.ts b/src/renderer/src/store/slices/workspace-attention/workspace-attention-slice.ts index 2d7de6d0d16..f1d9ff8ad02 100644 --- a/src/renderer/src/store/slices/workspace-attention/workspace-attention-slice.ts +++ b/src/renderer/src/store/slices/workspace-attention/workspace-attention-slice.ts @@ -145,7 +145,6 @@ export const createWorkspaceAttentionSlice: StateCreator< noteWorkspaceRead(worktreeId) const removed = removeAndDismiss(workspaceMarkerTabIds(get(), worktreeId)) // Update only the clicked host’s row; markers stay workspace-wide because tab keys omit host. - // Before the acknowledgement below, so its settle finds the flag already down and skips a second persist. void get().updateWorktreeMeta(worktreeId, { isUnread }, { executionHostId }) // Mark read reads the agents too, or their lines keep the dot the user just cleared. // Journal-positioned chat alerts retire only with this explicit read that bounds them. @@ -156,15 +155,6 @@ export const createWorkspaceAttentionSlice: StateCreator< if (paneKeys.size > 0) { get().acknowledgeAgents([...paneKeys], undefined, 'explicit') } - }, - settleWorkspacesAfterExplicitRead: (worktreeIds) => { - const marked = get().workspacesMarkedUnreadByUser - for (const worktreeId of new Set(worktreeIds)) { - // The user's own mark is a source of its own; reading an agent there doesn't answer it. - if (!marked[worktreeId]) { - settleWorkspaceUnreadFlag(set, get, worktreeId, onScreenHostId(get(), worktreeId)) - } - } } } } diff --git a/src/renderer/src/store/slices/workspace-attention/workspace-unread-explicit-read.test.ts b/src/renderer/src/store/slices/workspace-attention/workspace-unread-explicit-read.test.ts index 558e089908b..2a7c4d43f45 100644 --- a/src/renderer/src/store/slices/workspace-attention/workspace-unread-explicit-read.test.ts +++ b/src/renderer/src/store/slices/workspace-attention/workspace-unread-explicit-read.test.ts @@ -12,6 +12,7 @@ import { } from './workspace-unread-characterization-fixture' import { completeAgentTurn, toggleUnreadByUser } from './workspace-unread-characterization-driver' import { isWorkspaceFlagSet } from './workspace-unread-flag' +import { selectAgentRowsUnread } from '@/components/sidebar/workspace-card/activity/worktree-card-agent-ack-inputs' vi.mock('sonner', () => ({ toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn(), dismiss: vi.fn() } @@ -51,7 +52,7 @@ function finishedAgentOffScreen(store: AttentionStore): PaneTarget { return pane } -describe('explicit reads keep the workspace flag and its agents in step', () => { +describe('explicit workspace and agent reads', () => { it('Mark read also reads the agents, so their lines lose the dot with the flag', () => { const { store } = createAttentionStore() const pane = finishedAgentOffScreen(store) @@ -61,16 +62,21 @@ describe('explicit reads keep the workspace flag and its agents in step', () => toggleUnreadByUser(store, row) expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(false) - expect(store.getState().acknowledgedAgentsByPaneKey[pane.paneKey]).toBeGreaterThanOrEqual(900) + expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([false]) }) - it('reading the only unread agent elsewhere lowers the flag instead of leaving a phantom dot', () => { + it('reading an agent elsewhere preserves a workspace marked unread before restart', () => { + const { store: previousStore } = createAttentionStore() + previousStore.getState().setWorkspaceUnreadByUser(WS_A, true, 'local') const { store } = createAttentionStore() + store.setState({ worktreesByRepo: previousStore.getState().worktreesByRepo }) const pane = finishedAgentOffScreen(store) expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(true) + expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([true]) store.getState().acknowledgeAgents([pane.paneKey], undefined, 'explicit') - expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(false) + expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(true) + expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([false]) }) })