mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
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)
64 lines
1.6 KiB
TypeScript
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
|
|
},
|
|
}))
|