feat(desktop): 工业化升级 A —— 设计系统地基(primitives + lucide + 语义令牌)

把"手搓内联 class + Unicode 字符图标"换成统一组件与真实图标,为后续工业化打底。

- 依赖:装 lucide-react(描线图标,按需 tree-shake)
- 令牌:tailwind.config 加语义色 brand/accent/success/warn/danger + 圆角档位;
  强调色字面量(violet/cyan/emerald…)收敛到令牌,便于整体换肤
- primitives(src/ui,零重依赖自建):Button/Input/Textarea/Select/Field/Card/Panel/
  Badge/Dot/Tabs/Skeleton/EmptyState/Dialog/Toast(+useToast)/cn,桶文件统一引入
- 迁移:TopBar/LeftNav/BottomDrawer + Home/Report/Runs/Kb/Placeholder/ExecTrace/
  MemoryPanel/StudioView 全部换 primitives + lucide 图标;导航/能力卡/按钮告别
  ▤◆▣▦ 等 Unicode 字符;错误改用全局 Toast;空状态用 EmptyState
- App 包 ToastProvider

验证:tsc + vite build 通过;浏览器(Preview)走查工作台/报告页——真实图标、统一卡片/
按钮/输入;跑报告端到端正常(执行轨迹 lucide 状态图标点亮、章节耗时/字数/检索片段、
完成弹 Toast + 下载 Word)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-12 16:39:42 +08:00
parent 190c191ce4
commit 72bd43965f
25 changed files with 715 additions and 348 deletions
@@ -13,11 +13,13 @@ import {
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { Play, ShieldCheck } from "lucide-react";
import { nodeTypes, type NodeStatus } from "./TypedNode";
import { Inspector } from "./Inspector";
import { NODE_KINDS, NODE_ORDER } from "./nodeCatalog";
import { exportDsl, validate, type Issue, type TaskDsl } from "../lib/dsl";
import type { RunPhase } from "../lib/run";
import { Button } from "../ui";
let seq = 0;
@@ -113,28 +115,21 @@ export function StudioView({
{/* 中画布 */}
<div className="relative flex-1 bg-ink-950">
<div className="absolute left-0 right-0 top-0 z-10 flex items-center gap-2 border-b border-line bg-ink-900/90 px-2 py-1.5 backdrop-blur">
<button
onClick={run}
disabled={running || nodes.length === 0}
className="rounded-md bg-violet-600 px-3 py-1 text-xs font-medium text-white hover:bg-violet-500 disabled:opacity-40"
>
{running ? "运行中…" : "▶ 运行"}
</button>
<button
onClick={() => setIssues(validate(nodes, edges))}
className="rounded-md border border-line px-3 py-1 text-xs text-slate-300 hover:bg-ink-800"
>
<Button variant="primary" size="sm" icon={Play} onClick={run} disabled={running || nodes.length === 0}>
{running ? "运行中…" : "运行"}
</Button>
<Button size="sm" icon={ShieldCheck} onClick={() => setIssues(validate(nodes, edges))}>
</button>
</Button>
<span className="ml-1 text-[11px] text-slate-500">
{nodes.length} · {edges.length} 线
</span>
{issues && (
<span className="ml-auto text-[11px]">
{issues.length === 0 ? (
<span className="text-emerald-400"> </span>
<span className="text-success"> </span>
) : (
<span className="text-amber-400">{issues.length} </span>
<span className="text-warn">{issues.length} </span>
)}
</span>
)}
@@ -160,7 +155,7 @@ export function StudioView({
{issues && issues.length > 0 && (
<div className="absolute bottom-2 left-2 max-w-md rounded-lg border border-line bg-ink-850 p-2 text-[11px] shadow-card">
{issues.map((i, idx) => (
<div key={idx} className={i.level === "error" ? "text-rose-400" : "text-amber-400"}>
<div key={idx} className={i.level === "error" ? "text-danger" : "text-warn"}>
{i.level === "error" ? "✗" : "⚠"} {i.msg}
</div>
))}