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)
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { useTaskStore } from '../stores/taskStore'
|
|
import { useUIStore } from '../stores/uiStore'
|
|
import { Button } from '../components/shared/Button'
|
|
import { TaskList } from '../components/tasks/TaskList'
|
|
import { TaskEmptyState } from '../components/tasks/TaskEmptyState'
|
|
import { NewTaskModal } from '../components/tasks/NewTaskModal'
|
|
|
|
export function ScheduledTasks() {
|
|
const { tasks, fetchTasks, isLoading } = useTaskStore()
|
|
const { activeModal, openModal, closeModal } = useUIStore()
|
|
const [initialized, setInitialized] = useState(false)
|
|
|
|
useEffect(() => {
|
|
fetchTasks().then(() => setInitialized(true))
|
|
}, [fetchTasks])
|
|
|
|
return (
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="px-10 py-8">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-[var(--color-text-primary)]">Scheduled tasks</h1>
|
|
<p className="mt-1 text-sm text-[var(--color-text-secondary)]">
|
|
Run tasks on a schedule or whenever you need them.
|
|
</p>
|
|
</div>
|
|
<Button onClick={() => openModal('new-task')}>+ New task</Button>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
{!initialized && isLoading ? (
|
|
<div className="flex items-center justify-center py-16">
|
|
<div className="animate-spin w-6 h-6 border-2 border-[var(--color-brand)] border-t-transparent rounded-full" />
|
|
</div>
|
|
) : tasks.length === 0 ? (
|
|
<TaskEmptyState onCreateTask={() => openModal('new-task')} />
|
|
) : (
|
|
<TaskList tasks={tasks} />
|
|
)}
|
|
</div>
|
|
|
|
{/* New Task Modal */}
|
|
<NewTaskModal
|
|
open={activeModal === 'new-task'}
|
|
onClose={closeModal}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|