cc-haha/desktop/src/stores/taskStore.ts
程序员阿江(Relakkes) 993b96cd39 Stabilize the desktop transcript so long agent sessions stay readable
The desktop app now keeps the composer stable while turns are active,
reduces low-signal tool noise in the transcript, restores project context
under the composer after session creation, and relies on the CLI's own
permission requests instead of injecting broader desktop-side Bash asks.

This also brings in the supporting desktop app source tree and the server
routes/session metadata needed for git info, filesystem browsing, session
resume, slash commands, and SDK-backed permission bridging so the UI can
operate as a coherent feature instead of a partial patch.

Constraint: Desktop transcript needs to stay usable during long multi-tool sessions without hiding file-change diffs
Constraint: Permission prompts must mirror CLI behavior closely enough that read-only commands do not get desktop-only prompts
Rejected: Keep rendering Read/Bash bodies inline | too noisy and unlike the intended transcript model
Rejected: Commit only the touched desktop files | would leave the newly introduced desktop app incomplete in git history
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: Treat non-writing tools as summary-first transcript events; do not re-expand them by default without validating the UX against long sessions
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run
Tested: bun test src/server/__tests__/conversations.test.ts
Not-tested: Manual visual regression against the exact screenshots in a live desktop session
Not-tested: Full root TypeScript check (repository still has unrelated extracted-native parse failures)
2026-04-06 20:37:44 +08:00

64 lines
1.6 KiB
TypeScript

import { create } from 'zustand'
import { tasksApi } from '../api/tasks'
import type { CronTask, CreateTaskInput, TaskRun } from '../types/task'
type TaskStore = {
tasks: CronTask[]
recentRuns: TaskRun[]
isLoading: boolean
error: string | null
fetchTasks: () => Promise<void>
createTask: (input: CreateTaskInput) => Promise<void>
updateTask: (id: string, updates: Partial<CronTask>) => Promise<void>
deleteTask: (id: string) => Promise<void>
fetchRecentRuns: () => Promise<void>
fetchTaskRuns: (taskId: string) => Promise<TaskRun[]>
}
export const useTaskStore = create<TaskStore>((set) => ({
tasks: [],
recentRuns: [],
isLoading: false,
error: null,
fetchTasks: async () => {
set({ isLoading: true, error: null })
try {
const { tasks } = await tasksApi.list()
set({ tasks, isLoading: false })
} catch (err) {
set({ error: (err as Error).message, isLoading: false })
}
},
createTask: async (input) => {
const { task } = await tasksApi.create(input)
set((s) => ({ tasks: [...s.tasks, task] }))
},
updateTask: async (id, updates) => {
const { task } = await tasksApi.update(id, updates)
set((s) => ({ tasks: s.tasks.map((t) => (t.id === id ? task : t)) }))
},
deleteTask: async (id) => {
await tasksApi.delete(id)
set((s) => ({ tasks: s.tasks.filter((t) => t.id !== id) }))
},
fetchRecentRuns: async () => {
try {
const { runs } = await tasksApi.getRecentRuns()
set({ recentRuns: runs })
} catch {
// ignore
}
},
fetchTaskRuns: async (taskId) => {
const { runs } = await tasksApi.getTaskRuns(taskId)
return runs
},
}))