diff --git a/src/components/FocusModeTaskIndicators.tsx b/src/components/FocusModeTaskIndicators.tsx index 54c51fd5..f2673206 100644 --- a/src/components/FocusModeTaskIndicators.tsx +++ b/src/components/FocusModeTaskIndicators.tsx @@ -1,5 +1,10 @@ import { For, Show } from 'solid-js'; -import { getTaskAttentionState, getTaskDotStatus, setActiveTask, store } from '../store/store'; +import { + getTaskAttentionState, + getTaskDotStatus, + activateTaskFromPointer, + store, +} from '../store/store'; import { openPanelOrder } from '../store/navigation'; import { documentAgentTaskId } from '../documents/task-id'; import { StatusDot } from './StatusDot'; @@ -32,7 +37,7 @@ export function FocusModeTaskIndicators() { type="button" class={`focus-mode-task-indicator${isActive() ? ' active' : ''}`} onMouseDown={(event) => event.stopPropagation()} - onClick={() => setActiveTask(item.id)} + onClick={() => activateTaskFromPointer(item.id)} title={isActive() ? `${item.name} (current)` : `Switch to ${item.name}`} aria-label={isActive() ? `${item.name}, current item` : `Switch to ${item.name}`} aria-current={isActive() ? 'true' : undefined} diff --git a/src/components/SubTaskStrip.tsx b/src/components/SubTaskStrip.tsx index fbf44c16..0f72dffc 100644 --- a/src/components/SubTaskStrip.tsx +++ b/src/components/SubTaskStrip.tsx @@ -1,5 +1,5 @@ import { For, Show, createMemo, createSignal, createUniqueId, onMount } from 'solid-js'; -import { store, setActiveTask, getTaskDotStatus, uncollapseTask } from '../store/store'; +import { store, activateTaskFromPointer, getTaskDotStatus, uncollapseTask } from '../store/store'; import { getCoordinatorChildren } from '../store/sidebar-order'; import { invoke } from '../lib/ipc'; import { IPC } from '../../electron/ipc/channels'; @@ -189,7 +189,7 @@ export function SubTaskStrip(props: SubTaskStripProps) { if (task.collapsed) { uncollapseTask(task.id); } - setActiveTask(task.id); + activateTaskFromPointer(task.id); }} title={taskTone(task) ? `${task.name} — ${taskTone(task)?.label}` : task.name} style={{ diff --git a/src/components/TaskPanel.reasoning.client.test.tsx b/src/components/TaskPanel.reasoning.client.test.tsx index 81ac5b2e..71ca0565 100644 --- a/src/components/TaskPanel.reasoning.client.test.tsx +++ b/src/components/TaskPanel.reasoning.client.test.tsx @@ -44,6 +44,7 @@ vi.mock('../store/store', () => { setTaskFocusedPanel: vi.fn(), triggerFocus: vi.fn(), setActiveTask: (id: string) => setStore('activeTaskId', id), + activateTaskFromPointer: (id: string) => setStore('activeTaskId', id), toggleFocusMode: (on?: boolean) => setStore('focusMode', on ?? !store.focusMode), }; }); diff --git a/src/components/TaskPanel.tsx b/src/components/TaskPanel.tsx index 0d22193b..171753ed 100644 --- a/src/components/TaskPanel.tsx +++ b/src/components/TaskPanel.tsx @@ -3,7 +3,7 @@ import { Show, createSignal, createEffect, createMemo, onMount, onCleanup, batch import { store, retryCloseTask, - setActiveTask, + activateTaskFromPointer, setActiveAgent, clearInitialPrompt, clearPrefillPrompt, @@ -676,7 +676,7 @@ export function TaskPanel(props: TaskPanelProps) { position: 'relative', }} onClick={() => { - setActiveTask(props.task.id); + activateTaskFromPointer(props.task.id); }} > store.taskOrder, onReorder: reorderTask, - onTap: () => setActiveTask(props.task.id), + onTap: () => activateTaskFromPointer(props.task.id), }); } diff --git a/src/components/TerminalPanel.tsx b/src/components/TerminalPanel.tsx index 62fd737e..0df77018 100644 --- a/src/components/TerminalPanel.tsx +++ b/src/components/TerminalPanel.tsx @@ -3,7 +3,7 @@ import { store, closeTerminal, updateTerminalName, - setActiveTask, + activateTaskFromPointer, reorderTask, registerFocusFn, unregisterFocusFn, @@ -51,7 +51,7 @@ export function TerminalPanel(props: TerminalPanelProps) { itemId: props.terminal.id, getTaskOrder: () => store.taskOrder, onReorder: reorderTask, - onTap: () => setActiveTask(props.terminal.id), + onTap: () => activateTaskFromPointer(props.terminal.id), }); } @@ -69,7 +69,7 @@ export function TerminalPanel(props: TerminalPanelProps) { overflow: 'clip', position: 'relative', }} - onClick={() => setActiveTask(props.terminal.id)} + onClick={() => activateTaskFromPointer(props.terminal.id)} > {/* Title bar */}
({ invoke: vi.fn() })); +vi.mock('./persistence', () => ({ saveState: vi.fn() })); + +const ids = ['task-a', 'task-b']; +const panel = (id: string) => `ai-terminal:${id}-agent`; +let dispose: (() => void) | undefined; +let sidebarEl!: HTMLDivElement; +const terminals: Record = {}; + +function mount(activate: (id: string) => void) { + const root = document.createElement('div'); + document.body.appendChild(root); + dispose = render(() => { + createEffect(() => { + if (store.sidebarFocused) sidebarEl.focus(); + }); + registerFocusFn('sidebar', () => sidebarEl.focus()); + return ( +
+
+ + {(id) => { + createEffect(() => { + if (store.activeTaskId !== id) return; + const p = store.focusedPanel[id]; + if (p) scheduleTaskFocus(id, p); + }); + registerFocusFn(`${id}:${panel(id)}`, () => terminals[id].focus()); + return ( +
activate(id)}> +
+ handleDragReorder(e, { + itemId: id, + getTaskOrder: () => store.taskOrder, + onReorder: () => {}, + onTap: () => activate(id), + }) + } + > + title +
+