@qijenchen/design-system 0.1.0-beta.83 → 0.1.0-beta.84

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 (604) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.md +3 -2
  3. package/dist/components/Alert/alert.js.map +1 -1
  4. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  5. package/dist/components/AppShell/app-shell.d.ts +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  7. package/dist/components/AppShell/app-shell.js +22 -2
  8. package/dist/components/AppShell/app-shell.js.map +1 -1
  9. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar/avatar.js +4 -4
  11. package/dist/components/Avatar/avatar.js.map +1 -1
  12. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  13. package/dist/components/Breadcrumb/breadcrumb.js +1 -1
  14. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  15. package/dist/components/Button/button.d.ts +2 -2
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +12 -3
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +13 -5
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +7 -1
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  24. package/dist/components/Carousel/carousel.js +8 -3
  25. package/dist/components/Carousel/carousel.js.map +1 -1
  26. package/dist/components/Chart/chart.d.ts.map +1 -1
  27. package/dist/components/Chart/chart.js +1 -0
  28. package/dist/components/Chart/chart.js.map +1 -1
  29. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  30. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  31. package/dist/components/Checkbox/checkbox.js +8 -4
  32. package/dist/components/Checkbox/checkbox.js.map +1 -1
  33. package/dist/components/Chip/chip.d.ts +1 -1
  34. package/dist/components/Chip/chip.d.ts.map +1 -1
  35. package/dist/components/Chip/chip.js.map +1 -1
  36. package/dist/components/Coachmark/coachmark.d.ts +10 -3
  37. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  38. package/dist/components/Coachmark/coachmark.js.map +1 -1
  39. package/dist/components/Combobox/combobox.d.ts +5 -1
  40. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  41. package/dist/components/Combobox/combobox.js +30 -13
  42. package/dist/components/Combobox/combobox.js.map +1 -1
  43. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  44. package/dist/components/DataTable/cell-registry.js +53 -62
  45. package/dist/components/DataTable/cell-registry.js.map +1 -1
  46. package/dist/components/DataTable/column-types.d.ts +10 -4
  47. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  48. package/dist/components/DataTable/column-types.js.map +1 -1
  49. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
  50. package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
  51. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  52. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  53. package/dist/components/DataTable/data-table-filter-panel.js +607 -0
  54. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  55. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  56. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  58. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  59. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  60. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  61. package/dist/components/DataTable/data-table.d.ts +5 -6
  62. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  63. package/dist/components/DataTable/data-table.js +36 -15
  64. package/dist/components/DataTable/data-table.js.map +1 -1
  65. package/dist/components/DataTable/filter-operators.js +140 -0
  66. package/dist/components/DataTable/filter-operators.js.map +1 -0
  67. package/dist/components/DataTable/filter-tree.js +312 -0
  68. package/dist/components/DataTable/filter-tree.js.map +1 -0
  69. package/dist/components/DataTable/index.d.ts +3 -0
  70. package/dist/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/components/DataTable/index.js +12 -1
  72. package/dist/components/DataTable/index.js.map +1 -1
  73. package/dist/components/DataTable/lib/column-meta.js +25 -0
  74. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  75. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  76. package/dist/components/DateGrid/date-grid.js +3 -1
  77. package/dist/components/DateGrid/date-grid.js.map +1 -1
  78. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  79. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  80. package/dist/components/DatePicker/date-picker.js +105 -74
  81. package/dist/components/DatePicker/date-picker.js.map +1 -1
  82. package/dist/components/Dialog/dialog.d.ts +20 -3
  83. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  84. package/dist/components/Dialog/dialog.js +38 -20
  85. package/dist/components/Dialog/dialog.js.map +1 -1
  86. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  87. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  88. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  89. package/dist/components/Field/field-context.d.ts +45 -5
  90. package/dist/components/Field/field-context.d.ts.map +1 -1
  91. package/dist/components/Field/field-context.js +19 -0
  92. package/dist/components/Field/field-context.js.map +1 -1
  93. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  94. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  95. package/dist/components/Field/field-edit-keys.js +19 -0
  96. package/dist/components/Field/field-edit-keys.js.map +1 -0
  97. package/dist/components/Field/field-types.d.ts +20 -1
  98. package/dist/components/Field/field-types.d.ts.map +1 -1
  99. package/dist/components/Field/field-types.js.map +1 -1
  100. package/dist/components/Field/field-wrapper.d.ts +3 -2
  101. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/Field/field-wrapper.js +38 -39
  103. package/dist/components/Field/field-wrapper.js.map +1 -1
  104. package/dist/components/Field/field.d.ts +2 -1
  105. package/dist/components/Field/field.d.ts.map +1 -1
  106. package/dist/components/Field/field.js.map +1 -1
  107. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  108. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  109. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  110. package/dist/components/FileItem/file-item.js +1 -1
  111. package/dist/components/FileItem/file-item.js.map +1 -1
  112. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  113. package/dist/components/FileUpload/file-upload.js.map +1 -1
  114. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  115. package/dist/components/FileViewer/file-viewer.js +7 -2
  116. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  117. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  118. package/dist/components/FileViewer/image-renderer.js +22 -1
  119. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  120. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  121. package/dist/components/HoverCard/hover-card.js +5 -3
  122. package/dist/components/HoverCard/hover-card.js.map +1 -1
  123. package/dist/components/InlineEdit/index.d.ts +2 -0
  124. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  125. package/dist/components/InlineEdit/index.js +5 -0
  126. package/dist/components/InlineEdit/index.js.map +1 -0
  127. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  129. package/dist/components/InlineEdit/inline-edit.js +164 -0
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  131. package/dist/components/Input/input.d.ts +6 -11
  132. package/dist/components/Input/input.d.ts.map +1 -1
  133. package/dist/components/Input/input.js +43 -13
  134. package/dist/components/Input/input.js.map +1 -1
  135. package/dist/components/LinkInput/link-input.d.ts +2 -2
  136. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  137. package/dist/components/LinkInput/link-input.js +6 -5
  138. package/dist/components/LinkInput/link-input.js.map +1 -1
  139. package/dist/components/Menu/menu-item.d.ts +14 -2
  140. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  141. package/dist/components/Menu/menu-item.js +25 -3
  142. package/dist/components/Menu/menu-item.js.map +1 -1
  143. package/dist/components/NumberInput/number-input.d.ts +3 -3
  144. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  145. package/dist/components/NumberInput/number-input.js +6 -5
  146. package/dist/components/NumberInput/number-input.js.map +1 -1
  147. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  148. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  149. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  150. package/dist/components/Pagination/pagination.js +1 -1
  151. package/dist/components/Pagination/pagination.js.map +1 -1
  152. package/dist/components/PeoplePicker/index.js +7 -1
  153. package/dist/components/PeoplePicker/index.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  155. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  156. package/dist/components/PeoplePicker/people-picker.js +23 -10
  157. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  158. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  159. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.js +19 -14
  161. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  162. package/dist/components/Popover/popover.d.ts.map +1 -1
  163. package/dist/components/Popover/popover.js +3 -1
  164. package/dist/components/Popover/popover.js.map +1 -1
  165. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  166. package/dist/components/ProfileCard/profile-card.js +3 -3
  167. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  168. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  169. package/dist/components/RadioGroup/radio-group.js +15 -9
  170. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  171. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  172. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.js +6 -3
  174. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  175. package/dist/components/Select/select.d.ts +46 -5
  176. package/dist/components/Select/select.d.ts.map +1 -1
  177. package/dist/components/Select/select.js +109 -28
  178. package/dist/components/Select/select.js.map +1 -1
  179. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  180. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  181. package/dist/components/SelectMenu/select-menu.js +8 -1
  182. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  183. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  184. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  185. package/dist/components/SelectionControl/boolean-value.js +23 -0
  186. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  187. package/dist/components/SelectionControl/index.d.ts +1 -0
  188. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  189. package/dist/components/SelectionControl/index.js +2 -0
  190. package/dist/components/SelectionControl/index.js.map +1 -1
  191. package/dist/components/Sheet/index.js +2 -1
  192. package/dist/components/Sheet/sheet.d.ts +7 -1
  193. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  194. package/dist/components/Sheet/sheet.js +5 -2
  195. package/dist/components/Sheet/sheet.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +8 -10
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts.map +1 -1
  201. package/dist/components/Slider/slider.js.map +1 -1
  202. package/dist/components/Switch/switch.d.ts +6 -6
  203. package/dist/components/Switch/switch.d.ts.map +1 -1
  204. package/dist/components/Switch/switch.js +3 -2
  205. package/dist/components/Switch/switch.js.map +1 -1
  206. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  207. package/dist/components/Tabs/tabs.js +15 -1
  208. package/dist/components/Tabs/tabs.js.map +1 -1
  209. package/dist/components/Textarea/textarea.d.ts +2 -2
  210. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  211. package/dist/components/Textarea/textarea.js +50 -42
  212. package/dist/components/Textarea/textarea.js.map +1 -1
  213. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  214. package/dist/components/TimePicker/time-columns.js +42 -38
  215. package/dist/components/TimePicker/time-columns.js.map +1 -1
  216. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  217. package/dist/components/TimePicker/time-picker.js +8 -6
  218. package/dist/components/TimePicker/time-picker.js.map +1 -1
  219. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  220. package/dist/components/Tooltip/tooltip.js +7 -5
  221. package/dist/components/Tooltip/tooltip.js.map +1 -1
  222. package/dist/components/TreeView/tree-view.d.ts +56 -1
  223. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  224. package/dist/components/TreeView/tree-view.js +211 -12
  225. package/dist/components/TreeView/tree-view.js.map +1 -1
  226. package/dist/index.d.ts +115 -58
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +93 -108
  229. package/dist/index.js.map +1 -1
  230. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  231. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  232. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  233. package/dist/tokens/categorical-color.js +15 -0
  234. package/dist/tokens/categorical-color.js.map +1 -1
  235. package/dist/tokens/motion/motion.d.ts +7 -6
  236. package/dist/tokens/motion/motion.d.ts.map +1 -1
  237. package/dist/tokens/motion/motion.js +6 -6
  238. package/dist/tokens/motion/motion.js.map +1 -1
  239. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  240. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  241. package/dist/tokens/motion/overlay-motion.js +9 -0
  242. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  243. package/dist/tokens/uiSize/icon-size.js +5 -0
  244. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  245. package/ds-canonical/fork/governance.lock +31 -19
  246. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  247. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  248. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  249. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
  250. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  251. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  252. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  253. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  254. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
  255. package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
  256. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  257. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  258. package/ds-canonical/fork/manifest.json +15 -0
  259. package/ds-canonical/fork/preamble.md +20 -18
  260. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
  261. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  262. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
  263. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  264. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  265. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  266. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  267. package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
  268. package/ds-canonical/hooks/README.md +1 -1
  269. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  270. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  271. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  272. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  273. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
  274. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  275. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  276. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  277. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  278. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  279. package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
  280. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  281. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  282. package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
  283. package/ds-canonical/hooks/check_story_determinism.sh +2 -0
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  285. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  286. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  287. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  288. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  289. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  290. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  291. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  292. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  293. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  294. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  295. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  296. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  297. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
  298. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  299. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  300. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  301. package/ds-canonical/references/failure-class-registry.json +145 -0
  302. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  303. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  304. package/ds-canonical/references/scenario-definition.md +6 -9
  305. package/ds-canonical/references/ssot-consultation.md +2 -2
  306. package/ds-canonical/references/ssot-index.md +5 -4
  307. package/ds-canonical/references/story-baseline-registry.json +8 -2
  308. package/ds-canonical/references/structural-token-retention.md +6 -9
  309. package/ds-canonical/rules/meta-patterns.md +8 -8
  310. package/ds-canonical/rules/self-verify.md +4 -3
  311. package/ds-canonical/rules/story-rules.md +2 -2
  312. package/ds-canonical/rules/ui-development.md +2 -2
  313. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
  314. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  315. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  316. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  317. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  318. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  319. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  320. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
  321. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  322. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  323. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  324. package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
  325. package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
  326. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  327. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  328. package/ds-canonical/skills/governance-health/SKILL.md +1 -1
  329. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  330. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  331. package/ds-canonical/skills/new-component/SKILL.md +5 -5
  332. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  333. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  334. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  335. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  336. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  337. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  338. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  339. package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
  340. package/ds-story-manifest.json +29 -20
  341. package/llms-full.txt +22 -1
  342. package/llms.txt +5 -4
  343. package/package.json +7 -3
  344. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  345. package/src/components/Accordion/accordion.stories.tsx +16 -1
  346. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  347. package/src/components/Alert/alert.spec.md +8 -10
  348. package/src/components/Alert/alert.stories.tsx +28 -28
  349. package/src/components/Alert/alert.tsx +1 -1
  350. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  351. package/src/components/AppShell/app-shell.spec.md +4 -4
  352. package/src/components/AppShell/app-shell.tsx +28 -2
  353. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  354. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  355. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  356. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  357. package/src/components/Avatar/avatar.spec.md +2 -2
  358. package/src/components/Avatar/avatar.stories.tsx +8 -6
  359. package/src/components/Avatar/avatar.tsx +13 -5
  360. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  361. package/src/components/Badge/badge.spec.md +2 -0
  362. package/src/components/Badge/badge.stories.tsx +4 -2
  363. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  364. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  365. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  366. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  367. package/src/components/Button/button.principles.stories.tsx +1 -1
  368. package/src/components/Button/button.spec.md +2 -2
  369. package/src/components/Button/button.tsx +18 -3
  370. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  371. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  372. package/src/components/Calendar/calendar.spec.md +5 -4
  373. package/src/components/Calendar/calendar.stories.tsx +7 -4
  374. package/src/components/Calendar/calendar.tsx +26 -7
  375. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  376. package/src/components/Carousel/carousel.spec.md +2 -2
  377. package/src/components/Carousel/carousel.tsx +11 -3
  378. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  379. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  380. package/src/components/Chart/chart.stories.tsx +2 -8
  381. package/src/components/Chart/chart.tsx +3 -0
  382. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  383. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  384. package/src/components/Checkbox/checkbox.spec.md +11 -26
  385. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  386. package/src/components/Checkbox/checkbox.tsx +18 -13
  387. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  388. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  389. package/src/components/Chip/chip.spec.md +5 -4
  390. package/src/components/Chip/chip.tsx +5 -1
  391. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  392. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  393. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  394. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  395. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  396. package/src/components/Coachmark/coachmark.spec.md +5 -5
  397. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  398. package/src/components/Coachmark/coachmark.tsx +10 -3
  399. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  400. package/src/components/Combobox/combobox.tsx +53 -19
  401. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  402. package/src/components/Command/command.principles.stories.tsx +1 -1
  403. package/src/components/Command/command.stories.tsx +42 -10
  404. package/src/components/DataTable/cell-registry.tsx +86 -63
  405. package/src/components/DataTable/column-types.ts +10 -1
  406. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  407. package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
  408. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  409. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  410. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  411. package/src/components/DataTable/data-table.spec.md +32 -12
  412. package/src/components/DataTable/data-table.stories.tsx +16 -17
  413. package/src/components/DataTable/data-table.tsx +103 -60
  414. package/src/components/DataTable/index.ts +3 -0
  415. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  416. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  417. package/src/components/DateGrid/date-grid.spec.md +1 -1
  418. package/src/components/DateGrid/date-grid.tsx +3 -1
  419. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  420. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  421. package/src/components/DatePicker/date-picker.spec.md +3 -3
  422. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  423. package/src/components/DatePicker/date-picker.tsx +99 -45
  424. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  425. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  426. package/src/components/DescriptionList/description-list.spec.md +3 -3
  427. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  428. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  429. package/src/components/Dialog/dialog.spec.md +9 -0
  430. package/src/components/Dialog/dialog.stories.tsx +66 -5
  431. package/src/components/Dialog/dialog.tsx +60 -25
  432. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  433. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  434. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  435. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  436. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  437. package/src/components/Empty/empty.spec.md +2 -2
  438. package/src/components/Field/field-context.ts +69 -5
  439. package/src/components/Field/field-controls.spec.md +62 -8
  440. package/src/components/Field/field-edit-keys.ts +66 -0
  441. package/src/components/Field/field-types.ts +58 -9
  442. package/src/components/Field/field-wrapper.tsx +50 -42
  443. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  444. package/src/components/Field/field.spec.md +1 -1
  445. package/src/components/Field/field.stories.tsx +1 -1
  446. package/src/components/Field/field.tsx +2 -1
  447. package/src/components/Field/form-validation.spec.md +3 -3
  448. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  449. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  450. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  451. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  452. package/src/components/FileItem/file-item.spec.md +5 -8
  453. package/src/components/FileItem/file-item.stories.tsx +36 -25
  454. package/src/components/FileItem/file-item.tsx +8 -6
  455. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  456. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  457. package/src/components/FileUpload/file-upload.spec.md +1 -1
  458. package/src/components/FileUpload/file-upload.tsx +1 -1
  459. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  460. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  461. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  462. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  463. package/src/components/FileViewer/file-viewer.tsx +12 -4
  464. package/src/components/FileViewer/image-renderer.tsx +35 -7
  465. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  466. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  467. package/src/components/HoverCard/hover-card.spec.md +5 -5
  468. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  469. package/src/components/HoverCard/hover-card.tsx +5 -3
  470. package/src/components/InlineEdit/index.ts +5 -0
  471. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  472. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  473. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  474. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  475. package/src/components/Input/input.principles.stories.tsx +2 -12
  476. package/src/components/Input/input.spec.md +13 -18
  477. package/src/components/Input/input.tsx +69 -22
  478. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  479. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  480. package/src/components/LinkInput/link-input.tsx +11 -10
  481. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  482. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  483. package/src/components/Menu/menu-item.spec.md +22 -15
  484. package/src/components/Menu/menu-item.stories.tsx +39 -11
  485. package/src/components/Menu/menu-item.tsx +38 -4
  486. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  487. package/src/components/Notice/notice.spec.md +16 -3
  488. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  489. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  490. package/src/components/NumberInput/number-input.spec.md +4 -4
  491. package/src/components/NumberInput/number-input.tsx +19 -12
  492. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  493. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  494. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  495. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  496. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  497. package/src/components/Pagination/pagination.spec.md +1 -1
  498. package/src/components/Pagination/pagination.stories.tsx +2 -6
  499. package/src/components/Pagination/pagination.tsx +1 -1
  500. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  501. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  502. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  503. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  504. package/src/components/PeoplePicker/person-display.tsx +32 -16
  505. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  506. package/src/components/Popover/popover.tsx +3 -1
  507. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  508. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  509. package/src/components/ProfileCard/profile-card.tsx +6 -6
  510. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  511. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  512. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  513. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  514. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  515. package/src/components/RadioGroup/radio-group.tsx +41 -15
  516. package/src/components/Rating/rating.spec.md +11 -3
  517. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  518. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  519. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  520. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  521. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  522. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  523. package/src/components/Select/select.spec.md +27 -17
  524. package/src/components/Select/select.stories.tsx +33 -0
  525. package/src/components/Select/select.tsx +190 -36
  526. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  527. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  528. package/src/components/SelectMenu/select-menu.tsx +16 -3
  529. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  530. package/src/components/SelectionControl/index.ts +1 -0
  531. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  532. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  533. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  534. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  535. package/src/components/Separator/separator.spec.md +2 -2
  536. package/src/components/Separator/separator.stories.tsx +6 -27
  537. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  538. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  539. package/src/components/Sheet/sheet.spec.md +2 -2
  540. package/src/components/Sheet/sheet.tsx +9 -5
  541. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  542. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  543. package/src/components/Sidebar/sidebar.spec.md +13 -15
  544. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  545. package/src/components/Sidebar/sidebar.tsx +37 -25
  546. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  547. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  548. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  549. package/src/components/Slider/slider.spec.md +5 -5
  550. package/src/components/Slider/slider.tsx +3 -1
  551. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  552. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  553. package/src/components/Steps/steps.spec.md +1 -1
  554. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  555. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  556. package/src/components/Switch/switch.spec.md +8 -8
  557. package/src/components/Switch/switch.stories.tsx +9 -24
  558. package/src/components/Switch/switch.tsx +17 -13
  559. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  560. package/src/components/Tabs/tabs.spec.md +1 -1
  561. package/src/components/Tabs/tabs.tsx +21 -1
  562. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  563. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  564. package/src/components/Textarea/textarea.spec.md +4 -4
  565. package/src/components/Textarea/textarea.tsx +72 -46
  566. package/src/components/TimePicker/time-columns.tsx +6 -2
  567. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  568. package/src/components/TimePicker/time-picker.spec.md +3 -3
  569. package/src/components/TimePicker/time-picker.tsx +16 -10
  570. package/src/components/Toast/toast.stories.tsx +7 -4
  571. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  572. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  573. package/src/components/Tooltip/tooltip.spec.md +1 -1
  574. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  575. package/src/components/Tooltip/tooltip.tsx +8 -6
  576. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  577. package/src/components/TreeView/tree-view.spec.md +42 -11
  578. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  579. package/src/components/TreeView/tree-view.tsx +322 -17
  580. package/src/index.ts +584 -60
  581. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  582. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  583. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  584. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  585. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  586. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  587. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  588. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  589. package/src/styles/tokens.css +3 -0
  590. package/src/tokens/README.md +1 -1
  591. package/src/tokens/color/color.spec.md +3 -3
  592. package/src/tokens/color/color.stories.tsx +3 -3
  593. package/src/tokens/density/density.stories.tsx +16 -3
  594. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  595. package/src/tokens/motion/motion.css +33 -21
  596. package/src/tokens/motion/motion.spec.md +32 -15
  597. package/src/tokens/motion/motion.stories.tsx +58 -5
  598. package/src/tokens/motion/motion.ts +7 -6
  599. package/src/tokens/motion/overlay-motion.ts +26 -0
  600. package/src/tokens/orphan-tokens.spec.md +4 -4
  601. package/src/tokens/token-system.spec.md +1 -0
  602. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  603. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  604. package/src/tokens/utility-registry.json +160 -23
@@ -83,6 +83,25 @@ export interface TreeDragEndEvent {
83
83
  position: TreeDropPosition
84
84
  }
85
85
 
86
+ /**
87
+ * 鍵盤重排(`Cmd/Ctrl+Shift+方向鍵`,2026-07-14 v1)的 SR 播報文案 — zh-TW 預設,
88
+ * per-key merge 覆寫(i18n 覆寫點)。詳 tree-view.spec.md「鍵盤重排」。
89
+ * @public
90
+ */
91
+ // code-quality-allow: dead-export — public prop type — consumer 覆寫播報文案的參數型別
92
+ export interface TreeReorderAnnouncements {
93
+ /** 同層上/下移完成。`index` / `count` = 移動後在同層的 1-based 序數 / 同層總數 */
94
+ moved?: (args: { label: string; targetLabel: string; position: 'before' | 'after'; index: number; count: number }) => string
95
+ /** 移入 folder 完成。folder 原本收合(children 尚未 mount)時序數不可知 → `index` / `count` 為 undefined */
96
+ movedInside?: (args: { label: string; folderLabel: string; index?: number; count?: number }) => string
97
+ /** 移出到上層完成。`level` = 移動後所在層(1-based,同 aria-level 語意) */
98
+ movedOut?: (args: { label: string; parentLabel: string; level: number }) => string
99
+ /** 邊界 no-op:top(已在最上方)/ bottom(已在最下方)/ not-folder(無可移入的 folder)/ root(已在最外層) */
100
+ blocked?: (args: { reason: 'top' | 'bottom' | 'not-folder' | 'root'; label: string; targetLabel?: string }) => string
101
+ /** sr-only 操作說明(tree 容器 `aria-describedby` 指向;僅 `draggable` 時渲染) */
102
+ instructions?: string
103
+ }
104
+
86
105
  // ═══════════════════════════════════════════════════════════════════════════
87
106
  // Constants
88
107
  // ═══════════════════════════════════════════════════════════════════════════
@@ -92,11 +111,42 @@ export interface TreeDragEndEvent {
92
111
 
93
112
  // indentStep = chevronSize + gap-2(8px)。值 {24,24,28} 與 CSS token `--tree-indent-{sm,md,lg}`
94
113
  // (`tokens/uiSize/uiSize.css`)**完全一致、必須同步維護**(改一處要兩處一起改;非「token 取代 literal」)。
95
- // 為何兩源並存:本元件 render 用此 JS literal 算 `indentPx`(L808 inline calc 需 number)+ drop-indicator
96
- // pointer 數學(L352,需 number 做 px 命中判斷,CSS var 在 JS 計算層拿不到);DataTable nested rows
114
+ // 為何兩源並存:本元件 render 用此 JS literal 算 `indentPx`(TreeItem inline calc 需 number)+ drop-indicator
115
+ // pointer 數學(handleDragOver px 命中判斷,CSS var 在 JS 計算層拿不到);DataTable nested rows
97
116
  // 則走 CSS token 的 Tailwind class(跨元件視覺一致)。結構對齊:子 chevron 對齊父 icon,子 icon 對齊父 label。
98
117
  const INDENT_STEP: Record<SizeKey, number> = { sm: 24, md: 24, lg: 28 }
99
118
 
119
+ // ── 鍵盤重排(keyboard reorder,2026-07-14 v1)常數 ──
120
+ // 鍵位 = Cmd/Ctrl+Shift+方向鍵,對齊 Notion 移動 block 鍵位(https://www.notion.com/help/keyboard-shortcuts,
121
+ // WebFetch 驗證紀錄 .claude/logs/treeview-keyboard-dnd-design.md#L25);
122
+ // modifier 層與 APG tree 導覽鍵(無 modifier 的 ↑↓→←)完全正交,零衝突。
123
+ const REORDER_ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']
124
+ type ReorderArrowKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight'
125
+
126
+ // 鍵盤重排 SR 播報預設文案(zh-TW;consumer 經 `reorderAnnouncements` prop per-key 覆寫)。
127
+ // 格式 =「結果 + 序數位置」— 非視覺使用者靠「第 n 項,共 m 項」建立位置模型
128
+ // (世界級 cite 詳 tree-view.spec.md「鍵盤重排」段,M22)。
129
+ // i18n-allow-block: DS default SR-only 播報文案,prop 可覆寫(同 select-menu.tsx SelectMenuLiveStatus 先例)
130
+ const DEFAULT_REORDER_ANNOUNCEMENTS: Required<TreeReorderAnnouncements> = {
131
+ moved: ({ label, targetLabel, position, index, count }) =>
132
+ `已將『${label}』移到『${targetLabel}』${position === 'before' ? '之前' : '之後'},第 ${index} 項,共 ${count} 項`,
133
+ movedInside: ({ label, folderLabel, index, count }) =>
134
+ index !== undefined && count !== undefined
135
+ ? `已將『${label}』移入『${folderLabel}』,第 ${index} 項,共 ${count} 項`
136
+ : `已將『${label}』移入『${folderLabel}』`,
137
+ movedOut: ({ label, parentLabel, level }) =>
138
+ `已將『${label}』移出到『${parentLabel}』之後,第 ${level} 層`,
139
+ blocked: ({ reason, targetLabel }) => {
140
+ switch (reason) {
141
+ case 'top': return '已在最上方'
142
+ case 'bottom': return '已在最下方'
143
+ case 'not-folder': return targetLabel ? `無法移入:『${targetLabel}』不是資料夾` : '無法移入'
144
+ case 'root': return '已在最外層'
145
+ }
146
+ },
147
+ instructions: '按 Cmd(Ctrl)+Shift+方向鍵可重新排列項目',
148
+ }
149
+
100
150
  // ═══════════════════════════════════════════════════════════════════════════
101
151
  // Context
102
152
  // ═══════════════════════════════════════════════════════════════════════════
@@ -210,6 +260,12 @@ export interface TreeViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>
210
260
  draggable?: boolean
211
261
  /** Drag 結束時觸發,提供 sourceId、targetId、position。Consumer 負責 reorder。 */
212
262
  onDragEnd?: (event: TreeDragEndEvent) => void
263
+ /**
264
+ * 鍵盤重排(`Cmd/Ctrl+Shift+方向鍵`)的 SR 播報文案覆寫(per-key merge;zh-TW 預設)。
265
+ * 僅在 `draggable` 時生效;含 `instructions`(sr-only 操作說明)。
266
+ * @public
267
+ */
268
+ reorderAnnouncements?: TreeReorderAnnouncements
213
269
  /** ARIA label */
214
270
  'aria-label'?: string
215
271
  }
@@ -224,6 +280,7 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
224
280
  expandOnSelect = false,
225
281
  draggable = false,
226
282
  onDragEnd: onDragEndProp,
283
+ reorderAnnouncements: reorderAnnouncementsProp,
227
284
  expandedIds: controlledExpanded,
228
285
  onExpandedChange,
229
286
  selectedIds: controlledSelected,
@@ -287,6 +344,18 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
287
344
  // useId 確保多棵 TreeView 同頁 / node id 跨樹重複時 DOM id 不撞。
288
345
  const activeDescendantPrefix = React.useId()
289
346
 
347
+ // ── 鍵盤重排 SR 播報(2026-07-14 v1;詳 spec「鍵盤重排」)──
348
+ // 單一 polite live region 覆寫式更新(textContent 替換非 append → 快速連按天然只播最新)。
349
+ // 不可借 dnd-kit DndContext 內建 live region — 它只播 dnd-kit 管理的 drag session,
350
+ // 自建鍵盤路徑觸不到。消費 select-menu.tsx SelectMenuLiveStatus role="status" 先例。
351
+ const [reorderAnnouncement, setReorderAnnouncement] = React.useState('')
352
+ const announcements = React.useMemo(
353
+ () => ({ ...DEFAULT_REORDER_ANNOUNCEMENTS, ...reorderAnnouncementsProp }),
354
+ [reorderAnnouncementsProp]
355
+ )
356
+ // sr-only 操作說明節點 id(tree 容器 aria-describedby 指向;僅 draggable 渲染)
357
+ const reorderInstructionsId = `${activeDescendantPrefix}tree-reorder-instructions`
358
+
290
359
  // ── Keyboard vs mouse detection ──
291
360
  // focus ring 只在鍵盤操作時顯示,滑鼠點擊用 bg-neutral-selected 表達選中,不顯示 ring
292
361
  const isKeyboardRef = React.useRef(false)
@@ -301,6 +370,26 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
301
370
  // 用 ref 打斷 temporal dead zone。
302
371
  const toggleExpandRef = React.useRef<(id: string) => void>(() => {})
303
372
 
373
+ // ── Node registry ──(2026-07-14 上移至 drag handlers 之前:isInSubtree 消費 getNodeInfo,
374
+ // 而 handleDragOver / handleDragEnd / 鍵盤 reorder 都消費 isInSubtree)
375
+ const { registerNode, unregisterNode, getNodeInfo } = useNodeRegistry()
376
+
377
+ // 子樹判定:candidate 是否位於 rootId 的子樹內(含相等)。沿 registry parentId 鏈上溯。
378
+ // 雙路共用 SSOT:pointer 路徑 descendant drop guard(2026-07-14 R6 修:原 handleDragOver 只擋
379
+ // `over.id === active.id` 未擋 descendants → 可發出 targetId ∈ source 子樹的非法事件,
380
+ // consumer 按 remove→insert 實作會讓整個子樹靜默消失)+ 鍵盤 reorder commit 前防禦 assert。
381
+ const isInSubtree = React.useCallback(
382
+ (candidateId: string, rootId: string): boolean => {
383
+ let cur: string | null = candidateId
384
+ while (cur != null) {
385
+ if (cur === rootId) return true
386
+ cur = getNodeInfo(cur)?.parentId ?? null
387
+ }
388
+ return false
389
+ },
390
+ [getNodeInfo]
391
+ )
392
+
304
393
  const sensors = useSensors(
305
394
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } })
306
395
  )
@@ -323,7 +412,10 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
323
412
  //
324
413
  const handleDragOver = React.useCallback((event: DragOverEvent) => {
325
414
  const { over, active } = event
326
- if (!over || over.id === active.id) {
415
+ // 2026-07-14 R6 descendant guard:over active 自己「或其子樹內節點」→ 無效目標,
416
+ // 不設 dropTarget(拖 folder 進自己子樹會發出非法 TreeDragEndEvent;guard SSOT = isInSubtree,
417
+ // 鍵盤 reorder 路徑共用同一 helper)。
418
+ if (!over || isInSubtree(String(over.id), String(active.id))) {
327
419
  if (autoExpandTimerRef.current) { clearTimeout(autoExpandTimerRef.current); autoExpandTimerRef.current = null }
328
420
  setDropTarget(null)
329
421
  return
@@ -406,7 +498,7 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
406
498
  } else {
407
499
  if (autoExpandTimerRef.current) { clearTimeout(autoExpandTimerRef.current); autoExpandTimerRef.current = null }
408
500
  }
409
- }, [expandedIds])
501
+ }, [expandedIds, isInSubtree])
410
502
 
411
503
  const dropTargetRef = React.useRef(dropTarget)
412
504
  dropTargetRef.current = dropTarget
@@ -415,7 +507,8 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
415
507
  if (autoExpandTimerRef.current) { clearTimeout(autoExpandTimerRef.current); autoExpandTimerRef.current = null }
416
508
  const { active, over } = event
417
509
  const dt = dropTargetRef.current
418
- if (over && active.id !== over.id && dt) {
510
+ // descendant guard 同 handleDragOver(dt 已由 dragOver guard 保證為 null,此為 belt-and-braces)
511
+ if (over && !isInSubtree(String(over.id), String(active.id)) && dt) {
419
512
  onDragEndProp?.({
420
513
  sourceId: String(active.id),
421
514
  targetId: String(over.id),
@@ -424,7 +517,7 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
424
517
  }
425
518
  setDraggingId(null)
426
519
  setDropTarget(null)
427
- }, [onDragEndProp])
520
+ }, [onDragEndProp, isInSubtree])
428
521
 
429
522
  const handleDragCancel = React.useCallback(() => {
430
523
  if (autoExpandTimerRef.current) { clearTimeout(autoExpandTimerRef.current); autoExpandTimerRef.current = null }
@@ -432,9 +525,6 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
432
525
  setDropTarget(null)
433
526
  }, [])
434
527
 
435
- // ── Node registry ──
436
- const { registerNode, unregisterNode, getNodeInfo } = useNodeRegistry()
437
-
438
528
  // ── Actions ──
439
529
  const toggleExpand = React.useCallback(
440
530
  (id: string) => {
@@ -466,6 +556,168 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
466
556
  [selectionMode, setSelectedIds]
467
557
  )
468
558
 
559
+ // ── Keyboard reorder(Cmd/Ctrl+Shift+方向鍵,2026-07-14 v1)──
560
+ // 設計 SSOT:tree-view.spec.md「鍵盤重排」。與 pointer 路徑共用 TreeDragEndEvent 契約 +
561
+ // onDragEnd prop + registry(consumer API 零改動);每按一下立即 commit(無 grab-mode /
562
+ // 無預覽,對齊 Notion Cmd/Ctrl+Shift+Arrow 移動 block:https://www.notion.com/help/keyboard-shortcuts,
563
+ // WebFetch 驗證紀錄 .claude/logs/treeview-keyboard-dnd-design.md#L25)。
564
+ // 不用 dnd-kit KeyboardSensor — 其 activator 需可接收 DOM focus
565
+ // (https://dndkit.com/api-documentation/sensors/keyboard),與 row tabIndex={-1} +
566
+ // aria-activedescendant 虛擬焦點模型結構性不相容(2026-07-05 D4 拍板不翻案)。
567
+
568
+ // 播報用 label 文字:registry label 是 ReactNode — string 直接用,否則退 DOM row textContent。
569
+ const getNodeLabelText = React.useCallback(
570
+ (id: string): string => {
571
+ const info = getNodeInfo(id)
572
+ if (typeof info?.label === 'string') return info.label
573
+ const rowEl = treeRef.current?.querySelector<HTMLElement>(`[data-tree-row="${id}"]`)
574
+ return rowEl?.textContent?.trim() || id
575
+ },
576
+ [getNodeInfo]
577
+ )
578
+
579
+ // code-quality-allow: long-function — 四鍵語意(同層上下 / 移入 / 移出)+ 邊界播報結構緊密,
580
+ // 拆 sub-fn 會跨 fn 傳 siblings / announce state 反而複雜(同 handleKeyDown 先例)
581
+ const handleKeyboardReorder = React.useCallback(
582
+ (key: ReorderArrowKey) => {
583
+ const tree = treeRef.current
584
+ if (!tree || !focusedId) return
585
+ const sourceId = focusedId
586
+ const sourceEl = tree.querySelector<HTMLElement>(`[data-tree-id="${sourceId}"]`)
587
+ // disabled node 不可移(鍵盤導覽本就跳過 disabled,此為防禦 guard)
588
+ if (!sourceEl || sourceEl.getAttribute('aria-disabled') === 'true') return
589
+
590
+ const label = getNodeLabelText(sourceId)
591
+ // 同層 siblings(DOM 序)。focused node 可見 ⇒ 其 parent 已展開 ⇒ 全 siblings 已 mount。
592
+ const parentId = sourceEl.dataset.treeParentId || null
593
+ const visibleItems = Array.from(
594
+ tree.querySelectorAll<HTMLElement>('[role="treeitem"]:not([hidden])')
595
+ )
596
+ const siblings = visibleItems.filter((el) => (el.dataset.treeParentId || null) === parentId)
597
+ const sourceIndex = siblings.findIndex((el) => el.dataset.treeId === sourceId)
598
+ if (sourceIndex < 0) return
599
+ const isEnabled = (el: HTMLElement) => el.getAttribute('aria-disabled') !== 'true'
600
+
601
+ // 發出與 pointer 路徑同型的 TreeDragEndEvent,並排定 re-render 後 scrollIntoView
602
+ // (既有 effect 只在 isFocused 變化時跑;同 id 移動不 re-fire,故此處補)。
603
+ const commit = (targetId: string, position: TreeDropPosition): boolean => {
604
+ // 結構安全 assert:四鍵語意(sibling 間 / 進 prev sibling / 出 parent)結構上
605
+ // 不可能移進自己子樹;防禦性仍擋(與 pointer descendant guard 共用 isInSubtree SSOT)。
606
+ if (targetId === sourceId || isInSubtree(targetId, sourceId)) return false
607
+ onDragEndProp?.({ sourceId, targetId, position })
608
+ requestAnimationFrame(() => {
609
+ treeRef.current
610
+ ?.querySelector<HTMLElement>(`[data-tree-id="${sourceId}"]`)
611
+ ?.scrollIntoView({ block: 'nearest' })
612
+ })
613
+ return true
614
+ }
615
+
616
+ // 同層移動後的 1-based 新序數(播報用):移除 source 再插入的模擬;同層總數不變。
617
+ const movedIndex = (targetId: string, position: 'before' | 'after'): number => {
618
+ const order = siblings.map((el) => el.dataset.treeId).filter((id) => id !== sourceId)
619
+ const tIdx = order.indexOf(targetId)
620
+ return (position === 'before' ? tIdx : tIdx + 1) + 1
621
+ }
622
+
623
+ switch (key) {
624
+ case 'ArrowUp':
625
+ case 'ArrowDown': {
626
+ // disabled sibling 不可當 target 錨點(pointer useDroppable 同樣 disable)——
627
+ // 相鄰 sibling disabled 時錨到最近 enabled sibling 並翻轉 before/after 表達同一插槽
628
+ // (可達位置與 pointer 一致);整個方向皆無 enabled sibling → 視同邊界。
629
+ const dir = key === 'ArrowUp' ? -1 : 1
630
+ const boundary = dir < 0 ? ('top' as const) : ('bottom' as const)
631
+ const adjacent = siblings[sourceIndex + dir]
632
+ if (!adjacent) {
633
+ setReorderAnnouncement(announcements.blocked({ reason: boundary, label }))
634
+ return
635
+ }
636
+ let target: HTMLElement | undefined
637
+ let position: 'before' | 'after' = dir < 0 ? 'before' : 'after'
638
+ if (isEnabled(adjacent)) {
639
+ target = adjacent
640
+ } else {
641
+ for (let i = sourceIndex + dir; i >= 0 && i < siblings.length; i += dir) {
642
+ if (isEnabled(siblings[i])) { target = siblings[i]; break }
643
+ }
644
+ position = dir < 0 ? 'after' : 'before' // 錨到最近 enabled sibling 的另一側 = 相鄰插槽
645
+ }
646
+ if (!target) {
647
+ setReorderAnnouncement(announcements.blocked({ reason: boundary, label }))
648
+ return
649
+ }
650
+ const targetId = target.dataset.treeId!
651
+ if (commit(targetId, position)) {
652
+ setReorderAnnouncement(
653
+ announcements.moved({
654
+ label,
655
+ targetLabel: getNodeLabelText(targetId),
656
+ position,
657
+ index: movedIndex(targetId, position),
658
+ count: siblings.length,
659
+ })
660
+ )
661
+ }
662
+ return
663
+ }
664
+ case 'ArrowRight': {
665
+ // 移入:成為最近 enabled 上一 sibling 的子項(沿用 pointer「inside 限 folder」規則)
666
+ let target: HTMLElement | undefined
667
+ for (let i = sourceIndex - 1; i >= 0; i--) {
668
+ if (isEnabled(siblings[i])) { target = siblings[i]; break }
669
+ }
670
+ if (!target) {
671
+ setReorderAnnouncement(announcements.blocked({ reason: 'not-folder', label }))
672
+ return
673
+ }
674
+ const targetId = target.dataset.treeId!
675
+ if (target.dataset.treeHasChildren !== 'true') {
676
+ setReorderAnnouncement(
677
+ announcements.blocked({ reason: 'not-folder', label, targetLabel: getNodeLabelText(targetId) })
678
+ )
679
+ return
680
+ }
681
+ const wasExpanded = expandedIds.has(targetId)
682
+ if (commit(targetId, 'inside')) {
683
+ // 移入 collapsed folder:允許 + 自動展開(pointer 500ms hover auto-expand 的鍵盤對應物)
684
+ if (!wasExpanded) toggleExpand(targetId)
685
+ // 序數:consumer 對 inside 的慣例語意 = append 到 children 尾端;
686
+ // folder 原收合 → children 未 mount 無從計數 → 播報省略序數(誠實不猜)。
687
+ const childCount = wasExpanded
688
+ ? visibleItems.filter((el) => el.dataset.treeParentId === targetId).length
689
+ : undefined
690
+ setReorderAnnouncement(
691
+ announcements.movedInside({
692
+ label,
693
+ folderLabel: getNodeLabelText(targetId),
694
+ index: childCount !== undefined ? childCount + 1 : undefined,
695
+ count: childCount !== undefined ? childCount + 1 : undefined,
696
+ })
697
+ )
698
+ }
699
+ return
700
+ }
701
+ case 'ArrowLeft': {
702
+ // 移出:成為 parent 的下一個 sibling(outdent)
703
+ if (!parentId) {
704
+ setReorderAnnouncement(announcements.blocked({ reason: 'root', label }))
705
+ return
706
+ }
707
+ const parentEl = tree.querySelector<HTMLElement>(`[data-tree-id="${parentId}"]`)
708
+ const level = Number(parentEl?.getAttribute('aria-level') ?? '1')
709
+ if (commit(parentId, 'after')) {
710
+ setReorderAnnouncement(
711
+ announcements.movedOut({ label, parentLabel: getNodeLabelText(parentId), level })
712
+ )
713
+ }
714
+ return
715
+ }
716
+ }
717
+ },
718
+ [focusedId, expandedIds, toggleExpand, onDragEndProp, getNodeLabelText, isInSubtree, announcements]
719
+ )
720
+
469
721
  // ── Context value ──
470
722
  const contextValue = React.useMemo<TreeViewContextValue>(
471
723
  () => ({
@@ -524,14 +776,27 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
524
776
  isKeyboardRef.current = true
525
777
  if (!treeRef.current) return
526
778
 
779
+ // ── 鍵盤重排:Cmd/Ctrl+Shift+方向鍵(2026-07-14 v1,詳 spec「鍵盤重排」)──
780
+ // 必排在下方「無焦點時方向鍵先聚焦第一項」分支之前(該分支不分 modifier 攔 Arrow*);
781
+ // 非 modifier 導覽路徑(下方 switch)完全不變。
782
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && REORDER_ARROW_KEYS.includes(e.key)) {
783
+ if (!draggable) return // 未啟用拖曳 → 整組 no-op(modifier 組合不落入導覽 switch)
784
+ e.preventDefault() // 阻止瀏覽器原生行為(捲動 / 文字選取)
785
+ if (draggingId !== null) return // pointer 拖曳進行中 → 互斥,忽略鍵盤重排
786
+ handleKeyboardReorder(e.key as ReorderArrowKey)
787
+ return
788
+ }
789
+
527
790
  // 取得所有可見的 treeitem
528
791
  const items = Array.from(
529
- treeRef.current.querySelectorAll<HTMLElement>('[role="treeitem"]:not([hidden])')
792
+ // a11y:排除 disabled 節點 → 鍵盤導覽/選取/展開全跳過(APG:disabled 不可鍵盤操作)
793
+ treeRef.current.querySelectorAll<HTMLElement>('[role="treeitem"]:not([hidden]):not([aria-disabled="true"])')
530
794
  )
531
795
  const currentIndex = items.findIndex(
532
796
  (el) => el.dataset.treeId === focusedId
533
797
  )
534
- if (currentIndex < 0 && items.length > 0 && ['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) {
798
+ // a11y:沒焦點時任一方向鍵先聚焦第一個( ArrowLeft/Right,原漏 AT activedescendant 可讀)
799
+ if (currentIndex < 0 && items.length > 0 && ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {
535
800
  // 沒有焦點時,任何方向鍵先聚焦第一個
536
801
  setFocusedId(items[0].dataset.treeId ?? null)
537
802
  e.preventDefault()
@@ -603,17 +868,26 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
603
868
  }
604
869
  }
605
870
  },
606
- [focusedId, expandedIds, toggleExpand, select, setFocusedId]
871
+ [focusedId, expandedIds, toggleExpand, select, setFocusedId, draggable, draggingId, handleKeyboardReorder]
607
872
  )
608
873
 
609
874
  const treeEl = (
610
875
  <div
876
+ // {...props} 在最前:內部 role/style(--tree-px)/onKeyDown/tabIndex 必須勝過 consumer
877
+ // 誤傳(原 spread 在最後 → consumer style 會整組蓋掉 --tree-px、onKeyDown 蓋掉鍵盤導覽)
878
+ {...props}
611
879
  ref={treeRef}
612
880
  role="tree"
613
881
  aria-multiselectable={selectionMode === 'multiple' || undefined}
614
882
  // Virtual focus:DOM focus 停在容器(單一 tab stop),aria-activedescendant 指向目前 node
615
883
  // 的 DOM id,讓 AT 朗讀目前焦點 node(對齊 WAI-ARIA TreeView APG aria-activedescendant 模式)。
616
884
  aria-activedescendant={focusedId ? `${activeDescendantPrefix}treeitem-${focusedId}` : undefined}
885
+ // 鍵盤重排操作說明(sr-only,僅 draggable;與 consumer 傳入的 aria-describedby 合併)
886
+ aria-describedby={
887
+ [props['aria-describedby'], draggable ? reorderInstructionsId : undefined]
888
+ .filter(Boolean)
889
+ .join(' ') || undefined
890
+ }
617
891
  className={cn(
618
892
  // TreeView root 不加任何 py——呼吸空間由外層容器負責:
619
893
  // - 在 SidebarGroup 內: SidebarGroup py-2 提供
@@ -629,8 +903,18 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
629
903
  } as React.CSSProperties}
630
904
  onKeyDown={handleKeyDown}
631
905
  onMouseDown={handleMouseDown}
906
+ // a11y APG:Tab 進入時初始化 virtual focus(優先 selected,其次第一個可用節點);
907
+ // 原本無 onFocus init → focusedId=null → aria-activedescendant undefined,AT 讀不到目前節點
908
+ onFocus={(e) => {
909
+ ;(props as React.HTMLAttributes<HTMLDivElement>).onFocus?.(e)
910
+ if (e.target === e.currentTarget && !focusedId && treeRef.current) {
911
+ const first =
912
+ treeRef.current.querySelector<HTMLElement>('[role="treeitem"][aria-selected="true"]:not([hidden]):not([aria-disabled="true"])') ??
913
+ treeRef.current.querySelector<HTMLElement>('[role="treeitem"]:not([hidden]):not([aria-disabled="true"])')
914
+ if (first) setFocusedId(first.dataset.treeId ?? null)
915
+ }
916
+ }}
632
917
  tabIndex={0}
633
- {...props}
634
918
  >
635
919
  {children}
636
920
  </div>
@@ -640,7 +924,7 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
640
924
  <TreeViewContext.Provider value={contextValue}>
641
925
  {/* RowSizeProvider:讓 TreeView 子樹內任何 <ItemIcon> / <ItemAvatar> /
642
926
  <ItemInlineAction> 自動讀到對的 size,跟 SidebarProvider 同一條規則。
643
- 未來 TreeView 接 inlineActions API 後也吃這個 context。 */}
927
+ inlineActions API 也吃這個 context。 */}
644
928
  <RowSizeProvider value={size}>
645
929
  {/* 永遠包 DndContext(hooks 不能 conditional call)。不 draggable 時無 sensors = 不可拖 */}
646
930
  <DndContext
@@ -651,6 +935,22 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
651
935
  onDragCancel={handleDragCancel}
652
936
  >
653
937
  {treeEl}
938
+ {draggable && (
939
+ <>
940
+ {/* 鍵盤重排 SR 播報 — TreeView 自有 sr-only polite live region(單一節點覆寫式更新;
941
+ 消費 select-menu.tsx SelectMenuLiveStatus 先例)。渲染在 role="tree" 之外
942
+ (tree 的合法 children 只有 treeitem / group);dnd-kit DndContext 內建 live region
943
+ 只播 dnd-kit drag session,自建鍵盤路徑觸不到,故必須自有。 */}
944
+ <div role="status" aria-live="polite" className="sr-only">
945
+ {reorderAnnouncement}
946
+ </div>
947
+ {/* 鍵盤重排操作說明 — tree 容器 aria-describedby 指向(對齊 dnd-kit
948
+ screenReaderInstructions 慣例:https://dndkit.com/guides/accessibility) */}
949
+ <div id={reorderInstructionsId} className="sr-only">
950
+ {announcements.instructions}
951
+ </div>
952
+ </>
953
+ )}
654
954
  {draggable && (
655
955
  <DragOverlay dropAnimation={null}>
656
956
  {draggingId ? (() => {
@@ -946,7 +1246,8 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
946
1246
  // - default text 維持 fg-secondary muted(跟 single 對齊 hierarchy)
947
1247
  // - selected → 視覺信號只在 checkbox(auto-render below),text 不變、bg 不變
948
1248
  // - 對齊 SelectMenu multi pattern(menu-item.tsx:194-195 selected → bg only;multi → checkbox only)
949
- !disabled && !isSelected && 'text-fg-secondary',
1249
+ // multi-selected 也維持 fg-secondary(上方註解「text 不變」;原 !isSelected 條件讓 multi-selected 掉到繼承色)
1250
+ !disabled && (!isSelected || selectionMode === 'multiple') && 'text-fg-secondary',
950
1251
  !disabled && isSelected && selectionMode === 'single' && 'text-foreground',
951
1252
  isDropTarget && dropTarget?.position === 'inside' && dropIndicatorInside,
952
1253
  !disabled && 'hover:bg-neutral-hover hover:text-foreground',
@@ -967,7 +1268,8 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
967
1268
  // aria-describedby(鍵盤拖曳指示):role=button 污染 treeitem 語意、tabIndex=0
968
1269
  // 讓每 row 變 DOM tab stop 破壞單一 tab stop 虛擬焦點模型(DOM focus 在 row 上按
969
1270
  // Enter 會 bubble 到容器 handleKeyDown 但 handler 操作 state focusedId → 錯位);
970
- // sensors 僅 PointerSensor(無 KeyboardSensor),這些 attrs 換不到鍵盤拖曳能力。
1271
+ // sensors 僅 PointerSensor(無 KeyboardSensor),這些 attrs 換不到鍵盤拖曳能力;
1272
+ // 鍵盤重排(2026-07-14 v1)走容器 handleKeyDown 的 Cmd/Ctrl+Shift+Arrow 分支,非 dnd-kit sensor。
971
1273
  // aria-roledescription 保留在上方 treeitem 元素(有 role 才合法)。
972
1274
  {...(draggable ? dragListeners : {})}
973
1275
  {...props}
@@ -1074,7 +1376,10 @@ export const treeViewMeta = {
1074
1376
 
1075
1377
  },
1076
1378
  sizes: {
1077
-
1379
+ // 對齊 tree-view.spec.md frontmatter(compile-stories --check 驗 key 一致)
1380
+ sm: { typography: 'body', indent: 24 },
1381
+ md: { typography: 'body', indent: 24 }, // default
1382
+ lg: { typography: 'body-lg', indent: 28 },
1078
1383
  },
1079
1384
  // 'selected' = single-selection 持續選中(bg-neutral-selected + aria-selected);'active' 移除 —
1080
1385
  // 全檔無 Tailwind 按壓 utility,無按壓專屬視覺態(2026-07-07 詞彙統一對抗稽核補修)。