cc-haha/desktop/src/stores/cliTaskStore.ts
程序员阿江(Relakkes) da3d51b006 fix: suppress sticky TaskBar on page refresh when tasks already dismissed
When all tasks were completed and the user continued chatting, the inline
task summary was shown correctly. But on page refresh, loadHistory and
fetchSessionTasks would re-populate the task store, making the sticky bar
reappear.

- Add completedAndDismissed flag to cliTaskStore
- Detect user messages after last task tool call in history (V1 + V2)
- SessionTaskBar skips render when allCompleted && completedAndDismissed
- refreshTasks/setTasksFromTodos reset the flag for new task activity
- Fix InlineTaskSummary width to match parent container

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 17:27:50 +08:00

99 lines
2.8 KiB
TypeScript

import { create } from 'zustand'
import { cliTasksApi } from '../api/cliTasks'
import type { CLITask } from '../types/cliTask'
type TodoItem = {
content: string
status: string
activeForm?: string
}
type CLITaskStore = {
/** Current session ID being tracked */
sessionId: string | null
/** Tasks for the current session */
tasks: CLITask[]
/** Whether the task bar is expanded */
expanded: boolean
/** True when all tasks completed and the user already continued chatting.
* Set during history load so the sticky bar is suppressed on page refresh. */
completedAndDismissed: boolean
/** Fetch tasks for a given session (uses sessionId as taskListId) */
fetchSessionTasks: (sessionId: string) => Promise<void>
/** Refresh tasks for the currently tracked session */
refreshTasks: () => Promise<void>
/** Update tasks from TodoWrite V1 tool input (in-memory, no disk read needed) */
setTasksFromTodos: (todos: TodoItem[]) => void
/** Mark that completed tasks were already dismissed (conversation continued) */
markCompletedAndDismissed: () => void
/** Clear task tracking state */
clearTasks: () => void
/** Toggle expanded state */
toggleExpanded: () => void
}
export const useCLITaskStore = create<CLITaskStore>((set, get) => ({
sessionId: null,
tasks: [],
expanded: true,
completedAndDismissed: false,
fetchSessionTasks: async (sessionId) => {
set({ sessionId })
try {
const { tasks } = await cliTasksApi.getTasksForList(sessionId)
// Only update if still tracking the same session
if (get().sessionId === sessionId) {
set({ tasks })
}
} catch {
// No tasks for this session — that's fine
if (get().sessionId === sessionId) {
set({ tasks: [] })
}
}
},
refreshTasks: async () => {
const { sessionId } = get()
if (!sessionId) return
try {
const { tasks } = await cliTasksApi.getTasksForList(sessionId)
if (get().sessionId === sessionId) {
set({ tasks, completedAndDismissed: false })
}
} catch {
// ignore
}
},
setTasksFromTodos: (todos) => {
const tasks: CLITask[] = todos.map((todo, index) => ({
id: String(index + 1),
subject: todo.content,
description: '',
activeForm: todo.activeForm,
status: (['pending', 'in_progress', 'completed'].includes(todo.status)
? todo.status
: 'pending') as CLITask['status'],
blocks: [],
blockedBy: [],
taskListId: get().sessionId || '',
}))
set({ tasks, completedAndDismissed: false })
},
markCompletedAndDismissed: () => {
set({ completedAndDismissed: true })
},
clearTasks: () => {
set({ sessionId: null, tasks: [], completedAndDismissed: false })
},
toggleExpanded: () => {
set((s) => ({ expanded: !s.expanded }))
},
}))