@qijenchen/design-system 0.1.0-beta.73 → 0.1.0-beta.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  2. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  3. package/dist/components/BulkActionBar/bulk-action-bar.js +1 -1
  4. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  5. package/dist/components/DataTable/data-table.d.ts +27 -6
  6. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  7. package/dist/components/DataTable/data-table.js +57 -34
  8. package/dist/components/DataTable/data-table.js.map +1 -1
  9. package/ds-canonical/fork/governance.lock +7 -7
  10. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +2 -0
  11. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +2 -0
  12. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +2 -0
  13. package/ds-canonical/fork/skills/prototype/references/audit-checks.md +1 -0
  14. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -0
  15. package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -0
  16. package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +1 -0
  17. package/ds-canonical/hooks/check_plugin_fork_health.sh +2 -2
  18. package/ds-canonical/hooks/check_story_invariants.sh +26 -0
  19. package/ds-canonical/hooks/lib/_app_shell_primary_header_consistency.sh +31 -4
  20. package/ds-canonical/hooks/session_start_governance_check.sh +1 -1
  21. package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +46 -2
  22. package/ds-canonical/hooks/tests/test_check_story_invariants.sh +30 -0
  23. package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
  24. package/llms-full.txt +1 -1
  25. package/llms.txt +1 -1
  26. package/package.json +1 -1
  27. package/src/components/Accordion/accordion.principles.stories.tsx +3 -3
  28. package/src/components/Alert/alert.principles.stories.tsx +5 -5
  29. package/src/components/AppShell/_demo-helpers.tsx +23 -6
  30. package/src/components/AppShell/app-shell.principles.stories.tsx +9 -8
  31. package/src/components/AppShell/app-shell.spec.md +9 -8
  32. package/src/components/AppShell/app-shell.stories.tsx +5 -3
  33. package/src/components/AspectRatio/aspect-ratio.principles.stories.tsx +1 -1
  34. package/src/components/Avatar/avatar.principles.stories.tsx +3 -3
  35. package/src/components/Badge/badge.principles.stories.tsx +3 -3
  36. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +3 -3
  37. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +1 -1
  38. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +3 -3
  39. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  40. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +2 -2
  41. package/src/components/BulkActionBar/bulk-action-bar.tsx +3 -2
  42. package/src/components/Button/button.principles.stories.tsx +3 -3
  43. package/src/components/Calendar/calendar.principles.stories.tsx +3 -3
  44. package/src/components/Carousel/carousel.principles.stories.tsx +2 -2
  45. package/src/components/Chart/chart.principles.stories.tsx +4 -4
  46. package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
  47. package/src/components/Chip/chip.principles.stories.tsx +3 -3
  48. package/src/components/Coachmark/coachmark.principles.stories.tsx +3 -3
  49. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  50. package/src/components/Combobox/combobox.principles.stories.tsx +5 -5
  51. package/src/components/Command/command.principles.stories.tsx +7 -7
  52. package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
  53. package/src/components/DataTable/data-table.spec.md +23 -15
  54. package/src/components/DataTable/data-table.stories.tsx +29 -25
  55. package/src/components/DataTable/data-table.tsx +92 -44
  56. package/src/components/DateGrid/date-grid.principles.stories.tsx +4 -4
  57. package/src/components/DatePicker/date-picker.principles.stories.tsx +5 -5
  58. package/src/components/DescriptionList/description-list.principles.stories.tsx +5 -5
  59. package/src/components/Dialog/dialog.principles.stories.tsx +4 -4
  60. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  61. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +5 -5
  62. package/src/components/Empty/empty.principles.stories.tsx +2 -2
  63. package/src/components/Field/field.principles.stories.tsx +4 -4
  64. package/src/components/FileItem/file-item.principles.stories.tsx +5 -5
  65. package/src/components/FileUpload/file-upload.principles.stories.tsx +4 -4
  66. package/src/components/FileViewer/file-viewer.principles.stories.tsx +5 -5
  67. package/src/components/HoverCard/hover-card.principles.stories.tsx +5 -5
  68. package/src/components/Input/input.principles.stories.tsx +4 -4
  69. package/src/components/LinkInput/link-input.principles.stories.tsx +5 -5
  70. package/src/components/Menu/menu-item.principles.stories.tsx +7 -7
  71. package/src/components/Notice/notice.principles.stories.tsx +7 -7
  72. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  73. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +5 -5
  74. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +3 -3
  75. package/src/components/Popover/popover.principles.stories.tsx +1 -1
  76. package/src/components/ProfileCard/profile-card.principles.stories.tsx +1 -1
  77. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +2 -2
  78. package/src/components/RadioGroup/radio-group.principles.stories.tsx +2 -2
  79. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  80. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +4 -4
  81. package/src/components/Select/select.principles.stories.tsx +5 -5
  82. package/src/components/SelectMenu/select-menu.principles.stories.tsx +8 -8
  83. package/src/components/SelectionControl/selection-item.principles.stories.tsx +7 -7
  84. package/src/components/Separator/separator.principles.stories.tsx +4 -4
  85. package/src/components/Sheet/sheet.principles.stories.tsx +2 -2
  86. package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
  87. package/src/components/Sidebar/sidebar.spec.md +1 -1
  88. package/src/components/Skeleton/skeleton.principles.stories.tsx +5 -5
  89. package/src/components/Slider/slider.principles.stories.tsx +3 -3
  90. package/src/components/Steps/steps.principles.stories.tsx +4 -4
  91. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  92. package/src/components/Tabs/tabs.principles.stories.tsx +3 -3
  93. package/src/components/Tag/tag.principles.stories.tsx +3 -3
  94. package/src/components/Textarea/textarea.principles.stories.tsx +2 -2
  95. package/src/components/TimePicker/time-picker.principles.stories.tsx +5 -5
  96. package/src/components/Toast/toast.principles.stories.tsx +2 -2
  97. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  98. package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
  99. package/src/components/TreeView/tree-view.stories.tsx +1 -1
  100. package/src/tokens/color/color.spec.md +2 -0
@@ -110,11 +110,11 @@
110
110
  },
111
111
  {
112
112
  "file": "skills/bug-fix-rhythm/SKILL.md",
113
- "sha256": "9901dc89a2e58072687b37ab8160b0cfe477f28eb3fd812a70e559870d1ab407"
113
+ "sha256": "4bca87df4f2777fe76f8e302698d373fb0ba60699922fb8b35c8aefae1c51ae0"
114
114
  },
115
115
  {
116
116
  "file": "skills/code-quality-audit/SKILL.md",
117
- "sha256": "9fec4a0a89799f73361e6352d21e200e6e7b3557a61c33e8b9d5b0affaa2809d"
117
+ "sha256": "fb65ebf0c72f7b475640f4861a1bfa2e9140727e2ff051fbd58f8348c0dc187d"
118
118
  },
119
119
  {
120
120
  "file": "skills/delivery-handoff/references/flow-diagram.md",
@@ -150,7 +150,7 @@
150
150
  },
151
151
  {
152
152
  "file": "skills/product-ui-audit/SKILL.md",
153
- "sha256": "8d814dbd5409d8a6069a2a2a32360f5e1f356d44246005a7abba63b85599b723"
153
+ "sha256": "cef077d82f906fd26d1507bbab00f5215a75583780aa893805d3018eef000a05"
154
154
  },
155
155
  {
156
156
  "file": "skills/propose-options/SKILL.md",
@@ -158,7 +158,7 @@
158
158
  },
159
159
  {
160
160
  "file": "skills/prototype/references/audit-checks.md",
161
- "sha256": "76ccad0fcfaaaacf4c45825bc42419374e3621572b72c847f51a9e96778a745c"
161
+ "sha256": "6f17d6b4bc6ae94eedef8c39b68d4ab1baec92ef1035eeab462e17239c2803e3"
162
162
  },
163
163
  {
164
164
  "file": "skills/prototype/references/benchmark-sources.md",
@@ -186,7 +186,7 @@
186
186
  },
187
187
  {
188
188
  "file": "skills/scan-similar-bugs/SKILL.md",
189
- "sha256": "ad532b903eb9dc4963fc938ace0b32fbdf14031ee60ee851f6ca1fe703ea893c"
189
+ "sha256": "79c5125f6d65d9d530c09ba2f19afe60174278448b122f4d4a66f9280c1312ca"
190
190
  },
191
191
  {
192
192
  "file": "skills/ux-audit/SKILL.md",
@@ -198,7 +198,7 @@
198
198
  },
199
199
  {
200
200
  "file": "skills/visual-audit/references/audit-architecture.md",
201
- "sha256": "781ed6929c9bf354a0170f292fea23ce84dd7d7c8d9cba32698679ec42a2196f"
201
+ "sha256": "48c931ad559c1e37ecebd08d289509692c79d9c92d2574f65e89bf286b7c95cf"
202
202
  },
203
203
  {
204
204
  "file": "skills/visual-audit/references/visual-checklist.md",
@@ -210,7 +210,7 @@
210
210
  },
211
211
  {
212
212
  "file": "skills/visual-audit/SKILL.md",
213
- "sha256": "4e2cab48827f7301045656d10b926b2666582a89f6ce1e57a9a063beff82bb84"
213
+ "sha256": "0af4957c5ab08f8c04bbc944900ae45b719eed879e667de1fd139392e57c4525"
214
214
  }
215
215
  ]
216
216
  }
@@ -3,6 +3,8 @@ name: bug-fix-rhythm
3
3
  description: Batch-end-verify rhythm + parallel tool batch + user-listed N-rule MUST-ALL + claim runtime verify. Invoke when entering multi-fix session 或 user 列 numbered rules. Replaces M32(c)(d)(h1)(h2) sub-invariants(2026-05-12 split per Knowledge-Prune Checkpoint 2)。
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # /bug-fix-rhythm — 多 fix session 的 rhythm canonical
7
9
 
8
10
  **目的**:當 session 有 ≥ 2 fixes(常見 visual bug session / user 列 N 條 numbered 規則),走 batch-end-verify rhythm,而非 per-fix verify cycle waste。**M32 split 後 (c)(d)(h1)(h2) 的 home**。
@@ -3,6 +3,8 @@ name: code-quality-audit
3
3
  description: Clean code 量化稽核 — `any` 使用 / dead export / file size / long function / circular dep / magic number。補 `/design-system-audit` 只管「design canonical」的缺口。Invoke via /code-quality-audit scope=all(release / 季度)OR scope=changed(daily)OR scope=component:X(focused)。Auto-chain by `/design-system-audit --deep` Dim 27 + `/component-quality-gate` Ship phase + `/new-component` Phase 4.5。
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # Code Quality Audit — Clean Code 量化稽核
7
9
 
8
10
  Scope:**tsx / ts code hygiene**,跟 design canonical 正交。
@@ -3,6 +3,8 @@ name: product-ui-audit
3
3
  description: Audit product / consumer UI code (非 design-system 本身) against DS usage discipline + mindset adherence. Catches element misuse / design principle violations / token leakage / geometry bugs / a11y gaps across 7 dimensions. Invoke via /product-ui-audit when user says「audit 這個 UI」「檢查 X feature 用對 DS 嗎」「這段 code 符合設計原則嗎」,or auto-invoked by /prototype Phase 3.5 gate.
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # Product UI Audit Workflow
7
9
 
8
10
  Purpose: design-system-audit audits the **DS itself**(spec / cva / SSOT 三方漂移);product-ui-audit audits **consumer UI code**(不是 DS 本身,是 app / exploration / feature code — 即「consumer 用 DS 的地方」)。防止:
@@ -1,3 +1,4 @@
1
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
1
2
  # Phase 3.5 Self-audit checklist(/prototype 用)
2
3
 
3
4
  對齊 CLAUDE.md `# 稽核 canonical` M6(stakeholder-visible 產出 → 強制進階模式)。比稿本質是「給 stakeholder 選視覺方向」,6 維任一沒 audit 好就 present = 比稿品質失準。
@@ -3,6 +3,8 @@ name: scan-similar-bugs
3
3
  description: Auto-invoke after fix commits — extracts root-cause anti-pattern, greps DS-wide for same pattern, runs visual verify, batches related fixes. Closes M10 mechanical gap. Invoke via /scan-similar-bugs.
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # /scan-similar-bugs — Fix-time DS-wide Exhaustive Scan
7
9
 
8
10
  **目的**:任何 bug fix 提交後,**機械化掃 DS-wide 找同 pattern**,而非靠 model 記得 M10。
@@ -3,6 +3,8 @@ name: visual-audit
3
3
  description: Pixel-level visual audit for design-system components based on user-provided screenshots. Catches bug classes that code/spec audits cannot see — asymmetric padding / broken overlay positioning / gap-eaten-by-hover-bg / baseline misalignment / overflow indicator obscuring content / wrong zoom step / dark-mode token mismatch. Requires a screenshot to run; refuses to proceed on guesses. Invoke via /visual-audit when user says「視覺對齊不對」「排版有問題」「gap 好像錯了」「看起來歪了」or uploads a Storybook screenshot asking「這樣對嗎」,auto-invoked by `/design-system-audit` Phase 3 (post-fix visual verify) and `/component-quality-gate` Ship phase.
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # Visual Audit — screenshot-based pixel-level 稽核
7
9
 
8
10
  ## 存在意義
@@ -1,3 +1,4 @@
1
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
1
2
  # Visual audit — Layer A + Layer B 架構 + Interactive state coverage canonical
2
3
 
3
4
  (Extracted from SKILL.md 2026-05-04 — file budget consolidation,SKILL.md 留 1-line summary + pointer 本檔。)
@@ -113,12 +113,12 @@ if [ -z "$REMOTE_VERSION" ] || [ "$REMOTE_VERSION" = "null" ]; then exit 0; fi
113
113
  if [ "$LOCAL_VERSION" != "$REMOTE_VERSION" ]; then
114
114
  cat << EOF
115
115
 
116
- 📦 DS plugin update available:
116
+ 📦 DS governance update available:
117
117
  Local installed: $LOCAL_VERSION
118
118
  Latest published: $REMOTE_VERSION
119
119
 
120
120
  Run in terminal (1 command):
121
- npm run sync-all # npm + plugin marketplace + plugin install + restart prompt
121
+ npm run sync-all # npm install @beta(治理本體)+ 刷新接線骨架(committed launchers + settings + skills);npm-only,免 plugin
122
122
 
123
123
 
124
124
  (Per user 2026-05-27 directive「DS 增刪改自動同步」— this hook detects staleness on session start.)
@@ -708,6 +708,31 @@ rule_handcraft_overlay_header() {
708
708
  fi
709
709
  }
710
710
 
711
+ # ─────────────────────────────────────────────────────────────────────────────
712
+ # R10 — link_canonical(2026-06-22 codify per user;color.spec.md「Action — Primary」)
713
+ # canonical 文字連結 = text-primary + hover:text-primary-hover(hover 換色,無底線);
714
+ # 禁 `text-primary hover:underline`(用底線取代換色)。真元件(Breadcrumb / LinkInput /
715
+ # Button / RadioGroup)已遵循;本 rule 防 story LinkTo 導覽連結復發 drift(曾累積 232 處)。
716
+ # P1 WARN(story 導覽 style 低風險,不 block)。豁免:檔首 `// @link-style-allow:`。
717
+ # ─────────────────────────────────────────────────────────────────────────────
718
+ rule_link_canonical() {
719
+ [ "$EVENT" = "PostToolUse" ] && return 0
720
+ printf '%s' "$NEW_CONTENT" | grep -q '@link-style-allow:' && return 0
721
+ # text-primary …(中間可隔 tailwind class:cursor-pointer / font-medium 等)… hover:underline。
722
+ # 中間段限 class-char [a-z0-9:_-] → 不誤判描述用 label「text-primary · hover:underline」(· 非 class-char)。
723
+ if printf '%s' "$NEW_CONTENT" | grep -qE 'text-primary([[:space:]]+[a-z0-9:_-]+)*[[:space:]]+hover:underline'; then
724
+ {
725
+ echo ""
726
+ echo "╔═══ R10 link_canonical — 連結 hover 樣式 drift ═══"
727
+ echo "[P1 WARN] ${FILE_PATH}"
728
+ echo " 偵測到 'text-primary hover:underline' = 用底線取代換色,偏離 canonical。"
729
+ echo " canonical(color.spec.md「Action — Primary」)= text-primary + hover:text-primary-hover(換色,無底線)。"
730
+ echo " 修:hover:underline → hover:text-primary-hover。豁免:檔首 // @link-style-allow: <reason>"
731
+ } >&2
732
+ # P1 warn:not block
733
+ fi
734
+ }
735
+
711
736
  # ─── Run rules ───
712
737
  rule_anatomy
713
738
  rule_slot_split
@@ -718,5 +743,6 @@ rule_description_jargon
718
743
  rule_story_baseline_reference
719
744
  rule_story_archetype_registry
720
745
  rule_handcraft_overlay_header
746
+ rule_link_canonical
721
747
 
722
748
  exit $WORST
@@ -4,7 +4,7 @@
4
4
  # 2026-05-21 ship per user directive「該程式化的就程式化」+「確認當有 global header 時,
5
5
  # sidebar 內的 header 應該要拿掉」+ world-class GitHub/Gmail/Figma 共識。
6
6
  #
7
- # Detects 2 violations in AppShell consumer code:
7
+ # Detects 3 violations in AppShell consumer code:
8
8
  # V1) `layout="primary-header"` without `globalHeader=...` prop
9
9
  # → 缺 globalHeader 而 layout=primary-header 是邏輯矛盾(per app-shell.spec.md
10
10
  # 「primary-header = primary-sidebar + 一條 global header」)
@@ -12,6 +12,14 @@
12
12
  # V2) `layout="primary-header"` + 任何 `<SidebarHeader>...</SidebarHeader>` 在同 file
13
13
  # → WorkspaceBrand 已該在 globalHeader,sidebar 內不該再有 SidebarHeader
14
14
  # (per app-shell.spec.md「WorkspaceBrand 放置 SSOT」+ world-class GitHub/Gmail/Figma 一致)
15
+ # 例外:同 file 用 useSidebar/isMobile = mobile-only 補品牌的正確 responsive fork(豁免)
16
+ #
17
+ # V3) `layout="primary-header"` + 任何 `<SidebarFooter>...</SidebarFooter>` 在同 file(2026-06-18 beta.74)
18
+ # → primary-header 帳號入口家在 globalHeader 右(收成 Sheet 鏡像到 Sheet header 右),**不該**用
19
+ # sidebar footer〔那是 primary-sidebar 帳號家慣例〕→ 誤用 = 模式混淆
20
+ # (per app-shell.spec.md「帳號入口(Account entry)放置 SSOT」+ Material modal nav drawer
21
+ # 「account switcher 放 drawer header」)。與 V2 不同:primary-header 任何 breakpoint(含 mobile
22
+ # Sheet)帳號家都在 header 區 → V3 不設 isMobile 豁免;非帳號用途 footer 走 escape allowlist。
15
23
  #
16
24
  # 對齊 .claude/rules/self-verify.md「Pre-edit」階段 + check_chrome_header_handcraft.sh /
17
25
  # check_overlay_handcraft.sh 等既有 SSOT-enforcement hook idiom。
@@ -40,8 +48,12 @@ esac
40
48
  # Escape allowlist
41
49
  if grep -q "@app-shell-primary-header-allow:" "$TARGET"; then exit 0; fi
42
50
 
43
- # 偵測 layout="primary-header"
44
- if ! grep -q 'layout="primary-header"\|layout={["\047]primary-header["\047]}' "$TARGET"; then exit 0; fi
51
+ # 偵測 layout="primary-header"(三種 JSX 形式:layout="x" / layout={"x"} / layout={'x'})
52
+ # 2026-06-18 beta.74 fix(adversarial audit P1):舊 `["\047]` 想用 octal \047 表單引號,但 BRE char-class
53
+ # **不** interpret octal → 單引號 JSX `layout={'primary-header'}` 整個 hook 靜默 skip(false-negative)。
54
+ # 改 grep -E + 字面單/雙引號 alternation(`('\''|")`)— 三種引號形式皆match。⚠️ 驗證用 /usr/bin/grep,
55
+ # 互動 shell 的 wrapped grep 對 octal 給相反結果會藏 bug。
56
+ if ! grep -Eq 'layout="primary-header"|layout=\{('\''|")primary-header('\''|")\}' "$TARGET"; then exit 0; fi
45
57
 
46
58
  VIOLATIONS=()
47
59
 
@@ -54,10 +66,25 @@ fi
54
66
  # 例外(2026-06-18 responsive 精修,M34 hook-intent 對齊):同 file 也用 useSidebar/isMobile =
55
67
  # mobile-only 補品牌的「正確 responsive fork」(小螢幕 Sheet 蓋住 globalHeader → Sheet 內補同組 primitive,
56
68
  # desktop 仍無 SidebarHeader,非真重複)→ 不 flag。見 app-shell.spec.md WorkspaceBrand SSOT「Responsive 精修」子句。
57
- if grep -q '<SidebarHeader' "$TARGET" && ! grep -qE 'useSidebar|isMobile' "$TARGET"; then
69
+ # ⚠️ 限制(2026-06-18 beta.74 audit 記錄):V2 token-presence 啟發式(file 含 useSidebar/isMobile 即豁免),
70
+ # 非「isMobile 真的 guard 該 <SidebarHeader>」的 AST 級判斷 → 罕見假陰性(unguarded SidebarHeader 但 file
71
+ # 因別處用 isMobile → 漏擋)。bash 無法精準 AST;靠 code review + escape allowlist 兜底,目前唯一 consumer(stories)正確。
72
+ # 要嚴格需 TS AST lint rule(future)。
73
+ # tag boundary `([^A-Za-z]|$)`(2026-06-18 audit P2#4):避免 prefix-extended 名(<SidebarHeaderXyz>)誤觸
74
+ if grep -Eq '<SidebarHeader([^A-Za-z]|$)' "$TARGET" && ! grep -qE 'useSidebar|isMobile' "$TARGET"; then
58
75
  VIOLATIONS+=("V2 Sidebar 內含 SidebarHeader:primary-header mode WorkspaceBrand 該在 globalHeader,sidebar 內不該重複(per spec.md「WorkspaceBrand 放置 SSOT」+ world-class GitHub/Gmail/Figma 共識)。若是 mobile-only responsive 補品牌請用 useSidebar().isMobile 條件渲染(自動豁免);若 sidebar header 是其他內容(非 brand),加 escape allowlist `// @app-shell-primary-header-allow:` 並說明 reason")
59
76
  fi
60
77
 
78
+ # V3:layout="primary-header" + <SidebarFooter> 同 file → 帳號入口家在 globalHeader 右,不該用 sidebar footer。
79
+ # 與 V2 不同 — primary-header 收成 Sheet 時帳號鏡像到 Sheet **header** 右(非 footer),故任何 breakpoint 都不該
80
+ # 有 SidebarFooter 放帳號 → V3 不設 isMobile 豁免(per app-shell.spec.md「帳號入口(Account entry)放置 SSOT」
81
+ # Responsive 精修「不放 SidebarFooter」+ Material modal nav drawer「account switcher 放 drawer header」)。
82
+ # demo 天然不誤觸:layout="primary-header" 在 stories,<SidebarFooter> 在 _demo-helpers.tsx,分檔。
83
+ # 非帳號用途 footer(storage meter / collapse 等)→ escape allowlist 兜底。
84
+ if grep -Eq '<SidebarFooter([^A-Za-z]|$)' "$TARGET"; then
85
+ VIOLATIONS+=("V3 Sidebar 內含 SidebarFooter:primary-header mode 帳號入口家在 globalHeader 右(收成 Sheet 鏡像到 Sheet header 右),不該用 sidebar footer〔那是 primary-sidebar 慣例〕(per spec.md「帳號入口(Account entry)放置 SSOT」+ Material modal nav drawer)。若 footer 是非帳號用途,加 escape allowlist \`// @app-shell-primary-header-allow:\` 並說明 reason")
86
+ fi
87
+
61
88
  if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
62
89
  echo "🚨 AppShell primary-header consistency violation" >&2
63
90
  echo "Target: $TARGET" >&2
@@ -265,7 +265,7 @@ fi
265
265
  ENV_SMOKE=""
266
266
  # (a) plugin-mode 完整性 — CLAUDE_PLUGIN_ROOT 在 ds-repo native mode 可能 UNSET → 先 guard 才用
267
267
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ ! -d "${CLAUDE_PLUGIN_ROOT:-}/hooks" ]; then
268
- ENV_SMOKE="${ENV_SMOKE}\n - Plugin mode:\$CLAUDE_PLUGIN_ROOT 有設但 hooks/ 找不到 → plugin install 可能不完整( /plugin marketplace update)。"
268
+ ENV_SMOKE="${ENV_SMOKE}\n - Plugin mode:\$CLAUDE_PLUGIN_ROOT 有設但 hooks/ 找不到 → plugin install 可能不完整(C-prime 主路徑 = committed-config + \`npm run sync-all\`〔npm-only〕;刻意用 plugin 才跑 /plugin marketplace update)。"
269
269
  fi
270
270
  # (b) node 在 PATH — audit scripts 依賴
271
271
  if ! command -v node >/dev/null 2>&1; then
@@ -1,9 +1,10 @@
1
1
  #!/bin/bash
2
2
  # Tests for check_app_shell_primary_header_consistency.sh
3
3
  #
4
- # Hook(PreToolUse Edit/Write):偵測 AppShell consumer 2 violations:
4
+ # Hook(PreToolUse Edit/Write):偵測 AppShell consumer 3 violations:
5
5
  # V1 layout="primary-header" 缺 globalHeader prop
6
- # V2 layout="primary-header" + 同 file 含 <SidebarHeader>
6
+ # V2 layout="primary-header" + 同 file 含 <SidebarHeader>(useSidebar/isMobile 豁免)
7
+ # V3 layout="primary-header" + 同 file 含 <SidebarFooter>(帳號家在 header 右,非 footer;無 isMobile 豁免)
7
8
  #
8
9
  # Hook 透過 stdin 讀 tool_input(INPUT=$(cat) + jq;2026-05-31 改 env→stdin 對齊 sibling helper + 讓 dispatcher 能呼叫)
9
10
  # 且需 TARGET file 真實存在於 disk(`[[ ! -f "$TARGET" ]] && exit 0`)。
@@ -143,6 +144,49 @@ const { isMobile } = useSidebar()
143
144
  '
144
145
  expect_pass_silent "8. responsive isMobile + SidebarHeader → silent(mobile-only 補品牌豁免)"
145
146
 
147
+ # 9. layout="primary-header" + <SidebarFooter> → block (V3)
148
+ # (2026-06-18 beta.74:primary-header 帳號家在 header 右,sidebar footer 是 primary-sidebar 慣例;無 isMobile 豁免)
149
+ run_hook_on_file "src/ph-footer.tsx" '
150
+ <AppShell layout="primary-header" globalHeader={<GH />}>
151
+ <Sidebar>
152
+ <SidebarFooter>account</SidebarFooter>
153
+ </Sidebar>
154
+ </AppShell>
155
+ '
156
+ expect_block "9. V3 primary-header + SidebarFooter → block" "V3 Sidebar 內含 SidebarFooter"
157
+
158
+ # 10. layout="primary-header" + mobile header-right account(SidebarHeader 補品牌+帳號,無 SidebarFooter)→ silent
159
+ # (2026-06-18:帳號鏡像 globalHeader 到 Sheet header 右,無 footer = 合規;V2 isMobile 豁免 + V3 無 footer)
160
+ run_hook_on_file "src/ph-header-account.tsx" '
161
+ const { isMobile } = useSidebar()
162
+ <AppShell layout="primary-header" globalHeader={<GH />}>
163
+ <Sidebar>
164
+ {isMobile && <SidebarHeader><WorkspaceBrand /><AccountMenu /></SidebarHeader>}
165
+ </Sidebar>
166
+ </AppShell>
167
+ '
168
+ expect_pass_silent "10. primary-header + header-right account, no footer → silent"
169
+
170
+ # 11. single-quote JSX layout={'primary-header'} missing globalHeader → block (V1)
171
+ # (2026-06-18 beta.74 regression:octal \047 gate bug → 單引號 JSX 整個 hook 靜默 skip;fix 後三種引號形式皆偵測)
172
+ run_hook_on_file "src/single-quote.tsx" "
173
+ <AppShell layout={'primary-header'}>
174
+ <Sidebar />
175
+ </AppShell>
176
+ "
177
+ expect_block "11. single-quote layout JSX gate (octal fix regression) → V1 block" "V1 缺 globalHeader prop"
178
+
179
+ # 12. prefix-extended component name <SidebarFooterPanel> with primary-header → silent (tag-boundary guard, V3 no false-positive)
180
+ # (2026-06-18 beta.74 audit P2#4:`<SidebarFooter` 不該 match `<SidebarFooterPanel`)
181
+ run_hook_on_file "src/prefix-extended.tsx" '
182
+ <AppShell layout="primary-header" globalHeader={<GH />}>
183
+ <Sidebar>
184
+ <SidebarFooterPanel>not the DS SidebarFooter</SidebarFooterPanel>
185
+ </Sidebar>
186
+ </AppShell>
187
+ '
188
+ expect_pass_silent "12. prefix-extended <SidebarFooterPanel> → silent(V3 tag-boundary 不誤觸)"
189
+
146
190
  echo ""
147
191
  echo "=== Summary ==="
148
192
  echo "Passed: $PASS / $((PASS + FAIL))"
@@ -345,6 +345,36 @@ export const P = () => (<div className="px-[var(--layout-space-loose)] border-b
345
345
  '
346
346
  expect_pass_silent "23. R9 skip FileViewer(isOverlay 家)→ 不檢查"
347
347
 
348
+ # 24. R10 link_canonical: text-primary hover:underline → P1 warn(非 block)
349
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '
350
+ <span className="text-primary hover:underline font-medium cursor-pointer">查看詳情</span>
351
+ '
352
+ expect_warn "24. R10 text-primary hover:underline → P1 warn" "R10 link_canonical"
353
+
354
+ # 25. R10 canonical(hover:text-primary-hover 換色)→ silent
355
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '
356
+ <span className="text-primary hover:text-primary-hover font-medium cursor-pointer">查看詳情</span>
357
+ '
358
+ expect_pass_silent "25. R10 canonical hover:text-primary-hover → silent"
359
+
360
+ # 26. R10 escape marker @link-style-allow → silent
361
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '// @link-style-allow: legacy doc-nav underline
362
+ <span className="text-primary hover:underline">查看詳情</span>
363
+ '
364
+ expect_pass_silent "26. R10 @link-style-allow 豁免 → silent"
365
+
366
+ # 27. R10 broadened: class-separated(cursor-pointer 隔開)text-primary … hover:underline → warn
367
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.anatomy.stories.tsx" '
368
+ <button className="text-caption text-primary cursor-pointer hover:underline">重設</button>
369
+ '
370
+ expect_warn "27. R10 class-separated text-primary…hover:underline → warn" "R10 link_canonical"
371
+
372
+ # 28. R10 不誤判描述 label「text-primary · hover:underline」(· 非 class-char,如 LinkInput anatomy 註解)→ silent
373
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.anatomy.stories.tsx" '
374
+ <span className="text-fg-muted font-mono">text-primary · hover:underline · 點擊開啟連結</span>
375
+ '
376
+ expect_pass_silent "28. R10 描述 label(· 分隔)→ 不誤判"
377
+
348
378
  echo ""
349
379
  echo "=== Summary ==="
350
380
  echo "Passed: $PASS / $((PASS + FAIL))"
@@ -173,7 +173,7 @@ User 2026-05-15 verbatim 抓「DS 深度稽核漏 storybook content quality」+
173
173
  | 53 | **Code-to-spec reverse drift check**(2026-05-17 user 抓 Phase 1 漏抓 FileViewer h-14 spec drift,新加 dim)| 對每 component grep `packages/design-system/src/components/<X>/<X>.tsx` 的 className 硬寫 utility(`h-14` / `w-80` / `px-loose` 類)→ 反向掃對應 `<X>.spec.md` 是否仍寫「固定 h-NN」「寫死」keyword 但 code 已 migrate to token = drift。互補既有 forward Dim 15/20(spec → code)。Hook `check_spec_class_drift.sh` write-time soft P1 warn,本 dim batch verify 既有 60+ 元件 spec.md。錨例:2026-05-17 Phase 1 我 file-viewer.spec.md L103 寫「Known drift:h-14 硬寫不消費 token」但 file-viewer.tsx:333 已 `h-[var(--chrome-header-height)]`,3+ 次 `/design-system-audit --deep` 都沒抓到反向 drift |
174
174
  | 54 | **M35 Nearest same-purpose canonical compliance**(2026-05-20 codify per codex Layer B D4)| 對每 `*.stories.tsx` wrap 既有 primitive(Sidebar / DataTable / ChromeHeader / Dialog / Sheet / Popover)的 file 跑:(a) 檔頭含 `@story-baseline:` cite marker?(b) `.claude/references/story-baseline-registry.json` 內 primitive 的 `requiredHelpers` 全 import?(c) `antiPatterns` regex 任一 match → fail?(d) `variantRules` button variant + size + iconOnly + pressed 全 satisfy?Hook `check_story_invariants.sh R8` write-time soft warn,本 dim batch verify。錨例:2026-05-20 AppShell stories 連 5 round drift 後 codex Layer B 抓 root cause = SSOT 消費被當引用儀式 |
175
175
  | 55 | **Token cross-namespace mapping integrity**(2026-05-20 codify per user 抓 red→deep-orange bug 100+ audit 沒發現)| `tokens/color/semantic.css` 每 hue interaction token(`--blue-hover` / `--red-hover` / ...)必指向**同名**primitive(`--red-hover: var(--color-red-N)`),**禁**跨 hue 混(`--red-hover: var(--color-deep-orange-N)` 違反)。Primitive 12 hue 全該有對應 interaction(blue/red/deep-orange/orange/amber/yellow/lime/green/turquoise/indigo/purple/magenta)。Status semantic(`--error-hover` 等)直指 primitive,**不**透過 hue layer。錨例:2026-05-20 semantic.css:246 `--red-hover: var(--color-deep-orange-5)` cross-namespace bug,100+ audit 沒發現 = audit 沒檢 token mapping integrity |
176
- | 56 | **AppShell primary-header consistency**(2026-05-21 codify per user 抓「primary-header = primary-sidebar + 一條 global header」+「globalHeader 存在時 sidebar 內 header 該拿掉」)| 對每 consumer `.tsx`(stories / app code)grep `layout="primary-header"`,verify:(a) 同 file 含 `globalHeader=` prop(否則邏輯矛盾)/(b) file 不含 `<SidebarHeader>`(WorkspaceBrand 該在 globalHeader,不重複)。World-class cite:GitHub repo sidebar 無 header(org/repo 在 global breadcrumb)+ Gmail / Figma file editor sidebar 無 header(brand 在 global top bar)。Hook `check_app_shell_primary_header_consistency.sh` write-time block(P1 warn,可 escape `// @app-shell-primary-header-allow:`)。對應 `app-shell.spec.md`「WorkspaceBrand 放置 SSOT」段 |
176
+ | 56 | **AppShell primary-header consistency**(2026-05-21 codify;2026-06-18 beta.74 V3 + responsive 豁免)| 對每 consumer `.tsx`(stories / app code)grep `layout="primary-header"`(三種 JSX 引號形式 `layout="x"` / `{"x"}` / `{'x'}` 皆偵測,grep -E 字面引號),verify 3 條:(V1) 同 file 含 `globalHeader=` prop(否則邏輯矛盾)/(V2) 不含裸 `<SidebarHeader>`(WorkspaceBrand 該在 globalHeader,不重複;**例外**:同 file 用 `useSidebar`/`isMobile` = mobile-only Sheet 補品牌的 responsive fork → 豁免)/(V3) 不含 `<SidebarFooter>`(帳號入口家在 globalHeader 右、收成 Sheet 鏡像到 Sheet header 右,**不該**用 sidebar footer〔那是 primary-sidebar 慣例〕;**無** isMobile 豁免)trigger 用 tag-boundary `([^A-Za-z]|$)` 避 prefix-extended 名誤觸。World-class cite:GitHub repo sidebar 無 header + Gmail / Figma brand 在 global top bar + Material modal nav drawer「account switcher 放 drawer header」。Hook `check_app_shell_primary_header_consistency.sh`(V1-V3)write-time exit 2 BLOCKER(via chrome_header_dispatcher,可 escape `// @app-shell-primary-header-allow:`)。對應 `app-shell.spec.md`「WorkspaceBrand 放置 SSOT」+「帳號入口(Account entry)放置 SSOT」段(均含 Responsive 精修子句)|
177
177
  | 57 | **M29 DS Anchor Preflight enforcement coverage**(2026-05-26 codify per user verbatim「該程式化的都沒程式化」)| 對每 `*.tsx`(production code,非 stories / test)grep wrap DS primitive(`<Sidebar>` / `<AppShell>` / `<DataTable>` 等)→ verify 過去 30 turns transcript 含 `Grep`/`Read` tool call hit `packages/design-system/src/**/*.spec.md` 或 `*.stories.tsx`,OR 檔頭含 `@story-baseline:` marker 或 inline 3-column owner table。Hook `check_ds_anchor_preflight.sh` write-time soft BLOCKER。對應 meta-patterns.md M29 + self-verify.md Pre-edit phase。錨例:2026-05-26 App.tsx 漏 SidebarTrigger / collapsible / startIcon mock-drift = M29 hook 不存在使 infra 沒攔 |
178
178
  | 58 | **Fork-user plugin install enforcement**(2026-05-26 codify per user「我們做那麼多 plugin 不就是要避免這件事?」)| SessionStart hook `check_plugin_fork_health.sh(r1,2026-06-11 merge)` 偵測:(a) cwd 不是 DS repo(無 `packages/design-system/src`)/(b) `package.json` 含 `@qijenchen/design-system` dep /(c) `~/.claude/plugins/design-system/` OR `.claude/plugins/design-system/` 不存在 → 三題 YES 印強制提示。互補 product-workspace `scripts/check-plugin-installed.mjs` npm postinstall layer。**+ 2026-05-30 fork-committed bootstrap 層(補 chicken-egg:plugin 硬 hook 隨 plugin 才裝,沒裝前無 mechanical 防線)**:fork 自帶 `template/ds-product-template/.claude/hooks/check_plugin_bootstrap.sh`(SessionStart 每 session 提醒,fail-open)+ `block_production_edit_without_plugin.sh`(PreToolUse 硬攔 `apps/**` production .tsx/.ts/.css edit,沒裝 plugin → exit 2 BLOCK,escape `CLAUDE_BYPASS_PLUGIN_BOOTSTRAP=1`)+ `.claude/settings.json` hooks 註冊;**不依賴 plugin**(committed in fork),mirror allowlist `.claude` ship 給 published fork。對應 product-workspace/CLAUDE.md「第 −1 步」段 |
179
179
  | 59 | **Approval preflight scope coverage**(2026-05-26 extend per user「未來其他人 fork 用其他元件也偏移」)| `check_substantive_edit_approval_preflight.sh` scope 從 `packages/design-system/src/**` 擴大到 `apps/**.{tsx,ts,css}` + `node_modules/@qijenchen/design-system/**`。Audit verify hook regex 涵蓋三 scope + allowlist `*.stories.tsx`/`*.test.*`/`scripts/*`。對應 memory/feedback_ship_then_revert_anti_pattern.md SSOT |
@@ -192,7 +192,7 @@ User 2026-05-15 verbatim 抓「DS 深度稽核漏 storybook content quality」+
192
192
  | 72 | **DS API surface tightening**(2026-05-27 — 治標 vs 治本)| Hook 71 偵測 anti-pattern 是 lint 層攔截;治本要 DS API design 強到 misuse 即 fail tsc。Audit:逐 component review API surface — `size?: number` 該改 `'sm'\|'md'\|'lg'` enum / `columns: Column[]` 該加 min length runtime check / `title` + `description` 該有 type-level XOR / Overlay primitive `defaultOpen` 該 require explicit。配套 codify in `tightening-roadmap.md`(若存在;不存在則以 `props-naming.md` + 各 spec API 段為準,對齊 audit-prompts.md dim 72),分 quarter ship。對應 Dim 71 是攔當前 misuse,本 dim 是消除未來 misuse 可能 |
193
193
  | 73 | **Full-story visual+interaction sweep enforce**(2026-05-27 codex M31 P0 finding)| Audit report JSON `storyResults.length === manifest.totalStories`(916)。Sample < 916 = reject(per user「不准抽樣」)。Hook `check_full_story_visual_interaction_sweep.sh` PostToolUse audit-report.json BLOCKER。Escape `"_sampling_allowed": "<rationale>"`(極罕見)|
194
194
  | 74 | **Overlay open/focus/Escape probe**(2026-05-27 codex M31 P0 finding + user 7-bug 錨點「overlay 沒彈出」)| Consumer story 用 Tooltip / Popover / Dialog / Sheet / DropdownMenu / HoverCard Trigger 必含 `defaultOpen` OR `open={true}` OR `play()` interaction click。Trigger-only catalog = reject(visual snapshot 看不到 content)。Hook `check_overlay_open_focus_escape_probe.sh` BLOCKER。HoverCard exception via `@story-trait-allow: missing-opensnapshot` per codex |
195
- | 75 | **Plugin freshness session-start prompt**(2026-05-27 chain-C ship + user「主動引導」directive)| Fork user session_start hook `check_plugin_fork_health.sh(r2,2026-06-11 merge)` reads local installed plugin.json version → fetch GitHub raw marketplace.json → diff version → if stale prompt run `npm run sync-all`. Sync-all 1-command 整合 npm update + claude plugin marketplace update + claude plugin update + restart prompt(per user 2026-05-27「不需要獨立命令兩次」)|
195
+ | 75 | **Plugin freshness session-start prompt**(2026-05-27 chain-C ship + user「主動引導」directive)| Fork user session_start hook `check_plugin_fork_health.sh(r2,2026-06-11 merge)` reads local installed plugin.json version → fetch GitHub raw marketplace.json → diff version → if stale prompt run `npm run sync-all`. Sync-all 1-command(2026-06-17 npm-only,不再跑 `claude plugin`):npm update 治理本體 + idempotent 刷新接線骨架 + skills;生效分軌(機械即時 / settings 自動 hot-reload / 指引 `/clear` 或下個 session)|
196
196
  | 76 | **Escape marker abuse cap**(2026-05-27 per user「不亂加 escape markers」)| Consumer file 10 escape markers 累計 ≥3 distinct types OR ≥5 total → BLOCK。修法 3 選 1:重構走 DS canonical / 拆 file / env override `CLAUDE_BYPASS_ESCAPE_MARKER_AUDIT`。Hook `check_escape_marker_abuse.sh` enforces escape philosophy「rare per-line documented exception,非 daily tool」|
197
197
  | 77 | **Composition-fidelity:conformance-primary,pixel-identity opt-in**(2026-05-27 ship / **2026-06-02 model 修正**)| Consumer 對 DS 用法正確性**主要由靜態 conformance 驗**(dim 71 含 Pattern 8 + `check_layout_space_magic_numbers` + R7/R8),對齊世界級 static lint(Polaris stylelint-polaris / Atlassian eslint-plugin-design-system / Carbon stylelint-plugin-carbon-tokens;WebFetch verified)。`scripts/composition-fidelity-visual-diff.mjs` 的 pixel/DOM identity diff 改**明確 opt-in**:只比標 `@composition-fidelity-mode` 的 mapping(忠實複製 replica / same-story 跨版本回歸,對齊 Chromatic/Storybook same-story baseline);單獨 `@story-baseline` = conformance 不做 identity diff;0 opt-in → exit 0。**禁** 拿產品範本(內容刻意不同)pixel 比 DS showcase(world-class 公認反 pattern)。SSOT `composition-fidelity.md`。CI `.github/workflows/composition-fidelity.yml` |
198
198
  | 78 | **Codex brief 禁列檔 invariant**(2026-05-27 codify per codex v1+v2 token-burn 2× anchor)| `check_codex_brief_invariants.sh` 4th invariant check:codex CLI brief 必含「禁列檔 / 禁 rg --files / 只讀 N file / 直接出 verdict」keyword,否則 codex 自動跑 `rg --files` 列 1300+ files 燒光 reasoning,無法產出 Step 5 Verdict(M31 Step 4.5 last-verdict gate fail)。Hook PreToolUse Bash codex exec/review 偵測 → BLOCKER。Escape `// @codex-brief-invariant-skip:` |
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @qijenchen/design-system — 完整設計參考(llms-full)
2
2
 
3
- > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.73
3
+ > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.75
4
4
 
5
5
  # Components
6
6
 
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qijenchen/design-system
2
2
 
3
3
  > World-class React design system(Radix/shadcn + Tailwind v4 + 自訂 design token)。
4
- > 54 components + 4 public patterns + design tokens。v0.1.0-beta.73
4
+ > 54 components + 4 public patterns + design tokens。v0.1.0-beta.75
5
5
 
6
6
  本檔由 source(spec.md frontmatter + Storybook index)build-time 自動生成,**禁手改**(CI --check drift gate 守)。
7
7
  每元件 / pattern 的完整 variants / sizes / 禁止事項 全文見 [llms-full.txt](./llms-full.txt)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qijenchen/design-system",
3
- "version": "0.1.0-beta.73",
3
+ "version": "0.1.0-beta.75",
4
4
  "private": false,
5
5
  "description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
6
6
  "type": "module",
@@ -69,13 +69,13 @@ export const UsageGuidance: Story = {
69
69
  >
70
70
  <ul className="space-y-1">
71
71
  <li>
72
- <LinkTo kind="Design System/Components/Accordion/展示" name="FAQ 常見問題"><span className="text-primary hover:underline font-medium cursor-pointer">FAQ 常見問題</span></LinkTo>
72
+ <LinkTo kind="Design System/Components/Accordion/展示" name="FAQ 常見問題"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">FAQ 常見問題</span></LinkTo>
73
73
  </li>
74
74
  <li>
75
- <LinkTo kind="Design System/Components/Accordion/展示" name="設定分組"><span className="text-primary hover:underline font-medium cursor-pointer">設定分組</span></LinkTo>
75
+ <LinkTo kind="Design System/Components/Accordion/展示" name="設定分組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設定分組</span></LinkTo>
76
76
  </li>
77
77
  <li>
78
- <LinkTo kind="Design System/Components/Accordion/展示" name="進階選項可隱藏"><span className="text-primary hover:underline font-medium cursor-pointer">進階選項可隱藏</span></LinkTo>
78
+ <LinkTo kind="Design System/Components/Accordion/展示" name="進階選項可隱藏"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">進階選項可隱藏</span></LinkTo>
79
79
  </li>
80
80
  </ul>
81
81
  </Rule>
@@ -41,19 +41,19 @@ export const UsageGuidance: Story = {
41
41
  <p>適合 Alert 的真實業務場景(點擊跳轉「展示」頁範例):</p>
42
42
  <ul className="space-y-1">
43
43
  <li>
44
- <LinkTo kind="Design System/Components/Alert/展示" name="低調單行"><span className="text-primary hover:underline font-medium cursor-pointer">低調單行</span></LinkTo>
44
+ <LinkTo kind="Design System/Components/Alert/展示" name="低調單行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">低調單行</span></LinkTo>
45
45
  </li>
46
46
  <li>
47
- <LinkTo kind="Design System/Components/Alert/展示" name="實心單行"><span className="text-primary hover:underline font-medium cursor-pointer">實心單行</span></LinkTo>
47
+ <LinkTo kind="Design System/Components/Alert/展示" name="實心單行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">實心單行</span></LinkTo>
48
48
  </li>
49
49
  <li>
50
- <LinkTo kind="Design System/Components/Alert/展示" name="低調含說明文字"><span className="text-primary hover:underline font-medium cursor-pointer">低調含說明文字</span></LinkTo>
50
+ <LinkTo kind="Design System/Components/Alert/展示" name="低調含說明文字"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">低調含說明文字</span></LinkTo>
51
51
  </li>
52
52
  <li>
53
- <LinkTo kind="Design System/Components/Alert/展示" name="實心含說明文字"><span className="text-primary hover:underline font-medium cursor-pointer">實心含說明文字</span></LinkTo>
53
+ <LinkTo kind="Design System/Components/Alert/展示" name="實心含說明文字"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">實心含說明文字</span></LinkTo>
54
54
  </li>
55
55
  <li>
56
- <LinkTo kind="Design System/Components/Alert/展示" name="右上角操作群組"><span className="text-primary hover:underline font-medium cursor-pointer">右上角操作群組</span></LinkTo>
56
+ <LinkTo kind="Design System/Components/Alert/展示" name="右上角操作群組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">右上角操作群組</span></LinkTo>
57
57
  </li>
58
58
  </ul>
59
59
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -28,6 +28,7 @@ import {
28
28
  SidebarTrigger,
29
29
  useSidebar,
30
30
  } from '@/design-system/components/Sidebar/sidebar'
31
+ import { useAppShell } from '@/design-system/components/AppShell/app-shell'
31
32
  import { ChromeHeader } from '@/design-system/patterns/header-canonical/chrome-header'
32
33
  import {
33
34
  ItemAvatar,
@@ -126,17 +127,31 @@ export function AcmeSidebar({
126
127
  includeWorkspaceBrand?: boolean
127
128
  includeUserFooter?: boolean
128
129
  } = {}) {
129
- // Responsive 品牌/帳號(2026-06-18):小螢幕 sidebar 收成 Sheet(z-50)打開時會「蓋住」globalHeader →
130
- // primary-header 把品牌+帳號放 globalHeader,Sheet 內就看不到、drawer 變孤兒。故 mobile 時在 sidebar
131
- // 補回同一組 primitive(<SidebarHeader><WorkspaceBrand/> + <SidebarFooter><UserFooter/>,style SSOT
132
- // primary-sidebar 完全一致);desktop(globalHeader 可見)維持 headerless/footerless,不重複。
133
- // 「只能出現一次」精修為「同一時間一次,per breakpoint 判定」— app-shell.spec.md WorkspaceBrand/帳號入口 SSOT。
130
+ // Responsive 品牌/帳號(2026-06-18 v2):小螢幕 sidebar 收成 Sheet(z-50)打開時會「蓋住」globalHeader →
131
+ // primary-header 把品牌+帳號放 globalHeader,Sheet 內就看不到、drawer 變孤兒。故 mobile 時在 sidebar 內補回。
132
+ // **每 mode 鏡像自己桌面的帳號家**(per app-shell.spec.md「帳號入口(Account entry)放置 SSOT」Responsive 精修):
133
+ // - primary-header:桌面帳號在 globalHeader 右(brand 左 / account 右) Sheet 內把整條 globalHeader 搬進
134
+ // SidebarHeader(brand + flex-1 + AccountMenu;**SidebarHeader ChromeHeader 基底,與 GlobalHeader 同
135
+ // primitive** = 結構 SSOT,非手刻對齊),**不放 SidebarFooter**(footer 是 primary-sidebar 慣例;
136
+ // 對齊 Material modal navigation drawer「帳號 switcher 放 drawer header,非 footer」)。
137
+ // - primary-sidebar:桌面帳號在 sidebar 底(SidebarFooter)→ Sheet 維持 header=brand / footer=UserFooter(不變)。
138
+ // desktop(globalHeader 可見)維持 headerless/footerless,不重複。「只能出現一次」= 同一時間一次,per breakpoint 判定。
134
139
  const { isMobile } = useSidebar()
140
+ const { layout } = useAppShell()
141
+ // primary-header 收成 Sheet:帳號入口鏡像 globalHeader 放 SidebarHeader 右(SidebarHeader = ChromeHeader 基底,
142
+ // 與 GlobalHeader「brand 左 + flex-1 + account 右」完全同結構 → avatar 24px + 右緣距 px-loose 由 construction 保證)。
143
+ const headerHasAccount = layout === 'primary-header' && isMobile
135
144
  return (
136
145
  <Sidebar collapsible="icon" viewportInsetTop={viewportInsetTop}>
137
146
  {(includeWorkspaceBrand || isMobile) && (
138
147
  <SidebarHeader>
139
148
  <WorkspaceBrand />
149
+ {headerHasAccount && (
150
+ <>
151
+ <div className="flex-1" />
152
+ <AccountMenu />
153
+ </>
154
+ )}
140
155
  </SidebarHeader>
141
156
  )}
142
157
  <SidebarContent>
@@ -158,7 +173,9 @@ export function AcmeSidebar({
158
173
  </SidebarGroupContent>
159
174
  </SidebarGroup>
160
175
  </SidebarContent>
161
- {(includeUserFooter || isMobile) && (
176
+ {/* Footer 只 primary-sidebar(帳號在 sidebar );primary-header 帳號改放 SidebarHeader 右(見上),
177
+ mobile 也不放 footer(誤用他 mode 慣例)。 */}
178
+ {includeUserFooter && (
162
179
  <SidebarFooter>
163
180
  <UserFooter />
164
181
  </SidebarFooter>
@@ -18,15 +18,15 @@ export const UsageGuidance: Story = {
18
18
  <ul className="text-body space-y-2">
19
19
  <li>
20
20
  • 多頁 web service 的主結構——Linear / Notion / Slack / GitHub / Asana 這類「左側導覽 + 中央工作區」產品。完整組合見{' '}
21
- <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 — Linear 式議題追蹤"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主側欄佈局 — Linear 式議題追蹤</span></LinkTo>
21
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 — Linear 式議題追蹤"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主側欄佈局 — Linear 式議題追蹤</span></LinkTo>
22
22
  </li>
23
23
  <li>
24
24
  • 需要 sidebar + main 持續共存——在議題列表、看板、報表等頁面間切換時,左側導覽不重渲染、捲動位置不丟失。見{' '}
25
- <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 + 頁面分頁"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主側欄佈局 + 頁面分頁</span></LinkTo>
25
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 + 頁面分頁"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主側欄佈局 + 頁面分頁</span></LinkTo>
26
26
  </li>
27
27
  <li>
28
28
  • 需要右側詳情面板(議題詳情 / inspector / 成員資料)跟 main 並存——如 Linear 點選議題後右側展開詳情。開合行為見{' '}
29
- <LinkTo kind="Design System/Components/AppShell/設計規格" name="右側面板開合行為(兩種模式)"><span className="text-primary hover:underline font-medium cursor-pointer">設計規格 → 右側面板開合行為</span></LinkTo>
29
+ <LinkTo kind="Design System/Components/AppShell/設計規格" name="右側面板開合行為(兩種模式)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 右側面板開合行為</span></LinkTo>
30
30
  </li>
31
31
  </ul>
32
32
  </section>
@@ -46,7 +46,7 @@ export const UsageGuidance: Story = {
46
46
  <p className="text-body">
47
47
  兩種佈局模式(primary-sidebar / primary-header)的選型決策樹獨立成「佈局模式怎麼選」story(含
48
48
  WorkspaceBrand 放置規則),見本頁側欄或{' '}
49
- <LinkTo kind="Design System/Components/AppShell/設計原則" name="佈局模式怎麼選"><span className="text-primary hover:underline font-medium cursor-pointer">設計原則 → 佈局模式怎麼選</span></LinkTo>
49
+ <LinkTo kind="Design System/Components/AppShell/設計原則" name="佈局模式怎麼選"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計原則 → 佈局模式怎麼選</span></LinkTo>
50
50
  </p>
51
51
  </section>
52
52
 
@@ -73,8 +73,9 @@ export const UsageGuidance: Story = {
73
73
  ),
74
74
  }
75
75
 
76
- // 佈局模式選型 deep-dive — 內容錨定 app-shell.spec.md「兩 mode 差異」(L93-94 / L105 常見誤解)
77
- // +「WorkspaceBrand 放置 SSOT(L112-119);category-templates.md component-specific `{Topic}Rule` idiom
76
+ // 佈局模式選型 deep-dive — 內容錨定 app-shell.spec.md 的「Layout mode mode 差異」+「WorkspaceBrand 放置 SSOT」
77
+ // +「帳號入口(Account entry)放置 SSOT」段(均含 Responsive 精修子句;用段名錨定不寫死行號避免 drift)。
78
+ // category-templates.md component-specific `{Topic}Rule` idiom
78
79
  export const LayoutModeRule: Story = {
79
80
  name: '佈局模式怎麼選',
80
81
  render: () => (
@@ -97,12 +98,12 @@ export const LayoutModeRule: Story = {
97
98
  服務整個 app(account / workspace switcher / 跨頁 search / notifications);local toolbar
98
99
  <em>仍在</em> main col 頂,只是上面多了 global header。WorkspaceBrand 改放 globalHeader 左側。
99
100
  參考 GitHub / Slack / Gmail。完整組合見{' '}
100
- <LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
101
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
101
102
  </li>
102
103
  </ul>
103
104
  <p className="text-caption text-fg-secondary mt-2">
104
105
  兩 mode 是 product 角色表態——啟動時固定,不該在 runtime 切換。視覺對照圖見{' '}
105
- <LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:underline font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
106
+ <LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
106
107
  </p>
107
108
  </section>
108
109