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
+16
View File
@@ -0,0 +1,16 @@
/**
* Maestro 按钮。直角、等宽字体、hover 抬升或辉光。
* @startingPoint section="Components" subtitle="五种变体的等宽直角按钮" viewport="700x180"
*/
export interface ButtonProps {
/** 'default' 描边 · 'solid' 实心绿(主操作) · 'ghost' 无边框 · 'accept' 绿描边(通过) · 'reject' 红描边(驳回) */
variant?: 'default' | 'solid' | 'ghost' | 'accept' | 'reject';
/** 'xs' 紧凑尺寸(区块头内联动作) */
size?: 'xs';
disabled?: boolean;
type?: 'button' | 'submit';
onClick?: () => void;
style?: React.CSSProperties;
children?: React.ReactNode;
}
export declare function Button(props: ButtonProps): JSX.Element;
+31
View File
@@ -0,0 +1,31 @@
const css = `
.m-btn{font-family:var(--mono);font-size:12px;font-weight:600;letter-spacing:.08em;background:transparent;color:var(--ink);border:1px solid var(--line);padding:6px 14px;cursor:pointer;transition:all .12s;border-radius:var(--radius-sm,4px);white-space:nowrap}
.m-btn:hover{border-color:var(--muted);background:var(--panel-2)}
.m-btn--xs{padding:2px 8px;font-size:11px}
.m-btn--ghost{border-color:transparent;color:var(--muted)}
.m-btn--ghost:hover{color:var(--green);border-color:var(--green-dim);background:transparent}
.m-btn--solid{background:var(--green);color:var(--bg-deep);border-color:var(--green)}
.m-btn--solid:hover{background:#79ec94;border-color:#79ec94;box-shadow:0 0 14px rgba(95,221,125,.35)}
.m-btn--accept{border-color:var(--green-dim);color:var(--green)}
.m-btn--accept:hover{background:var(--green);color:var(--bg-deep);border-color:var(--green);box-shadow:0 0 14px rgba(95,221,125,.3)}
.m-btn--reject{border-color:var(--red-dim);color:var(--red)}
.m-btn--reject:hover{background:var(--red);color:var(--bg-deep);border-color:var(--red);box-shadow:0 0 14px rgba(255,93,93,.3)}
.m-btn:disabled{opacity:.4;cursor:not-allowed}
`;
function ensureCss() {
if (typeof document === 'undefined' || document.getElementById('m-btn-css')) return;
const s = document.createElement('style'); s.id = 'm-btn-css'; s.textContent = css;
document.head.appendChild(s);
}
export function Button({ variant = 'default', size, disabled, onClick, type = 'button', style, children }) {
ensureCss();
const cls = ['m-btn'];
if (variant !== 'default') cls.push('m-btn--' + variant);
if (size === 'xs') cls.push('m-btn--xs');
return (
<button type={type} className={cls.join(' ')} disabled={disabled} onClick={onClick} style={style}>
{children}
</button>
);
}
+10
View File
@@ -0,0 +1,10 @@
Maestro 的按钮:直角等宽,描边为默认形态,实心绿仅用于每屏最多一个主操作;accept/reject 成对出现在审批闸。
```jsx
<Button variant="solid">创建任务</Button>
<Button variant="accept"> 通过</Button>
<Button variant="reject"> 驳回</Button>
<Button variant="ghost" size="xs">+ 新建</Button>
```
变体:`default`(描边)· `solid`(主操作)· `ghost`(低调内联)· `accept` / `reject`(审批对,hover 填充信号色+辉光)。`size="xs"` 用于区块头内的小动作。disabled = opacity .4。
+7
View File
@@ -0,0 +1,7 @@
/**
* 复杂度徽章:HARD(红)/ MED(琥珀)/ EASY(绿),信号色 7% 透明底。
*/
export interface ComplexityBadgeProps {
complexity?: 'hard' | 'medium' | 'easy';
}
export declare function ComplexityBadge(props: ComplexityBadgeProps): JSX.Element;
@@ -0,0 +1,17 @@
const css = `
.m-cplx{flex:none;display:inline-block;font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em;padding:1px 7px;border:1px solid;border-radius:var(--radius-xs,3px)}
.m-cplx--hard{color:var(--red);border-color:var(--red-dim);background:rgba(255,93,93,.07)}
.m-cplx--medium{color:var(--amber);border-color:var(--amber-dim);background:rgba(240,180,41,.07)}
.m-cplx--easy{color:var(--green);border-color:var(--green-dim);background:rgba(95,221,125,.07)}
`;
function ensureCss() {
if (typeof document === 'undefined' || document.getElementById('m-cplx-css')) return;
const s = document.createElement('style'); s.id = 'm-cplx-css'; s.textContent = css;
document.head.appendChild(s);
}
const LABEL = { hard: 'HARD', medium: 'MED', easy: 'EASY' };
export function ComplexityBadge({ complexity = 'medium' }) {
ensureCss();
return <span className={'m-cplx m-cplx--' + complexity}>{LABEL[complexity] || complexity}</span>;
}
@@ -0,0 +1,6 @@
复杂度徽章:任务行右侧的 HARD / MED / EASY 三级标签,红/琥珀/绿 + 7% 透明信号底。
```jsx
<ComplexityBadge complexity="hard" />
<ComplexityBadge complexity="easy" />
```
+11
View File
@@ -0,0 +1,11 @@
/**
* 顶栏计数徽章:⚠ 待审批(紫脉冲)· ▸ 待执行(绿)· ◉ 执行中(青)· ⛓ 被阻塞(琥珀虚线边)· Σ 总量(中性)。hover 展开文字标签;count 为 0 时灰化。
*/
export interface CountBadgeProps {
kind?: 'gate' | 'ready' | 'run' | 'blocked' | 'total';
count?: number;
/** 覆盖默认中文标签 */
label?: string;
title?: string;
}
export declare function CountBadge(props: CountBadgeProps): JSX.Element;
+40
View File
@@ -0,0 +1,40 @@
const css = `
.m-bdg{display:inline-flex;align-items:center;font-family:var(--mono);font-size:11.5px;line-height:1;letter-spacing:.08em;height:28px;padding:0 10px;cursor:default;white-space:nowrap;border-radius:var(--radius-sm,4px)}
.m-bdg .m-bdg-ico{margin-right:5px;font-size:12px;line-height:1;font-family:var(--mono)}
.m-bdg .m-bdg-n{font-weight:700}
.m-bdg .m-bdg-label{max-width:0;opacity:0;overflow:hidden;transition:max-width .28s ease,opacity .22s ease,margin-left .28s ease}
.m-bdg:hover .m-bdg-label{max-width:8em;opacity:1;margin-left:5px}
.m-bdg--gate{color:var(--violet);border:1px solid var(--violet-dim)}
.m-bdg--gate:not(.m-bdg--zero){animation:maestro-pulse 2.2s infinite}
.m-bdg--ready{color:var(--green);border:1px solid var(--green-dim)}
.m-bdg--run{color:var(--cyan);border:1px solid var(--cyan-dim)}
.m-bdg--run:not(.m-bdg--zero) .m-bdg-ico{animation:maestro-pulse .9s infinite}
.m-bdg--blocked{color:var(--amber);border:1px dashed var(--amber-dim)}
.m-bdg--total{color:var(--ink);border:1px solid var(--line)}
.m-bdg--zero{color:var(--faint);border-color:var(--line-soft);animation:none}
`;
function ensureCss() {
if (typeof document === 'undefined' || document.getElementById('m-bdg-css')) return;
const s = document.createElement('style'); s.id = 'm-bdg-css'; s.textContent = css;
document.head.appendChild(s);
}
const META = {
gate: { ico: '⚠', label: '项待审批' },
ready: { ico: '▸', label: '待执行' },
run: { ico: '◉', label: '执行中' },
blocked: { ico: '⛓', label: '被阻塞' },
total: { ico: 'Σ', label: '总量' },
};
export function CountBadge({ kind = 'ready', count = 0, label, title }) {
ensureCss();
const m = META[kind] || META.ready;
const cls = 'm-bdg m-bdg--' + kind + (count === 0 ? ' m-bdg--zero' : '');
return (
<span className={cls} title={title}>
<span className="m-bdg-ico">{m.ico}</span>
<span className="m-bdg-n">{count}</span>
<span className="m-bdg-label">{label || m.label}</span>
</span>
);
}
@@ -0,0 +1,9 @@
顶栏徽章:图标 + 数字,hover 横向展开中文标签。三种语义固定:gate ⚠ 琥珀 / ready ▸ 绿 / run ◉ 青。
```jsx
<CountBadge kind="gate" count={2} />
<CountBadge kind="ready" count={5} />
<CountBadge kind="run" count={1} />
```
count=0 自动灰化并停掉脉冲动画。
+15
View File
@@ -0,0 +1,15 @@
/**
* 额度/用量计量条:LED 分段小灯 + 同色加粗百分比,用量分级自动变色(≤80% 青 / >80% 琥珀 / >95% 红),已用格带辉光。
*/
export interface QuotaMeterProps {
/** 左侧小标签,如 "5h" / "周" */
label?: string;
/** 用量百分比 0100 */
pct?: number;
/** 右侧弱化说明,如 "3h 8m 后重置" */
detail?: string;
/** 分段格数,默认 16 */
cells?: number;
style?: React.CSSProperties;
}
export declare function QuotaMeter(props: QuotaMeterProps): JSX.Element;
+24
View File
@@ -0,0 +1,24 @@
// 额度计量条:LED 分段 + 用量分级变色(≤80% 青 / >80% 琥珀 / >95% 红,与产品 .agent-usage 阈值对齐)
export function QuotaMeter({ label, pct = 0, detail, cells = 16, style }) {
const p = Math.max(0, Math.min(100, pct));
const filled = Math.round((p / 100) * cells);
const color = p > 95 ? 'var(--red)' : p > 80 ? 'var(--amber)' : 'var(--cyan)';
const glow = p > 95 ? 'rgba(255,93,93,.4)' : p > 80 ? 'rgba(240,180,41,.35)' : 'rgba(89,200,216,.4)';
return (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontFamily: 'var(--mono)', ...style }}
title={(label ? label + ' · ' : '') + p + '%' + (detail ? ' · ' + detail : '')}>
{label ? <span style={{ fontSize: 11, color: 'var(--muted)', letterSpacing: '.08em', flex: 'none' }}>{label}</span> : null}
<span style={{ display: 'inline-flex', gap: 2, padding: 3, background: 'var(--bg-deep)', border: '1px solid var(--line-soft)', borderRadius: 3 }}>
{Array.from({ length: cells }, (_, i) => (
<span key={i} style={{
width: 4, height: 9, borderRadius: 1,
background: i < filled ? color : 'var(--panel-2)',
boxShadow: i < filled ? '0 0 5px ' + glow : 'none',
}}></span>
))}
</span>
<b style={{ fontSize: 12, color }}>{p}%</b>
{detail ? <span style={{ fontSize: 10.5, color: 'var(--faint)' }}>{detail}</span> : null}
</span>
);
}
@@ -0,0 +1,9 @@
额度计量条:agent 用量/配额的 LED 分段显示,按用量自动变色(青→琥珀→红),已用格带辉光。
```jsx
<QuotaMeter label="5h" pct={50} detail="3h 8m 后重置" />
<QuotaMeter label="周" pct={78} detail="16h 38m 后重置" />
<QuotaMeter pct={94} cells={24} />
```
`pct` 决定填充与颜色(≥70% 琥珀、≥90% 红);`detail` 放重置时间等弱化说明;`cells` 调分段密度。
+14
View File
@@ -0,0 +1,14 @@
/**
* 区块标题:▍色块标记 + 全大写宽字距标题,可挂右侧动作。
*/
export interface SectionHeadProps {
/** ▍标记颜色:绿(默认)/ 紫(审批)/ 琥珀(警示)/ 青(agent) */
mark?: 'green' | 'violet' | 'amber' | 'cyan';
title?: React.ReactNode;
/** 右侧动作(通常是 ghost xs 按钮) */
action?: React.ReactNode;
/** 滚动容器内吸顶 + 底缘淡出 */
sticky?: boolean;
style?: React.CSSProperties;
}
export declare function SectionHead(props: SectionHeadProps): JSX.Element;
+17
View File
@@ -0,0 +1,17 @@
export function SectionHead({ mark = 'green', title, action, sticky, style }) {
const markColor = { green: 'var(--green)', amber: 'var(--amber)', cyan: 'var(--cyan)', violet: 'var(--violet)' }[mark] || 'var(--green)';
return (
<div style={{
display: 'flex', alignItems: 'center', gap: 6,
fontFamily: 'var(--mono)', fontSize: 11, fontWeight: 600, letterSpacing: '.22em',
color: 'var(--muted)', textTransform: 'uppercase',
padding: '18px 0 10px',
...(sticky ? { position: 'sticky', top: 0, zIndex: 5, background: 'linear-gradient(var(--bg) 75%, transparent)' } : {}),
...style,
}}>
<span style={{ color: markColor, flex: 'none' }}></span>
<span style={{ whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', minWidth: 0 }}>{title}</span>
{action ? <span style={{ marginLeft: 'auto', flex: 'none' }}>{action}</span> : null}
</div>
);
}
@@ -0,0 +1,6 @@
区块标题:▍ + 全大写 .22em 字距小标题,Maestro 每个区块的开头。mark 颜色表达区块语义(amber=审批闸、cyan=agent)。
```jsx
<SectionHead title="任务树" action={<Button variant="ghost" size="xs">+ 新建任务</Button>} />
<SectionHead mark="amber" title="审批闸 · 等待裁决" />
```
+12
View File
@@ -0,0 +1,12 @@
/**
* 任务状态 chip:圆点 + 压缩状态词,按状态机分组上色(等待类脉冲动画)。
*/
export interface StatusChipProps {
/** 状态机 16 态之一;决定颜色组与动画 */
status?: 'init' | 'analyzing' | 'plan_review' | 'decomposed' | 'speccing' | 'spec_review'
| 'ready' | 'blocked' | 'queued' | 'executing' | 'exec_review' | 'failed'
| 'needs_attention' | 'done' | 'paused' | 'cancelled';
/** 覆盖默认中文标签 */
label?: string;
}
export declare function StatusChip(props: StatusChipProps): JSX.Element;
+46
View File
@@ -0,0 +1,46 @@
const STATUS_LABEL = {
init: '新建', analyzing: '分析拆解中', plan_review: '待确认拆解',
decomposed: '已拆解', speccing: '写方案中', spec_review: '待确认方案',
ready: '可执行', blocked: '被依赖阻塞', queued: '排队中',
executing: '执行中', exec_review: '待审/合', failed: '失败',
needs_attention: '需人工', done: '完成', paused: '暂停', cancelled: '取消',
};
const STATUS_GROUP = {
init: 'idle', analyzing: 'work', speccing: 'work',
plan_review: 'gate', spec_review: 'gate', exec_review: 'gate',
decomposed: 'container', ready: 'go', queued: 'go', executing: 'run',
blocked: 'hold', paused: 'hold', failed: 'bad', needs_attention: 'bad',
done: 'done', cancelled: 'dead',
};
const css = `
.m-chip{flex:none;display:inline-flex;align-items:center;gap:5px;font-family:var(--mono);font-size:11px;padding:1px 8px;border:1px solid var(--line);color:var(--muted);white-space:nowrap;border-radius:var(--radius-xs,3px)}
.m-chip::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
.m-chip--work{color:var(--cyan);border-color:var(--cyan-dim)}
.m-chip--gate{color:var(--violet);border-color:var(--violet-dim)}
.m-chip--gate::before{animation:maestro-pulse 1.4s infinite}
.m-chip--container{color:#9bb4c8;border-color:#2c3c4a}
.m-chip--go{color:var(--green);border-color:var(--green-dim)}
.m-chip--run{color:var(--cyan);border-color:var(--cyan);box-shadow:0 0 10px rgba(89,200,216,.25)}
.m-chip--run::before{animation:maestro-pulse .8s infinite}
.m-chip--hold{color:var(--faint)}
.m-chip--bad{color:var(--red);border-color:var(--red-dim)}
.m-chip--bad::before{animation:maestro-pulse 1s infinite}
.m-chip--done{color:var(--bg-deep);background:var(--green);border-color:var(--green);font-weight:700}
.m-chip--dead{color:var(--faint);text-decoration:line-through}
.m-chip--dead::before{background:var(--faint)}
`;
function ensureCss() {
if (typeof document === 'undefined' || document.getElementById('m-chip-css')) return;
const s = document.createElement('style'); s.id = 'm-chip-css'; s.textContent = css;
document.head.appendChild(s);
}
export function StatusChip({ status = 'init', label }) {
ensureCss();
const group = STATUS_GROUP[status] || 'idle';
return (
<span className={'m-chip m-chip--' + group}>
{label || STATUS_LABEL[status] || status}
</span>
);
}
@@ -0,0 +1,9 @@
状态 chip:把 16 个状态机状态渲染成「圆点 + 中文短词」,颜色按组(gate=琥珀脉冲、run=青辉光、done=实心绿、bad=红脉冲)。
```jsx
<StatusChip status="executing" />
<StatusChip status="exec_review" />
<StatusChip status="done" />
```
`status` 即可,标签与颜色自动对应;`label` 可覆盖文字。等待审批/执行中/失败的圆点会脉冲。
+53
View File
@@ -0,0 +1,53 @@
<!-- @dsCard group="Components" viewport="700x280" name="Core · 按钮与徽章" subtitle="Button 五变体 · StatusChip · ComplexityBadge · CountBadge · QuotaMeter · SectionHead" -->
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
<link rel="stylesheet" href="../../styles.css">
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script src="../../_ds_bundle.js"></script>
<style>body{margin:0;background:var(--bg);font-family:var(--mono);color:var(--ink);padding:14px 16px}</style>
</head><body><div id="root"></div>
<script type="text/babel">
const { Button, StatusChip, ComplexityBadge, CountBadge, SectionHead, QuotaMeter } = window.MaestroDesignSystem_a6a290;
const row = { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' };
ReactDOM.createRoot(document.getElementById('root')).render(
<div style={{ display: 'grid', gap: 12 }}>
<div style={row}>
<Button>默认</Button>
<Button variant="solid">创建任务</Button>
<Button variant="accept"> 通过</Button>
<Button variant="reject"> 驳回</Button>
<Button variant="ghost">+ 新建</Button>
<Button size="xs"> 同步 todo</Button>
<Button disabled>禁用</Button>
</div>
<div style={row}>
<StatusChip status="ready" />
<StatusChip status="executing" />
<StatusChip status="exec_review" />
<StatusChip status="blocked" />
<StatusChip status="failed" />
<StatusChip status="done" />
<StatusChip status="cancelled" />
</div>
<div style={row}>
<ComplexityBadge complexity="hard" />
<ComplexityBadge complexity="medium" />
<ComplexityBadge complexity="easy" />
<span style={{ width: 10 }}></span>
<CountBadge kind="gate" count={2} />
<CountBadge kind="ready" count={5} />
<CountBadge kind="run" count={1} />
<CountBadge kind="blocked" count={1} />
<CountBadge kind="total" count={5} />
<CountBadge kind="run" count={0} />
</div>
<div style={row}>
<QuotaMeter label="5h" pct={50} detail="3h 8m 后重置" />
<QuotaMeter label="周" pct={78} detail="16h 38m 后重置" />
<QuotaMeter pct={94} />
</div>
<SectionHead title="任务树" style={{ padding: '4px 0 0' }} action={<Button variant="ghost" size="xs">+ 新建任务</Button>} />
</div>
);
</script></body></html>