@exxatdesignux/ui 0.2.19 → 0.3.0

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 (688) hide show
  1. package/CHANGELOG.md +60 -7
  2. package/bin/sync-extras.mjs +116 -29
  3. package/consumer-extras/README.md +42 -7
  4. package/consumer-extras/cursor-rules/exxat-accessibility.mdc +39 -0
  5. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +26 -0
  6. package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +21 -0
  7. package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +21 -0
  8. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +44 -0
  9. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +32 -0
  10. package/consumer-extras/cursor-rules/exxat-command-menu.mdc +22 -0
  11. package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +53 -0
  12. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +41 -0
  13. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +25 -0
  14. package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +22 -0
  15. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +56 -0
  16. package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +31 -0
  17. package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +100 -0
  18. package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +28 -0
  19. package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +21 -0
  20. package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +31 -0
  21. package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +24 -0
  22. package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +31 -0
  23. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +30 -0
  24. package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +78 -0
  25. package/consumer-extras/cursor-rules/exxat-no-toast.mdc +25 -0
  26. package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +23 -0
  27. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +47 -0
  28. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +52 -0
  29. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +28 -0
  30. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +34 -0
  31. package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +77 -0
  32. package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +103 -0
  33. package/consumer-extras/cursor-skills/exxat-accessibility/SKILL.md +1 -1
  34. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +2 -2
  35. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +4 -15
  36. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +13 -28
  37. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
  38. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +2 -4
  39. package/consumer-extras/handbook/HANDBOOK.md +185 -0
  40. package/consumer-extras/handbook/glossary.md +57 -0
  41. package/consumer-extras/handbook/reference-implementations.md +126 -0
  42. package/consumer-extras/handbook/voice-and-tone.md +262 -0
  43. package/consumer-extras/patterns/command-menu-pattern.md +1 -1
  44. package/consumer-extras/patterns/consumer-upgrade-checklist.md +0 -20
  45. package/consumer-extras/patterns/data-views-pattern.md +17 -54
  46. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +3 -5
  47. package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
  48. package/dist/components/data-table/filter-date-calendar.js +280 -0
  49. package/dist/components/data-table/filter-date-calendar.js.map +1 -0
  50. package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
  51. package/dist/components/data-table/filter-text-value-input.js +561 -0
  52. package/dist/components/data-table/filter-text-value-input.js.map +1 -0
  53. package/dist/components/data-table/index.d.ts +45 -0
  54. package/dist/components/data-table/index.js +3085 -0
  55. package/dist/components/data-table/index.js.map +1 -0
  56. package/dist/components/data-table/pagination.d.ts +28 -0
  57. package/dist/components/data-table/pagination.js +3264 -0
  58. package/dist/components/data-table/pagination.js.map +1 -0
  59. package/dist/components/data-table/types.d.ts +84 -0
  60. package/dist/components/data-table/types.js +3 -0
  61. package/dist/components/data-table/types.js.map +1 -0
  62. package/dist/components/data-table/use-table-state.d.ts +116 -0
  63. package/dist/components/data-table/use-table-state.js +670 -0
  64. package/dist/components/data-table/use-table-state.js.map +1 -0
  65. package/dist/components/data-views/board-card-primitives.d.ts +22 -0
  66. package/dist/components/data-views/board-card-primitives.js +84 -0
  67. package/dist/components/data-views/board-card-primitives.js.map +1 -0
  68. package/dist/components/data-views/data-row-list.d.ts +33 -0
  69. package/dist/components/data-views/data-row-list.js +106 -0
  70. package/dist/components/data-views/data-row-list.js.map +1 -0
  71. package/dist/components/data-views/finder-panel-view.d.ts +54 -0
  72. package/dist/components/data-views/finder-panel-view.js +388 -0
  73. package/dist/components/data-views/finder-panel-view.js.map +1 -0
  74. package/dist/components/data-views/folder-grid-view.d.ts +22 -0
  75. package/dist/components/data-views/folder-grid-view.js +58 -0
  76. package/dist/components/data-views/folder-grid-view.js.map +1 -0
  77. package/dist/components/data-views/hub-table.d.ts +167 -0
  78. package/dist/components/data-views/hub-table.js +5561 -0
  79. package/dist/components/data-views/hub-table.js.map +1 -0
  80. package/dist/components/data-views/index.d.ts +27 -0
  81. package/dist/components/data-views/index.js +6575 -0
  82. package/dist/components/data-views/index.js.map +1 -0
  83. package/dist/components/data-views/list-page-board-card.d.ts +72 -0
  84. package/dist/components/data-views/list-page-board-card.js +264 -0
  85. package/dist/components/data-views/list-page-board-card.js.map +1 -0
  86. package/dist/components/data-views/list-page-board-template.d.ts +24 -0
  87. package/dist/components/data-views/list-page-board-template.js +137 -0
  88. package/dist/components/data-views/list-page-board-template.js.map +1 -0
  89. package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
  90. package/dist/components/data-views/list-page-connected-view-body.js +116 -0
  91. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
  92. package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
  93. package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
  94. package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
  95. package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
  96. package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
  97. package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
  98. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
  99. package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
  100. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
  101. package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
  102. package/dist/components/data-views/list-page-tree-column-header.js +22 -0
  103. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
  104. package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
  105. package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
  106. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
  107. package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
  108. package/dist/components/data-views/os-folder-glyph.js +104 -0
  109. package/dist/components/data-views/os-folder-glyph.js.map +1 -0
  110. package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
  111. package/dist/components/data-views/outline-tree-menu.js +131 -0
  112. package/dist/components/data-views/outline-tree-menu.js.map +1 -0
  113. package/dist/components/table-properties/column-row.d.ts +22 -0
  114. package/dist/components/table-properties/column-row.js +153 -0
  115. package/dist/components/table-properties/column-row.js.map +1 -0
  116. package/dist/components/table-properties/draggable-list.d.ts +24 -0
  117. package/dist/components/table-properties/draggable-list.js +53 -0
  118. package/dist/components/table-properties/draggable-list.js.map +1 -0
  119. package/dist/components/table-properties/drawer-button.d.ts +110 -0
  120. package/dist/components/table-properties/drawer-button.js +2748 -0
  121. package/dist/components/table-properties/drawer-button.js.map +1 -0
  122. package/dist/components/table-properties/drawer.d.ts +100 -0
  123. package/dist/components/table-properties/drawer.js +2595 -0
  124. package/dist/components/table-properties/drawer.js.map +1 -0
  125. package/dist/components/table-properties/filter-card.d.ts +24 -0
  126. package/dist/components/table-properties/filter-card.js +854 -0
  127. package/dist/components/table-properties/filter-card.js.map +1 -0
  128. package/dist/components/table-properties/index.d.ts +14 -0
  129. package/dist/components/table-properties/index.js +2768 -0
  130. package/dist/components/table-properties/index.js.map +1 -0
  131. package/dist/components/table-properties/sort-card.d.ts +20 -0
  132. package/dist/components/table-properties/sort-card.js +102 -0
  133. package/dist/components/table-properties/sort-card.js.map +1 -0
  134. package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
  135. package/dist/components/templates/dedicated-search-landing-template.js +254 -0
  136. package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
  137. package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
  138. package/dist/components/templates/dedicated-search-results-template.js +16 -0
  139. package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
  140. package/dist/components/templates/index.d.ts +9 -0
  141. package/dist/components/templates/index.js +2720 -0
  142. package/dist/components/templates/index.js.map +1 -0
  143. package/dist/components/templates/list-page.d.ts +83 -0
  144. package/dist/components/templates/list-page.js +2433 -0
  145. package/dist/components/templates/list-page.js.map +1 -0
  146. package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
  147. package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
  148. package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
  149. package/dist/components/ui/accordion.d.ts +10 -0
  150. package/dist/components/ui/accordion.js +74 -0
  151. package/dist/components/ui/accordion.js.map +1 -0
  152. package/dist/components/ui/alert-dialog.d.ts +37 -0
  153. package/dist/components/ui/alert-dialog.js +201 -0
  154. package/dist/components/ui/alert-dialog.js.map +1 -0
  155. package/dist/components/ui/avatar.d.ts +84 -0
  156. package/dist/components/ui/avatar.js +328 -0
  157. package/dist/components/ui/avatar.js.map +1 -0
  158. package/dist/components/ui/badge.d.ts +13 -0
  159. package/dist/components/ui/badge.js +49 -0
  160. package/dist/components/ui/badge.js.map +1 -0
  161. package/dist/components/ui/banner.d.ts +62 -0
  162. package/dist/components/ui/banner.js +364 -0
  163. package/dist/components/ui/banner.js.map +1 -0
  164. package/dist/components/ui/breadcrumb.d.ts +14 -0
  165. package/dist/components/ui/breadcrumb.js +114 -0
  166. package/dist/components/ui/breadcrumb.js.map +1 -0
  167. package/dist/components/ui/button.d.ts +16 -0
  168. package/dist/components/ui/button.js +59 -0
  169. package/dist/components/ui/button.js.map +1 -0
  170. package/dist/components/ui/calendar.d.ts +13 -0
  171. package/dist/components/ui/calendar.js +238 -0
  172. package/dist/components/ui/calendar.js.map +1 -0
  173. package/dist/components/ui/card.d.ts +14 -0
  174. package/dist/components/ui/card.js +102 -0
  175. package/dist/components/ui/card.js.map +1 -0
  176. package/dist/components/ui/chart.d.ts +58 -0
  177. package/dist/components/ui/chart.js +292 -0
  178. package/dist/components/ui/chart.js.map +1 -0
  179. package/dist/components/ui/checkbox.d.ts +23 -0
  180. package/dist/components/ui/checkbox.js +155 -0
  181. package/dist/components/ui/checkbox.js.map +1 -0
  182. package/dist/components/ui/coach-mark.d.ts +27 -0
  183. package/dist/components/ui/coach-mark.js +306 -0
  184. package/dist/components/ui/coach-mark.js.map +1 -0
  185. package/dist/components/ui/collapsible.d.ts +8 -0
  186. package/dist/components/ui/collapsible.js +35 -0
  187. package/dist/components/ui/collapsible.js.map +1 -0
  188. package/dist/components/ui/command.d.ts +36 -0
  189. package/dist/components/ui/command.js +274 -0
  190. package/dist/components/ui/command.js.map +1 -0
  191. package/dist/components/ui/context-menu.d.ts +32 -0
  192. package/dist/components/ui/context-menu.js +245 -0
  193. package/dist/components/ui/context-menu.js.map +1 -0
  194. package/dist/components/ui/date-picker-field.d.ts +38 -0
  195. package/dist/components/ui/date-picker-field.js +550 -0
  196. package/dist/components/ui/date-picker-field.js.map +1 -0
  197. package/dist/components/ui/dialog.d.ts +22 -0
  198. package/dist/components/ui/dialog.js +200 -0
  199. package/dist/components/ui/dialog.js.map +1 -0
  200. package/dist/components/ui/dot-pattern.d.ts +21 -0
  201. package/dist/components/ui/dot-pattern.js +139 -0
  202. package/dist/components/ui/dot-pattern.js.map +1 -0
  203. package/dist/components/ui/drag-handle-grip.d.ts +10 -0
  204. package/dist/components/ui/drag-handle-grip.js +15 -0
  205. package/dist/components/ui/drag-handle-grip.js.map +1 -0
  206. package/dist/components/ui/drawer.d.ts +16 -0
  207. package/dist/components/ui/drawer.js +125 -0
  208. package/dist/components/ui/drawer.js.map +1 -0
  209. package/dist/components/ui/dropdown-menu.d.ts +45 -0
  210. package/dist/components/ui/dropdown-menu.js +353 -0
  211. package/dist/components/ui/dropdown-menu.js.map +1 -0
  212. package/dist/components/ui/export-drawer.d.ts +11 -0
  213. package/dist/components/ui/export-drawer.js +1658 -0
  214. package/dist/components/ui/export-drawer.js.map +1 -0
  215. package/dist/components/ui/field.d.ts +30 -0
  216. package/dist/components/ui/field.js +249 -0
  217. package/dist/components/ui/field.js.map +1 -0
  218. package/dist/components/ui/form.d.ts +28 -0
  219. package/dist/components/ui/form.js +110 -0
  220. package/dist/components/ui/form.js.map +1 -0
  221. package/dist/components/ui/hover-card.d.ts +9 -0
  222. package/dist/components/ui/hover-card.js +43 -0
  223. package/dist/components/ui/hover-card.js.map +1 -0
  224. package/dist/components/ui/input-group.d.ts +20 -0
  225. package/dist/components/ui/input-group.js +219 -0
  226. package/dist/components/ui/input-group.js.map +1 -0
  227. package/dist/components/ui/input-mask.d.ts +39 -0
  228. package/dist/components/ui/input-mask.js +118 -0
  229. package/dist/components/ui/input-mask.js.map +1 -0
  230. package/dist/components/ui/input.d.ts +5 -0
  231. package/dist/components/ui/input.js +30 -0
  232. package/dist/components/ui/input.js.map +1 -0
  233. package/dist/components/ui/kbd.d.ts +20 -0
  234. package/dist/components/ui/kbd.js +45 -0
  235. package/dist/components/ui/kbd.js.map +1 -0
  236. package/dist/components/ui/key-metrics-context.d.ts +19 -0
  237. package/dist/components/ui/key-metrics-context.js +26 -0
  238. package/dist/components/ui/key-metrics-context.js.map +1 -0
  239. package/dist/components/ui/key-metrics.d.ts +131 -0
  240. package/dist/components/ui/key-metrics.js +1015 -0
  241. package/dist/components/ui/key-metrics.js.map +1 -0
  242. package/dist/components/ui/label.d.ts +6 -0
  243. package/dist/components/ui/label.js +28 -0
  244. package/dist/components/ui/label.js.map +1 -0
  245. package/dist/components/ui/list-page-view-frame.d.ts +22 -0
  246. package/dist/components/ui/list-page-view-frame.js +24 -0
  247. package/dist/components/ui/list-page-view-frame.js.map +1 -0
  248. package/dist/components/ui/page-header.d.ts +51 -0
  249. package/dist/components/ui/page-header.js +372 -0
  250. package/dist/components/ui/page-header.js.map +1 -0
  251. package/dist/components/ui/payment-card-fields.d.ts +10 -0
  252. package/dist/components/ui/payment-card-fields.js +80 -0
  253. package/dist/components/ui/payment-card-fields.js.map +1 -0
  254. package/dist/components/ui/popover.d.ts +10 -0
  255. package/dist/components/ui/popover.js +47 -0
  256. package/dist/components/ui/popover.js.map +1 -0
  257. package/dist/components/ui/radio-group.d.ts +29 -0
  258. package/dist/components/ui/radio-group.js +190 -0
  259. package/dist/components/ui/radio-group.js.map +1 -0
  260. package/dist/components/ui/resizable.d.ts +16 -0
  261. package/dist/components/ui/resizable.js +51 -0
  262. package/dist/components/ui/resizable.js.map +1 -0
  263. package/dist/components/ui/scroll-area.d.ts +8 -0
  264. package/dist/components/ui/scroll-area.js +66 -0
  265. package/dist/components/ui/scroll-area.js.map +1 -0
  266. package/dist/components/ui/select.d.ts +18 -0
  267. package/dist/components/ui/select.js +186 -0
  268. package/dist/components/ui/select.js.map +1 -0
  269. package/dist/components/ui/selection-tile-grid.d.ts +52 -0
  270. package/dist/components/ui/selection-tile-grid.js +347 -0
  271. package/dist/components/ui/selection-tile-grid.js.map +1 -0
  272. package/dist/components/ui/separator.d.ts +7 -0
  273. package/dist/components/ui/separator.js +33 -0
  274. package/dist/components/ui/separator.js.map +1 -0
  275. package/dist/components/ui/sheet.d.ts +18 -0
  276. package/dist/components/ui/sheet.js +181 -0
  277. package/dist/components/ui/sheet.js.map +1 -0
  278. package/dist/components/ui/sidebar.d.ts +94 -0
  279. package/dist/components/ui/sidebar.js +805 -0
  280. package/dist/components/ui/sidebar.js.map +1 -0
  281. package/dist/components/ui/skeleton.d.ts +5 -0
  282. package/dist/components/ui/skeleton.js +22 -0
  283. package/dist/components/ui/skeleton.js.map +1 -0
  284. package/dist/components/ui/slider.d.ts +7 -0
  285. package/dist/components/ui/slider.js +66 -0
  286. package/dist/components/ui/slider.js.map +1 -0
  287. package/dist/components/ui/sonner.d.ts +6 -0
  288. package/dist/components/ui/sonner.js +38 -0
  289. package/dist/components/ui/sonner.js.map +1 -0
  290. package/dist/components/ui/status-badge.d.ts +38 -0
  291. package/dist/components/ui/status-badge.js +77 -0
  292. package/dist/components/ui/status-badge.js.map +1 -0
  293. package/dist/components/ui/table.d.ts +13 -0
  294. package/dist/components/ui/table.js +115 -0
  295. package/dist/components/ui/table.js.map +1 -0
  296. package/dist/components/ui/tabs.d.ts +15 -0
  297. package/dist/components/ui/tabs.js +93 -0
  298. package/dist/components/ui/tabs.js.map +1 -0
  299. package/dist/components/ui/textarea.d.ts +6 -0
  300. package/dist/components/ui/textarea.js +25 -0
  301. package/dist/components/ui/textarea.js.map +1 -0
  302. package/dist/components/ui/tip.d.ts +12 -0
  303. package/dist/components/ui/tip.js +61 -0
  304. package/dist/components/ui/tip.js.map +1 -0
  305. package/dist/components/ui/toggle-group.d.ts +14 -0
  306. package/dist/components/ui/toggle-group.js +104 -0
  307. package/dist/components/ui/toggle-group.js.map +1 -0
  308. package/dist/components/ui/toggle-switch.d.ts +10 -0
  309. package/dist/components/ui/toggle-switch.js +33 -0
  310. package/dist/components/ui/toggle-switch.js.map +1 -0
  311. package/dist/components/ui/toggle.d.ts +13 -0
  312. package/dist/components/ui/toggle.js +51 -0
  313. package/dist/components/ui/toggle.js.map +1 -0
  314. package/dist/components/ui/tooltip.d.ts +10 -0
  315. package/dist/components/ui/tooltip.js +68 -0
  316. package/dist/components/ui/tooltip.js.map +1 -0
  317. package/dist/components/ui/view-segmented-control.d.ts +31 -0
  318. package/dist/components/ui/view-segmented-control.js +167 -0
  319. package/dist/components/ui/view-segmented-control.js.map +1 -0
  320. package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
  321. package/dist/hooks/use-app-theme.d.ts +24 -0
  322. package/dist/hooks/use-app-theme.js +286 -0
  323. package/dist/hooks/use-app-theme.js.map +1 -0
  324. package/dist/hooks/use-coach-mark.d.ts +86 -0
  325. package/dist/hooks/use-coach-mark.js +218 -0
  326. package/dist/hooks/use-coach-mark.js.map +1 -0
  327. package/dist/hooks/use-mobile.d.ts +3 -0
  328. package/dist/hooks/use-mobile.js +29 -0
  329. package/dist/hooks/use-mobile.js.map +1 -0
  330. package/dist/hooks/use-mod-key-label.d.ts +6 -0
  331. package/dist/hooks/use-mod-key-label.js +25 -0
  332. package/dist/hooks/use-mod-key-label.js.map +1 -0
  333. package/dist/index.d.ts +120 -0
  334. package/dist/index.js +13324 -0
  335. package/dist/index.js.map +1 -0
  336. package/dist/lib/compose-refs.d.ts +6 -0
  337. package/dist/lib/compose-refs.js +17 -0
  338. package/dist/lib/compose-refs.js.map +1 -0
  339. package/dist/lib/conditional-rule-match.d.ts +30 -0
  340. package/dist/lib/conditional-rule-match.js +66 -0
  341. package/dist/lib/conditional-rule-match.js.map +1 -0
  342. package/dist/lib/data-list-display-options.d.ts +26 -0
  343. package/dist/lib/data-list-display-options.js +14 -0
  344. package/dist/lib/data-list-display-options.js.map +1 -0
  345. package/dist/lib/data-list-view-registry.d.ts +2 -0
  346. package/dist/lib/data-list-view-registry.js +102 -0
  347. package/dist/lib/data-list-view-registry.js.map +1 -0
  348. package/dist/lib/data-list-view-surface.d.ts +2 -0
  349. package/dist/lib/data-list-view-surface.js +80 -0
  350. package/dist/lib/data-list-view-surface.js.map +1 -0
  351. package/dist/lib/data-list-view.d.ts +21 -0
  352. package/dist/lib/data-list-view.js +25 -0
  353. package/dist/lib/data-list-view.js.map +1 -0
  354. package/dist/lib/date-filter.d.ts +22 -0
  355. package/dist/lib/date-filter.js +61 -0
  356. package/dist/lib/date-filter.js.map +1 -0
  357. package/dist/lib/dev-log.d.ts +8 -0
  358. package/dist/lib/dev-log.js +10 -0
  359. package/dist/lib/dev-log.js.map +1 -0
  360. package/dist/lib/dropdown-menu-surface.d.ts +14 -0
  361. package/dist/lib/dropdown-menu-surface.js +6 -0
  362. package/dist/lib/dropdown-menu-surface.js.map +1 -0
  363. package/dist/lib/editable-target.d.ts +12 -0
  364. package/dist/lib/editable-target.js +12 -0
  365. package/dist/lib/editable-target.js.map +1 -0
  366. package/dist/lib/list-page-table-properties.d.ts +35 -0
  367. package/dist/lib/list-page-table-properties.js +81 -0
  368. package/dist/lib/list-page-table-properties.js.map +1 -0
  369. package/dist/lib/raf-throttle.d.ts +23 -0
  370. package/dist/lib/raf-throttle.js +27 -0
  371. package/dist/lib/raf-throttle.js.map +1 -0
  372. package/dist/lib/row-height.d.ts +16 -0
  373. package/dist/lib/row-height.js +10 -0
  374. package/dist/lib/row-height.js.map +1 -0
  375. package/dist/lib/table-properties-types.d.ts +83 -0
  376. package/dist/lib/table-properties-types.js +19 -0
  377. package/dist/lib/table-properties-types.js.map +1 -0
  378. package/dist/lib/utils.d.ts +5 -0
  379. package/dist/lib/utils.js +11 -0
  380. package/dist/lib/utils.js.map +1 -0
  381. package/package.json +83 -19
  382. package/src/components/data-table/filter-date-calendar.tsx +38 -0
  383. package/src/components/data-table/filter-text-value-input.tsx +77 -0
  384. package/src/components/data-table/index.tsx +1678 -0
  385. package/src/components/data-table/pagination.tsx +255 -0
  386. package/src/components/data-table/types.ts +96 -0
  387. package/src/components/data-table/use-table-state.ts +767 -0
  388. package/src/components/data-views/board-card-primitives.tsx +93 -0
  389. package/src/components/data-views/data-row-list.tsx +183 -0
  390. package/src/components/data-views/finder-panel-view.tsx +405 -0
  391. package/src/components/data-views/folder-grid-view.tsx +86 -0
  392. package/src/components/data-views/hub-table.tsx +498 -0
  393. package/src/components/data-views/index.ts +28 -0
  394. package/src/components/data-views/list-page-board-card.tsx +192 -0
  395. package/src/components/data-views/list-page-board-template.tsx +122 -0
  396. package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
  397. package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  398. package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  399. package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
  400. package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
  401. package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  402. package/src/components/data-views/os-folder-glyph.tsx +141 -0
  403. package/src/components/data-views/outline-tree-menu.tsx +157 -0
  404. package/src/components/table-properties/column-row.tsx +90 -0
  405. package/src/components/table-properties/draggable-list.ts +54 -0
  406. package/src/components/table-properties/drawer-button.tsx +300 -0
  407. package/src/components/table-properties/drawer.tsx +1148 -0
  408. package/src/components/table-properties/filter-card.tsx +251 -0
  409. package/src/components/table-properties/index.ts +36 -0
  410. package/src/components/table-properties/sort-card.tsx +63 -0
  411. package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
  412. package/src/components/templates/dedicated-search-results-template.tsx +19 -0
  413. package/src/components/templates/index.ts +33 -0
  414. package/src/components/templates/list-page.tsx +602 -0
  415. package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
  416. package/src/components/ui/accordion.tsx +92 -0
  417. package/src/components/ui/alert-dialog.tsx +221 -0
  418. package/src/components/ui/avatar.tsx +13 -2
  419. package/src/components/ui/banner.tsx +2 -2
  420. package/src/components/ui/button.tsx +4 -4
  421. package/src/components/ui/calendar.tsx +1 -1
  422. package/src/components/ui/coach-mark.tsx +1 -1
  423. package/src/components/ui/context-menu.tsx +291 -0
  424. package/src/components/ui/date-picker-field.tsx +2 -2
  425. package/src/components/ui/dot-pattern.tsx +183 -0
  426. package/src/components/ui/export-drawer.tsx +375 -0
  427. package/src/components/ui/hover-card.tsx +66 -0
  428. package/src/components/ui/key-metrics-context.tsx +78 -0
  429. package/src/components/ui/key-metrics.tsx +1133 -0
  430. package/src/components/ui/list-page-view-frame.tsx +64 -0
  431. package/src/components/ui/page-header.tsx +244 -0
  432. package/src/components/ui/payment-card-fields.tsx +2 -2
  433. package/src/components/ui/resizable.tsx +68 -0
  434. package/src/components/ui/scroll-area.tsx +72 -0
  435. package/src/components/ui/selection-tile-grid.tsx +9 -2
  436. package/src/components/ui/sidebar.tsx +84 -12
  437. package/src/components/ui/slider.tsx +83 -0
  438. package/src/globals.css +2201 -7
  439. package/src/globals.d.ts +20 -0
  440. package/src/index.ts +68 -1
  441. package/src/lib/conditional-rule-match.ts +119 -0
  442. package/src/lib/data-list-display-options.ts +35 -0
  443. package/src/lib/data-list-view-registry.ts +104 -0
  444. package/src/lib/data-list-view-surface.ts +83 -0
  445. package/src/lib/data-list-view.ts +47 -0
  446. package/src/lib/dev-log.ts +10 -0
  447. package/src/lib/editable-target.ts +20 -0
  448. package/src/lib/list-page-table-properties.ts +48 -0
  449. package/src/lib/raf-throttle.ts +45 -0
  450. package/src/lib/row-height.ts +19 -0
  451. package/src/lib/table-properties-types.ts +98 -0
  452. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  453. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +3 -3
  454. package/template/.cursor/rules/exxat-data-tables.mdc +1 -1
  455. package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
  456. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +2 -2
  457. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  458. package/template/AGENTS.md +104 -78
  459. package/template/app/(app)/dashboard/loading.tsx +15 -3
  460. package/template/app/(app)/dashboard/page.tsx +14 -2
  461. package/template/app/(app)/examples/page.tsx +0 -2
  462. package/template/app/(app)/layout.tsx +17 -4
  463. package/template/app/(app)/loading.tsx +18 -1
  464. package/template/app/(app)/question-bank/find/page.tsx +1 -2
  465. package/template/app/(app)/question-bank/layout.tsx +1 -1
  466. package/template/app/(app)/question-bank/library/page.tsx +1 -2
  467. package/template/app/(app)/question-bank/list/page.tsx +1 -2
  468. package/template/app/(app)/question-bank/new/page.tsx +15 -20
  469. package/template/app/(app)/question-bank/page.tsx +1 -2
  470. package/template/app/(app)/settings/page.tsx +5 -4
  471. package/template/app/globals.css +14 -16
  472. package/template/components/ask-leo-sidebar.tsx +5 -1
  473. package/template/components/brand-color-picker.tsx +2 -2
  474. package/template/components/charts-overview.tsx +1 -1
  475. package/template/components/compliance-board-view.tsx +142 -0
  476. package/template/components/compliance-client.tsx +92 -0
  477. package/template/components/compliance-page-header.tsx +89 -0
  478. package/template/components/compliance-table.tsx +468 -0
  479. package/template/components/dashboard-report-charts.tsx +1 -1
  480. package/template/components/dashboard-tabs.tsx +1 -1
  481. package/template/components/data-table/filter-date-calendar.tsx +1 -38
  482. package/template/components/data-table/filter-text-value-input.tsx +1 -77
  483. package/template/components/data-table/index.tsx +1 -1634
  484. package/template/components/data-table/pagination.tsx +1 -255
  485. package/template/components/data-table/types.ts +1 -94
  486. package/template/components/data-table/use-table-state.test.ts +420 -0
  487. package/template/components/data-table/use-table-state.ts +1 -758
  488. package/template/components/data-view-dashboard-charts-compliance.tsx +963 -0
  489. package/template/components/data-view-dashboard-charts-team.tsx +971 -0
  490. package/template/components/data-view-dashboard-charts.tsx +1503 -0
  491. package/template/components/data-views/board-card-primitives.tsx +1 -93
  492. package/template/components/data-views/data-row-list.tsx +1 -183
  493. package/template/components/data-views/finder-panel-view.tsx +1 -405
  494. package/template/components/data-views/folder-grid-view.tsx +1 -86
  495. package/template/components/data-views/hub-table.tsx +1 -0
  496. package/template/components/data-views/index.ts +50 -37
  497. package/template/components/data-views/list-page-board-card.tsx +1 -192
  498. package/template/components/data-views/list-page-board-template.tsx +1 -122
  499. package/template/components/data-views/list-page-connected-view-body.tsx +1 -66
  500. package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
  501. package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -68
  502. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
  503. package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
  504. package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
  505. package/template/components/data-views/list-page-view-frame.tsx +5 -53
  506. package/template/components/data-views/os-folder-glyph.tsx +1 -129
  507. package/template/components/data-views/outline-tree-menu.tsx +1 -157
  508. package/template/components/export-drawer.test.tsx +71 -0
  509. package/template/components/export-drawer.tsx +1 -375
  510. package/template/components/exxat-product-logo.tsx +5 -5
  511. package/template/components/hub-tree-panel-view.tsx +2 -2
  512. package/template/components/invite-collaborators-drawer.tsx +3 -3
  513. package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
  514. package/template/components/key-metrics.tsx +1 -1063
  515. package/template/components/leo-insight-indicator.tsx +2 -2
  516. package/template/components/new-placement-back-btn.tsx +28 -0
  517. package/template/components/new-placement-form.tsx +942 -0
  518. package/template/components/new-question-composer.tsx +456 -408
  519. package/template/components/onboarding/index.ts +9 -0
  520. package/template/components/onboarding/onboarding-01.tsx +1 -1
  521. package/template/components/onboarding/onboarding-02.tsx +1 -1
  522. package/template/components/onboarding/onboarding-03.tsx +1 -1
  523. package/template/components/onboarding/onboarding-04.tsx +1 -1
  524. package/template/components/page-header.tsx +8 -226
  525. package/template/components/placement-board-card.tsx +250 -0
  526. package/template/components/placement-detail.tsx +438 -0
  527. package/template/components/placements-board-view.tsx +397 -0
  528. package/template/components/placements-client.tsx +220 -0
  529. package/template/components/placements-list-view.tsx +124 -0
  530. package/template/components/placements-page-header.tsx +166 -0
  531. package/template/components/placements-table-cells.test.tsx +22 -0
  532. package/template/components/placements-table-cells.tsx +173 -0
  533. package/template/components/placements-table-columns.tsx +210 -0
  534. package/template/components/placements-table.tsx +934 -0
  535. package/template/components/product-switcher.tsx +3 -4
  536. package/template/components/product-wordmark.tsx +2 -1
  537. package/template/components/question-bank-client.tsx +5 -5
  538. package/template/components/question-bank-hub-client.tsx +1 -1
  539. package/template/components/question-bank-new-folder-sheet.tsx +2 -2
  540. package/template/components/question-bank-secondary-nav.tsx +3 -3
  541. package/template/components/question-bank-table.tsx +541 -431
  542. package/template/components/rotations-empty-state.tsx +50 -0
  543. package/template/components/rotations-panel-activator.tsx +8 -0
  544. package/template/components/settings-appearance-card.tsx +3 -4
  545. package/template/components/settings-client.tsx +15 -59
  546. package/template/components/settings-form-row.tsx +4 -9
  547. package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
  548. package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +59 -74
  549. package/template/components/sidebar/index.ts +16 -0
  550. package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
  551. package/template/components/{secondary-panel.tsx → sidebar/secondary-panel.tsx} +50 -7
  552. package/template/components/{sidebar-auto-collapse.tsx → sidebar/sidebar-auto-collapse.tsx} +6 -2
  553. package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
  554. package/template/components/site-header.tsx +1 -1
  555. package/template/components/sites-board-view.tsx +67 -0
  556. package/template/components/sites-client.tsx +154 -0
  557. package/template/components/sites-table.tsx +249 -0
  558. package/template/components/table-properties/column-row.tsx +1 -90
  559. package/template/components/table-properties/draggable-list.ts +1 -49
  560. package/template/components/table-properties/drawer-button.tsx +1 -262
  561. package/template/components/table-properties/drawer.tsx +1 -1166
  562. package/template/components/table-properties/filter-card.tsx +1 -251
  563. package/template/components/table-properties/sort-card.tsx +1 -59
  564. package/template/components/table-properties/types.ts +28 -71
  565. package/template/components/team-board-view.tsx +122 -0
  566. package/template/components/team-client.tsx +100 -0
  567. package/template/components/team-page-header.tsx +92 -0
  568. package/template/components/team-table.tsx +553 -0
  569. package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
  570. package/template/components/templates/dedicated-search-results-template.tsx +1 -19
  571. package/template/components/templates/list-page.tsx +1 -608
  572. package/template/components/templates/nested-secondary-panel-shell.tsx +1 -63
  573. package/template/components/templates/new-focus-template.tsx +659 -0
  574. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  575. package/template/components/ui/accordion.tsx +1 -0
  576. package/template/components/ui/alert-dialog.tsx +1 -0
  577. package/template/components/ui/context-menu.tsx +1 -0
  578. package/template/components/ui/dot-pattern.tsx +1 -183
  579. package/template/components/ui/hover-card.tsx +1 -0
  580. package/template/components/ui/resizable.tsx +1 -68
  581. package/template/components/ui/scroll-area.tsx +1 -0
  582. package/template/components/ui/slider.tsx +1 -0
  583. package/template/docs/blueprints/README.md +86 -0
  584. package/template/docs/blueprints/_template.md +91 -0
  585. package/template/docs/blueprints/board-card.md +123 -0
  586. package/template/docs/blueprints/data-table.md +139 -0
  587. package/template/docs/blueprints/key-metrics.md +128 -0
  588. package/template/docs/blueprints/list-page-template.md +123 -0
  589. package/template/docs/blueprints/page-header.md +130 -0
  590. package/template/docs/command-menu-pattern.md +1 -1
  591. package/template/docs/component-selection-guide.md +224 -0
  592. package/template/docs/components-audit-2026-05.md +158 -0
  593. package/template/docs/data-views-pattern.md +17 -54
  594. package/template/docs/drawer-vs-dialog-pattern.md +1 -3
  595. package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
  596. package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
  597. package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
  598. package/template/docs/migrations/README.md +100 -0
  599. package/template/docs/migrations/_template.md +64 -0
  600. package/template/docs/shell-surface-elevation-pattern.md +3 -5
  601. package/template/docs/token-taxonomy.md +416 -0
  602. package/template/eslint.config.mjs +27 -0
  603. package/template/hooks/use-secondary-panel-hub-nav.ts +1 -1
  604. package/template/lib/command-menu-config.ts +0 -1
  605. package/template/lib/command-menu-search-data.ts +27 -11
  606. package/template/lib/compliance-supported-views.ts +10 -0
  607. package/template/lib/conditional-rule-match.ts +6 -97
  608. package/template/lib/data-list-display-options.ts +1 -49
  609. package/template/lib/data-list-view-registry.ts +1 -104
  610. package/template/lib/data-list-view-surface.ts +1 -83
  611. package/template/lib/data-list-view.ts +1 -47
  612. package/template/lib/data-view-dashboard-placements-layout.ts +215 -0
  613. package/template/lib/data-view-dashboard-storage.ts +35 -38
  614. package/template/lib/dev-log.ts +1 -8
  615. package/template/lib/editable-target.ts +1 -10
  616. package/template/lib/list-page-table-properties.ts +1 -48
  617. package/template/lib/list-status-badges.ts +97 -4
  618. package/template/lib/mock/compliance-kpi.ts +61 -0
  619. package/template/lib/mock/compliance.ts +146 -0
  620. package/template/lib/mock/navigation.tsx +0 -9
  621. package/template/lib/mock/placements-kpi.ts +134 -0
  622. package/template/lib/mock/placements.ts +176 -0
  623. package/template/lib/mock/sites-directory.ts +16 -0
  624. package/template/lib/mock/sites-kpi.ts +25 -0
  625. package/template/lib/mock/team-kpi.ts +60 -0
  626. package/template/lib/mock/team.ts +118 -0
  627. package/template/lib/placement-board-card-layout.ts +79 -0
  628. package/template/lib/placements-supported-views.ts +12 -0
  629. package/template/lib/question-bank-supported-views.ts +0 -1
  630. package/template/lib/raf-throttle.ts +1 -45
  631. package/template/lib/row-height.ts +4 -10
  632. package/template/lib/sidebar-state-cookie.ts +11 -2
  633. package/template/lib/sites-supported-views.ts +10 -0
  634. package/template/lib/table-state-lifecycle.ts +2 -2
  635. package/template/lib/team-supported-views.ts +10 -0
  636. package/template/package.json +1 -0
  637. package/template/tests/setup.ts +25 -0
  638. package/consumer-extras/AGENTS.md +0 -76
  639. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +0 -37
  640. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +0 -57
  641. package/consumer-extras/patterns/consumer-app-pattern.md +0 -39
  642. package/consumer-extras/patterns/focused-workflow-page-pattern.md +0 -84
  643. package/src/components/ui/button-group.tsx +0 -81
  644. package/src/theme.css +0 -16
  645. package/src/tokens/README.md +0 -15
  646. package/src/tokens/base.css +0 -337
  647. package/src/tokens/high-contrast.css +0 -1195
  648. package/src/tokens/layers.css +0 -224
  649. package/src/tokens/tailwind-bridge.css +0 -118
  650. package/src/tokens/themes.css +0 -201
  651. package/template/app/(app)/data-list/layout.tsx +0 -43
  652. package/template/app/(app)/data-list/page.tsx +0 -10
  653. package/template/app/(app)/examples/focused-workflow/page.tsx +0 -5
  654. package/template/components/app-route-loading.tsx +0 -14
  655. package/template/components/dashboard-onboarding-gallery.tsx +0 -13
  656. package/template/components/dashboard-onboarding.tsx +0 -21
  657. package/template/components/data-views/list-page-calendar-view.tsx +0 -593
  658. package/template/components/data-views/list-page-folder-columns-panel.tsx +0 -345
  659. package/template/components/examples/focused-workflow-showcase.tsx +0 -183
  660. package/template/components/list-hub-board-view.tsx +0 -68
  661. package/template/components/list-hub-client.tsx +0 -186
  662. package/template/components/list-hub-list-view.tsx +0 -36
  663. package/template/components/list-hub-panel-activator.tsx +0 -8
  664. package/template/components/list-hub-secondary-nav.tsx +0 -121
  665. package/template/components/list-hub-table.tsx +0 -336
  666. package/template/components/question-bank-folder-columns-panel.tsx +0 -104
  667. package/template/components/question-bank-list-view.tsx +0 -53
  668. package/template/components/secondary-panel/nav-link-rows.tsx +0 -83
  669. package/template/components/secondary-panels/list-hub-panel.tsx +0 -39
  670. package/template/components/secondary-panels/question-bank-panel.tsx +0 -39
  671. package/template/components/secondary-panels/registry.tsx +0 -15
  672. package/template/components/section-cards.tsx +0 -106
  673. package/template/components/templates/focused-workflow-layouts.tsx +0 -448
  674. package/template/components/templates/focused-workflow-page-template.tsx +0 -69
  675. package/template/components/templates/page-loading-shell.tsx +0 -262
  676. package/template/components/ui/button-group.tsx +0 -1
  677. package/template/docs/consumer-app-pattern.md +0 -39
  678. package/template/docs/focused-workflow-page-pattern.md +0 -84
  679. package/template/lib/list-hub-nav.ts +0 -121
  680. package/template/lib/mock/list-hub-directory.ts +0 -27
  681. package/template/lib/mock/list-hub-kpi.ts +0 -27
  682. package/template/lib/page-loading-variant.ts +0 -40
  683. /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
  684. /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
  685. /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
  686. /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
  687. /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
  688. /package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +0 -0
package/CHANGELOG.md CHANGED
@@ -15,25 +15,78 @@ After the user bumps `@exxatdesignux/ui`, do this in order:
15
15
 
16
16
  ---
17
17
 
18
- ## [0.2.19] - 2026-05-19
18
+ ## [Unreleased]
19
+
20
+ ## [0.3.0] – 2026-05-21
19
21
 
20
22
  ### Added
21
23
 
22
- - **Design tokens (CSS modules):** Split theme surface into `src/tokens/` (`base.css`, `themes.css`, `high-contrast.css`, `layers.css`, `tailwind-bridge.css`). Entry points `globals.css` / `theme.css` re-export only **no token value changes**.
23
- - **Package export:** `@exxatdesignux/ui/tokens/*` for consumers that import token partials alongside Tailwind v4.
24
+ - **`exxat-ui sync-extras` now installs the full AI bundle in one command.** Previously the CLI shipped only Cursor skills + pattern docs; consumers had to `degit` Cursor rules and handbook tier docs separately. The CLI now writes, in one pass:
25
+ 1. **Skills to both clients** — `.cursor/skills/exxat-*/` **and** `.claude/skills/exxat-*/` (same `SKILL.md` shape, two readers, one source of truth — no more `ln -s` step).
26
+ 2. **Cursor rules** — `.cursor/rules/exxat-*.mdc` (29 binding `MUST` / `MUST NOT` files: data-tables, accessibility, kbd shortcuts, KPI trends/flat-band/max-four, board cards, list-page-view-shells, centralized-list-dataset, reuse-before-custom, no-toast, drawer-vs-dialog, page-vs-drawer, no-slds-leakage, token-discipline, mono-ids, fontawesome-icons, dashboard-view-charts, …).
27
+ 3. **Handbook tier** — `docs/exxat-ds/handbook/{HANDBOOK,glossary,voice-and-tone,reference-implementations}.md`. These are stage-rewritten copies of `apps/web/docs/*.md` — links to shipped patterns use `../`, links to unshipped neighbours (blueprints, token-taxonomy, root `AGENTS.md`, `lib/*`) are rewritten to absolute GitHub URLs at bundle time so every link in the consumer's repo resolves.
28
+ 4. **Patterns + checklist** — unchanged from prior releases (`docs/exxat-ds/*-pattern.md`, `consumer-upgrade-checklist.md`).
29
+
30
+ The CLI is **namespaced** — it only writes files starting with `exxat-` (skills + rules), so a consumer's own non-Exxat rules / skills survive an upgrade untouched. Product code under `app/`, `pages/`, `src/`, plus the consumer's own `AGENTS.md`, are never modified. Bundle source lives at `packages/ui/consumer-extras/{cursor-skills,cursor-rules,patterns,handbook}/`.
31
+
32
+ - **Single centralized `<HubTable<TRow>>`** — `components/data-views/hub-table.tsx` is now the one table surface every list-page hub renders. It owns the previously per-hub scaffolding that drifted across five files: `useTableState`, `displayOptions` (controlled or internal), `conditionalRules` add/remove/update, `filterFields` + `fieldDefinitions` + `resolveColumnLabel` memos, `TablePropertiesDrawerButton` wiring with `supportedViewTypes`, the imperative `openPropertiesDrawer()` handle (`HubTableHandle`), the default `DataTable` for `view === "table"`, and `ListPageConnectedViewBody` routing for every other view. Hubs now pass only **what's different**: column defs, a typed `renderers: HubTableRenderers<TRow>` map for non-table views, and entity-specific labels/sorts/empties. The renderer args (`state`, `toolbar`, `toolbarShell`, `drawerToolbarProps`, `displayOptions`, `patchDisplay`) cover both the common "wrap a body in the shared toolbar" case (`toolbarShell(<MyView rows={state.rows} />)`) and the escape-hatch case (dashboards that own their own toolbar with extra layout-edit actions splat `drawerToolbarProps` back into a custom `<TablePropertiesDrawerButton>`). Hub-specific helpers (`columnsToFilterFields`, `columnsToFieldDefinitions`) are exported from the same module so column defs become the single source of truth for filters + drawer field options. Mirrored to `packages/ui/template/components/data-views/hub-table.tsx`.
33
+ - **Per-hub `supportedViewTypes` allowlists** — `lib/team-supported-views.ts`, `lib/compliance-supported-views.ts`, `lib/placements-supported-views.ts`, `lib/sites-supported-views.ts` (matching the existing `question-bank-supported-views.ts` and `list-hub-supported-views.ts`). Each hub passes its allowlist into `<HubTable>` so `TablePropertiesDrawerButton` filters the view-type tile grid to the hub's implemented views — Sites cannot pick Calendar, Placements cannot pick Calendar, Team cannot pick Folder/Tree, etc. Pair these with `ListPageTemplate.supportedViewTypes` on the hub client to filter the Add-view menu + ⌘1–9 shortcuts in tandem.
24
34
 
25
35
  ### Changed
26
36
 
27
- - **Starter `template/`** and **consumer extras:** List hub shell, secondary panel registry, route-aware loading skeletons, focused-workflow settings layout, KPI flat band + shell elevation docs/skills synced from `apps/web`.
37
+ - **Every hub table migrated to `<HubTable>` + `ListPageConnectedViewBody`** the previous "single centralized list dataset" rule is now structurally enforced (one `useTableState` per hub, one row bag every view body reads). Net code: hub `*-table.tsx` files dropped from **4,402** → **2,984** lines (-32%) by removing duplicated scaffolding; the new `hub-table.tsx` adds ~350 lines once. Per-hub deltas:
38
+ 1. **`placements-table.tsx`** 1,642 → 955 (-42%). Six bespoke `DataList*Shell` sub-components (board / list / dashboard / folder / tree-panel / panel) deleted — each one was a separate copy of `<DataTableToolbar … toolbarSlot={s => <TablePropertiesDrawerButton …/>}>` wrapped around a different view body. Replaced by per-view renderer entries that call `toolbarShell(<body />)`. Pagination chrome (`CountSyncer` + `PaginationBar`) moves into a custom `tableRenderer` + the `list-with-toolbar` renderer; `HubTable.paginationOverride` is forwarded into `useTableState`. The dashboard's layout-edit + coach-mark + persistence state lives in an internal `<PlacementsDashboardBody>` component (renderers can't host hooks — `ListPageConnectedViewBody` calls them inline).
39
+ 2. **`question-bank-table.tsx`** 1,073 → 823 (-23%). The custom `<HubFolderColumnsPanel>` miller-view + `<HubTreePanelView>` stay (genuinely entity-specific); every shell/toolbar wrapper around them is gone. The `QuestionBankNewFolderSheet` modal state (shared by panel + tree-panel) moves to the hub's top scope, so both renderers share one sheet instance instead of duplicating it. URL search sync is now `HubTable.syncedSearchFromUrl={searchLanding ? undefined : urlListSearch}` (was a 5th positional argument to `useTableState`).
40
+ 3. **`team-table.tsx`** 693 → 534 (-23%). Dashboard layout + coach-mark + persistence state extracted into a `<TeamDashboardBody>` internal component, same pattern as Placements. `TeamFinderListRow` + `TeamFinderDetail` panel helpers unchanged.
41
+ 4. **`compliance-table.tsx`** 612 → 448 (-27%). Same dashboard-body extraction; panel groups builder unchanged.
42
+ 5. **`sites-table.tsx`** 382 → 224 (-41%). Inline panel + dashboard renderers; sites has no custom dashboard-layout state.
43
+ - **`HubTable` API additions** (back-compat — all new props optional) — `displayOptions` + `onDisplayOptionsChange` (controlled mode for hubs that persist display state at the page level, e.g. Placements; otherwise `HubTable` owns the state internally), `pagination` + `onPaginationChange` (forwarded to `TablePropertiesDrawerButton` so the drawer's Display panel can offer "Show pagination"), `paginationOverride` (forwarded to `useTableState` for server-style paged tables), `syncedSearchFromUrl` (forwarded to `useTableState` for `?q=`-bound toolbar search), `onRowClick`, and `tableRenderer` (override for the `data-table` view when the hub needs pagination chrome or a custom wrapper around `<DataTable>`).
44
+ - **`OpenTablePropertiesHandle` aliases** — each `*-table.tsx` now exports `*TableHandle = HubTableHandle` instead of `= OpenTablePropertiesHandle` directly. Both resolve to the same shape (`{ openPropertiesDrawer(): void }`), so existing `tablePropertiesRef` consumers continue to work without changes.
28
45
 
29
- ### Chore (monorepo)
46
+ ### Added (continued, from earlier in this Unreleased section)
47
+
48
+ - **View registry + per-hub `supportedViewTypes` plumbing** — single source of truth for list-page view types and their render kinds, with per-hub allowlists so Properties, Add view, and ⌘1–9 shortcuts never offer a view the hub does not render.
49
+ 1. **`lib/data-list-view-registry.ts`** — `DATA_LIST_VIEW_REGISTRY` (one `DataListViewDefinition` per `DataListViewType`), `dataListViewDefinition`, `dataListViewTilesForHub` / `dataListViewSelectionTilesForHub` (filtered tiles for a hub's `supportedViewTypes`), `isDataListSurfaceViewType` (now derived from the registry — the previous local `SURFACE_VIEW_TYPES` literal in `lib/list-page-table-properties.ts` is gone), `showsListPageHubMetricsStrip` (false for `calendar` and `dashboard`).
50
+ 2. **`components/data-views/list-page-connected-view-body.tsx`** — `<ListPageConnectedViewBody view renderers hubLabel />` switches list-hub view bodies by render kind. Missing kinds render a clear `<ListPageViewNotConfigured>` empty state — never silently fall through to dashboard. Renderers can be `React.ReactNode` or `() => ReactNode` (the latter for lazy / heavy bodies).
51
+ 3. **`lib/hub-connected-view-renderers.ts`** — typed `defineHubViewRenderers(supported, renderers)` builder; the `Supported` generic constrains the renderer keys to the hub's actual render kinds, and dev builds emit a console warning when a supported view has no body.
52
+ 4. **`lib/question-bank-supported-views.ts`** + **`lib/list-hub-supported-views.ts`** — per-hub allowlists (e.g. Question bank supports `table | list | board | dashboard | folder | panel | tree-panel`; List hub supports `table | list | board | calendar | panel`). Hubs pass these into `ListPageTemplate.supportedViewTypes` and `TablePropertiesDrawerButton.supportedViewTypes`.
53
+ 5. **`DataListViewType` gains `"calendar"`** — and `data-list-view-surface.ts` adds the matching `calendar-with-toolbar` render kind. Existing hubs are unaffected (no hub claims `calendar` in its supported set yet); the type is now forward-compatible with a calendar body.
54
+
55
+ ### Changed
56
+
57
+ - **`TablePropertiesDrawer` + `TablePropertiesDrawerButton`** accept optional `supportedViewTypes?: readonly DataListViewType[]`. When set, the view-type tile grid in Properties is filtered to that hub's implemented views (via `dataListViewTilesForHub`) so users cannot switch a hub into a view it does not render. Default behavior (omitting the prop) is unchanged — all registered tiles are shown.
58
+ - **`ListPageTemplate`** accepts the same `supportedViewTypes` prop and filters **both** the Add view dropdown menu and the ⌘1–9 shortcut bindings to the hub's actual views. Previously, `VIEW_TYPES.slice(0, 9)` bound every registered view globally even on hubs that only implement a subset, so a user could ⌘4 → Dashboard into a hub that had no dashboard renderer (it would fall through to the not-configured empty state).
59
+ - **Question bank** (`question-bank-client.tsx`, `question-bank-table.tsx`) now passes `QUESTION_BANK_SUPPORTED_VIEWS` into `ListPageTemplate.supportedViewTypes` and `TablePropertiesDrawerButton.supportedViewTypes` end-to-end. Other hubs (Placements, Team, Compliance, Sites) are unchanged for this release — they still expose every registered view; their `supportedViewTypes` props are opt-in.
60
+ - **`lib/list-page-table-properties.ts`** no longer maintains its own `SURFACE_VIEW_TYPES` literal. `isDataListSurfaceViewType` is re-exported from `lib/data-list-view-registry.ts` so adding a new `DataListViewType` no longer requires editing three files.
61
+
62
+ ### Promoted into `@exxatdesignux/ui` (shared primitives)
63
+
64
+ - **`components/resizable`** + **`components/dot-pattern`** moved from `apps/web/components/ui/` to `packages/ui/src/components/ui/`. App imports continue to work through `apps/web/components/ui/{resizable,dot-pattern}.tsx`, which are now one-line re-exports from `@exxatdesignux/ui/components/{resizable,dot-pattern}`. Added `motion@^12.38.0` and `react-resizable-panels@^4.10.0` to `packages/ui/package.json` `dependencies`.
65
+
66
+ ### Removed
67
+
68
+ - **`components/section-cards.tsx`** (dead since the shadcn starter — zero imports across `apps/web` and `packages/ui/template/`).
69
+
70
+ ### Renamed
30
71
 
31
- - Package **`version`** **0.2.19** publish with tag **`ui-v0.2.19`**.
72
+ - **`components/sites-all-client.tsx`** → **`components/sites-client.tsx`**; `SitesAllClient` export → `SitesClient`. The hub is currently unmounted (no `app/(app)/sites/` route), but the file is kept as a reference Sites hub composition and renamed for consistency with `placements-client`, `team-client`, `compliance-client`. Mirrored in `packages/ui/template/`.
32
73
 
33
74
  ## [0.2.18] - 2026-05-19
34
75
 
35
76
  ### Fixed
36
77
 
78
+ - **Dark mode surface elevation ladder — popover / card split, secondary panel nestled** (`globals.css`):
79
+ 1. **`--popover` lifted to L=0.275** (was 0.225). Sharing the card's subtle 0.225 value made floating dropdowns blend straight into the canvas (only +0.025 above L=0.20) — the dropdown chrome lost its boundary. Popover now sits +0.075 above canvas with a slightly elevated chroma (built-ins 0.022 / custom `max(0.018, c·0.16)`), giving dropdowns and menus the visible lift they need over content. `--card` stays at 0.225 — it's an *inline* surface, not a floating one, and the earlier "subtle wash" reading was correct for it.
80
+ 2. **`--secondary-panel-bg` rewired** from `var(--brand-tint)` (which resolved to L=0.30) down to **L=0.22**. The old value made the secondary nav panel *brighter* than the surrounding sidebar (L=0.245), so the Library / question-bank rail popped out as a bright tile instead of nesting in. The new value wedges it between sidebar (0.245) and canvas (0.20). Per-theme overrides carry the brand hue + chroma (Exxat One 0.025, Prism 0.030, Assessment 0.025, custom `max(0.020, c·0.20)`).
81
+ 3. **Dark surface ladder is now** `--background` 0.20 → `--secondary-panel-bg` 0.22 → `--card` 0.225 → `--sidebar` / `--input-background` 0.245 → `--popover` 0.275 — clear, monotonic stepping with floating elements lifting further than inline ones.
82
+ - **Dark mode card / popover / input-background — softened to a subtle brand wash, not a tinted panel** (`globals.css`): The previous pass at `L=0.255 C=0.030` made cards read as a *separate tinted surface* floating above canvas. Pulled lightness down to `L=0.225` (only 0.025 above `--background` at L=0.20) and chroma down to `C=0.017` (built-ins) / `max(0.015, calc(c*0.13))` (custom). Cards now read as the same surface family as canvas with a small elevation step and a whisper of brand hue — the saturated brand expression remains on `--brand-tint` and `--sidebar` where it belongs. Input background follows the same shift (L 0.245 / C 0.010). Foreground contrast unchanged (≥ 12:1).
83
+ - **Hydration warnings from the Cursor IDE browser preview** (`app/(app)/layout.tsx`, `components/templates/nested-secondary-panel-shell.tsx`, `components/ask-leo-sidebar.tsx`): The in-IDE browser MCP injects a `data-cursor-ref` attribute on top-level layout chrome *before* React hydrates so it can target those nodes for click automation. React then warned about an attribute it didn't render. Added `suppressHydrationWarning` on just the three SSR-rendered shell roots that the MCP tags. The flag is scoped to *each element's own attributes only* — children still hydrate normally and any real mismatch inside the panel still surfaces. Has zero effect outside the Cursor IDE preview.
84
+ - **Dark mode brand surfaces — every product chrome surface now reads as the brand per product** (`globals.css`):
85
+ 1. **`--brand-tint`** was never overridden in `.dark` or any `.theme-*.dark` block, so it cascaded down from the light-mode pale value (e.g. `oklch(0.97 0.02 343)` for Prism). Because the dark `--secondary-panel-bg` formula derived it as `color-mix(--card 32%, --brand-tint 68%)`, the question-bank Library secondary panel rendered as a pale rose / lavender pastel on dark canvas. Fixed by adding `--brand-tint` / `--brand-tint-light` / `--brand-tint-subtle` overrides at dark-mode lightness inside `.dark`, `.theme-one.dark`, `.theme-prism.dark`, `.theme-assessment.dark`, and `.theme-custom.dark`.
86
+ 2. **`--secondary-panel-bg` formula in `.dark`** rewritten from `color-mix(--card 32%, --brand-tint 68%)` to plain `var(--brand-tint)`. The old mix folded in `--card`'s neutral hue 270 — never overridden per theme in dark mode — so every product's secondary panel was pulled toward the same purple-neutral. Now the panel renders as a fully brand-tinted dark surface that varies clearly per product: Exxat One `#2c2a46` (navy-lavender), Prism `#402235` (wine), Assessment `#143525` (forest green).
87
+ 3. **`--brand-tint` / `--sidebar` chroma** raised from `0.014–0.015` to `0.04–0.055` at their dark-mode lightnesses. At L≈0.245–0.30 the OKLCH hue-perception threshold is much higher than at L≈0.97 (Helmholtz–Kohlrausch effect), so the previous chroma was below the perception floor — products looked identical even though their hues were set correctly. The new chroma is well within WCAG contrast bounds (foreground stays ≥ 11:1 on every variant).
88
+ 4. **`--card` / `--popover` / `--input-background`** were only set in base `.dark` at neutral hue 270, never overridden per theme. KPI tiles, popovers, and outlined surfaces therefore stayed dead grey no matter which product was active — directly visible on the dashboard's six KPI tiles in the custom-orange theme. Added per-theme overrides at L≈0.255 with chroma 0.03 (built-ins) or `max(0.022, calc(c * 0.22))` (custom) at brand hue. Cards now render as a "darker version of the brand color" per product: Exxat One `#212131`, Prism `#2d1d27`, Assessment `#15271e`, custom-orange `#2f1e16`.
89
+ - **Dark canvas brand cohesion** (`globals.css`): `--background` chroma in every `.theme-*.dark` block bumped from `0.008` to `0.014`. At L 0.20 the previous chroma sat below the OKLCH hue-perception threshold, so the canvas read as dead-neutral grey regardless of the brand hue. The new value gives the canvas a perceptible whisper of brand chroma without affecting text contrast (foreground stays ≥ 12:1 against background).
37
90
  - **`Sidebar`**: Read `sidebar_state` on the server for `defaultOpen` so the documents **Resources** heading and rail chrome match the first client paint (fixes hydration warnings). Skip redundant cookie restore when state already matches.
38
91
  - **`TablePropertiesDrawer`**: Portaled **Add filter / sort / rule** menus use `z-[90]` above the properties sheet (`z-[80]`); `Sheet` and dropdowns use `modal={false}`; filter updates apply synchronously (no `startTransition` deferral). Drawer button routes column/display handlers through refs for the portaled sheet.
39
92
 
@@ -60,7 +113,7 @@ After the user bumps `@exxatdesignux/ui`, do this in order:
60
113
 
61
114
  ### Changed
62
115
 
63
- - **Tokens**: `globals.css` / `theme.css` refinements and starter **`template/`** parity with the web app (layout, Question bank hub chrome, navigation).
116
+ - **Tokens**: `globals.css` refinements and starter **`template/`** parity with the web app (layout, Question bank hub chrome, navigation). *(Note: `theme.css` was a duplicate of `globals.css` and has been removed — see [`0003-globals-css-canonical.md`](../../apps/web/docs/migrations/0003-globals-css-canonical.md).)*
64
117
  - **Consumer extras**: Cursor skills + pattern docs refreshed for collaboration / Question bank hub header.
65
118
 
66
119
  ### Chore (monorepo)
@@ -2,12 +2,20 @@
2
2
  /**
3
3
  * Consumer: copy packaged design-system extras into the app repo.
4
4
  *
5
- * Overwrites ONLY:
5
+ * Writes (and overwrites) ONLY:
6
6
  * - `.cursor/skills/<bundled exxat-* skill folders>/`
7
- * - `docs/exxat-ds/*.md` (pattern reference docs + **consumer-upgrade-checklist.md**)
7
+ * - `.claude/skills/<bundled exxat-* skill folders>/` (same SKILL.md shape;
8
+ * Claude Code reads from `.claude/skills/` instead of `.cursor/skills/`)
9
+ * - `.cursor/rules/<bundled exxat-*.mdc rule files>`
10
+ * - `docs/exxat-ds/*.md` (pattern reference docs + consumer-upgrade-checklist.md)
11
+ * - `docs/exxat-ds/handbook/*.md` (HANDBOOK, glossary, voice-and-tone, reference-implementations)
8
12
  *
9
- * Does NOT touch `app/`, `pages/`, `src/` product code, or npm dependencies.
10
- * Run after `npm install @exxatdesignux/ui@…` / `pnpm update @exxatdesignux/ui`.
13
+ * The CLI is **namespaced** it only touches files starting with `exxat-`
14
+ * (skills + rules) so a consumer's own non-Exxat rules/skills are preserved.
15
+ * Product code under `app/`, `pages/`, `src/`, plus the consumer's `AGENTS.md`
16
+ * and any of their own docs are never modified.
17
+ *
18
+ * Run after every `npm install @exxatdesignux/ui@…` / `pnpm update @exxatdesignux/ui`.
11
19
  *
12
20
  * npx --package=@exxatdesignux/ui@latest exxat-ui sync-extras
13
21
  */
@@ -20,46 +28,125 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
20
28
  const pkgRoot = resolve(__dirname, "..")
21
29
  const cwd = process.cwd()
22
30
 
23
- const skillsSrc = join(pkgRoot, "consumer-extras", "cursor-skills")
24
- const skillsDest = join(cwd, ".cursor", "skills")
31
+ const SOURCES = {
32
+ skills: join(pkgRoot, "consumer-extras", "cursor-skills"),
33
+ rules: join(pkgRoot, "consumer-extras", "cursor-rules"),
34
+ patterns: join(pkgRoot, "consumer-extras", "patterns"),
35
+ handbook: join(pkgRoot, "consumer-extras", "handbook"),
36
+ }
37
+
38
+ const DESTINATIONS = {
39
+ cursorSkills: join(cwd, ".cursor", "skills"),
40
+ claudeSkills: join(cwd, ".claude", "skills"),
41
+ cursorRules: join(cwd, ".cursor", "rules"),
42
+ patterns: join(cwd, "docs", "exxat-ds"),
43
+ handbook: join(cwd, "docs", "exxat-ds", "handbook"),
44
+ }
25
45
 
26
- const patternsSrc = join(pkgRoot, "consumer-extras", "patterns")
27
- const patternsDest = join(cwd, "docs", "exxat-ds")
46
+ let wrote = 0
47
+ let skipped = 0
28
48
 
29
- function syncSkills() {
30
- if (!existsSync(skillsSrc)) {
31
- console.error(
32
- "[sync-extras] Missing packaged skills. Is @exxatdesignux/ui installed? Expected:",
33
- skillsSrc,
34
- )
49
+ /** Replace `dest` with `src` (directory) — preserves only what we own. */
50
+ function replaceDir(src, dest) {
51
+ if (existsSync(dest)) rmSync(dest, { recursive: true, force: true })
52
+ cpSync(src, dest, { recursive: true })
53
+ }
54
+
55
+ /**
56
+ * Skills are bundled as directories like `cursor-skills/exxat-board-cards/SKILL.md`.
57
+ * Copy every `exxat-*` directory into the destination, replacing any prior copy.
58
+ * The same source tree is written to BOTH Cursor and Claude (identical SKILL.md
59
+ * shape, two readers) so one upgrade lights up both clients.
60
+ */
61
+ function syncSkillsTo(label, dest) {
62
+ if (!existsSync(SOURCES.skills)) {
63
+ console.error("[sync-extras] missing packaged skills:", SOURCES.skills)
35
64
  process.exit(1)
36
65
  }
37
- mkdirSync(skillsDest, { recursive: true })
38
- for (const name of readdirSync(skillsSrc)) {
39
- const src = join(skillsSrc, name)
66
+ mkdirSync(dest, { recursive: true })
67
+ for (const name of readdirSync(SOURCES.skills)) {
68
+ const src = join(SOURCES.skills, name)
40
69
  if (!statSync(src).isDirectory()) continue
41
70
  if (!name.startsWith("exxat-")) continue
42
- const dest = join(skillsDest, name)
43
- if (existsSync(dest)) rmSync(dest, { recursive: true, force: true })
44
- cpSync(src, dest, { recursive: true })
45
- console.log(`[sync-extras] wrote .cursor/skills/${name}/`)
71
+ replaceDir(src, join(dest, name))
72
+ console.log(`[sync-extras] wrote ${label}/${name}/`)
73
+ wrote++
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Rules ship as flat `.mdc` files under `consumer-extras/cursor-rules/`.
79
+ * Only files starting with `exxat-` are copied so the consumer's own rules
80
+ * (and any other vendor's rules) survive an upgrade untouched.
81
+ */
82
+ function syncRules() {
83
+ if (!existsSync(SOURCES.rules)) {
84
+ console.warn("[sync-extras] no packaged cursor-rules (optional)")
85
+ skipped++
86
+ return
87
+ }
88
+ mkdirSync(DESTINATIONS.cursorRules, { recursive: true })
89
+ for (const name of readdirSync(SOURCES.rules)) {
90
+ const src = join(SOURCES.rules, name)
91
+ if (!statSync(src).isFile()) continue
92
+ if (!name.startsWith("exxat-") || !name.endsWith(".mdc")) continue
93
+ cpSync(src, join(DESTINATIONS.cursorRules, name))
94
+ console.log(`[sync-extras] wrote .cursor/rules/${name}`)
95
+ wrote++
46
96
  }
47
97
  }
48
98
 
99
+ /**
100
+ * Pattern docs (data-views-pattern, command-menu-pattern, …) + the
101
+ * `consumer-upgrade-checklist.md` land flat in `docs/exxat-ds/`.
102
+ */
49
103
  function syncPatterns() {
50
- if (!existsSync(patternsSrc)) {
51
- console.warn("[sync-extras] no packaged patterns (optional).")
104
+ if (!existsSync(SOURCES.patterns)) {
105
+ console.warn("[sync-extras] no packaged patterns (optional)")
106
+ skipped++
52
107
  return
53
108
  }
54
- mkdirSync(patternsDest, { recursive: true })
55
- for (const name of readdirSync(patternsSrc)) {
56
- const src = join(patternsSrc, name)
109
+ mkdirSync(DESTINATIONS.patterns, { recursive: true })
110
+ for (const name of readdirSync(SOURCES.patterns)) {
111
+ const src = join(SOURCES.patterns, name)
57
112
  if (!statSync(src).isFile()) continue
58
- cpSync(src, join(patternsDest, name))
113
+ cpSync(src, join(DESTINATIONS.patterns, name))
59
114
  console.log(`[sync-extras] wrote docs/exxat-ds/${name}`)
115
+ wrote++
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Handbook tier (HANDBOOK · glossary · voice-and-tone · reference-implementations)
121
+ * lands in a nested `docs/exxat-ds/handbook/` so the patterns dir stays flat
122
+ * and the handbook's internal cross-links resolve.
123
+ *
124
+ * The bundled handbook files are stage-rewritten copies of the workspace
125
+ * source (`apps/web/docs/*.md`) — links to shipped patterns use `../`, links
126
+ * to unshipped neighbours (blueprints, token-taxonomy, etc.) are absolute
127
+ * GitHub URLs so they continue to work in the consumer's repo.
128
+ */
129
+ function syncHandbook() {
130
+ if (!existsSync(SOURCES.handbook)) {
131
+ console.warn("[sync-extras] no packaged handbook (optional)")
132
+ skipped++
133
+ return
134
+ }
135
+ mkdirSync(DESTINATIONS.handbook, { recursive: true })
136
+ for (const name of readdirSync(SOURCES.handbook)) {
137
+ const src = join(SOURCES.handbook, name)
138
+ if (!statSync(src).isFile()) continue
139
+ cpSync(src, join(DESTINATIONS.handbook, name))
140
+ console.log(`[sync-extras] wrote docs/exxat-ds/handbook/${name}`)
141
+ wrote++
60
142
  }
61
143
  }
62
144
 
63
- syncSkills()
145
+ syncSkillsTo(".cursor/skills", DESTINATIONS.cursorSkills)
146
+ syncSkillsTo(".claude/skills", DESTINATIONS.claudeSkills)
147
+ syncRules()
64
148
  syncPatterns()
65
- console.log("[sync-extras] Done. Product routes and app content were not modified.")
149
+ syncHandbook()
150
+
151
+ console.log(`[sync-extras] Done. ${wrote} files / dirs written; ${skipped} optional sources missing.`)
152
+ console.log("[sync-extras] Product routes and app content were not modified.")
@@ -6,16 +6,21 @@ touching product routes or pages.
6
6
 
7
7
  | Path in this tarball | After `exxat-ui sync-extras` in your repo |
8
8
  |----------------------|-------------------------------------------|
9
- | `CHANGELOG.md` (package root) | Stay in `node_modules/…` — read for release notes; **`exxat-ui changelog`** prints it |
10
- | `cursor-skills/exxat-*` | `.cursor/skills/exxat-*` (replaced) |
11
- | `patterns/*.md` | `docs/exxat-ds/*.md` (replaced) — includes **`consumer-upgrade-checklist.md`**, **`consumer-app-pattern.md`**, view-registry + shell docs |
12
- | `AGENTS.md` (this folder) | Copy or merge into your app handbook as **`AGENTS-consumer.md`** |
13
- | `cursor-skills/exxat-consumer-app` | Consumer-repo checklist (npm install, template diff) |
9
+ | `CHANGELOG.md` (package root) | Stays in `node_modules/…` — read for release notes; **`exxat-ui changelog`** prints it |
10
+ | `cursor-skills/exxat-*` | `.cursor/skills/exxat-*` **and** `.claude/skills/exxat-*` (replaced; same `SKILL.md` shape, two readers) |
11
+ | `cursor-rules/exxat-*.mdc` | `.cursor/rules/exxat-*.mdc` (replaced) — binding **MUST / MUST NOT** files |
12
+ | `patterns/*.md` | `docs/exxat-ds/*.md` (replaced) pattern docs + **`consumer-upgrade-checklist.md`** |
13
+ | `handbook/*.md` | `docs/exxat-ds/handbook/*.md` (replaced) `HANDBOOK`, `glossary`, `voice-and-tone`, `reference-implementations` |
14
+
15
+ The CLI is **namespaced**: it only touches files starting with `exxat-` (skills + rules)
16
+ so a consumer's own non-Exxat rules / skills are preserved. Product code under `app/`,
17
+ `pages/`, `src/`, plus the consumer's `AGENTS.md` and any of their own docs are never
18
+ modified.
14
19
 
15
20
  **Components and hooks** still come only from `node_modules/@exxatdesignux/ui`
16
21
  via normal semver installs — `sync-extras` does not copy TS source into your app.
17
22
 
18
- **Maintainers** (this monorepo): when skills or web docs change, run:
23
+ **Maintainers** (this monorepo): when skills, rules, or web docs change, run:
19
24
 
20
25
  ```bash
21
26
  pnpm --filter @exxatdesignux/ui vendor:consumer-extras
@@ -23,4 +28,34 @@ pnpm --filter @exxatdesignux/ui vendor:consumer-extras
23
28
 
24
29
  …then bump `packages/ui` version and publish so consumers get the new bundle.
25
30
 
26
- **List-page stack** (registry, `ListPageConnectedViewBody`, `supportedViewTypes`) lives in **`template/`** inside the tarball — not in `@exxatdesignux/ui` component exports. After changing **`apps/web`** or **`packages/ui/template`**, diff both trees and re-publish so consumer apps that copy from **`node_modules/@exxatdesignux/ui/template/`** stay aligned.
31
+ ## What lands where (full install map)
32
+
33
+ ```
34
+ <consumer-repo>/
35
+ ├── .cursor/
36
+ │ ├── rules/exxat-*.mdc ← binding MUST / MUST NOT (cursor-rules/)
37
+ │ └── skills/exxat-*/SKILL.md ← agent workflows + checklists (cursor-skills/)
38
+ ├── .claude/
39
+ │ └── skills/exxat-*/SKILL.md ← Claude mirror of the same skills (cursor-skills/)
40
+ └── docs/
41
+ └── exxat-ds/
42
+ ├── *-pattern.md ← narrative pattern docs (patterns/)
43
+ ├── consumer-upgrade-checklist.md
44
+ └── handbook/
45
+ ├── HANDBOOK.md
46
+ ├── glossary.md
47
+ ├── voice-and-tone.md
48
+ └── reference-implementations.md
49
+ ```
50
+
51
+ ## Note on handbook link rewriting
52
+
53
+ The bundled handbook files (`handbook/*.md`) are stage-rewritten copies of the
54
+ workspace source (`apps/web/docs/*.md`). Inter-doc links are normalised at
55
+ bundle time:
56
+
57
+ - Links to files we ship → relative (`./glossary.md`, `../data-views-pattern.md`)
58
+ - Links to files we don't ship (blueprints, `token-taxonomy.md`, `component-selection-guide.md`, `lib/*`, root `AGENTS.md`) → absolute GitHub URLs
59
+
60
+ This keeps every link clickable from the consumer's `docs/exxat-ds/handbook/`
61
+ directory without requiring them to clone the workspace.
@@ -0,0 +1,39 @@
1
+ ---
2
+ description: Exxat DS — WCAG 2.1 AA, ARIA tablists, 24px targets, contrast; see AGENTS.md §8 and exxat-accessibility skill
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Exxat DS — accessibility (binding summary)
7
+
8
+ **Full checklist (ARIA, touch targets, color, sidebar badges, audit follow-ups):** repo **`.cursor/skills/exxat-accessibility/SKILL.md`**.
9
+
10
+ **Product rules in prose + checklist:** **`apps/web/AGENTS.md` §8**.
11
+
12
+ ## Non‑negotiables
13
+
14
+ 1. **Target:** **WCAG 2.1 Level AA** (2.2 where noted — e.g. target size).
15
+ 2. **`role="tablist"`** — only **`role="tab"`** (or equivalent) as direct tab semantics. **MUST NOT** put `role="button"`, menus (`aria-haspopup`), or other controls **inside** the same `tablist` container.
16
+ 3. **Composite view switchers** (tabs + per-tab menu + remove): use **`role="toolbar"`** + **`aria-label`**; **`aria-pressed`** on toggles — **MUST NOT** misuse `tab`/`tablist` for those controls.
17
+ 4. **Touch targets (2.5.8):** interactive controls **≥ 24×24 CSS px** or **24px** spacing so hit areas do not overlap — use **`min-h-6 min-w-6` / `size-6`** for icon-only targets; avoid **`size-4`** as the sole target.
18
+ 5. **Contrast:** normal text **≥ 4.5:1**; UI components / focus where required **≥ 3:1**; muted text on tinted surfaces use tokens against the correct surface (e.g. sidebar), not only `--background`.
19
+ 6. **Minimum text size:** visible product copy **≥ 11px** — use **`text-xs`** or larger; **MUST NOT** use arbitrary Tailwind font sizes below that (see **`apps/web/AGENTS.md` §8.3**, **`apps/web/app/globals.css`** `--text-xs`).
20
+ 7. **Dialogs / sheets / drawers:** must expose a **Title** (`DialogTitle` / `SheetTitle` / `DrawerTitle`); use **`sr-only`** if visually hidden (shadcn pattern).
21
+ 8. **Format hints are persistent, not placeholders (SC 3.3.2 Labels or Instructions, 1.3.1).** Any field that expects a specific format — **date, time, phone, currency, ID pattern, GPA scale, URL, hours, unit-bearing numbers** — MUST show the format as **persistent helper text** via **`FormDescription`** (or the field's description slot). Placeholders disappear on focus and are unreliable for AT, so **MUST NOT** be the sole carrier of the format. Example: GPA → "Out of 4.0"; Date → "MM/DD/YYYY"; Phone → "+1 (555) 555-0100"; Student ID → "STU-YYYY-####". Pair with `inputMode`, `pattern`, or a picker primitive (e.g. `DatePickerField`) where applicable; never rely on a free-text date input.
22
+ 9. **Every icon that communicates information MUST have a text alternative** — not just icon-only buttons. Three cases (SC 1.1.1 Non-text Content, 3.3.2 Labels or Instructions, 2.4.6 Headings and Labels):
23
+
24
+ - **A. Decorative icon next to text that already names it** (e.g. `<i class="fa-light fa-calendar-days" aria-hidden /> 12/14/2025 – 12/20/2025`) → icon MUST be **`aria-hidden`**; MUST NOT add `aria-label` (screen readers would announce the meaning twice). No tooltip needed.
25
+ - **B. Informational icon standing alone** — a calendar glyph used to mean "date range", clock for "updated at", pin for "site", graduation cap for "student", trending arrow for direction, status dot, icon-only chart legend — MUST pair **`role="img"` + `aria-label`** (or `aria-labelledby` on a wrapping element) with a visible **`Tooltip`** so sighted users who don't recognise the glyph learn the meaning. The icon wrapper MUST be keyboard-focusable (`tabIndex={0}` on the `span[role="img"]`) so the tooltip opens on focus.
26
+ - **C. Interactive icon-only button/link** (close `×`, chevron, overflow `⋯`, sort direction, filter chip dismiss, copy, Ask Leo toggle, row actions) → MUST pair **`aria-label`** on the `<button>` with a wrapping **`Tooltip`**. `aria-label` alone is NOT enough — sighted mouse users and keyboard users rely on the tooltip to discover what a bare icon does.
27
+
28
+ In all three cases, the inner `<i>` / `<svg>` MUST be `aria-hidden`; the accessible name lives on the wrapping element. Tooltip text MUST match the accessible name. Narrow exception: a chevron inside a labelled composite (`Select`, `Combobox`) where the parent control already names the whole thing. See **§8.6 (Case A/B/C)** in `AGENTS.md` and the accessibility skill.
29
+ 10. **Keyboard shortcut hints inside buttons** MUST use **`<Kbd variant="bare">`** (no background, no border, inherits `currentColor` at 70%). The default `tile` variant is reserved for **tooltips** and **menu `shortcut=` slots**. Glue multi-key chords into one bare kbd (e.g. `<Kbd variant="bare">⌘⌥K</Kbd>`), not one tile per key. Reference: `Next` / `Back` buttons in `new-placement-form.tsx`; see `.cursor/rules/exxat-kbd-shortcuts.mdc`.
30
+
31
+ After changing **views toolbar** or **tab** UIs, re-run **axe** (or equivalent) on **Placements** (or the affected page).
32
+
33
+ **Charts:** Keyboard exploration uses **`ChartFigure`**; selected data points should have **visible** focus feedback — see skill **§ Charts (keyboard exploration)** and **`AGENTS.md` §4.3** (`chart-keyboard-selection`).
34
+
35
+ ## See also
36
+
37
+ - **`apps/web/AGENTS.md`** §8 — accessibility in project context.
38
+ - **`.cursor/rules/exxat-kbd-shortcuts.mdc`** — shortcuts paired with `Kbd` hints.
39
+ - **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** — Data view chart keyboard parity.
@@ -0,0 +1,26 @@
1
+ ---
2
+ description: Exxat DS — board (kanban) cards; ListPageBoardCard, badges, primitives
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Exxat DS — board cards
7
+
8
+ **Authoritative detail:** **`apps/web/AGENTS.md` §4.4** and **`.cursor/skills/exxat-board-cards/SKILL.md`**.
9
+
10
+ ## MUST
11
+
12
+ 1. **Shell** — Use **`ListPageBoardCard`** from **`components/data-views/list-page-board-card.tsx`** for product board cards (same **`Card` `size="sm"`** treatment as Placements).
13
+ 2. **Hierarchy** — **Title** (`ListPageBoardCardTitleRow`) → optional **avatar** (`ListPageBoardCardAvatar` on `trailing`) → **status row** (`ListPageBoardCardBadgeRow` + **`ListHubStatusBadge`** **`surface="board"`**) when the entity has status → **body** (`ListPageBoardCardBody`) with **`BoardCardTwoLineBlock`** / **`BoardCardIconRow`** (**`components/data-views/board-card-primitives.tsx`**).
14
+ 3. **Status** — **Placements** (**`PLACEMENT_STATUS_*`** + **`StatusBadge`** in **`placements-table-cells.tsx`**), **Team / Compliance / Question bank** (**`ListHubStatusBadge`** + entity maps): all labels/tints/icons live in **`lib/list-status-badges.ts`**. **`surface="table"`** in grid/list rows, **`surface="board"`** on kanban cards. Prefer semantic **`LIST_HUB_STATUS_TINT_*`**. **MUST NOT** use **`uppercase`** on those chips.
15
+ 4. **Simple column boards** — **`ListPageBoardTemplate`** + **`renderCard`**; compose **`ListPageBoardCard`** inside **`renderCard`**.
16
+
17
+ ## MUST NOT
18
+
19
+ - Duplicate **`ListPageBoardCard`** with ad-hoc **`<button>`** + border/padding classes for the same hub pattern.
20
+ - Show **status** only as plain text in the **body** when the hub uses **status** elsewhere — use the **badge row** + shared maps.
21
+
22
+ ## See also
23
+
24
+ - **`apps/web/AGENTS.md` §4.4**, **§13** checklist
25
+ - **`apps/web/docs/data-views-pattern.md`** — Board UI reuse
26
+ - **`lib/initials-from-name.ts`** — owner initials when mock has no `initials` field
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Do not duplicate parent navigation with a Back control when breadcrumbs exist
3
+ globs: apps/web/**/*.tsx
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Exxat DS — breadcrumbs and back navigation
8
+
9
+ ## MUST NOT
10
+
11
+ When a page uses **`SiteHeader`** with **`breadcrumbs`** (a visible trail such as Patterns → Library → current title), **do not** add a **“Back to …”** link or button in the page body that goes to the same parent as the breadcrumb segment. Breadcrumbs already provide hierarchy and one-click navigation up the tree.
12
+
13
+ ## MAY
14
+
15
+ - Rely on **`SiteHeader`** breadcrumbs only for returning to parent routes.
16
+ - Use a **single** explicit back affordance on flows that **omit** breadcrumbs by design (e.g. full-screen step, modal route) where product copy requires it.
17
+
18
+ ## See also
19
+
20
+ - `components/site-header.tsx`
21
+ - `components/templates/primary-page-template.tsx`
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Exxat DS — when to use cards vs DataTable rows vs simple list rows.
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Exxat DS — cards vs rows vs lists
7
+
8
+ ## MUST
9
+
10
+ 1. **Dense, comparable records (10+)** — **`DataTable`** + **`ListPageTemplate`** + **`useTableState`** for primary hubs (**`exxat-data-tables.mdc`**).
11
+ 2. **Board / tiles / visual browse** — **`ListPageBoardCard`** (and related shells), **`ListPageViewFrame`** for non-table bodies (**`exxat-board-cards.mdc`**, **`exxat-list-page-view-shells.mdc`**).
12
+ 3. **One dataset** — Cards and tables read the **same** **`tableState.rows`**; no forked mock arrays (**`exxat-centralized-list-dataset.mdc`**).
13
+
14
+ ## MUST NOT
15
+
16
+ - Replace a **primary data hub grid** with a **card wall** when users need column sort, filter chips, and export parity.
17
+ - Introduce a **second table stack** for the same entity.
18
+
19
+ ## See also
20
+
21
+ - **`docs/card-vs-rows-pattern.md`** · **`.cursor/skills/exxat-card-vs-list-rows/SKILL.md`**
@@ -0,0 +1,44 @@
1
+ ---
2
+ description: Single source of truth for hub rows, KPIs, table properties, detail views, and shared view chrome — dataset + presentation consistency across DataListViewType surfaces
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Exxat DS — centralized list hub dataset + presentation
7
+
8
+ **Authoritative detail:** **`apps/web/AGENTS.md` §4.1** and **§4.5** (connected views + view shells), **`docs/data-views-pattern.md`**, **`.cursor/rules/exxat-list-page-connected-views.mdc`**, and **`.cursor/rules/exxat-list-page-view-shells.mdc`**. This rule tightens **where data lives** and **where reusable view chrome lives** so **table, list, board, dashboard, folder, panel, tree, and inspectors** never drift.
9
+
10
+ ## Single dataset
11
+
12
+ 1. **Rows** — One typed collection per entity (**`lib/mock/<entity>.ts`** until API wiring). **MUST NOT** maintain a second parallel array (e.g. “tree only” or “panel only”) with overlapping IDs unless it is a **derived view** (computed from the same source via selectors).
13
+
14
+ 2. **`useTableState`** — One instance per tab table; **`tableState.rows`** is the **only** filtered/sorted row bag that **list / board / dashboard / folder / panel / tree** surfaces read. **MUST NOT** bypass filters by importing raw mock arrays into alternate views.
15
+
16
+ 3. **Detail / inspector / finder / split-panel bodies** — Resolve the active record by **`id`** (or stable key) against **`tableState.rows`** (or the same upstream state that feeds **`useTableState`**). **MUST NOT** hydrate inspector fields from ad-hoc literals when the row already exists on the dataset.
17
+
18
+ 4. **Columns & table “properties”** — Column defs **`accessorKey` / cell renderers** map to the **same** row interface as KPI helpers and board cards. **MUST NOT** introduce incompatible duplicate TypeScript shapes per view.
19
+
20
+ 5. **`TablePropertiesDrawer`** — Stays wired to the **`DataTable`** that owns **`useTableState`** (**§4.2**): **`currentView`**, **`onViewChange`**, column visibility/density — all reflect manipulation of the **same** underlying rows.
21
+
22
+ 6. **Labels / status / chips** — Prefer shared maps (**`lib/list-status-badges.ts`**, **`lib/data-list-view.ts`** tiles, entity-specific maps **next to** **`lib/mock/<entity>.ts`**). **MUST NOT** fork label strings or badge colors per view file.
23
+
24
+ 7. **KPIs & charts** — **`MetricItem`** / **`MetricInsight`** builders take **`tableState.rows`** (or equivalent filtered list). Same inputs as the grid after search/filters.
25
+
26
+ ## Centralized presentation (layout + components)
27
+
28
+ 8. **`ListPageViewFrame`** — Non-**`DataTable`** view bodies (**folder**, **panel**, icon grids, comparable dashboard sections) **MUST** use **`ListPageViewFrame`** (and exported max-width constants) instead of copy-pasted **`mx-*` / `max-w-*`** per hub (**`exxat-list-page-view-shells.mdc`**).
29
+
30
+ 9. **`components/data-views/`** — New **record-bearing** view layouts (**grids**, **OS folder**, **finder split**) **MUST** land as **generic** building blocks under **`data-views/`** with **`rows`**, **`getRowId`**, render props — hub **`TeamTable`** / **`QuestionBankTable`** **only** wires props and branch logic (**`AGENTS.md` §4.5**).
31
+
32
+ 10. **Hub client composition** — One **`*-client.tsx`** owns **`useTableState`**, passes **`tableState.rows`** into every **`viewType`** branch, and mounts **template** slots (**metrics**, **export**, **`beforeSiteHeader`**) — **MUST NOT** split the same hub across multiple clients with different row sources.
33
+
34
+ ## MUST NOT
35
+
36
+ - Ship alternate mock datasets per **`DataListViewType`** for the same hub without documenting them as **computed derivatives** of one canonical list.
37
+ - Duplicate entity fields in inspector-only types that contradict **`QuestionBankItem`** / **`Placement`** / etc.; extend the shared interface **once**.
38
+
39
+ ## See also
40
+
41
+ - **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** — workflow + checklist.
42
+ - **`.cursor/rules/exxat-table-properties-drawer.mdc`** — **`currentView`** / **`onViewChange`**.
43
+ - **`exxat-list-page-connected-views.mdc`** — toolbar + **`tableState.rows`**.
44
+ - **`exxat-list-page-view-shells.mdc`** — **`ListPageViewFrame`** + **`data-views/`** extraction.
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Shared hubs — collaboration header, invite sheet, library access vs directory roles
3
+ globs: apps/web/components/**/*.{tsx,ts},apps/web/lib/**/*.{tsx,ts}
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Exxat DS — collaboration & access
8
+
9
+ **Authoritative detail:** **`apps/web/AGENTS.md` §4.7** and **`apps/web/docs/collaboration-access-pattern.md`**.
10
+
11
+ ## MUST
12
+
13
+ 1. **Shared hubs** — Use **`PageHeader`** **`variant="collaboration"`** with **`collaborators`** (`PageHeaderCollaborator[]`) and optional **`accessInfo`**.
14
+ 2. **Header chrome** — **Empty roster:** outline **Add collaborator** (`COLLABORATION_HEADER_ADD_LABEL` / **`addCollaboratorLabel`**) opens the invite sheet. **Non-empty roster:** face rail only; faces and **`+N`** open the same sheet via **`onCollaboratorsOpen`**.
15
+ 3. **Invite entry** — **⋯ More** → **Invite people** on the entity page header; opens **`InviteCollaboratorsDrawer`** (floating sheet, same family as **`ExportDrawer`**).
16
+ 4. **State** — Prefer **`CollaborationAccessFlow`** on hub **`*-client.tsx`**; otherwise own **`collaborators`** + **`inviteOpen`**. Successful invite updates **`collaborators`** so the header and sheet roster match.
17
+ 5. **Library access** — Labels and invite options from **`lib/collaborator-access.ts`**; trailing roster badge = access (Owner / Editor / Commenter / Viewer).
18
+ 6. **Directory roles** — Optional **`roles`** on **`PageHeaderCollaborator`** as **`Badge variant="outline"`** chips (Faculty, Program coordinator, Director) — **not** library access.
19
+ 7. **Roster layout** — One bordered list with row dividers; per row: **name**, **email**, role tags, access badge — **not** one card per person.
20
+ 8. **Invite field** — **`FieldGroup`** + **`Field`**; combined email + access row uses **`InputGroup`** + **`InputGroupInput`** + **`InputGroupAddon`** with shadcn **`Select`** (**`SelectGroup`** / **`SelectItem`**, **`position="popper"`**); persistent **`FieldDescription`** for email format; **no** **`toast()`** (**`exxat-no-toast.mdc`**).
21
+
22
+ ## MUST NOT
23
+
24
+ - A second invite control **beside** a populated face rail.
25
+ - Fork access enums/labels per hub — extend **`collaborator-access.ts`** once.
26
+ - Use **`Select`** inside **`InputGroupAddon`** without **`InputGroupInput`** / **`SelectGroup`**; put email above name in the roster (order is **name → email → role tags**).
27
+
28
+ ## See also
29
+
30
+ - **`exxat-page-vs-drawer.mdc`** — invite is a **sheet**, not a new route.
31
+ - **`exxat-kbd-shortcuts.mdc`** — workflow **Cancel** / **Send invite** shortcuts on the sheet.
32
+ - **`exxat-question-bank-hub-header.mdc`** — Question bank library: when URL is **folder-scoped**, **⋯ More** also includes **Customize folder** (hub client hosts **`QuestionBankNewFolderSheet`**).
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Exxat DS — global command palette (⌘K) as search + quick AI vs Ask Leo for long answers.
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Exxat DS — global command palette (`CommandMenu`)
7
+
8
+ ## Intent
9
+
10
+ - **`CommandMenu`** (**⌘K** / **Ctrl+K**) is **global search** (routes, library, patterns, AI starters, optional row data such as placements / student names) — see **`apps/web/AGENTS.md` §7.1** and **`apps/web/docs/command-menu-pattern.md`**.
11
+ - **Quick / lookup / short AI:** Prefer **results inside the palette** when the product can return compact answers or lightweight “research” without leaving the flow.
12
+ - **Long or complex answers:** **Ask Leo** side panel (**⌘⌥K** / **Ctrl+Alt+K**)—not forced into the palette.
13
+
14
+ ## Implementation pointers
15
+
16
+ - Shell: `apps/web/components/command-menu.tsx`; config **`buildCommandMenuConfig()`** in **`apps/web/lib/command-menu-config.ts`**; optional **`dataGroups`** from **`apps/web/lib/command-menu-search-data.ts`** (e.g. **`getCommandMenuSearchDataGroups()`**), wired in **`apps/web/app/(app)/layout.tsx`**. Keep domain mapping out of the shell.
17
+ - Large indexes: set **`searchOnly: true`** on **`CommandMenuGroup`** so **`command-menu.tsx`** skips the group until the user types (avoids listing every row on open; cmdk shows all items when the search string is empty).
18
+
19
+ ## See also
20
+
21
+ - **`apps/web/AGENTS.md` §7.1**
22
+ - **`exxat-kbd-shortcuts.mdc`** (⌘K vs ⌘⌥K)