From 5a1e185b1a609f8719306e1a2f8b912a6b2e0d46 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Sat, 13 Jun 2026 14:29:07 +0800 Subject: [PATCH] =?UTF-8?q?docs(design):=20=E8=A7=A3=E5=8E=8B=20Maestro=20?= =?UTF-8?q?Design=20System=20=E5=88=B0=20design/=EF=BC=88=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E5=8F=82=E7=85=A7=E6=9D=90=E6=96=99=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- design/CLAUDE.md | 76 + design/SKILL.md | 50 + design/_adherence.oxlintrc.json | 389 ++ design/_ds_bundle.js | 6186 +++++++++++++++++ design/_ds_manifest.json | 1 + design/assets/icons/add.svg | 1 + design/assets/icons/close.svg | 1 + design/assets/icons/config.svg | 1 + design/assets/icons/deps.svg | 1 + design/assets/icons/event.svg | 1 + design/assets/icons/flow.svg | 1 + design/assets/icons/gate.svg | 1 + design/assets/icons/git.svg | 1 + design/assets/icons/project.svg | 1 + design/assets/icons/ready.svg | 1 + design/assets/icons/reason.svg | 1 + design/assets/icons/running.svg | 1 + design/assets/icons/search.svg | 1 + design/assets/icons/sync.svg | 1 + design/assets/icons/task.svg | 1 + design/components/core/Button.d.ts | 16 + design/components/core/Button.jsx | 31 + design/components/core/Button.prompt.md | 10 + design/components/core/ComplexityBadge.d.ts | 7 + design/components/core/ComplexityBadge.jsx | 17 + .../components/core/ComplexityBadge.prompt.md | 6 + design/components/core/CountBadge.d.ts | 11 + design/components/core/CountBadge.jsx | 40 + design/components/core/CountBadge.prompt.md | 9 + design/components/core/QuotaMeter.d.ts | 15 + design/components/core/QuotaMeter.jsx | 24 + design/components/core/QuotaMeter.prompt.md | 9 + design/components/core/SectionHead.d.ts | 14 + design/components/core/SectionHead.jsx | 17 + design/components/core/SectionHead.prompt.md | 6 + design/components/core/StatusChip.d.ts | 12 + design/components/core/StatusChip.jsx | 46 + design/components/core/StatusChip.prompt.md | 9 + design/components/core/core.card.html | 53 + design/components/forms/ComplexitySeg.d.ts | 16 + design/components/forms/ComplexitySeg.jsx | 32 + .../components/forms/ComplexitySeg.prompt.md | 11 + design/components/forms/Input.d.ts | 17 + design/components/forms/Input.jsx | 28 + design/components/forms/Input.prompt.md | 6 + design/components/forms/Select.d.ts | 13 + design/components/forms/Select.jsx | 69 + design/components/forms/Select.prompt.md | 8 + design/components/forms/Textarea.d.ts | 16 + design/components/forms/Textarea.jsx | 17 + design/components/forms/Textarea.prompt.md | 6 + design/components/forms/forms.card.html | 27 + design/components/surfaces/EventItem.d.ts | 14 + design/components/surfaces/EventItem.jsx | 38 + .../components/surfaces/EventItem.prompt.md | 8 + design/components/surfaces/GateCard.d.ts | 33 + design/components/surfaces/GateCard.jsx | 61 + design/components/surfaces/GateCard.prompt.md | 10 + design/components/surfaces/Panel.d.ts | 9 + design/components/surfaces/Panel.jsx | 10 + design/components/surfaces/Panel.prompt.md | 5 + design/components/surfaces/Timeline.d.ts | 18 + design/components/surfaces/Timeline.jsx | 28 + design/components/surfaces/Timeline.prompt.md | 9 + design/components/surfaces/Toast.d.ts | 10 + design/components/surfaces/Toast.jsx | 17 + design/components/surfaces/Toast.prompt.md | 6 + design/components/surfaces/surfaces.card.html | 46 + design/guidelines/brand-glyphs.card.html | 28 + design/guidelines/brand-logo.card.html | 64 + design/guidelines/brand-motion.card.html | 13 + design/guidelines/brand-texture.card.html | 11 + design/guidelines/colors-ink.card.html | 10 + design/guidelines/colors-light.card.html | 26 + design/guidelines/colors-signals.card.html | 52 + design/guidelines/colors-surfaces.card.html | 13 + design/guidelines/colors-tints.card.html | 13 + design/guidelines/spacing-borders.card.html | 11 + design/guidelines/spacing-glow.card.html | 10 + design/guidelines/spacing-radius.card.html | 11 + design/guidelines/spacing-scale.card.html | 14 + design/guidelines/type-family.card.html | 12 + design/guidelines/type-scale.card.html | 14 + design/guidelines/type-tracking.card.html | 13 + design/readme.md | 79 + design/styles.css | 4 + design/tokens/colors.css | 81 + design/tokens/effects.css | 45 + design/tokens/fonts.css | 4 + design/tokens/typography.css | 30 + design/ui_kits/console/ArchiveSection.jsx | 182 + design/ui_kits/console/EventPanel.jsx | 45 + design/ui_kits/console/GateSection.jsx | 101 + design/ui_kits/console/Sidebar.jsx | 397 ++ design/ui_kits/console/TaskTree.jsx | 317 + design/ui_kits/console/Topbar.jsx | 172 + design/ui_kits/console/data.js | 99 + design/ui_kits/console/i18n.js | 231 + design/ui_kits/console/index.html | 183 + .../ui_kits/console_mobile/MobileChrome.jsx | 93 + design/ui_kits/console_mobile/MobileViews.jsx | 198 + design/ui_kits/console_mobile/index.html | 90 + 102 files changed, 10323 insertions(+) create mode 100644 design/CLAUDE.md create mode 100644 design/SKILL.md create mode 100644 design/_adherence.oxlintrc.json create mode 100644 design/_ds_bundle.js create mode 100644 design/_ds_manifest.json create mode 100644 design/assets/icons/add.svg create mode 100644 design/assets/icons/close.svg create mode 100644 design/assets/icons/config.svg create mode 100644 design/assets/icons/deps.svg create mode 100644 design/assets/icons/event.svg create mode 100644 design/assets/icons/flow.svg create mode 100644 design/assets/icons/gate.svg create mode 100644 design/assets/icons/git.svg create mode 100644 design/assets/icons/project.svg create mode 100644 design/assets/icons/ready.svg create mode 100644 design/assets/icons/reason.svg create mode 100644 design/assets/icons/running.svg create mode 100644 design/assets/icons/search.svg create mode 100644 design/assets/icons/sync.svg create mode 100644 design/assets/icons/task.svg create mode 100644 design/components/core/Button.d.ts create mode 100644 design/components/core/Button.jsx create mode 100644 design/components/core/Button.prompt.md create mode 100644 design/components/core/ComplexityBadge.d.ts create mode 100644 design/components/core/ComplexityBadge.jsx create mode 100644 design/components/core/ComplexityBadge.prompt.md create mode 100644 design/components/core/CountBadge.d.ts create mode 100644 design/components/core/CountBadge.jsx create mode 100644 design/components/core/CountBadge.prompt.md create mode 100644 design/components/core/QuotaMeter.d.ts create mode 100644 design/components/core/QuotaMeter.jsx create mode 100644 design/components/core/QuotaMeter.prompt.md create mode 100644 design/components/core/SectionHead.d.ts create mode 100644 design/components/core/SectionHead.jsx create mode 100644 design/components/core/SectionHead.prompt.md create mode 100644 design/components/core/StatusChip.d.ts create mode 100644 design/components/core/StatusChip.jsx create mode 100644 design/components/core/StatusChip.prompt.md create mode 100644 design/components/core/core.card.html create mode 100644 design/components/forms/ComplexitySeg.d.ts create mode 100644 design/components/forms/ComplexitySeg.jsx create mode 100644 design/components/forms/ComplexitySeg.prompt.md create mode 100644 design/components/forms/Input.d.ts create mode 100644 design/components/forms/Input.jsx create mode 100644 design/components/forms/Input.prompt.md create mode 100644 design/components/forms/Select.d.ts create mode 100644 design/components/forms/Select.jsx create mode 100644 design/components/forms/Select.prompt.md create mode 100644 design/components/forms/Textarea.d.ts create mode 100644 design/components/forms/Textarea.jsx create mode 100644 design/components/forms/Textarea.prompt.md create mode 100644 design/components/forms/forms.card.html create mode 100644 design/components/surfaces/EventItem.d.ts create mode 100644 design/components/surfaces/EventItem.jsx create mode 100644 design/components/surfaces/EventItem.prompt.md create mode 100644 design/components/surfaces/GateCard.d.ts create mode 100644 design/components/surfaces/GateCard.jsx create mode 100644 design/components/surfaces/GateCard.prompt.md create mode 100644 design/components/surfaces/Panel.d.ts create mode 100644 design/components/surfaces/Panel.jsx create mode 100644 design/components/surfaces/Panel.prompt.md create mode 100644 design/components/surfaces/Timeline.d.ts create mode 100644 design/components/surfaces/Timeline.jsx create mode 100644 design/components/surfaces/Timeline.prompt.md create mode 100644 design/components/surfaces/Toast.d.ts create mode 100644 design/components/surfaces/Toast.jsx create mode 100644 design/components/surfaces/Toast.prompt.md create mode 100644 design/components/surfaces/surfaces.card.html create mode 100644 design/guidelines/brand-glyphs.card.html create mode 100644 design/guidelines/brand-logo.card.html create mode 100644 design/guidelines/brand-motion.card.html create mode 100644 design/guidelines/brand-texture.card.html create mode 100644 design/guidelines/colors-ink.card.html create mode 100644 design/guidelines/colors-light.card.html create mode 100644 design/guidelines/colors-signals.card.html create mode 100644 design/guidelines/colors-surfaces.card.html create mode 100644 design/guidelines/colors-tints.card.html create mode 100644 design/guidelines/spacing-borders.card.html create mode 100644 design/guidelines/spacing-glow.card.html create mode 100644 design/guidelines/spacing-radius.card.html create mode 100644 design/guidelines/spacing-scale.card.html create mode 100644 design/guidelines/type-family.card.html create mode 100644 design/guidelines/type-scale.card.html create mode 100644 design/guidelines/type-tracking.card.html create mode 100644 design/readme.md create mode 100644 design/styles.css create mode 100644 design/tokens/colors.css create mode 100644 design/tokens/effects.css create mode 100644 design/tokens/fonts.css create mode 100644 design/tokens/typography.css create mode 100644 design/ui_kits/console/ArchiveSection.jsx create mode 100644 design/ui_kits/console/EventPanel.jsx create mode 100644 design/ui_kits/console/GateSection.jsx create mode 100644 design/ui_kits/console/Sidebar.jsx create mode 100644 design/ui_kits/console/TaskTree.jsx create mode 100644 design/ui_kits/console/Topbar.jsx create mode 100644 design/ui_kits/console/data.js create mode 100644 design/ui_kits/console/i18n.js create mode 100644 design/ui_kits/console/index.html create mode 100644 design/ui_kits/console_mobile/MobileChrome.jsx create mode 100644 design/ui_kits/console_mobile/MobileViews.jsx create mode 100644 design/ui_kits/console_mobile/index.html diff --git a/design/CLAUDE.md b/design/CLAUDE.md new file mode 100644 index 0000000..484b57c --- /dev/null +++ b/design/CLAUDE.md @@ -0,0 +1,76 @@ +# CLAUDE.md — Maestro Design System + +Engineering conventions for working in this design-system repo. Read alongside `README.md` (design foundations) and `SKILL.md` (the design language quick-start). The phosphor-console aesthetic and the rules in SKILL.md are binding — this file covers **how the code is organized and how to extend it correctly**. + +## What this repo is + +A compiler-backed design system. On every change an automated compiler reads the sources and regenerates three files — **never hand-edit these**: + +- `_ds_bundle.js` — the compiled component library, exposed as `window.MaestroDesignSystem_a6a290` +- `_ds_manifest.json` — card / token / component index +- `_adherence.oxlintrc.json` — lint rules + +The only fixed entry point is `styles.css` (root). It must stay a list of `@import` lines — never inline rules there. + +## Layout + +``` +styles.css # entry — @import only +tokens/ # CSS custom properties, one file per concern + colors.css # base + signal + -dim + semantic aliases + [data-theme=light] scope + typography.css # --mono family, size/leading/tracking scale + effects.css # spacing, radii, borders, shadows, @keyframes + fonts.css # Google Fonts @import (IBM Plex Mono + Noto Sans SC) +guidelines/*.card.html # specimen cards for the Design System tab +components// # core | forms | surfaces + Name.jsx # the component (named export) + Name.d.ts # props contract (+ @startingPoint JSDoc if applicable) + Name.prompt.md # one-line what/when + JSX example + .card.html # @dsCard showcase for the whole group +ui_kits/console/ # desktop console recreation (composes components) +ui_kits/console_mobile/ # 390px mobile recreation +assets/icons/*.svg # double-tone icon set +``` + +## Authoring a component (the contract the compiler enforces) + +A component is discovered when a directory holds **all three**: `Name.jsx` (PascalCase, with `export function Name(props)`), `Name.d.ts`, and ideally `Name.prompt.md`. To add or change one: + +1. Write `Name.jsx` — React only, no npm deps, no CSS-in-JS libs. Style via the CSS custom properties (`var(--…)`). Self-contained; siblings may import each other by relative path. +2. Inject component CSS once via an `ensureCss()` guard that appends a ` +
+ diff --git a/design/components/forms/ComplexitySeg.d.ts b/design/components/forms/ComplexitySeg.d.ts new file mode 100644 index 0000000..3d8b89d --- /dev/null +++ b/design/components/forms/ComplexitySeg.d.ts @@ -0,0 +1,16 @@ +/** + * 复杂度分段选择:HARD / MEDIUM / EASY 三段,选中段填充对应信号色 dim 底。 + * 可选第四段 auto(智能 · 由模型决定复杂度),青色信号。 + */ +export interface ComplexitySegProps { + value?: 'hard' | 'medium' | 'easy' | 'auto'; + defaultValue?: 'hard' | 'medium' | 'easy' | 'auto'; + onChange?: (value: 'hard' | 'medium' | 'easy' | 'auto') => void; + /** 追加 auto 段(青色):由模型决定复杂度 */ + includeAuto?: boolean; + /** auto 段文字,默认 'AUTO'(可传本地化「智能」) */ + autoLabel?: string; + /** 覆盖各段文字:{ hard, medium, easy, auto } 局部即可 */ + labels?: Partial>; +} +export declare function ComplexitySeg(props: ComplexitySegProps): JSX.Element; diff --git a/design/components/forms/ComplexitySeg.jsx b/design/components/forms/ComplexitySeg.jsx new file mode 100644 index 0000000..f351905 --- /dev/null +++ b/design/components/forms/ComplexitySeg.jsx @@ -0,0 +1,32 @@ +const css = ` +.m-seg{display:inline-flex;border:1px solid var(--line);font-family:var(--mono);border-radius:var(--radius-sm,4px);overflow:hidden} +.m-seg button{font-family:var(--mono);padding:7px 13px;cursor:pointer;font-size:11px;font-weight:700;letter-spacing:.12em;color:var(--faint);background:transparent;border:none;border-radius:0} +.m-seg button + button{border-left:1px solid var(--line)} +.m-seg button.on-hard{background:var(--red-dim);color:var(--red)} +.m-seg button.on-medium{background:var(--amber-dim);color:var(--amber)} +.m-seg button.on-easy{background:var(--green-dim);color:var(--green)} +.m-seg button.on-auto{background:var(--cyan-dim);color:var(--cyan)} +`; +function ensureCss() { + if (typeof document === 'undefined' || document.getElementById('m-seg-css')) return; + const s = document.createElement('style'); s.id = 'm-seg-css'; s.textContent = css; + document.head.appendChild(s); +} +const BASE_OPTS = [['hard', 'HARD'], ['medium', 'MEDIUM'], ['easy', 'EASY']]; + +export function ComplexitySeg({ value, defaultValue = 'medium', onChange, includeAuto, autoLabel = 'AUTO', labels }) { + ensureCss(); + const [inner, setInner] = React.useState(defaultValue); + const cur = value !== undefined ? value : inner; + const opts = includeAuto ? [...BASE_OPTS, ['auto', autoLabel]] : BASE_OPTS; + return ( + + {opts.map(([v, lab]) => ( + + ))} + + ); +} diff --git a/design/components/forms/ComplexitySeg.prompt.md b/design/components/forms/ComplexitySeg.prompt.md new file mode 100644 index 0000000..7dbe932 --- /dev/null +++ b/design/components/forms/ComplexitySeg.prompt.md @@ -0,0 +1,11 @@ +复杂度三段选择器,建任务表单用。选中段填充信号色 dim 底(红/琥珀/绿)。 + +```jsx + setCplx(v)} /> +``` + +可加第四段 `auto`(青色,「智能」= 由模型决定复杂度)。`autoLabel` 传本地化文字,`labels` 覆盖任意段: + +```jsx + +``` diff --git a/design/components/forms/Input.d.ts b/design/components/forms/Input.d.ts new file mode 100644 index 0000000..07ef3cd --- /dev/null +++ b/design/components/forms/Input.d.ts @@ -0,0 +1,17 @@ +/** + * 文本/数字输入框:深底直角,focus 绿 dim 边框 + 1px ring。可带顶部小标签与红色必填星。 + */ +export interface InputProps { + /** 顶部 11px 字距标签 */ + label?: string; + /** 标签后追加红色 * */ + required?: boolean; + type?: 'text' | 'number'; + placeholder?: string; + value?: string; + defaultValue?: string; + onChange?: (value: string) => void; + width?: number | string; + style?: React.CSSProperties; +} +export declare function Input(props: InputProps): JSX.Element; diff --git a/design/components/forms/Input.jsx b/design/components/forms/Input.jsx new file mode 100644 index 0000000..b555a16 --- /dev/null +++ b/design/components/forms/Input.jsx @@ -0,0 +1,28 @@ +const css = ` +.m-label{display:flex;flex-direction:column;gap:4px;font-family:var(--mono);font-size:11px;color:var(--muted);letter-spacing:.08em} +.m-input{font-family:var(--mono);font-size:13px;background:var(--bg-deep);color:var(--ink);border:1px solid var(--line);padding:7px 10px;outline:none;transition:border-color .12s;border-radius:var(--radius-sm,4px)} +.m-input:focus{border-color:var(--green-dim);box-shadow:0 0 0 1px var(--green-dim)} +.m-input::placeholder{color:var(--faint)} +.m-req{color:var(--red)} +`; +export function ensureFieldCss() { + if (typeof document === 'undefined' || document.getElementById('m-field-css')) return; + const s = document.createElement('style'); s.id = 'm-field-css'; s.textContent = css; + document.head.appendChild(s); +} + +export function Input({ label, required, type = 'text', placeholder, value, defaultValue, onChange, width, style }) { + ensureFieldCss(); + const ctrl = ( + onChange(e.target.value) : undefined} readOnly={value !== undefined && !onChange} /> + ); + if (!label) return ctrl; + return ( + + ); +} diff --git a/design/components/forms/Input.prompt.md b/design/components/forms/Input.prompt.md new file mode 100644 index 0000000..5061fb9 --- /dev/null +++ b/design/components/forms/Input.prompt.md @@ -0,0 +1,6 @@ +输入框:bg-deep 底、直角、focus 时绿 dim 描边+ring。占位文案给真实示例(如 `/path/to/repo`)。 + +```jsx + + +``` diff --git a/design/components/forms/Select.d.ts b/design/components/forms/Select.d.ts new file mode 100644 index 0000000..badd0d8 --- /dev/null +++ b/design/components/forms/Select.d.ts @@ -0,0 +1,13 @@ +/** + * 下拉选择(自绘弹层,非原生 select):触发器与 Input 同形制,弹出磷光风格选项菜单——当前项绿色 ▍ 标记 + 抬升底。选项文案用「值 · 中文」格式(如 "manual · 手动")。 + */ +export interface SelectProps { + label?: string; + required?: boolean; + options?: Array; + value?: string; + defaultValue?: string; + onChange?: (value: string) => void; + style?: React.CSSProperties; +} +export declare function Select(props: SelectProps): JSX.Element; diff --git a/design/components/forms/Select.jsx b/design/components/forms/Select.jsx new file mode 100644 index 0000000..d00c937 --- /dev/null +++ b/design/components/forms/Select.jsx @@ -0,0 +1,69 @@ +import { ensureFieldCss } from './Input.jsx'; + +const css = ` +.m-select{position:relative;display:inline-flex;flex-direction:column;min-width:120px} +.m-select-trigger{font-family:var(--mono);font-size:13px;background:var(--bg-deep);color:var(--ink);border:1px solid var(--line);border-radius:var(--radius-sm,4px);padding:7px 10px;cursor:pointer;outline:none;transition:border-color .12s;display:flex;align-items:center;gap:10px;text-align:left;width:100%} +.m-select-trigger:hover{border-color:var(--muted)} +.m-select-trigger.open,.m-select-trigger:focus{border-color:var(--green-dim);box-shadow:0 0 0 1px var(--green-dim)} +.m-select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.m-select-caret{flex:none;font-size:8px;color:var(--faint);transition:transform .12s} +.m-select-trigger.open .m-select-caret{transform:rotate(180deg)} +.m-select-menu{position:absolute;top:calc(100% + 5px);left:0;right:0;z-index:80;display:grid;background:var(--bg-deep);border:1px solid var(--line);border-radius:var(--radius-md,6px);padding:4px;box-shadow:0 10px 30px rgba(0,0,0,.65);animation:maestro-rise .12s ease both;max-height:260px;overflow-y:auto} +.m-select-opt{font-family:var(--mono);font-size:12.5px;text-align:left;background:transparent;color:var(--ink);border:none;border-radius:4px;padding:7px 10px 7px 8px;cursor:pointer;display:flex;align-items:center;gap:7px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.m-select-opt:hover{background:var(--panel)} +.m-select-opt.on{background:var(--panel-2);color:var(--green);font-weight:700} +.m-select-mark{flex:none;width:8px;color:var(--green);font-size:10px} +`; +function ensureSelectCss() { + if (typeof document === 'undefined' || document.getElementById('m-select-css')) return; + const s = document.createElement('style'); s.id = 'm-select-css'; s.textContent = css; + document.head.appendChild(s); +} + +export function Select({ label, required, options = [], value, defaultValue, onChange, style }) { + ensureFieldCss(); + ensureSelectCss(); + const opts = options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o)); + const [inner, setInner] = React.useState(defaultValue !== undefined ? defaultValue : (opts[0] ? opts[0].value : '')); + const cur = value !== undefined ? value : inner; + const curOpt = opts.find((o) => o.value === cur); + const [open, setOpen] = React.useState(false); + const ref = React.useRef(null); + React.useEffect(() => { + if (!open) return; + const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; + const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; + document.addEventListener('mousedown', onDoc); + document.addEventListener('keydown', onKey); + return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); }; + }, [open]); + const pick = (v) => { + setInner(v); setOpen(false); + if (onChange) onChange(v); + }; + const ctrl = ( + + + {open ? ( + + {opts.map((o) => ( + + ))} + + ) : null} + + ); + if (!label) return ctrl; + return ( + + ); +} diff --git a/design/components/forms/Select.prompt.md b/design/components/forms/Select.prompt.md new file mode 100644 index 0000000..547a6eb --- /dev/null +++ b/design/components/forms/Select.prompt.md @@ -0,0 +1,8 @@ +下拉选择,与 Input 同形制。选项标签遵循「英文值 · 中文说明」惯例。 + +```jsx +