feat(desktop): 深色 AI 控制台视觉改造 + 工作台仪表盘

桌面端从扁平 dev 工具风改为深色高端 AI 控制台:分层表面 + 紫青强调 + 微光 +
首页仪表盘,提升第一印象与吸引力。

- 设计 tokens: tailwind ink 分层调色板 + glow/card 阴影; index.css 深色基底 +
  品牌渐变 + 深色滚动条
- shell: TopBar(品牌渐变+毛玻璃+发光健康灯) / LeftNav(激活态紫色高亮+左光条) /
  BottomDrawer(深色+状态色)
- 新 views/Home 工作台仪表盘: 渐变 hero + 4 状态卡 + 3 能力卡 + 快速开始(默认首页)
- 画布: TypedNode 深色节点卡; StudioView ReactFlow colorMode=dark + 深色工具栏/面板/
  MiniMap; Inspector 深色表单
- KbView/MemoryView/MemoryPanel/Placeholder 全深色化; 进度条改紫青渐变
- 纯前端改造, npm build✓; 浏览器验证: 仪表盘 + 编排画布深色呈现

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-11 16:56:04 +08:00
parent d623b8590e
commit 2291f60380
18 changed files with 327 additions and 207 deletions
@@ -93,15 +93,15 @@ export function StudioView({
return (
<div className="flex h-full">
{/* 左节点面板 */}
<div className="w-40 shrink-0 overflow-auto border-r bg-gray-50 p-2">
<div className="mb-1 px-1 text-[11px] font-semibold text-gray-500"></div>
<div className="w-40 shrink-0 overflow-auto border-r border-line bg-ink-900 p-2">
<div className="mb-1 px-1 text-[11px] font-semibold text-slate-500"></div>
{NODE_ORDER.map((kind) => {
const k = NODE_KINDS[kind];
return (
<button
key={kind}
onClick={() => addNode(kind)}
className={`mb-1 flex w-full items-center gap-2 rounded border border-l-4 bg-white px-2 py-1.5 text-left text-xs hover:bg-gray-50 ${k.accent}`}
className={`mb-1 flex w-full items-center gap-2 rounded-md border border-l-[3px] border-line bg-ink-800 px-2 py-1.5 text-left text-xs text-slate-300 hover:bg-ink-700 ${k.accent}`}
title={k.desc}
>
{k.label}
@@ -111,36 +111,37 @@ export function StudioView({
</div>
{/* 中画布 */}
<div className="relative flex-1">
<div className="absolute left-0 right-0 top-0 z-10 flex items-center gap-2 border-b bg-white/90 px-2 py-1.5">
<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 bg-violet-600 px-3 py-1 text-xs font-medium text-white disabled:opacity-40"
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 border px-3 py-1 text-xs hover:bg-gray-50"
className="rounded-md border border-line px-3 py-1 text-xs text-slate-300 hover:bg-ink-800"
>
</button>
<span className="ml-1 text-[11px] text-gray-400">
<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-600"> </span>
<span className="text-emerald-400"> </span>
) : (
<span className="text-amber-600">{issues.length} </span>
<span className="text-amber-400">{issues.length} </span>
)}
</span>
)}
</div>
<ReactFlow
colorMode="dark"
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
@@ -151,15 +152,15 @@ export function StudioView({
onPaneClick={() => setSelId(null)}
fitView
>
<Background />
<Background color="#242b3c" gap={18} />
<Controls />
<MiniMap zoomable pannable className="!bg-white" />
<MiniMap zoomable pannable className="!bg-ink-850" />
</ReactFlow>
{issues && issues.length > 0 && (
<div className="absolute bottom-2 left-2 max-w-md rounded border bg-white p-2 text-[11px] shadow">
<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-600" : "text-amber-600"}>
<div key={idx} className={i.level === "error" ? "text-rose-400" : "text-amber-400"}>
{i.level === "error" ? "✗" : "⚠"} {i.msg}
</div>
))}
@@ -168,7 +169,7 @@ export function StudioView({
</div>
{/* 右检查器 */}
<div className="w-72 shrink-0 border-l bg-white">
<div className="w-72 shrink-0 border-l border-line bg-ink-900">
<Inspector node={selected} onChange={patchNode} onDelete={deleteNode} />
</div>
</div>