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:
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 事件流条目:左侧 5px 方形色标 + 事件类型 + 时间 + 详情,类型决定颜色。放在 ul 里使用。
|
||||
*/
|
||||
export interface EventItemProps {
|
||||
/** 事件类型 → 中文标签 + 色标颜色 */
|
||||
type?: 'task.created' | 'task.updated' | 'status.changed' | 'approval.requested'
|
||||
| 'approval.granted' | 'approval.rejected' | 'run.started' | 'run.finished' | 'project.synced';
|
||||
/** 右对齐时间文字,如 "14:02:31" */
|
||||
time?: string;
|
||||
detail?: React.ReactNode;
|
||||
/** 覆盖默认类型标签 */
|
||||
label?: string;
|
||||
}
|
||||
export declare function EventItem(props: EventItemProps): JSX.Element;
|
||||
@@ -0,0 +1,38 @@
|
||||
const css = `
|
||||
.m-ev{font-family:var(--mono);padding:7px 0 7px 12px;border-bottom:1px solid var(--line-soft);position:relative;font-size:11.5px;color:var(--ink);animation:maestro-rise .25s ease both;list-style:none}
|
||||
.m-ev::before{content:'';position:absolute;left:0;top:13px;width:5px;height:5px;background:var(--m-ev-c,var(--muted));border-radius:1.5px}
|
||||
.m-ev-head{display:flex;gap:8px;align-items:baseline}
|
||||
.m-ev-type{font-weight:600;color:var(--m-ev-c,var(--ink));letter-spacing:.04em}
|
||||
.m-ev-time{color:var(--faint);font-size:10.5px;margin-left:auto;flex:none}
|
||||
.m-ev-detail{color:var(--muted);margin-top:1px;word-break:break-word}
|
||||
`;
|
||||
function ensureCss() {
|
||||
if (typeof document === 'undefined' || document.getElementById('m-ev-css')) return;
|
||||
const s = document.createElement('style'); s.id = 'm-ev-css'; s.textContent = css;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
const TYPE_META = {
|
||||
'task.created': ['任务创建', 'var(--green)'],
|
||||
'task.updated': ['任务更新', 'var(--muted)'],
|
||||
'status.changed': ['状态变更', 'var(--cyan)'],
|
||||
'approval.requested': ['请求审批', 'var(--violet)'],
|
||||
'approval.granted': ['审批通过', 'var(--green)'],
|
||||
'approval.rejected': ['审批驳回', 'var(--red)'],
|
||||
'run.started': ['运行开始', 'var(--cyan)'],
|
||||
'run.finished': ['运行结束', 'var(--cyan)'],
|
||||
'project.synced': ['todo 同步', 'var(--green)'],
|
||||
};
|
||||
|
||||
export function EventItem({ type = 'task.updated', time, detail, label }) {
|
||||
ensureCss();
|
||||
const [lab, color] = TYPE_META[type] || [type, 'var(--muted)'];
|
||||
return (
|
||||
<li className="m-ev" style={{ '--m-ev-c': color }}>
|
||||
<div className="m-ev-head">
|
||||
<span className="m-ev-type">{label || lab}</span>
|
||||
{time ? <span className="m-ev-time">{time}</span> : null}
|
||||
</div>
|
||||
{detail ? <div className="m-ev-detail">{detail}</div> : null}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
事件流条目:右栏审计流的一行——方形色标(注意:方的,不是圆点)+ 类型 + 时间 + 详情。
|
||||
|
||||
```jsx
|
||||
<ul style={{ padding: 0 }}>
|
||||
<EventItem type="approval.rejected" time="14:02" detail="重构 sync 模块 ↳ 缺少回滚方案" />
|
||||
<EventItem type="run.started" time="14:05" detail="executor · worktree wt-127" />
|
||||
</ul>
|
||||
```
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 审批闸卡片:琥珀 dim 边 + 实心琥珀闸标签 + 产出文档块 + accept/reject 动作脚。配套 GateStripe 斜纹警示条。
|
||||
*/
|
||||
export interface GateCardProps {
|
||||
/** 闸类型颜色:plan 拆解评审 · spec 方案评审 · exec 结果评审 */
|
||||
gate?: 'plan' | 'spec' | 'exec';
|
||||
/** 覆盖闸标签文字(多语言场景) */
|
||||
kindLabel?: string;
|
||||
title?: React.ReactNode;
|
||||
/** 头部右侧 meta 文字 */
|
||||
meta?: React.ReactNode;
|
||||
/** 文档块上方 .18em 字距小标签,如 "改动方案(SPEC)" */
|
||||
docLabel?: string;
|
||||
/** 产出文本,渲染进 pre.doc(左侧绿 dim 强调边) */
|
||||
doc?: string;
|
||||
/** 底部动作行(accept / reject 按钮对) */
|
||||
actions?: React.ReactNode;
|
||||
/** 折叠时隐藏正文与动作脚,仅留头部 */
|
||||
collapsed?: boolean;
|
||||
/** 显示头部右上的折叠/展开 ▾▸ 按钮 */
|
||||
foldable?: boolean;
|
||||
onToggleFold?: () => void;
|
||||
foldTitle?: string;
|
||||
/** 头部右侧自定义元素(折叠按钮左侧) */
|
||||
headerExtra?: React.ReactNode;
|
||||
/** 头部双击回调(如全屏阅读) */
|
||||
onHeaderDoubleClick?: () => void;
|
||||
/** 头部/整卡 title 提示(如「双击全屏」) */
|
||||
headerTitle?: string;
|
||||
children?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export declare function GateCard(props: GateCardProps): JSX.Element;
|
||||
@@ -0,0 +1,61 @@
|
||||
const css = `
|
||||
.m-gate{background:var(--panel);border:1px solid var(--violet-dim);font-family:var(--mono);color:var(--ink);animation:maestro-rise .25s ease both;border-radius:var(--radius-md,6px);overflow:hidden}
|
||||
.m-gate-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 14px;border-bottom:1px solid var(--line-soft)}
|
||||
.m-gate--collapsed .m-gate-head{border-bottom:none}
|
||||
.m-gate-hx{margin-left:auto;display:flex;align-items:center;gap:6px;flex:none}
|
||||
.m-gate-fold{font-family:var(--mono);font-size:12px;line-height:1;cursor:pointer;background:transparent;color:var(--muted);border:1px solid var(--line);border-radius:var(--radius-sm,4px);padding:3px 7px;transition:color .12s,border-color .12s}
|
||||
.m-gate-fold:hover{color:var(--violet);border-color:var(--violet-dim)}
|
||||
.m-gate-kind{font-size:10.5px;font-weight:700;letter-spacing:.18em;color:var(--bg);background:var(--violet);padding:2px 8px;border-radius:var(--radius-xs,3px)}
|
||||
.m-gate-title{font-weight:600;font-size:13px}
|
||||
.m-gate-body{padding:12px 14px;font-size:12.5px}
|
||||
.m-gate-actions{display:flex;gap:10px;align-items:center;padding:10px 14px;border-top:1px solid var(--line-soft)}
|
||||
.m-gate-doclabel{font-size:10.5px;color:var(--muted);letter-spacing:.18em;margin:8px 0 4px}
|
||||
.m-gate-doclabel:first-child{margin-top:0}
|
||||
.m-gate-doc{background:var(--bg-deep);border:1px solid var(--line-soft);border-left:2px solid var(--green-dim);padding:10px 12px;font-family:var(--mono);font-size:12.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;max-height:280px;overflow-y:auto;color:var(--ink);margin:0;border-radius:var(--radius-sm,4px)}
|
||||
.m-gate-stripe{height:5px;background:repeating-linear-gradient(-45deg,var(--violet) 0 9px,transparent 9px 18px);opacity:.8;border-radius:3px}
|
||||
`;
|
||||
function ensureCss() {
|
||||
if (typeof document === 'undefined' || document.getElementById('m-gate-css')) return;
|
||||
const s = document.createElement('style'); s.id = 'm-gate-css'; s.textContent = css;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
const GATE_LABEL = { plan: '拆解评审', spec: '方案评审', exec: '结果评审' };
|
||||
|
||||
export function GateCard({ gate = 'spec', kindLabel, title, meta, docLabel, doc, actions, children, style, collapsed, foldable, onToggleFold, foldTitle, headerExtra, onHeaderDoubleClick, headerTitle }) {
|
||||
ensureCss();
|
||||
return (
|
||||
<div className={'m-gate' + (collapsed ? ' m-gate--collapsed' : '')} style={style} onDoubleClick={onHeaderDoubleClick}>
|
||||
<div className="m-gate-head" title={headerTitle}>
|
||||
<span className="m-gate-kind">{kindLabel || GATE_LABEL[gate] || gate}</span>
|
||||
<span className="m-gate-title">{title}</span>
|
||||
{meta ? <span style={{ fontSize: 11, color: 'var(--muted)' }}>{meta}</span> : null}
|
||||
{(headerExtra || foldable) ? (
|
||||
<span className="m-gate-hx">
|
||||
{headerExtra}
|
||||
{foldable ? (
|
||||
<button type="button" className="m-gate-fold" title={foldTitle}
|
||||
onClick={(e) => { e.stopPropagation(); onToggleFold && onToggleFold(); }}>
|
||||
{collapsed ? '▸' : '▾'}
|
||||
</button>
|
||||
) : null}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{!collapsed ? (
|
||||
<React.Fragment>
|
||||
<div className="m-gate-body">
|
||||
{docLabel ? <div className="m-gate-doclabel">{docLabel}</div> : null}
|
||||
{doc ? <pre className="m-gate-doc">{doc}</pre> : null}
|
||||
{children}
|
||||
</div>
|
||||
{actions ? <div className="m-gate-actions">{actions}</div> : null}
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function GateStripe() {
|
||||
ensureCss();
|
||||
return <div className="m-gate-stripe"></div>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
审批闸卡片——Maestro 的核心交互单元。琥珀边框 + 实心琥珀闸标签,体内放 agent 产出文档,脚部放 accept/reject 按钮对(reject 必填理由)。
|
||||
|
||||
```jsx
|
||||
<GateStripe />
|
||||
<GateCard gate="spec" title="重构 sync 模块" meta="maestro · MED"
|
||||
docLabel="改动方案(SPEC)" doc={specText}
|
||||
actions={<><Button variant="accept">✓ 通过</Button><Button variant="reject">✕ 驳回</Button></>} />
|
||||
```
|
||||
|
||||
`GateStripe` 是审批区顶部的琥珀斜纹警示条,整区一条即可。
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 基础面板:--panel 底 + 1px --line 边,直角无阴影。表单、配置区的容器。
|
||||
*/
|
||||
export interface PanelProps {
|
||||
children?: React.ReactNode;
|
||||
padding?: string | number;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export declare function Panel(props: PanelProps): JSX.Element;
|
||||
@@ -0,0 +1,10 @@
|
||||
export function Panel({ children, padding = '14px', style }) {
|
||||
return (
|
||||
<div style={{
|
||||
background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 'var(--radius-md, 6px)',
|
||||
fontFamily: 'var(--mono)', color: 'var(--ink)', padding, ...style,
|
||||
}}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
基础面板容器:panel 底 + line 边 + 直角。无阴影——阴影只属于浮层。
|
||||
|
||||
```jsx
|
||||
<Panel><SectionHead title="新建项目" style={{ padding: 0 }} /> …表单… </Panel>
|
||||
```
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 状态流转时间线:左侧 2px 竖线 + 圆点的只读事件序列,用于归档详情/任务历史。
|
||||
*/
|
||||
export interface TimelineItem {
|
||||
/** 时间文字,如 "2026-06-12 14:02:31" */
|
||||
time: string;
|
||||
/** 事件文字,如 "执行中 → 待审/合" */
|
||||
text: string;
|
||||
/** 操作者/来源,如 "编排器 · 依赖满足自动放行" */
|
||||
who?: string;
|
||||
/** 圆点颜色(CSS 值),默认 --muted */
|
||||
color?: string;
|
||||
}
|
||||
export interface TimelineProps {
|
||||
items?: TimelineItem[];
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export declare function Timeline(props: TimelineProps): JSX.Element;
|
||||
@@ -0,0 +1,28 @@
|
||||
const css = `
|
||||
.m-tl{display:grid;font-family:var(--mono)}
|
||||
.m-tl-item{display:flex;gap:12px;align-items:baseline;padding:5px 0 5px 14px;font-size:12px;border-left:2px solid var(--line);position:relative;color:var(--ink)}
|
||||
.m-tl-item::before{content:'';position:absolute;left:-4px;top:11px;width:6px;height:6px;border-radius:50%;background:var(--m-tl-c,var(--muted))}
|
||||
.m-tl-time{flex:none;color:var(--faint);font-size:11px;min-width:130px}
|
||||
.m-tl-text{color:var(--ink)}
|
||||
.m-tl-who{color:var(--muted);font-size:11px}
|
||||
`;
|
||||
function ensureCss() {
|
||||
if (typeof document === 'undefined' || document.getElementById('m-tl-css')) return;
|
||||
const s = document.createElement('style'); s.id = 'm-tl-css'; s.textContent = css;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
|
||||
export function Timeline({ items = [], style }) {
|
||||
ensureCss();
|
||||
return (
|
||||
<div className="m-tl" style={style}>
|
||||
{items.map((it, i) => (
|
||||
<div key={i} className="m-tl-item" style={it.color ? { '--m-tl-c': it.color } : null}>
|
||||
<span className="m-tl-time">{it.time}</span>
|
||||
<span className="m-tl-text">{it.text}</span>
|
||||
{it.who ? <span className="m-tl-who">{it.who}</span> : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
状态流转时间线:归档详情/任务历史的只读事件序列——左竖线 + 圆点 + 时间/文字/操作者三段。
|
||||
|
||||
```jsx
|
||||
<Timeline items={[
|
||||
{ time: '06-12 05:58', text: '审批驳回(方案评审):缺少回滚方案', who: '审批人 you', color: 'var(--red)' },
|
||||
{ time: '06-12 06:10', text: '写方案中 → 待确认方案', who: '编排器' },
|
||||
{ time: '06-12 06:31', text: '审批通过(方案评审)', color: 'var(--green)' },
|
||||
]} />
|
||||
```
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Toast 提示:深底 + 信号色边框与文字(ok/err/warn),底部居中堆叠,8px 上移入场。
|
||||
*/
|
||||
export interface ToastProps {
|
||||
/** 无 kind = 中性 ink;ok 绿 · err 红 · warn 琥珀 */
|
||||
kind?: 'ok' | 'err' | 'warn';
|
||||
children?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export declare function Toast(props: ToastProps): JSX.Element;
|
||||
@@ -0,0 +1,17 @@
|
||||
const css = `
|
||||
.m-toast{font-family:var(--mono);font-size:12.5px;background:var(--bg-deep);border:1px solid var(--line);color:var(--ink);padding:9px 16px;max-width:70vw;animation:m-toast-in .18s ease both;box-shadow:0 8px 30px rgba(0,0,0,.55);border-radius:var(--radius-md,6px)}
|
||||
.m-toast--err{border-color:var(--red);color:var(--red)}
|
||||
.m-toast--ok{border-color:var(--green-dim);color:var(--green)}
|
||||
.m-toast--warn{border-color:var(--amber-dim);color:var(--amber)}
|
||||
@keyframes m-toast-in{from{opacity:0;transform:translateY(8px)}}
|
||||
`;
|
||||
function ensureCss() {
|
||||
if (typeof document === 'undefined' || document.getElementById('m-toast-css')) return;
|
||||
const s = document.createElement('style'); s.id = 'm-toast-css'; s.textContent = css;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
|
||||
export function Toast({ kind, children, style }) {
|
||||
ensureCss();
|
||||
return <div className={'m-toast' + (kind ? ' m-toast--' + kind : '')} style={style}>{children}</div>;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
Toast:屏幕底部居中堆叠的提示条,深底 + 信号色描边同色文字。文案短促:「已通过」「驳回需填理由」。
|
||||
|
||||
```jsx
|
||||
<Toast kind="ok">已通过 · 合并到 main</Toast>
|
||||
<Toast kind="err">驳回必须填写改进意见</Toast>
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- @dsCard group="Components" viewport="700x400" name="Surfaces · 面板与反馈" subtitle="GateCard 审批闸 · Toast · EventItem 事件流 · Timeline 时间线 · Panel" -->
|
||||
<!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 { GateCard, GateStripe, Toast, EventItem, Button, Timeline } = window.MaestroDesignSystem_a6a290;
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 240px', gap: 14 }}>
|
||||
<div>
|
||||
<GateStripe />
|
||||
<GateCard gate="spec" title="重构 sync 模块" meta="maestro · MED" style={{ marginTop: 10 }}
|
||||
docLabel="改动方案(SPEC)"
|
||||
doc={"把轮询改为 WS 推送:\n1. store 层加 events 订阅\n2. web 端断线重连 + 指数退避"}
|
||||
actions={<>
|
||||
<Button variant="accept">✓ 通过</Button>
|
||||
<Button variant="reject">✕ 驳回</Button>
|
||||
</>} />
|
||||
<div style={{ display: 'grid', gap: 8, marginTop: 12, justifyItems: 'start' }}>
|
||||
<Toast kind="ok">已通过 · 进入 ready</Toast>
|
||||
<Toast kind="err">驳回必须填写改进意见</Toast>
|
||||
<Toast kind="warn">daemon 重连中…</Toast>
|
||||
</div>
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<div style={{ fontSize: 10.5, color: 'var(--muted)', letterSpacing: '.18em', marginBottom: 6 }}>状态流转时间线</div>
|
||||
<Timeline items={[
|
||||
{ time: '06-12 05:58', text: '审批驳回(方案评审):缺少回滚方案', who: 'you', color: 'var(--red)' },
|
||||
{ time: '06-12 06:10', text: '写方案中 → 待确认方案', who: '编排器' },
|
||||
{ time: '06-12 06:31', text: '审批通过(方案评审)', who: 'you', color: 'var(--green)' },
|
||||
]} />
|
||||
</div>
|
||||
</div>
|
||||
<ul style={{ padding: 0, margin: 0 }}>
|
||||
<EventItem type="task.created" time="13:58" detail="重构 sync 模块" />
|
||||
<EventItem type="approval.requested" time="14:00" detail="spec_review · 等待裁决" />
|
||||
<EventItem type="approval.rejected" time="14:02" detail="↳ 缺少回滚方案" />
|
||||
<EventItem type="run.started" time="14:05" detail="executor · worktree wt-127" />
|
||||
<EventItem type="status.changed" time="14:09" detail="executing → exec_review" />
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
</script></body></html>
|
||||
Reference in New Issue
Block a user