diff --git a/cc-haha/desktop/src/components/layout/ContentRouter.tsx b/cc-haha/desktop/src/components/layout/ContentRouter.tsx index c79176c..be463ed 100644 --- a/cc-haha/desktop/src/components/layout/ContentRouter.tsx +++ b/cc-haha/desktop/src/components/layout/ContentRouter.tsx @@ -5,6 +5,7 @@ import { ActiveSession } from '../../pages/ActiveSession' import { ScheduledTasks } from '../../pages/ScheduledTasks' import { Settings } from '../../pages/Settings' import { TerminalSettings } from '../../pages/TerminalSettings' +import { HeicodeTasksHome } from '../../pages/HeicodeTasksHome' export function ContentRouter() { const activeTabId = useTabStore((s) => s.activeTabId) @@ -19,6 +20,8 @@ export function ContentRouter() { page = } else if (activeTabType === 'scheduled') { page = + } else if (activeTabType === 'heicode_tasks') { + page = } else if (activeTabType !== 'terminal') { page = } diff --git a/cc-haha/desktop/src/components/layout/Sidebar.tsx b/cc-haha/desktop/src/components/layout/Sidebar.tsx index 83ee65f..1236149 100644 --- a/cc-haha/desktop/src/components/layout/Sidebar.tsx +++ b/cc-haha/desktop/src/components/layout/Sidebar.tsx @@ -5,7 +5,12 @@ import { useTranslation } from '../../i18n' import { ProjectFilter } from './ProjectFilter' import { ConfirmDialog } from '../shared/ConfirmDialog' import type { SessionListItem } from '../../types/session' -import { useTabStore, SETTINGS_TAB_ID, SCHEDULED_TAB_ID } from '../../stores/tabStore' +import { + useTabStore, + SETTINGS_TAB_ID, + SCHEDULED_TAB_ID, + HEICODE_TASKS_TAB_ID, +} from '../../stores/tabStore' import { useChatStore } from '../../stores/chatStore' import { useHeicodeAuthStore } from '../../stores/heicodeAuthStore' @@ -186,6 +191,19 @@ export function Sidebar() { > {t('sidebar.newSession')} + + useTabStore + .getState() + .openTab(HEICODE_TASKS_TAB_ID, t('sidebar.heicodeTasks'), 'heicode_tasks') + } + icon={target} + > + {t('sidebar.heicodeTasks')} + = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': '新建会话', + 'sidebar.heicodeTasks': '我的任务', 'sidebar.scheduled': '定时任务', 'sidebar.terminal': '终端', 'sidebar.settings': '设置', @@ -991,6 +992,36 @@ export const zh: Record = { 'approval.action.postpone': '稍后提醒', 'approval.action.approve': '批准 {minutes} 分钟', 'approval.queue.more': '另有 {count} 个高危请求等待处理', + + // ─── 任务驾驶舱(wireframe §2 / §3)──────────────────── + 'tasks.home.currentLabel': '当前任务', + 'tasks.home.currentNone': '未选择', + 'tasks.home.intentPrompt': '你想把什么想法变成可以上线的软件?', + 'tasks.home.intentPlaceholder': '做一个小团队任务管理 SaaS,需要登录、任务、评论、通知,部署到 Azure。', + 'tasks.home.intentHint': '⌘/Ctrl + Enter 发送', + 'tasks.home.send': '发送', + 'tasks.home.recentTitle': '最近任务', + 'tasks.home.recentEmpty': '还没有任务。从上面的输入框开始第一个吧。', + 'tasks.home.managerHint': '辅助:打开 Manager 绑定资源、部署 Agnet、查看状态。', + + 'tasks.workspace.back': '返回', + 'tasks.workspace.replyPlaceholder': '继续补充你的要求…', + + 'tasks.card.title': '任务卡', + 'tasks.card.goal': '目标', + 'tasks.card.scope': '第一版范围', + 'tasks.card.generated': 'Heicode 将自动生成', + 'tasks.card.managerActions': '需要 Manager 辅助', + 'tasks.card.editGoal': '修改目标', + 'tasks.card.openManager': '去 Manager 准备', + + 'tasks.status.draft': '草案', + 'tasks.status.configuring': '准备中', + 'tasks.status.running': '运行中', + 'tasks.status.awaiting_approval': '待审批', + 'tasks.status.completed': '已完成', + 'tasks.status.failed': '失败', + 'tasks.status.paused': '已暂停', 'login.footnote': 'Heicode 直接连 TaijiAICloud,API Key 仅保存在你这台机器上。', 'login.tags.recommended': '推荐', 'login.tags.comingSoon': '即将开放', diff --git a/cc-haha/desktop/src/pages/HeicodeTasksHome.tsx b/cc-haha/desktop/src/pages/HeicodeTasksHome.tsx new file mode 100644 index 0000000..a7f5f74 --- /dev/null +++ b/cc-haha/desktop/src/pages/HeicodeTasksHome.tsx @@ -0,0 +1,456 @@ +// desktop/src/pages/HeicodeTasksHome.tsx +// +// Heicode 任务驾驶舱(首页 → 工作台)。覆盖 wireframe §2 (intent input +// + 最近任务) 和 §3 (追问对话 + 任务卡)。Slice 7 是 mock skeleton, +// 数据来自 useHeicodeTaskStore 的 seed。 + +import { useMemo, useRef, useState } from 'react' +import { useTranslation } from '../i18n' +import { + useHeicodeTaskStore, + type ChatTurn, + type FollowupQuestion, + type HeicodeTask, + type HeicodeTaskStatus, +} from '../stores/heicodeTaskStore' + +const STATUS_TONE: Record = { + draft: + 'border-[var(--color-warning)]/30 bg-[var(--color-warning)]/10 text-[var(--color-warning)]', + configuring: + 'border-[var(--color-secondary)]/30 bg-[var(--color-secondary)]/10 text-[var(--color-secondary)]', + running: + 'border-[var(--color-primary)]/30 bg-[var(--color-primary)]/10 text-[var(--color-primary)]', + awaiting_approval: + 'border-[var(--color-error)]/30 bg-[var(--color-error-container)] text-[var(--color-error)]', + completed: + 'border-[var(--color-success)]/30 bg-[var(--color-success)]/10 text-[var(--color-success)]', + failed: + 'border-[var(--color-error)]/40 bg-[var(--color-error)]/10 text-[var(--color-error)]', + paused: + 'border-[var(--color-border)] bg-[var(--color-surface-container-low)] text-[var(--color-text-tertiary)]', +} + +export function HeicodeTasksHome() { + const t = useTranslation() + const tasks = useHeicodeTaskStore((s) => s.tasks) + const currentTaskId = useHeicodeTaskStore((s) => s.currentTaskId) + const submitIntent = useHeicodeTaskStore((s) => s.submitIntent) + const openTask = useHeicodeTaskStore((s) => s.openTask) + + const currentTask = useMemo( + () => (currentTaskId ? tasks.find((x) => x.id === currentTaskId) ?? null : null), + [tasks, currentTaskId], + ) + + if (currentTask) { + return + } + + return ( + { + submitIntent(intent) + }} + onOpenTask={openTask} + tStatus={(s) => t(`tasks.status.${s}` as const)} + tCopy={t} + /> + ) +} + +// ─── Home view (wireframe §2) ───────────────────────────────── + +function Home({ + tasks, + onSubmit, + onOpenTask, + tStatus, + tCopy, +}: { + tasks: HeicodeTask[] + onSubmit: (intent: string) => void + onOpenTask: (id: string) => void + tStatus: (s: HeicodeTaskStatus) => string + tCopy: ReturnType +}) { + const [intent, setIntent] = useState('') + const taRef = useRef(null) + + const handleSend = () => { + const t = intent.trim() + if (!t) return + onSubmit(t) + setIntent('') + } + + return ( +
+
+ {/* Header */} +
+

+ {tCopy('tasks.home.currentLabel')} +

+

+ {tCopy('tasks.home.currentNone')} +

+
+ + {/* Intent input */} +
+

+ {tCopy('tasks.home.intentPrompt')} +

+
+