Files
maestro/design/_adherence.oxlintrc.json
T
wangjia 5a1e185b1a 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>
2026-06-13 14:29:07 +08:00

389 lines
15 KiB
JSON

{
"plugins": [
"react",
"import"
],
"rules": {
"react/forbid-elements": [
"warn",
{
"forbid": []
}
],
"no-restricted-imports": [
"warn",
{
"patterns": [
{
"group": [
"components/core/**",
"components/forms/**",
"components/surfaces/**",
"ui_kits/console/**",
"ui_kits/console_mobile/**"
],
"message": "Import design-system components from 'index.js', not component internals."
}
]
}
],
"no-restricted-syntax": [
"warn",
{
"selector": "Literal[value=/#[0-9a-fA-F]{3,8}\\b/]",
"message": "Raw hex color — use a design-system color token via var()."
},
{
"selector": "Literal[value=/\\b\\d+px\\b/]",
"message": "Raw px value — use a design-system spacing token via var()."
},
{
"selector": "Literal[value=/font-family\\s*:\\s*(?!['\\\"]?(?:IBM Plex Mono|Noto Sans SC))/i]",
"message": "Font not provided by the design system. Available: IBM Plex Mono, Noto Sans SC."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute > JSXIdentifier[name!=/^(?:variant|size|disabled|type|onClick|style|children|key|ref|className|style|children)$/]",
"message": "<Button> doesn't accept that prop. Declared props: variant, size, disabled, type, onClick, style, children."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='variant'] > Literal[value!=/^(?:default|solid|ghost|accept|reject)$/]",
"message": "<Button> variant must be one of 'default' | 'solid' | 'ghost' | 'accept' | 'reject'."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:xs)$/]",
"message": "<Button> size must be one of 'xs'."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='type'] > Literal[value!=/^(?:button|submit)$/]",
"message": "<Button> type must be one of 'button' | 'submit'."
},
{
"selector": "JSXOpeningElement[name.name='ComplexityBadge'] > JSXAttribute > JSXIdentifier[name!=/^(?:complexity|key|ref|className|style|children)$/]",
"message": "<ComplexityBadge> doesn't accept that prop. Declared props: complexity."
},
{
"selector": "JSXOpeningElement[name.name='ComplexityBadge'] > JSXAttribute[name.name='complexity'] > Literal[value!=/^(?:hard|medium|easy)$/]",
"message": "<ComplexityBadge> complexity must be one of 'hard' | 'medium' | 'easy'."
},
{
"selector": "JSXOpeningElement[name.name='ComplexitySeg'] > JSXAttribute > JSXIdentifier[name!=/^(?:value|defaultValue|onChange|includeAuto|autoLabel|labels|key|ref|className|style|children)$/]",
"message": "<ComplexitySeg> doesn't accept that prop. Declared props: value, defaultValue, onChange, includeAuto, autoLabel, labels."
},
{
"selector": "JSXOpeningElement[name.name='ComplexitySeg'] > JSXAttribute[name.name='value'] > Literal[value!=/^(?:hard|medium|easy|auto)$/]",
"message": "<ComplexitySeg> value must be one of 'hard' | 'medium' | 'easy' | 'auto'."
},
{
"selector": "JSXOpeningElement[name.name='ComplexitySeg'] > JSXAttribute[name.name='defaultValue'] > Literal[value!=/^(?:hard|medium|easy|auto)$/]",
"message": "<ComplexitySeg> defaultValue must be one of 'hard' | 'medium' | 'easy' | 'auto'."
},
{
"selector": "JSXOpeningElement[name.name='CountBadge'] > JSXAttribute > JSXIdentifier[name!=/^(?:kind|count|label|title|key|ref|className|style|children)$/]",
"message": "<CountBadge> doesn't accept that prop. Declared props: kind, count, label, title."
},
{
"selector": "JSXOpeningElement[name.name='CountBadge'] > JSXAttribute[name.name='kind'] > Literal[value!=/^(?:gate|ready|run|blocked|total)$/]",
"message": "<CountBadge> kind must be one of 'gate' | 'ready' | 'run' | 'blocked' | 'total'."
},
{
"selector": "JSXOpeningElement[name.name='EventItem'] > JSXAttribute > JSXIdentifier[name!=/^(?:type|time|detail|label|key|ref|className|style|children)$/]",
"message": "<EventItem> doesn't accept that prop. Declared props: type, time, detail, label."
},
{
"selector": "JSXOpeningElement[name.name='EventItem'] > JSXAttribute[name.name='type'] > Literal[value!=/^(?:task\\.created|task\\.updated|status\\.changed|approval\\.requested|approval\\.granted|approval\\.rejected|run\\.started|run\\.finished|project\\.synced)$/]",
"message": "<EventItem> type must be one of 'task.created' | 'task.updated' | 'status.changed' | 'approval.requested' | 'approval.granted' | 'approval.rejected' | 'run.started' | 'run.finished' | 'project.synced'."
},
{
"selector": "JSXOpeningElement[name.name='GateCard'] > JSXAttribute > JSXIdentifier[name!=/^(?:gate|kindLabel|title|meta|docLabel|doc|actions|collapsed|foldable|onToggleFold|foldTitle|headerExtra|onHeaderDoubleClick|headerTitle|children|style|key|ref|className|style|children)$/]",
"message": "<GateCard> doesn't accept that prop. Declared props: gate, kindLabel, title, meta, docLabel, doc, actions, collapsed, foldable, onToggleFold, foldTitle, headerExtra, onHeaderDoubleClick, headerTitle, children, style."
},
{
"selector": "JSXOpeningElement[name.name='GateCard'] > JSXAttribute[name.name='gate'] > Literal[value!=/^(?:plan|spec|exec)$/]",
"message": "<GateCard> gate must be one of 'plan' | 'spec' | 'exec'."
},
{
"selector": "JSXOpeningElement[name.name='Input'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|required|type|placeholder|value|defaultValue|onChange|width|style|key|ref|className|style|children)$/]",
"message": "<Input> doesn't accept that prop. Declared props: label, required, type, placeholder, value, defaultValue, onChange, width, style."
},
{
"selector": "JSXOpeningElement[name.name='Input'] > JSXAttribute[name.name='type'] > Literal[value!=/^(?:text|number)$/]",
"message": "<Input> type must be one of 'text' | 'number'."
},
{
"selector": "JSXOpeningElement[name.name='Panel'] > JSXAttribute > JSXIdentifier[name!=/^(?:children|padding|style|key|ref|className|style|children)$/]",
"message": "<Panel> doesn't accept that prop. Declared props: children, padding, style."
},
{
"selector": "JSXOpeningElement[name.name='QuotaMeter'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|pct|detail|cells|style|key|ref|className|style|children)$/]",
"message": "<QuotaMeter> doesn't accept that prop. Declared props: label, pct, detail, cells, style."
},
{
"selector": "JSXOpeningElement[name.name='SectionHead'] > JSXAttribute > JSXIdentifier[name!=/^(?:mark|title|action|sticky|style|key|ref|className|style|children)$/]",
"message": "<SectionHead> doesn't accept that prop. Declared props: mark, title, action, sticky, style."
},
{
"selector": "JSXOpeningElement[name.name='SectionHead'] > JSXAttribute[name.name='mark'] > Literal[value!=/^(?:green|violet|amber|cyan)$/]",
"message": "<SectionHead> mark must be one of 'green' | 'violet' | 'amber' | 'cyan'."
},
{
"selector": "JSXOpeningElement[name.name='Select'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|required|options|value|defaultValue|onChange|style|key|ref|className|style|children)$/]",
"message": "<Select> doesn't accept that prop. Declared props: label, required, options, value, defaultValue, onChange, style."
},
{
"selector": "JSXOpeningElement[name.name='StatusChip'] > JSXAttribute > JSXIdentifier[name!=/^(?:status|label|key|ref|className|style|children)$/]",
"message": "<StatusChip> doesn't accept that prop. Declared props: status, label."
},
{
"selector": "JSXOpeningElement[name.name='StatusChip'] > JSXAttribute[name.name='status'] > Literal[value!=/^(?:init|analyzing|plan_review|decomposed|speccing|spec_review|ready|blocked|queued|executing|exec_review|failed|needs_attention|done|paused|cancelled)$/]",
"message": "<StatusChip> status must be one of 'init' | 'analyzing' | 'plan_review' | 'decomposed' | 'speccing' | 'spec_review' | 'ready' | 'blocked' | 'queued' | 'executing' | 'exec_review' | 'failed' | 'needs_attention' | 'done' | 'paused' | 'cancelled'."
},
{
"selector": "JSXOpeningElement[name.name='Textarea'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|required|placeholder|value|defaultValue|onChange|minHeight|danger|style|key|ref|className|style|children)$/]",
"message": "<Textarea> doesn't accept that prop. Declared props: label, required, placeholder, value, defaultValue, onChange, minHeight, danger, style."
},
{
"selector": "JSXOpeningElement[name.name='TimelineItem'] > JSXAttribute > JSXIdentifier[name!=/^(?:time|text|who|color|key|ref|className|style|children)$/]",
"message": "<TimelineItem> doesn't accept that prop. Declared props: time, text, who, color."
},
{
"selector": "JSXOpeningElement[name.name='Toast'] > JSXAttribute > JSXIdentifier[name!=/^(?:kind|children|style|key|ref|className|style|children)$/]",
"message": "<Toast> doesn't accept that prop. Declared props: kind, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Toast'] > JSXAttribute[name.name='kind'] > Literal[value!=/^(?:ok|err|warn)$/]",
"message": "<Toast> kind must be one of 'ok' | 'err' | 'warn'."
}
]
},
"overrides": [
{
"files": [
"**/index.js"
],
"rules": {
"no-restricted-imports": "off"
}
}
],
"x-omelette": {
"components": {
"Button": {
"replaces": []
},
"ComplexityBadge": {
"replaces": []
},
"ComplexitySeg": {
"replaces": []
},
"CountBadge": {
"replaces": []
},
"EventItem": {
"replaces": []
},
"GateCard": {
"replaces": []
},
"Input": {
"replaces": []
},
"Panel": {
"replaces": []
},
"QuotaMeter": {
"replaces": []
},
"SectionHead": {
"replaces": []
},
"Select": {
"replaces": []
},
"StatusChip": {
"replaces": []
},
"Textarea": {
"replaces": []
},
"TimelineItem": {
"replaces": []
},
"Toast": {
"replaces": []
}
},
"tokens": [
"--accent",
"--accent-dim",
"--amber",
"--amber-dim",
"--bg",
"--bg-deep",
"--border-accent-w",
"--border-default",
"--border-soft",
"--border-w",
"--cyan",
"--cyan-dim",
"--ease-base",
"--ease-fast",
"--faint",
"--font-body",
"--glow-cyan",
"--glow-green",
"--glow-red",
"--glow-violet",
"--green",
"--green-dim",
"--ink",
"--leading-body",
"--leading-doc",
"--line",
"--line-soft",
"--mono",
"--muted",
"--pad-btn",
"--pad-btn-xs",
"--pad-card",
"--pad-chip",
"--pad-input",
"--panel",
"--panel-2",
"--radius-dot",
"--radius-lg",
"--radius-md",
"--radius-sm",
"--radius-xs",
"--red",
"--red-dim",
"--shadow-modal",
"--shadow-pop",
"--shadow-toast",
"--space-1",
"--space-2",
"--space-3",
"--space-4",
"--space-5",
"--space-6",
"--space-7",
"--space-8",
"--space-9",
"--status-bad",
"--status-gate",
"--status-go",
"--status-run",
"--surface-card",
"--surface-page",
"--surface-rail",
"--surface-raised",
"--text-2xl",
"--text-2xs",
"--text-body",
"--text-disabled",
"--text-lg",
"--text-md",
"--text-num",
"--text-secondary",
"--text-sm",
"--text-xl",
"--text-xs",
"--tracking-badge",
"--tracking-head",
"--tracking-label",
"--tracking-logo",
"--tracking-tight",
"--tracking-widest",
"--violet",
"--violet-dim"
],
"tokenKinds": {
"--bg": "color",
"--bg-deep": "color",
"--panel": "color",
"--panel-2": "color",
"--line": "color",
"--line-soft": "color",
"--ink": "color",
"--muted": "color",
"--faint": "color",
"--green": "color",
"--green-dim": "color",
"--violet": "color",
"--violet-dim": "color",
"--amber": "color",
"--amber-dim": "color",
"--red": "color",
"--red-dim": "color",
"--cyan": "color",
"--cyan-dim": "color",
"--glow-green": "shadow",
"--glow-violet": "shadow",
"--glow-cyan": "shadow",
"--glow-red": "shadow",
"--surface-page": "color",
"--surface-rail": "color",
"--surface-card": "color",
"--surface-raised": "color",
"--text-body": "font",
"--text-secondary": "font",
"--text-disabled": "font",
"--border-default": "color",
"--border-soft": "color",
"--accent": "color",
"--accent-dim": "color",
"--status-go": "color",
"--status-gate": "color",
"--status-bad": "color",
"--status-run": "color",
"--mono": "font",
"--font-body": "font",
"--text-2xs": "font",
"--text-xs": "font",
"--text-sm": "font",
"--text-md": "font",
"--text-lg": "font",
"--text-xl": "font",
"--text-2xl": "font",
"--text-num": "font",
"--leading-body": "font",
"--leading-doc": "font",
"--tracking-tight": "font",
"--tracking-label": "font",
"--tracking-badge": "font",
"--tracking-head": "font",
"--tracking-logo": "font",
"--tracking-widest": "font",
"--radius-xs": "radius",
"--radius-sm": "radius",
"--radius-md": "radius",
"--radius-lg": "radius",
"--radius-dot": "radius",
"--border-w": "spacing",
"--border-accent-w": "spacing",
"--space-1": "spacing",
"--space-2": "spacing",
"--space-3": "spacing",
"--space-4": "spacing",
"--space-5": "spacing",
"--space-6": "spacing",
"--space-7": "spacing",
"--space-8": "spacing",
"--space-9": "spacing",
"--pad-btn": "spacing",
"--pad-btn-xs": "spacing",
"--pad-input": "spacing",
"--pad-chip": "spacing",
"--pad-card": "spacing",
"--shadow-pop": "shadow",
"--shadow-modal": "shadow",
"--shadow-toast": "shadow",
"--ease-fast": "other",
"--ease-base": "other"
},
"fontFamilies": [
"IBM Plex Mono",
"Noto Sans SC"
]
}
}