docs(design): 解压 Maestro Design System 到 design/(重构参照材料)

phosphor console v1.1 设计系统(从现网 web/style.css 演化):tokens(colors/effects/
typography/fonts) + components(core/forms/surfaces .jsx) + ui_kits(console + console_mobile)
+ assets/icons(15 双色 SVG) + guidelines + CLAUDE/SKILL/readme。供 B1–B6 重构任务对照实现。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 14:29:07 +08:00
parent 0b3ebbd569
commit 5a1e185b1a
102 changed files with 10323 additions and 0 deletions
@@ -0,0 +1,198 @@
// 移动版 · 四个 Tab 视图:任务 / 审批 / 事件 / 项目
function MobTaskRow({ task, depth = 0, expandedId, setExpandedId }) {
const { ComplexityBadge, StatusChip } = window.MaestroDesignSystem_a6a290;
const kids = task.children || [];
const expanded = expandedId === task.id;
const isGate = ['plan_review', 'spec_review', 'exec_review'].includes(task.status);
return (
<React.Fragment>
<div onClick={() => setExpandedId(expanded ? null : task.id)} style={{
display: 'flex', flexDirection: 'column', gap: 6,
padding: '12px 14px', minHeight: 44, cursor: 'pointer',
paddingLeft: 14 + depth * 16,
borderBottom: '1px solid var(--line-soft)',
borderLeft: depth ? '1px solid var(--line-soft)' : 'none',
background: expanded ? 'var(--panel-2)' : isGate ? 'rgba(184,142,245,.05)' : 'transparent',
}}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
<span style={{ fontWeight: 500, fontSize: 14, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{task.title}</span>
<span style={{ color: 'var(--faint)', fontSize: 10.5, flex: 'none' }}>{task.id}</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<ComplexityBadge complexity={task.cplx} />
<StatusChip status={task.status} />
<span style={{ marginLeft: 'auto', fontSize: 10.5, color: task.prio === 'P0' ? 'var(--amber)' : 'var(--faint)' }}>{task.prio}</span>
</div>
</div>
{expanded ? (
<div style={{ background: 'var(--panel)', borderBottom: '1px solid var(--line)', borderLeft: '2px solid var(--green-dim)', padding: '12px 14px', animation: 'maestro-rise .2s ease both' }}>
{(task.doc || task.ops) ? (
<React.Fragment>
<div style={{ fontSize: 10.5, color: 'var(--muted)', letterSpacing: '.18em', marginBottom: 4 }}>{task.doc ? '改动方案(SPEC' : '将执行的操作(OPERATIONS'}</div>
<pre style={{ background: 'var(--bg-deep)', border: '1px solid var(--line-soft)', borderLeft: '2px solid var(--green-dim)', borderRadius: 4, padding: '10px 12px', fontFamily: 'var(--mono)', fontSize: 12, whiteSpace: 'pre-wrap', wordBreak: 'break-word', margin: 0, color: 'var(--ink)', lineHeight: 1.6 }}>{task.doc || task.ops}</pre>
</React.Fragment>
) : (
<div style={{ padding: '10px 12px', border: '1px dashed var(--line)', borderRadius: 6, color: 'var(--faint)', fontStyle: 'italic', fontSize: 12 }}> Claude Code 产出 MCP 写入并提交评审</div>
)}
</div>
) : null}
{kids.map((k) => <MobTaskRow key={k.id} task={k} depth={depth + 1} expandedId={expandedId} setExpandedId={setExpandedId} />)}
</React.Fragment>
);
}
function MobTasks({ tasks, agents }) {
const { SectionHead } = window.MaestroDesignSystem_a6a290;
const [expandedId, setExpandedId] = React.useState(null);
return (
<div>
{agents.length ? (
<div style={{ margin: '12px 14px 0', display: 'flex', gap: 10, alignItems: 'center', background: 'var(--panel)', border: '1px solid var(--cyan-dim)', borderRadius: 6, padding: '10px 12px' }}>
<span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--cyan)', boxShadow: '0 0 8px var(--cyan)', animation: 'maestro-pulse .9s infinite', flex: 'none' }}></span>
<span style={{ fontSize: 12, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{agents[0].title}</span>
<span style={{ marginLeft: 'auto', flex: 'none', fontSize: 10, letterSpacing: '.1em', color: 'var(--cyan)', border: '1px solid var(--cyan-dim)', borderRadius: 3, padding: '0 6px' }}>{agents[0].kind}</span>
</div>
) : null}
<SectionHead title="任务树" style={{ padding: '14px 14px 8px' }} />
<div>
{tasks.map((t) => <MobTaskRow key={t.id} task={t} expandedId={expandedId} setExpandedId={setExpandedId} />)}
</div>
</div>
);
}
function MobGatePreview({ item, onDecide, onClose }) {
const { ComplexityBadge, Button, Textarea } = window.MaestroDesignSystem_a6a290;
const [rejecting, setRejecting] = React.useState(false);
const [reason, setReason] = React.useState('');
if (!item) return null;
return (
<div style={{ position: 'fixed', inset: 0, zIndex: 1300, display: 'flex', flexDirection: 'column', background: 'var(--bg)', animation: 'maestro-rise .18s ease both' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 14px', borderBottom: '1px solid var(--line)', background: 'var(--panel-2)', flex: 'none' }}>
<span style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: '.18em', color: 'var(--bg)', background: 'var(--violet)', padding: '2px 8px', borderRadius: 3, flex: 'none' }}>{item.gateLabel}</span>
<span style={{ fontWeight: 700, fontSize: 14, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.title}</span>
<span style={{ flex: 1 }}></span>
<button onClick={onClose} title="关闭" style={{ fontFamily: 'var(--mono)', fontSize: 15, background: 'transparent', color: 'var(--muted)', border: 'none', cursor: 'pointer', padding: '8px 10px', minHeight: 44 }}></button>
</div>
<div style={{ flex: 1, overflowY: 'auto', padding: '14px 16px 24px' }}>
<div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 10 }}>{item.meta}</div>
{item.docLabel ? <div style={{ fontSize: 10.5, color: 'var(--muted)', letterSpacing: '.18em', margin: '0 0 6px' }}>{item.docLabel}</div> : null}
<div style={{ background: 'var(--bg-deep)', border: '1px solid var(--line-soft)', borderLeft: '2px solid var(--green-dim)', borderRadius: 4, padding: '12px 14px', fontFamily: 'var(--mono)', fontSize: 13.5, lineHeight: 1.7, whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: 'var(--ink)' }}>{item.doc}</div>
</div>
<div style={{ flex: 'none', padding: '12px 14px calc(12px + env(safe-area-inset-bottom))', borderTop: '1px solid var(--line)', background: 'var(--panel-2)', display: 'grid', gap: 10 }}>
{rejecting ? (
<React.Fragment>
<Textarea danger placeholder="改进意见(必填)" minHeight={56} value={reason} onChange={setReason} />
<div style={{ display: 'flex', gap: 10 }}>
<Button variant="reject" disabled={!reason.trim()} style={{ flex: 1, minHeight: 44 }}
onClick={() => { onDecide(item, 'reject', reason); onClose(); }}>确认驳回</Button>
<Button style={{ flex: 1, minHeight: 44 }} onClick={() => { setRejecting(false); setReason(''); }}>取消</Button>
</div>
</React.Fragment>
) : (
<div style={{ display: 'flex', gap: 10 }}>
<Button variant="accept" style={{ flex: 1, minHeight: 44 }} onClick={() => { onDecide(item, 'accept'); onClose(); }}> 通过</Button>
<Button variant="reject" style={{ flex: 1, minHeight: 44 }} onClick={() => setRejecting(true)}> 驳回</Button>
</div>
)}
</div>
</div>
);
}
function MobGates({ approvals, onDecide }) {
const { GateCard, GateStripe, Button, Textarea, SectionHead } = window.MaestroDesignSystem_a6a290;
const [rejecting, setRejecting] = React.useState(null);
const [reason, setReason] = React.useState('');
const [preview, setPreview] = React.useState(null);
if (!approvals.length) {
return <div style={{ margin: '24px 14px', padding: '38px 0', textAlign: 'center', color: 'var(--faint)', border: '1px dashed var(--line)', borderRadius: 6, fontSize: 12 }}>无待裁决审批</div>;
}
return (
<div style={{ padding: '12px 14px' }}>
<GateStripe />
<SectionHead mark="violet" title="审批闸 · 等待裁决" style={{ padding: '10px 0 8px', color: 'var(--violet)' }} />
{approvals.map((a) => (
<div key={a.id} style={{ marginBottom: 12 }}>
<GateCard gate={a.gate} title={a.title} meta={a.meta} docLabel={a.docLabel} doc={a.doc}
actions={
<React.Fragment>
<Button variant="accept" style={{ flex: 1, minHeight: 44 }} onClick={() => onDecide(a, 'accept')}> 通过</Button>
<Button variant="reject" style={{ flex: 1, minHeight: 44 }} onClick={() => setRejecting(rejecting === a.id ? null : a.id)}> 驳回</Button>
<Button variant="ghost" style={{ minHeight: 44 }} title="全屏阅读"
onClick={() => setPreview({ ...a, gateLabel: { plan: '拆解评审', spec: '方案评审', exec: '结果评审' }[a.gate] || a.gate })}></Button>
</React.Fragment>
}>
{rejecting === a.id ? (
<div style={{ display: 'grid', gap: 10, marginTop: 10 }}>
<Textarea danger placeholder="改进意见(必填)" minHeight={56} value={reason} onChange={setReason} />
<Button variant="reject" disabled={!reason.trim()} style={{ minHeight: 44 }}
onClick={() => { onDecide(a, 'reject', reason); setRejecting(null); setReason(''); }}>
确认驳回
</Button>
</div>
) : null}
</GateCard>
</div>
))}
<MobGatePreview item={preview} onDecide={onDecide} onClose={() => setPreview(null)} />
</div>
);
}
function MobEvents({ events }) {
const { EventItem, SectionHead } = window.MaestroDesignSystem_a6a290;
return (
<div>
<SectionHead title="事件流" style={{ padding: '14px 14px 8px' }} />
<ul style={{ listStyle: 'none', padding: '0 14px', margin: 0 }}>
{events.map((e, i) => <EventItem key={i} type={e.type} time={e.time} detail={e.detail} />)}
</ul>
</div>
);
}
function MobProjects({ projects, currentId, onSelect, project }) {
const { SectionHead, Select, Input, Button } = window.MaestroDesignSystem_a6a290;
return (
<div>
<SectionHead title="项目" style={{ padding: '14px 14px 8px' }} />
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
{projects.map((p) => {
const active = p.id === currentId;
return (
<li key={p.id} onClick={() => onSelect(p.id)} style={{
display: 'flex', alignItems: 'center', gap: 10, minHeight: 52,
padding: '10px 14px', cursor: 'pointer',
borderBottom: '1px solid var(--line-soft)',
borderLeft: '2px solid ' + (active ? 'var(--green)' : 'transparent'),
background: active ? 'var(--panel-2)' : 'transparent',
color: active ? 'var(--green)' : 'var(--muted)',
}}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flex: 'none' }}>
<line x1="6" y1="3" x2="6" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" />
</svg>
<span style={{ display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0 }}>
<span style={{ fontWeight: 600, fontSize: 13, color: active ? 'var(--green)' : 'var(--ink)' }}>{p.name}</span>
<span style={{ fontSize: 10.5, color: 'var(--faint)' }}>{p.path}</span>
</span>
{active ? <span style={{ marginLeft: 'auto', fontSize: 10, letterSpacing: '.12em', color: 'var(--green)' }}>当前</span> : null}
</li>
);
})}
</ul>
<SectionHead title="项目配置" style={{ padding: '18px 14px 8px' }} />
<div style={{ margin: '0 14px', background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 6, padding: 14, display: 'grid', gap: 12 }}>
<Input label="最大并发" type="number" defaultValue={String(project.concurrency)} />
<Select label="工作模式" defaultValue={project.autonomy} options={[
{ value: 'manual', label: 'manual · 手动' },
{ value: 'auto-easy', label: 'auto-easy · 自动执行 Easy' },
{ value: 'auto-approved', label: 'auto-approved · 自动执行已批准' },
]} />
<Button variant="solid" style={{ minHeight: 44 }}>保存配置</Button>
</div>
</div>
);
}
Object.assign(window, { MaestroMobTasks: MobTasks, MaestroMobGates: MobGates, MaestroMobEvents: MobEvents, MaestroMobProjects: MobProjects });