@exxatdesignux/ui 0.2.18 → 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 (618) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/bin/sync-extras.mjs +116 -29
  3. package/consumer-extras/README.md +43 -4
  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 +1 -1
  36. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +9 -9
  37. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
  38. package/consumer-extras/handbook/HANDBOOK.md +185 -0
  39. package/consumer-extras/handbook/glossary.md +57 -0
  40. package/consumer-extras/handbook/reference-implementations.md +126 -0
  41. package/consumer-extras/handbook/voice-and-tone.md +262 -0
  42. package/consumer-extras/patterns/command-menu-pattern.md +1 -1
  43. package/consumer-extras/patterns/data-views-pattern.md +14 -14
  44. package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
  45. package/dist/components/data-table/filter-date-calendar.js +280 -0
  46. package/dist/components/data-table/filter-date-calendar.js.map +1 -0
  47. package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
  48. package/dist/components/data-table/filter-text-value-input.js +561 -0
  49. package/dist/components/data-table/filter-text-value-input.js.map +1 -0
  50. package/dist/components/data-table/index.d.ts +45 -0
  51. package/dist/components/data-table/index.js +3085 -0
  52. package/dist/components/data-table/index.js.map +1 -0
  53. package/dist/components/data-table/pagination.d.ts +28 -0
  54. package/dist/components/data-table/pagination.js +3264 -0
  55. package/dist/components/data-table/pagination.js.map +1 -0
  56. package/dist/components/data-table/types.d.ts +84 -0
  57. package/dist/components/data-table/types.js +3 -0
  58. package/dist/components/data-table/types.js.map +1 -0
  59. package/dist/components/data-table/use-table-state.d.ts +116 -0
  60. package/dist/components/data-table/use-table-state.js +670 -0
  61. package/dist/components/data-table/use-table-state.js.map +1 -0
  62. package/dist/components/data-views/board-card-primitives.d.ts +22 -0
  63. package/dist/components/data-views/board-card-primitives.js +84 -0
  64. package/dist/components/data-views/board-card-primitives.js.map +1 -0
  65. package/dist/components/data-views/data-row-list.d.ts +33 -0
  66. package/dist/components/data-views/data-row-list.js +106 -0
  67. package/dist/components/data-views/data-row-list.js.map +1 -0
  68. package/dist/components/data-views/finder-panel-view.d.ts +54 -0
  69. package/dist/components/data-views/finder-panel-view.js +388 -0
  70. package/dist/components/data-views/finder-panel-view.js.map +1 -0
  71. package/dist/components/data-views/folder-grid-view.d.ts +22 -0
  72. package/dist/components/data-views/folder-grid-view.js +58 -0
  73. package/dist/components/data-views/folder-grid-view.js.map +1 -0
  74. package/dist/components/data-views/hub-table.d.ts +167 -0
  75. package/dist/components/data-views/hub-table.js +5561 -0
  76. package/dist/components/data-views/hub-table.js.map +1 -0
  77. package/dist/components/data-views/index.d.ts +27 -0
  78. package/dist/components/data-views/index.js +6575 -0
  79. package/dist/components/data-views/index.js.map +1 -0
  80. package/dist/components/data-views/list-page-board-card.d.ts +72 -0
  81. package/dist/components/data-views/list-page-board-card.js +264 -0
  82. package/dist/components/data-views/list-page-board-card.js.map +1 -0
  83. package/dist/components/data-views/list-page-board-template.d.ts +24 -0
  84. package/dist/components/data-views/list-page-board-template.js +137 -0
  85. package/dist/components/data-views/list-page-board-template.js.map +1 -0
  86. package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
  87. package/dist/components/data-views/list-page-connected-view-body.js +116 -0
  88. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
  89. package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
  90. package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
  91. package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
  92. package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
  93. package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
  94. package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
  95. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
  96. package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
  97. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
  98. package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
  99. package/dist/components/data-views/list-page-tree-column-header.js +22 -0
  100. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
  101. package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
  102. package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
  103. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
  104. package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
  105. package/dist/components/data-views/os-folder-glyph.js +104 -0
  106. package/dist/components/data-views/os-folder-glyph.js.map +1 -0
  107. package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
  108. package/dist/components/data-views/outline-tree-menu.js +131 -0
  109. package/dist/components/data-views/outline-tree-menu.js.map +1 -0
  110. package/dist/components/table-properties/column-row.d.ts +22 -0
  111. package/dist/components/table-properties/column-row.js +153 -0
  112. package/dist/components/table-properties/column-row.js.map +1 -0
  113. package/dist/components/table-properties/draggable-list.d.ts +24 -0
  114. package/dist/components/table-properties/draggable-list.js +53 -0
  115. package/dist/components/table-properties/draggable-list.js.map +1 -0
  116. package/dist/components/table-properties/drawer-button.d.ts +110 -0
  117. package/dist/components/table-properties/drawer-button.js +2748 -0
  118. package/dist/components/table-properties/drawer-button.js.map +1 -0
  119. package/dist/components/table-properties/drawer.d.ts +100 -0
  120. package/dist/components/table-properties/drawer.js +2595 -0
  121. package/dist/components/table-properties/drawer.js.map +1 -0
  122. package/dist/components/table-properties/filter-card.d.ts +24 -0
  123. package/dist/components/table-properties/filter-card.js +854 -0
  124. package/dist/components/table-properties/filter-card.js.map +1 -0
  125. package/dist/components/table-properties/index.d.ts +14 -0
  126. package/dist/components/table-properties/index.js +2768 -0
  127. package/dist/components/table-properties/index.js.map +1 -0
  128. package/dist/components/table-properties/sort-card.d.ts +20 -0
  129. package/dist/components/table-properties/sort-card.js +102 -0
  130. package/dist/components/table-properties/sort-card.js.map +1 -0
  131. package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
  132. package/dist/components/templates/dedicated-search-landing-template.js +254 -0
  133. package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
  134. package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
  135. package/dist/components/templates/dedicated-search-results-template.js +16 -0
  136. package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
  137. package/dist/components/templates/index.d.ts +9 -0
  138. package/dist/components/templates/index.js +2720 -0
  139. package/dist/components/templates/index.js.map +1 -0
  140. package/dist/components/templates/list-page.d.ts +83 -0
  141. package/dist/components/templates/list-page.js +2433 -0
  142. package/dist/components/templates/list-page.js.map +1 -0
  143. package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
  144. package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
  145. package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
  146. package/dist/components/ui/accordion.d.ts +10 -0
  147. package/dist/components/ui/accordion.js +74 -0
  148. package/dist/components/ui/accordion.js.map +1 -0
  149. package/dist/components/ui/alert-dialog.d.ts +37 -0
  150. package/dist/components/ui/alert-dialog.js +201 -0
  151. package/dist/components/ui/alert-dialog.js.map +1 -0
  152. package/dist/components/ui/avatar.d.ts +84 -0
  153. package/dist/components/ui/avatar.js +328 -0
  154. package/dist/components/ui/avatar.js.map +1 -0
  155. package/dist/components/ui/badge.d.ts +13 -0
  156. package/dist/components/ui/badge.js +49 -0
  157. package/dist/components/ui/badge.js.map +1 -0
  158. package/dist/components/ui/banner.d.ts +62 -0
  159. package/dist/components/ui/banner.js +364 -0
  160. package/dist/components/ui/banner.js.map +1 -0
  161. package/dist/components/ui/breadcrumb.d.ts +14 -0
  162. package/dist/components/ui/breadcrumb.js +114 -0
  163. package/dist/components/ui/breadcrumb.js.map +1 -0
  164. package/dist/components/ui/button.d.ts +16 -0
  165. package/dist/components/ui/button.js +59 -0
  166. package/dist/components/ui/button.js.map +1 -0
  167. package/dist/components/ui/calendar.d.ts +13 -0
  168. package/dist/components/ui/calendar.js +238 -0
  169. package/dist/components/ui/calendar.js.map +1 -0
  170. package/dist/components/ui/card.d.ts +14 -0
  171. package/dist/components/ui/card.js +102 -0
  172. package/dist/components/ui/card.js.map +1 -0
  173. package/dist/components/ui/chart.d.ts +58 -0
  174. package/dist/components/ui/chart.js +292 -0
  175. package/dist/components/ui/chart.js.map +1 -0
  176. package/dist/components/ui/checkbox.d.ts +23 -0
  177. package/dist/components/ui/checkbox.js +155 -0
  178. package/dist/components/ui/checkbox.js.map +1 -0
  179. package/dist/components/ui/coach-mark.d.ts +27 -0
  180. package/dist/components/ui/coach-mark.js +306 -0
  181. package/dist/components/ui/coach-mark.js.map +1 -0
  182. package/dist/components/ui/collapsible.d.ts +8 -0
  183. package/dist/components/ui/collapsible.js +35 -0
  184. package/dist/components/ui/collapsible.js.map +1 -0
  185. package/dist/components/ui/command.d.ts +36 -0
  186. package/dist/components/ui/command.js +274 -0
  187. package/dist/components/ui/command.js.map +1 -0
  188. package/dist/components/ui/context-menu.d.ts +32 -0
  189. package/dist/components/ui/context-menu.js +245 -0
  190. package/dist/components/ui/context-menu.js.map +1 -0
  191. package/dist/components/ui/date-picker-field.d.ts +38 -0
  192. package/dist/components/ui/date-picker-field.js +550 -0
  193. package/dist/components/ui/date-picker-field.js.map +1 -0
  194. package/dist/components/ui/dialog.d.ts +22 -0
  195. package/dist/components/ui/dialog.js +200 -0
  196. package/dist/components/ui/dialog.js.map +1 -0
  197. package/dist/components/ui/dot-pattern.d.ts +21 -0
  198. package/dist/components/ui/dot-pattern.js +139 -0
  199. package/dist/components/ui/dot-pattern.js.map +1 -0
  200. package/dist/components/ui/drag-handle-grip.d.ts +10 -0
  201. package/dist/components/ui/drag-handle-grip.js +15 -0
  202. package/dist/components/ui/drag-handle-grip.js.map +1 -0
  203. package/dist/components/ui/drawer.d.ts +16 -0
  204. package/dist/components/ui/drawer.js +125 -0
  205. package/dist/components/ui/drawer.js.map +1 -0
  206. package/dist/components/ui/dropdown-menu.d.ts +45 -0
  207. package/dist/components/ui/dropdown-menu.js +353 -0
  208. package/dist/components/ui/dropdown-menu.js.map +1 -0
  209. package/dist/components/ui/export-drawer.d.ts +11 -0
  210. package/dist/components/ui/export-drawer.js +1658 -0
  211. package/dist/components/ui/export-drawer.js.map +1 -0
  212. package/dist/components/ui/field.d.ts +30 -0
  213. package/dist/components/ui/field.js +249 -0
  214. package/dist/components/ui/field.js.map +1 -0
  215. package/dist/components/ui/form.d.ts +28 -0
  216. package/dist/components/ui/form.js +110 -0
  217. package/dist/components/ui/form.js.map +1 -0
  218. package/dist/components/ui/hover-card.d.ts +9 -0
  219. package/dist/components/ui/hover-card.js +43 -0
  220. package/dist/components/ui/hover-card.js.map +1 -0
  221. package/dist/components/ui/input-group.d.ts +20 -0
  222. package/dist/components/ui/input-group.js +219 -0
  223. package/dist/components/ui/input-group.js.map +1 -0
  224. package/dist/components/ui/input-mask.d.ts +39 -0
  225. package/dist/components/ui/input-mask.js +118 -0
  226. package/dist/components/ui/input-mask.js.map +1 -0
  227. package/dist/components/ui/input.d.ts +5 -0
  228. package/dist/components/ui/input.js +30 -0
  229. package/dist/components/ui/input.js.map +1 -0
  230. package/dist/components/ui/kbd.d.ts +20 -0
  231. package/dist/components/ui/kbd.js +45 -0
  232. package/dist/components/ui/kbd.js.map +1 -0
  233. package/dist/components/ui/key-metrics-context.d.ts +19 -0
  234. package/dist/components/ui/key-metrics-context.js +26 -0
  235. package/dist/components/ui/key-metrics-context.js.map +1 -0
  236. package/dist/components/ui/key-metrics.d.ts +131 -0
  237. package/dist/components/ui/key-metrics.js +1015 -0
  238. package/dist/components/ui/key-metrics.js.map +1 -0
  239. package/dist/components/ui/label.d.ts +6 -0
  240. package/dist/components/ui/label.js +28 -0
  241. package/dist/components/ui/label.js.map +1 -0
  242. package/dist/components/ui/list-page-view-frame.d.ts +22 -0
  243. package/dist/components/ui/list-page-view-frame.js +24 -0
  244. package/dist/components/ui/list-page-view-frame.js.map +1 -0
  245. package/dist/components/ui/page-header.d.ts +51 -0
  246. package/dist/components/ui/page-header.js +372 -0
  247. package/dist/components/ui/page-header.js.map +1 -0
  248. package/dist/components/ui/payment-card-fields.d.ts +10 -0
  249. package/dist/components/ui/payment-card-fields.js +80 -0
  250. package/dist/components/ui/payment-card-fields.js.map +1 -0
  251. package/dist/components/ui/popover.d.ts +10 -0
  252. package/dist/components/ui/popover.js +47 -0
  253. package/dist/components/ui/popover.js.map +1 -0
  254. package/dist/components/ui/radio-group.d.ts +29 -0
  255. package/dist/components/ui/radio-group.js +190 -0
  256. package/dist/components/ui/radio-group.js.map +1 -0
  257. package/dist/components/ui/resizable.d.ts +16 -0
  258. package/dist/components/ui/resizable.js +51 -0
  259. package/dist/components/ui/resizable.js.map +1 -0
  260. package/dist/components/ui/scroll-area.d.ts +8 -0
  261. package/dist/components/ui/scroll-area.js +66 -0
  262. package/dist/components/ui/scroll-area.js.map +1 -0
  263. package/dist/components/ui/select.d.ts +18 -0
  264. package/dist/components/ui/select.js +186 -0
  265. package/dist/components/ui/select.js.map +1 -0
  266. package/dist/components/ui/selection-tile-grid.d.ts +52 -0
  267. package/dist/components/ui/selection-tile-grid.js +347 -0
  268. package/dist/components/ui/selection-tile-grid.js.map +1 -0
  269. package/dist/components/ui/separator.d.ts +7 -0
  270. package/dist/components/ui/separator.js +33 -0
  271. package/dist/components/ui/separator.js.map +1 -0
  272. package/dist/components/ui/sheet.d.ts +18 -0
  273. package/dist/components/ui/sheet.js +181 -0
  274. package/dist/components/ui/sheet.js.map +1 -0
  275. package/dist/components/ui/sidebar.d.ts +94 -0
  276. package/dist/components/ui/sidebar.js +805 -0
  277. package/dist/components/ui/sidebar.js.map +1 -0
  278. package/dist/components/ui/skeleton.d.ts +5 -0
  279. package/dist/components/ui/skeleton.js +22 -0
  280. package/dist/components/ui/skeleton.js.map +1 -0
  281. package/dist/components/ui/slider.d.ts +7 -0
  282. package/dist/components/ui/slider.js +66 -0
  283. package/dist/components/ui/slider.js.map +1 -0
  284. package/dist/components/ui/sonner.d.ts +6 -0
  285. package/dist/components/ui/sonner.js +38 -0
  286. package/dist/components/ui/sonner.js.map +1 -0
  287. package/dist/components/ui/status-badge.d.ts +38 -0
  288. package/dist/components/ui/status-badge.js +77 -0
  289. package/dist/components/ui/status-badge.js.map +1 -0
  290. package/dist/components/ui/table.d.ts +13 -0
  291. package/dist/components/ui/table.js +115 -0
  292. package/dist/components/ui/table.js.map +1 -0
  293. package/dist/components/ui/tabs.d.ts +15 -0
  294. package/dist/components/ui/tabs.js +93 -0
  295. package/dist/components/ui/tabs.js.map +1 -0
  296. package/dist/components/ui/textarea.d.ts +6 -0
  297. package/dist/components/ui/textarea.js +25 -0
  298. package/dist/components/ui/textarea.js.map +1 -0
  299. package/dist/components/ui/tip.d.ts +12 -0
  300. package/dist/components/ui/tip.js +61 -0
  301. package/dist/components/ui/tip.js.map +1 -0
  302. package/dist/components/ui/toggle-group.d.ts +14 -0
  303. package/dist/components/ui/toggle-group.js +104 -0
  304. package/dist/components/ui/toggle-group.js.map +1 -0
  305. package/dist/components/ui/toggle-switch.d.ts +10 -0
  306. package/dist/components/ui/toggle-switch.js +33 -0
  307. package/dist/components/ui/toggle-switch.js.map +1 -0
  308. package/dist/components/ui/toggle.d.ts +13 -0
  309. package/dist/components/ui/toggle.js +51 -0
  310. package/dist/components/ui/toggle.js.map +1 -0
  311. package/dist/components/ui/tooltip.d.ts +10 -0
  312. package/dist/components/ui/tooltip.js +68 -0
  313. package/dist/components/ui/tooltip.js.map +1 -0
  314. package/dist/components/ui/view-segmented-control.d.ts +31 -0
  315. package/dist/components/ui/view-segmented-control.js +167 -0
  316. package/dist/components/ui/view-segmented-control.js.map +1 -0
  317. package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
  318. package/dist/hooks/use-app-theme.d.ts +24 -0
  319. package/dist/hooks/use-app-theme.js +286 -0
  320. package/dist/hooks/use-app-theme.js.map +1 -0
  321. package/dist/hooks/use-coach-mark.d.ts +86 -0
  322. package/dist/hooks/use-coach-mark.js +218 -0
  323. package/dist/hooks/use-coach-mark.js.map +1 -0
  324. package/dist/hooks/use-mobile.d.ts +3 -0
  325. package/dist/hooks/use-mobile.js +29 -0
  326. package/dist/hooks/use-mobile.js.map +1 -0
  327. package/dist/hooks/use-mod-key-label.d.ts +6 -0
  328. package/dist/hooks/use-mod-key-label.js +25 -0
  329. package/dist/hooks/use-mod-key-label.js.map +1 -0
  330. package/dist/index.d.ts +120 -0
  331. package/dist/index.js +13324 -0
  332. package/dist/index.js.map +1 -0
  333. package/dist/lib/compose-refs.d.ts +6 -0
  334. package/dist/lib/compose-refs.js +17 -0
  335. package/dist/lib/compose-refs.js.map +1 -0
  336. package/dist/lib/conditional-rule-match.d.ts +30 -0
  337. package/dist/lib/conditional-rule-match.js +66 -0
  338. package/dist/lib/conditional-rule-match.js.map +1 -0
  339. package/dist/lib/data-list-display-options.d.ts +26 -0
  340. package/dist/lib/data-list-display-options.js +14 -0
  341. package/dist/lib/data-list-display-options.js.map +1 -0
  342. package/dist/lib/data-list-view-registry.d.ts +2 -0
  343. package/dist/lib/data-list-view-registry.js +102 -0
  344. package/dist/lib/data-list-view-registry.js.map +1 -0
  345. package/dist/lib/data-list-view-surface.d.ts +2 -0
  346. package/dist/lib/data-list-view-surface.js +80 -0
  347. package/dist/lib/data-list-view-surface.js.map +1 -0
  348. package/dist/lib/data-list-view.d.ts +21 -0
  349. package/dist/lib/data-list-view.js +25 -0
  350. package/dist/lib/data-list-view.js.map +1 -0
  351. package/dist/lib/date-filter.d.ts +22 -0
  352. package/dist/lib/date-filter.js +61 -0
  353. package/dist/lib/date-filter.js.map +1 -0
  354. package/dist/lib/dev-log.d.ts +8 -0
  355. package/dist/lib/dev-log.js +10 -0
  356. package/dist/lib/dev-log.js.map +1 -0
  357. package/dist/lib/dropdown-menu-surface.d.ts +14 -0
  358. package/dist/lib/dropdown-menu-surface.js +6 -0
  359. package/dist/lib/dropdown-menu-surface.js.map +1 -0
  360. package/dist/lib/editable-target.d.ts +12 -0
  361. package/dist/lib/editable-target.js +12 -0
  362. package/dist/lib/editable-target.js.map +1 -0
  363. package/dist/lib/list-page-table-properties.d.ts +35 -0
  364. package/dist/lib/list-page-table-properties.js +81 -0
  365. package/dist/lib/list-page-table-properties.js.map +1 -0
  366. package/dist/lib/raf-throttle.d.ts +23 -0
  367. package/dist/lib/raf-throttle.js +27 -0
  368. package/dist/lib/raf-throttle.js.map +1 -0
  369. package/dist/lib/row-height.d.ts +16 -0
  370. package/dist/lib/row-height.js +10 -0
  371. package/dist/lib/row-height.js.map +1 -0
  372. package/dist/lib/table-properties-types.d.ts +83 -0
  373. package/dist/lib/table-properties-types.js +19 -0
  374. package/dist/lib/table-properties-types.js.map +1 -0
  375. package/dist/lib/utils.d.ts +5 -0
  376. package/dist/lib/utils.js +11 -0
  377. package/dist/lib/utils.js.map +1 -0
  378. package/package.json +83 -18
  379. package/src/components/data-table/filter-date-calendar.tsx +38 -0
  380. package/src/components/data-table/filter-text-value-input.tsx +77 -0
  381. package/src/components/data-table/index.tsx +1678 -0
  382. package/src/components/data-table/pagination.tsx +255 -0
  383. package/src/components/data-table/types.ts +96 -0
  384. package/src/components/data-table/use-table-state.ts +767 -0
  385. package/src/components/data-views/board-card-primitives.tsx +93 -0
  386. package/src/components/data-views/data-row-list.tsx +183 -0
  387. package/src/components/data-views/finder-panel-view.tsx +405 -0
  388. package/src/components/data-views/folder-grid-view.tsx +86 -0
  389. package/src/components/data-views/hub-table.tsx +498 -0
  390. package/src/components/data-views/index.ts +28 -0
  391. package/src/components/data-views/list-page-board-card.tsx +192 -0
  392. package/src/components/data-views/list-page-board-template.tsx +122 -0
  393. package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
  394. package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  395. package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  396. package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
  397. package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
  398. package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  399. package/src/components/data-views/os-folder-glyph.tsx +141 -0
  400. package/src/components/data-views/outline-tree-menu.tsx +157 -0
  401. package/src/components/table-properties/column-row.tsx +90 -0
  402. package/src/components/table-properties/draggable-list.ts +54 -0
  403. package/src/components/table-properties/drawer-button.tsx +300 -0
  404. package/src/components/table-properties/drawer.tsx +1148 -0
  405. package/src/components/table-properties/filter-card.tsx +251 -0
  406. package/src/components/table-properties/index.ts +36 -0
  407. package/src/components/table-properties/sort-card.tsx +63 -0
  408. package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
  409. package/src/components/templates/dedicated-search-results-template.tsx +19 -0
  410. package/src/components/templates/index.ts +33 -0
  411. package/src/components/templates/list-page.tsx +602 -0
  412. package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
  413. package/src/components/ui/accordion.tsx +92 -0
  414. package/src/components/ui/alert-dialog.tsx +221 -0
  415. package/src/components/ui/avatar.tsx +13 -2
  416. package/src/components/ui/banner.tsx +2 -2
  417. package/src/components/ui/calendar.tsx +1 -1
  418. package/src/components/ui/coach-mark.tsx +1 -1
  419. package/src/components/ui/context-menu.tsx +291 -0
  420. package/src/components/ui/date-picker-field.tsx +2 -2
  421. package/src/components/ui/dot-pattern.tsx +183 -0
  422. package/src/components/ui/export-drawer.tsx +375 -0
  423. package/src/components/ui/hover-card.tsx +66 -0
  424. package/src/components/ui/key-metrics-context.tsx +78 -0
  425. package/src/components/ui/key-metrics.tsx +1133 -0
  426. package/src/components/ui/list-page-view-frame.tsx +64 -0
  427. package/src/components/ui/page-header.tsx +244 -0
  428. package/src/components/ui/payment-card-fields.tsx +2 -2
  429. package/src/components/ui/resizable.tsx +68 -0
  430. package/src/components/ui/scroll-area.tsx +72 -0
  431. package/src/components/ui/selection-tile-grid.tsx +9 -2
  432. package/src/components/ui/sidebar.tsx +84 -12
  433. package/src/components/ui/slider.tsx +83 -0
  434. package/src/globals.css +494 -151
  435. package/src/globals.d.ts +20 -0
  436. package/src/index.ts +68 -1
  437. package/src/lib/conditional-rule-match.ts +119 -0
  438. package/src/lib/data-list-display-options.ts +35 -0
  439. package/src/lib/data-list-view-registry.ts +104 -0
  440. package/src/lib/data-list-view-surface.ts +83 -0
  441. package/src/lib/data-list-view.ts +47 -0
  442. package/src/lib/dev-log.ts +10 -0
  443. package/src/lib/editable-target.ts +20 -0
  444. package/src/lib/list-page-table-properties.ts +48 -0
  445. package/src/lib/raf-throttle.ts +45 -0
  446. package/src/lib/row-height.ts +19 -0
  447. package/src/lib/table-properties-types.ts +98 -0
  448. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  449. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +3 -3
  450. package/template/.cursor/rules/exxat-data-tables.mdc +1 -1
  451. package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
  452. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +2 -2
  453. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  454. package/template/AGENTS.md +84 -20
  455. package/template/app/(app)/examples/page.tsx +0 -1
  456. package/template/app/(app)/layout.tsx +17 -4
  457. package/template/app/(app)/question-bank/layout.tsx +1 -1
  458. package/template/app/(app)/question-bank/new/page.tsx +11 -24
  459. package/template/app/globals.css +13 -1972
  460. package/template/components/ask-leo-sidebar.tsx +5 -1
  461. package/template/components/brand-color-picker.tsx +2 -2
  462. package/template/components/charts-overview.tsx +1 -1
  463. package/template/components/compliance-table.tsx +240 -384
  464. package/template/components/dashboard-report-charts.tsx +1 -1
  465. package/template/components/dashboard-tabs.tsx +1 -1
  466. package/template/components/data-table/filter-date-calendar.tsx +1 -38
  467. package/template/components/data-table/filter-text-value-input.tsx +1 -77
  468. package/template/components/data-table/index.tsx +1 -1634
  469. package/template/components/data-table/pagination.tsx +1 -255
  470. package/template/components/data-table/types.ts +1 -94
  471. package/template/components/data-table/use-table-state.test.ts +420 -0
  472. package/template/components/data-table/use-table-state.ts +1 -758
  473. package/template/components/data-view-dashboard-charts-compliance.tsx +2 -2
  474. package/template/components/data-view-dashboard-charts-team.tsx +2 -2
  475. package/template/components/data-view-dashboard-charts.tsx +2 -2
  476. package/template/components/data-views/board-card-primitives.tsx +1 -93
  477. package/template/components/data-views/data-row-list.tsx +1 -183
  478. package/template/components/data-views/finder-panel-view.tsx +1 -405
  479. package/template/components/data-views/folder-grid-view.tsx +1 -86
  480. package/template/components/data-views/hub-table.tsx +1 -0
  481. package/template/components/data-views/index.ts +42 -1
  482. package/template/components/data-views/list-page-board-card.tsx +1 -192
  483. package/template/components/data-views/list-page-board-template.tsx +1 -122
  484. package/template/components/data-views/list-page-connected-view-body.tsx +1 -0
  485. package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
  486. package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -60
  487. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
  488. package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
  489. package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
  490. package/template/components/data-views/list-page-view-frame.tsx +5 -53
  491. package/template/components/data-views/os-folder-glyph.tsx +1 -129
  492. package/template/components/data-views/outline-tree-menu.tsx +1 -157
  493. package/template/components/export-drawer.test.tsx +71 -0
  494. package/template/components/export-drawer.tsx +1 -375
  495. package/template/components/exxat-product-logo.tsx +5 -5
  496. package/template/components/hub-tree-panel-view.tsx +2 -2
  497. package/template/components/invite-collaborators-drawer.tsx +3 -3
  498. package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
  499. package/template/components/key-metrics.tsx +1 -1063
  500. package/template/components/leo-insight-indicator.tsx +2 -2
  501. package/template/components/new-placement-back-btn.tsx +1 -1
  502. package/template/components/new-placement-form.tsx +63 -189
  503. package/template/components/new-question-composer.tsx +432 -402
  504. package/template/components/onboarding/index.ts +9 -0
  505. package/template/components/onboarding/onboarding-01.tsx +1 -1
  506. package/template/components/onboarding/onboarding-02.tsx +1 -1
  507. package/template/components/onboarding/onboarding-03.tsx +1 -1
  508. package/template/components/onboarding/onboarding-04.tsx +1 -1
  509. package/template/components/page-header.tsx +8 -226
  510. package/template/components/placement-board-card.tsx +71 -83
  511. package/template/components/placements-board-view.tsx +3 -10
  512. package/template/components/placements-client.tsx +10 -42
  513. package/template/components/placements-list-view.tsx +22 -69
  514. package/template/components/placements-table-columns.tsx +8 -438
  515. package/template/components/placements-table.tsx +588 -1296
  516. package/template/components/product-switcher.tsx +1 -1
  517. package/template/components/product-wordmark.tsx +2 -1
  518. package/template/components/question-bank-client.tsx +4 -1
  519. package/template/components/question-bank-hub-client.tsx +1 -1
  520. package/template/components/question-bank-new-folder-sheet.tsx +2 -2
  521. package/template/components/question-bank-secondary-nav.tsx +3 -3
  522. package/template/components/question-bank-table.tsx +294 -526
  523. package/template/components/rotations-empty-state.tsx +1 -1
  524. package/template/components/rotations-panel-activator.tsx +1 -1
  525. package/template/components/settings-appearance-card.tsx +1 -1
  526. package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
  527. package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +4 -4
  528. package/template/components/sidebar/index.ts +16 -0
  529. package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
  530. package/template/components/{secondary-panel.tsx → sidebar/secondary-panel.tsx} +6 -3
  531. package/template/components/{sidebar-auto-collapse.tsx → sidebar/sidebar-auto-collapse.tsx} +6 -2
  532. package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
  533. package/template/components/site-header.tsx +1 -1
  534. package/template/components/{sites-all-client.tsx → sites-client.tsx} +1 -1
  535. package/template/components/sites-table.tsx +124 -257
  536. package/template/components/table-properties/column-row.tsx +1 -90
  537. package/template/components/table-properties/draggable-list.ts +1 -49
  538. package/template/components/table-properties/drawer-button.tsx +1 -249
  539. package/template/components/table-properties/drawer.tsx +1 -1105
  540. package/template/components/table-properties/filter-card.tsx +1 -251
  541. package/template/components/table-properties/sort-card.tsx +1 -59
  542. package/template/components/table-properties/types.ts +28 -71
  543. package/template/components/team-table.tsx +242 -382
  544. package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
  545. package/template/components/templates/dedicated-search-results-template.tsx +1 -19
  546. package/template/components/templates/list-page.tsx +1 -584
  547. package/template/components/templates/nested-secondary-panel-shell.tsx +1 -62
  548. package/template/components/templates/new-focus-template.tsx +659 -0
  549. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  550. package/template/components/ui/accordion.tsx +1 -0
  551. package/template/components/ui/alert-dialog.tsx +1 -0
  552. package/template/components/ui/context-menu.tsx +1 -0
  553. package/template/components/ui/dot-pattern.tsx +1 -183
  554. package/template/components/ui/hover-card.tsx +1 -0
  555. package/template/components/ui/resizable.tsx +1 -68
  556. package/template/components/ui/scroll-area.tsx +1 -0
  557. package/template/components/ui/slider.tsx +1 -0
  558. package/template/docs/blueprints/README.md +86 -0
  559. package/template/docs/blueprints/_template.md +91 -0
  560. package/template/docs/blueprints/board-card.md +123 -0
  561. package/template/docs/blueprints/data-table.md +139 -0
  562. package/template/docs/blueprints/key-metrics.md +128 -0
  563. package/template/docs/blueprints/list-page-template.md +123 -0
  564. package/template/docs/blueprints/page-header.md +130 -0
  565. package/template/docs/command-menu-pattern.md +1 -1
  566. package/template/docs/component-selection-guide.md +224 -0
  567. package/template/docs/components-audit-2026-05.md +158 -0
  568. package/template/docs/data-views-pattern.md +14 -14
  569. package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
  570. package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
  571. package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
  572. package/template/docs/migrations/README.md +100 -0
  573. package/template/docs/migrations/_template.md +64 -0
  574. package/template/docs/token-taxonomy.md +416 -0
  575. package/template/eslint.config.mjs +27 -0
  576. package/template/hooks/use-secondary-panel-hub-nav.ts +1 -1
  577. package/template/lib/command-menu-config.ts +0 -1
  578. package/template/lib/compliance-supported-views.ts +10 -0
  579. package/template/lib/conditional-rule-match.ts +6 -97
  580. package/template/lib/data-list-display-options.ts +1 -35
  581. package/template/lib/data-list-view-registry.ts +1 -0
  582. package/template/lib/data-list-view-surface.ts +1 -69
  583. package/template/lib/data-list-view.ts +1 -38
  584. package/template/lib/dev-log.ts +1 -8
  585. package/template/lib/editable-target.ts +1 -10
  586. package/template/lib/hub-connected-view-renderers.ts +58 -0
  587. package/template/lib/list-hub-supported-views.ts +10 -0
  588. package/template/lib/list-page-table-properties.ts +1 -52
  589. package/template/lib/mock/navigation.tsx +0 -8
  590. package/template/lib/mock/placements.ts +0 -7
  591. package/template/lib/placement-board-card-layout.ts +41 -41
  592. package/template/lib/placements-supported-views.ts +12 -0
  593. package/template/lib/question-bank-supported-views.ts +12 -0
  594. package/template/lib/raf-throttle.ts +1 -45
  595. package/template/lib/row-height.ts +4 -10
  596. package/template/lib/sidebar-state-cookie.ts +11 -2
  597. package/template/lib/sites-supported-views.ts +10 -0
  598. package/template/lib/team-supported-views.ts +10 -0
  599. package/template/package.json +1 -0
  600. package/template/tests/setup.ts +25 -0
  601. package/src/theme.css +0 -1132
  602. package/template/app/(app)/data-list/[id]/page.tsx +0 -44
  603. package/template/app/(app)/data-list/new/page.tsx +0 -34
  604. package/template/app/(app)/data-list/page.tsx +0 -10
  605. package/template/components/compliance-list-view.tsx +0 -54
  606. package/template/components/dashboard-onboarding-gallery.tsx +0 -13
  607. package/template/components/dashboard-onboarding.tsx +0 -21
  608. package/template/components/question-bank-list-view.tsx +0 -53
  609. package/template/components/section-cards.tsx +0 -106
  610. package/template/components/sites-list-view.tsx +0 -42
  611. package/template/components/team-list-view.tsx +0 -59
  612. package/template/lib/placement-lifecycle.ts +0 -5
  613. /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
  614. /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
  615. /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
  616. /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
  617. /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
  618. /package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +0 -0
@@ -68,6 +68,7 @@ import {
68
68
  } from "@/components/ui/dropdown-menu"
69
69
  import { Tip } from "@/components/ui/tip"
70
70
  import { PageHeader } from "@/components/page-header"
71
+ import { NewFocusTemplate } from "@/components/templates/new-focus-template"
71
72
  import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
72
73
  import {
73
74
  SelectionTileGrid,
@@ -270,6 +271,10 @@ type QuestionFormValues = z.infer<typeof questionSchema>
270
271
 
271
272
  const OPTION_LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H"] as const
272
273
 
274
+ // Runtime ID generators — called from user actions (Add option, Add pair, etc.) which
275
+ // only fire AFTER hydration, so `Math.random()` is safe here. NEVER call these from
276
+ // `buildInitial*` factories below — those run during the server render too and a
277
+ // random ID would mismatch the client tree on hydration.
273
278
  function newOptionId() {
274
279
  return `opt-${Math.random().toString(36).slice(2, 9)}`
275
280
  }
@@ -286,16 +291,20 @@ function newBlankId() {
286
291
  return `blk-${Math.random().toString(36).slice(2, 9)}`
287
292
  }
288
293
 
294
+ // SSR-safe defaults — deterministic, index-based IDs so the server and client render
295
+ // the same `id` / `key` / DOM attributes (e.g. `Checkbox` IDs derived from `option.id`).
296
+ // Hydration mismatch issue: `Math.random()` produces different values per call, so
297
+ // using it in initial defaults makes every render emit a different tree.
289
298
  function buildInitialOptions(type: AuthoringQuestionType): QuestionFormValues["options"] {
290
299
  if (type === "true_false") {
291
300
  return [
292
- { id: newOptionId(), text: "True", isCorrect: false, rationale: "" },
293
- { id: newOptionId(), text: "False", isCorrect: false, rationale: "" },
301
+ { id: "opt-true", text: "True", isCorrect: false, rationale: "" },
302
+ { id: "opt-false", text: "False", isCorrect: false, rationale: "" },
294
303
  ]
295
304
  }
296
305
  if (type === "mcq_single" || type === "mcq_multiple") {
297
- return Array.from({ length: AUTHORING_DEFAULT_OPTION_COUNT }, () => ({
298
- id: newOptionId(),
306
+ return Array.from({ length: AUTHORING_DEFAULT_OPTION_COUNT }, (_, i) => ({
307
+ id: `opt-init-${i + 1}`,
299
308
  text: "",
300
309
  isCorrect: false,
301
310
  rationale: "",
@@ -305,13 +314,20 @@ function buildInitialOptions(type: AuthoringQuestionType): QuestionFormValues["o
305
314
  }
306
315
 
307
316
  function buildInitialPairs(): QuestionFormValues["pairs"] {
308
- return Array.from({ length: 3 }, () => ({ id: newPairId(), left: "", right: "" }))
317
+ return Array.from({ length: 3 }, (_, i) => ({
318
+ id: `pair-init-${i + 1}`,
319
+ left: "",
320
+ right: "",
321
+ }))
309
322
  }
310
323
  function buildInitialOrderedItems(): QuestionFormValues["orderedItems"] {
311
- return Array.from({ length: 4 }, () => ({ id: newOrderedId(), text: "" }))
324
+ return Array.from({ length: 4 }, (_, i) => ({
325
+ id: `ord-init-${i + 1}`,
326
+ text: "",
327
+ }))
312
328
  }
313
329
  function buildInitialFillBlankAnswers(): QuestionFormValues["fillBlankAnswers"] {
314
- return [{ id: newBlankId(), accepted: "" }]
330
+ return [{ id: "blk-init-1", accepted: "" }]
315
331
  }
316
332
 
317
333
  function folderBreadcrumb(folderId: string, folders: QuestionBankFolder[]): string {
@@ -419,7 +435,7 @@ function BuilderSection({
419
435
  <h2 className="text-sm font-semibold text-foreground">
420
436
  {title}
421
437
  {required ? (
422
- <span className="ml-1 text-destructive" aria-hidden="true">
438
+ <span className="ms-1 text-destructive" aria-hidden="true">
423
439
  *
424
440
  </span>
425
441
  ) : null}
@@ -764,6 +780,8 @@ interface NewQuestionComposerProps {
764
780
  defaultFolderId?: string
765
781
  /** Where to send the user when they cancel or save. */
766
782
  backHref: string
783
+ /** Label displayed in the `SiteHeader` back-icon (e.g. "Back to Favorites"). */
784
+ backLabel?: string
767
785
  folders?: QuestionBankFolder[]
768
786
  }
769
787
 
@@ -771,6 +789,7 @@ export function NewQuestionComposer({
771
789
  draftQuestionId,
772
790
  defaultFolderId,
773
791
  backHref,
792
+ backLabel = "Back",
774
793
  folders = DEFAULT_QUESTION_BANK_FOLDERS,
775
794
  }: NewQuestionComposerProps) {
776
795
  const router = useRouter()
@@ -1089,6 +1108,292 @@ export function NewQuestionComposer({
1089
1108
  </>
1090
1109
  )
1091
1110
 
1111
+ // Inspector body — wired into `NewFocusTemplate.form-inspector` via the `inspector`
1112
+ // render-prop. Renders both the collapsed-rail and expanded-card states so the composer
1113
+ // keeps its existing UX while the template owns the outer `<aside>` chrome (width
1114
+ // transition + sticky positioning).
1115
+ const inspectorContent = (
1116
+ <>
1117
+ {!inspectorOpen ? (
1118
+ <div
1119
+ className={cn(
1120
+ "flex w-12 flex-col items-center gap-1 rounded-xl bg-[var(--secondary-panel-bg)] px-1.5 py-2 ring-1 ring-border shadow-sm",
1121
+ )}
1122
+ >
1123
+ <Tip side="left" label="Show inspector">
1124
+ <button
1125
+ type="button"
1126
+ onClick={() => setInspectorOpen(true)}
1127
+ aria-label="Show inspector"
1128
+ aria-expanded={false}
1129
+ className={cn(
1130
+ "flex size-9 shrink-0 items-center justify-center rounded-md text-sidebar-foreground transition-colors",
1131
+ "hover:bg-sidebar-accent/50",
1132
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
1133
+ )}
1134
+ >
1135
+ <i
1136
+ className="fa-light fa-arrow-left-to-line text-[15px] leading-none"
1137
+ aria-hidden="true"
1138
+ />
1139
+ </button>
1140
+ </Tip>
1141
+ </div>
1142
+ ) : (
1143
+ <div
1144
+ className={cn(
1145
+ "flex flex-col gap-6 rounded-xl border border-border bg-card p-4",
1146
+ "lg:max-h-[calc(100dvh-var(--header-height)-2rem)] lg:overflow-y-auto lg:overscroll-y-contain",
1147
+ )}
1148
+ >
1149
+ {/* Inspector header — title + collapse control. */}
1150
+ <div className="flex items-center justify-between">
1151
+ <p className="text-xs font-medium text-muted-foreground">
1152
+ Inspector
1153
+ </p>
1154
+ <Tip side="bottom" label="Hide inspector">
1155
+ <Button
1156
+ type="button"
1157
+ variant="ghost"
1158
+ size="icon-sm"
1159
+ onClick={() => setInspectorOpen(false)}
1160
+ aria-label="Hide inspector"
1161
+ aria-expanded={true}
1162
+ >
1163
+ <i
1164
+ className="fa-light fa-arrow-right-to-line"
1165
+ aria-hidden="true"
1166
+ />
1167
+ </Button>
1168
+ </Tip>
1169
+ </div>
1170
+
1171
+ {/* Question format — first-time landing shows the full
1172
+ `SelectionTileGrid` (matches the "File format" pattern
1173
+ in `ExportDrawer`). After the author picks once it
1174
+ collapses into a single selected-type tile with a
1175
+ "Change" affordance that re-opens the grid. */}
1176
+ <FormField
1177
+ control={form.control}
1178
+ name="type"
1179
+ render={({ field }) => (
1180
+ <FormItem>
1181
+ <FormControl>
1182
+ {typeChooserOpen ? (
1183
+ <SelectionTileGrid
1184
+ sectionLabel="Question format"
1185
+ options={QUESTION_TYPE_TILES}
1186
+ columns={2}
1187
+ value={field.value}
1188
+ onValueChange={v => changeType(v)}
1189
+ interaction="radio"
1190
+ idPrefix="qb-format"
1191
+ itemVariant="outline"
1192
+ itemMotion="pop"
1193
+ />
1194
+ ) : (
1195
+ <div className="flex flex-col gap-2">
1196
+ <Label
1197
+ className="text-xs font-medium text-muted-foreground"
1198
+ >
1199
+ Question format
1200
+ </Label>
1201
+ <button
1202
+ type="button"
1203
+ onClick={() => setTypeChooserOpen(true)}
1204
+ aria-label={`Change question format — currently ${activeType.label}`}
1205
+ className={cn(
1206
+ "group flex items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 text-left transition-colors",
1207
+ "hover:border-foreground/30 hover:bg-muted/40",
1208
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1209
+ )}
1210
+ >
1211
+ <span
1212
+ className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--icon-disc-brand-bg)] text-[var(--icon-disc-brand-fg)]"
1213
+ aria-hidden="true"
1214
+ >
1215
+ <i className={cn("fa-light text-base", activeType.icon)} />
1216
+ </span>
1217
+ <span className="flex min-w-0 flex-1 flex-col">
1218
+ <span className="truncate text-sm font-medium text-foreground">
1219
+ {activeType.label}
1220
+ </span>
1221
+ <span className="truncate text-xs text-muted-foreground">
1222
+ {activeType.tileSummary ?? activeType.description}
1223
+ </span>
1224
+ </span>
1225
+ <span
1226
+ className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors group-hover:bg-muted group-hover:text-foreground"
1227
+ aria-hidden="true"
1228
+ >
1229
+ <i className="fa-light fa-pen-to-square text-xs" />
1230
+ </span>
1231
+ </button>
1232
+ </div>
1233
+ )}
1234
+ </FormControl>
1235
+ {typeChooserOpen ? (
1236
+ <FormDescription>{activeType.description}</FormDescription>
1237
+ ) : null}
1238
+ <FormMessage />
1239
+ </FormItem>
1240
+ )}
1241
+ />
1242
+
1243
+ {/* Location — compact selected tile (mirrors the
1244
+ collapsed question-format card). Click opens a Command
1245
+ popover with search and an "Add new folder" footer
1246
+ that bridges into `QuestionBankNewFolderSheet`. */}
1247
+ <FormField
1248
+ control={form.control}
1249
+ name="folderId"
1250
+ render={({ field }) => (
1251
+ <FormItem>
1252
+ <Label
1253
+ className="text-xs font-medium text-muted-foreground"
1254
+ >
1255
+ Location
1256
+ </Label>
1257
+ <FormControl>
1258
+ <FolderPickerControl
1259
+ folders={localFolders}
1260
+ value={field.value}
1261
+ onChange={v => field.onChange(v)}
1262
+ open={folderPickerOpen}
1263
+ onOpenChange={setFolderPickerOpen}
1264
+ onRequestNewFolder={() => {
1265
+ setFolderPickerOpen(false)
1266
+ setNewFolderOpen(true)
1267
+ }}
1268
+ />
1269
+ </FormControl>
1270
+ <FormMessage />
1271
+ </FormItem>
1272
+ )}
1273
+ />
1274
+
1275
+ {/* Difficulty — meter + AI estimate + PBI + folder note.
1276
+ Defaults to AI mode (the meter follows the folder
1277
+ recommendation); "Override" flips to manual chips for
1278
+ authors who want to lock the level themselves. */}
1279
+ <FormField
1280
+ control={form.control}
1281
+ name="difficulty"
1282
+ render={({ field }) => (
1283
+ <DifficultyMeter
1284
+ value={field.value}
1285
+ onChange={v => field.onChange(v)}
1286
+ mode={difficultyMode}
1287
+ onModeChange={setDifficultyMode}
1288
+ insight={difficultyInsight}
1289
+ />
1290
+ )}
1291
+ />
1292
+
1293
+ <FormField
1294
+ control={form.control}
1295
+ name="bloom"
1296
+ render={({ field }) => (
1297
+ <InspectorSection title="Bloom's taxonomy">
1298
+ <ToggleGroup
1299
+ type="single"
1300
+ variant="outline"
1301
+ size="sm"
1302
+ spacing={1}
1303
+ value={field.value}
1304
+ onValueChange={v => field.onChange(v)}
1305
+ className="flex-wrap"
1306
+ >
1307
+ {AUTHORING_BLOOM_OPTIONS.map(b => (
1308
+ <ToggleGroupItem
1309
+ key={b.value}
1310
+ value={b.value}
1311
+ title={b.hint}
1312
+ className="rounded-full px-3"
1313
+ >
1314
+ {b.label}
1315
+ </ToggleGroupItem>
1316
+ ))}
1317
+ </ToggleGroup>
1318
+ </InspectorSection>
1319
+ )}
1320
+ />
1321
+
1322
+ <FormField
1323
+ control={form.control}
1324
+ name="cogLevel"
1325
+ render={({ field }) => (
1326
+ <InspectorSection
1327
+ title="NBME cognitive level"
1328
+ description="Distinct from Bloom — broader buckets used by NBME item writers."
1329
+ >
1330
+ <ToggleGroup
1331
+ type="single"
1332
+ variant="outline"
1333
+ size="sm"
1334
+ spacing={1}
1335
+ value={field.value}
1336
+ onValueChange={v => field.onChange(v)}
1337
+ className="flex-wrap"
1338
+ >
1339
+ {AUTHORING_COG_LEVEL_OPTIONS.map(c => (
1340
+ <ToggleGroupItem
1341
+ key={c.value}
1342
+ value={c.value}
1343
+ title={c.hint}
1344
+ className="rounded-full px-3"
1345
+ >
1346
+ {c.label}
1347
+ </ToggleGroupItem>
1348
+ ))}
1349
+ </ToggleGroup>
1350
+ </InspectorSection>
1351
+ )}
1352
+ />
1353
+
1354
+ <InspectorSection title="Tags" htmlFor="qb-tag-input">
1355
+ {watchedTags.length > 0 ? (
1356
+ <div className="flex flex-wrap gap-1.5">
1357
+ {watchedTags.map(t => (
1358
+ <Badge key={t} variant="secondary" className="gap-1.5">
1359
+ <span>#{t}</span>
1360
+ <Tip side="top" label={`Remove tag ${t}`}>
1361
+ <button
1362
+ type="button"
1363
+ onClick={() => removeTag(t)}
1364
+ aria-label={`Remove tag ${t}`}
1365
+ className="-me-0.5 inline-flex size-3.5 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1366
+ >
1367
+ <i
1368
+ className="fa-light fa-xmark text-[9px]"
1369
+ aria-hidden="true"
1370
+ />
1371
+ </button>
1372
+ </Tip>
1373
+ </Badge>
1374
+ ))}
1375
+ </div>
1376
+ ) : null}
1377
+ <Input
1378
+ id="qb-tag-input"
1379
+ value={tagDraft}
1380
+ onChange={e => setTagDraft(e.target.value)}
1381
+ onKeyDown={e => {
1382
+ if (e.key === "Enter" || e.key === ",") {
1383
+ e.preventDefault()
1384
+ commitTag()
1385
+ }
1386
+ }}
1387
+ onBlur={commitTag}
1388
+ placeholder="STEMI, antibiotics…"
1389
+ className="h-8 text-xs"
1390
+ />
1391
+ </InspectorSection>
1392
+ </div>
1393
+ )}
1394
+ </>
1395
+ )
1396
+
1092
1397
  return (
1093
1398
  <Form {...form}>
1094
1399
  {/* Global shortcuts — bound while the composer is mounted. The
@@ -1108,125 +1413,140 @@ export function NewQuestionComposer({
1108
1413
  onInvoke={() => setInspectorOpen(o => !o)}
1109
1414
  />
1110
1415
 
1416
+ {/*
1417
+ `<form>` participates in the (app)/layout flex row alongside the sidebar +
1418
+ secondary panel + Ask Leo rail, so it MUST behave like a normal flex column
1419
+ host (flex-1 + min-w-0). Without these classes the form shrinks to its
1420
+ intrinsic content width and the page collapses into a narrow column on the
1421
+ left with the rest of the viewport empty. See `new-placement-form.tsx`.
1422
+ */}
1111
1423
  <form
1112
1424
  onSubmit={form.handleSubmit(values => persist({ ...values, status: "in_review" }, "publish"))}
1113
1425
  noValidate
1114
1426
  aria-label="New question form"
1115
- className="flex flex-col gap-6"
1427
+ className="flex min-h-0 min-w-0 flex-1 flex-col"
1116
1428
  >
1117
- <PageHeader
1118
- title="New question"
1119
- subtitle={headerSubtitle}
1120
- actions={
1121
- <>
1122
- {/* Save as draft — icon-only button (leftmost). */}
1123
- <Tip side="bottom" label="Save as draft">
1429
+ <NewFocusTemplate
1430
+ variant="form-inspector"
1431
+ title="New question"
1432
+ back={{ href: backHref, label: backLabel, ariaLabel: `Back to ${backLabel}` }}
1433
+ useSiteHeaderBack
1434
+ hideInspectorToggle
1435
+ inspectorOpen={inspectorOpen}
1436
+ onInspectorOpenChange={setInspectorOpen}
1437
+ inspectorAriaLabel="Question inspector"
1438
+ inspector={() => inspectorContent}
1439
+ header={
1440
+ <PageHeader
1441
+ title="New question"
1442
+ subtitle={headerSubtitle}
1443
+ actions={
1444
+ <>
1445
+ {/* Save as draft — icon-only button (leftmost). */}
1446
+ <Tip side="bottom" label="Save as draft">
1447
+ <Button
1448
+ type="button"
1449
+ size="lg"
1450
+ variant="outline"
1451
+ className="aspect-square px-0"
1452
+ disabled={submitting}
1453
+ onClick={handleSaveAsDraft}
1454
+ aria-label="Save as draft"
1455
+ >
1456
+ <i className="fa-light fa-file-pen text-base" aria-hidden="true" />
1457
+ </Button>
1458
+ </Tip>
1459
+
1460
+ {/* Primary — Save Question. Full validation; the
1461
+ question moves to In review and the user is
1462
+ returned to the parent hub. */}
1124
1463
  <Button
1125
1464
  type="button"
1126
1465
  size="lg"
1127
- variant="outline"
1128
- className="aspect-square px-0"
1129
1466
  disabled={submitting}
1130
- onClick={handleSaveAsDraft}
1131
- aria-label="Save as draft"
1467
+ aria-busy={submitting}
1468
+ onClick={handleSaveQuestion}
1132
1469
  >
1133
- <i className="fa-light fa-file-pen text-base" aria-hidden="true" />
1470
+ {submitting ? (
1471
+ <>
1472
+ <i
1473
+ className="fa-light fa-spinner-third fa-spin text-[13px]"
1474
+ aria-hidden="true"
1475
+ />
1476
+ Saving…
1477
+ </>
1478
+ ) : (
1479
+ <>
1480
+ Save question
1481
+ <KbdGroup className="ms-1.5">
1482
+ <Kbd variant="bare">⏎</Kbd>
1483
+ </KbdGroup>
1484
+ </>
1485
+ )}
1134
1486
  </Button>
1135
- </Tip>
1136
-
1137
- {/* Primary — Save Question. Full validation; the
1138
- question moves to In review and the user is
1139
- returned to the parent hub. */}
1140
- <Button
1141
- type="button"
1142
- size="lg"
1143
- disabled={submitting}
1144
- aria-busy={submitting}
1145
- onClick={handleSaveQuestion}
1146
- >
1147
- {submitting ? (
1148
- <>
1149
- <i
1150
- className="fa-light fa-spinner-third fa-spin text-[13px]"
1151
- aria-hidden="true"
1152
- />
1153
- Saving…
1154
- </>
1155
- ) : (
1156
- <>
1157
- Save question
1158
- <KbdGroup className="ml-1.5">
1159
- <Kbd variant="bare">⏎</Kbd>
1160
- </KbdGroup>
1161
- </>
1162
- )}
1163
- </Button>
1164
1487
 
1165
- {/* More — overflow menu (⌘⌥M). */}
1166
- <DropdownMenu open={moreOpen} onOpenChange={setMoreOpen}>
1167
- <Tip side="bottom" label="More actions">
1168
- <DropdownMenuTrigger asChild>
1169
- <Button
1170
- type="button"
1171
- size="lg"
1172
- variant="outline"
1173
- className="aspect-square px-0"
1174
- aria-label="More actions"
1488
+ {/* More — overflow menu (⌘⌥M). */}
1489
+ <DropdownMenu open={moreOpen} onOpenChange={setMoreOpen}>
1490
+ <Tip side="bottom" label="More actions">
1491
+ <DropdownMenuTrigger asChild>
1492
+ <Button
1493
+ type="button"
1494
+ size="lg"
1495
+ variant="outline"
1496
+ className="aspect-square px-0"
1497
+ aria-label="More actions"
1498
+ >
1499
+ <i
1500
+ className="fa-light fa-ellipsis text-base"
1501
+ aria-hidden="true"
1502
+ />
1503
+ </Button>
1504
+ </DropdownMenuTrigger>
1505
+ </Tip>
1506
+ <DropdownMenuContent align="end">
1507
+ <DropdownMenuItem
1508
+ shortcut="⌘⌥]"
1509
+ onSelect={() => {
1510
+ window.setTimeout(
1511
+ () => setInspectorOpen(o => !o),
1512
+ 0,
1513
+ )
1514
+ }}
1175
1515
  >
1176
1516
  <i
1177
- className="fa-light fa-ellipsis text-base"
1517
+ className={cn(
1518
+ "fa-light",
1519
+ inspectorOpen ? "fa-sidebar-flip" : "fa-sidebar",
1520
+ )}
1178
1521
  aria-hidden="true"
1179
1522
  />
1180
- </Button>
1181
- </DropdownMenuTrigger>
1182
- </Tip>
1183
- <DropdownMenuContent align="end">
1184
- <DropdownMenuItem
1185
- shortcut="⌘⌥]"
1186
- onSelect={() => {
1187
- window.setTimeout(
1188
- () => setInspectorOpen(o => !o),
1189
- 0,
1190
- )
1191
- }}
1192
- >
1193
- <i
1194
- className={cn(
1195
- "fa-light",
1196
- inspectorOpen ? "fa-sidebar-flip" : "fa-sidebar",
1197
- )}
1198
- aria-hidden="true"
1199
- />
1200
- {inspectorOpen ? "Hide inspector" : "Show inspector"}
1201
- </DropdownMenuItem>
1202
- <DropdownMenuSeparator />
1203
- <DropdownMenuItem
1204
- shortcut="Esc"
1205
- onSelect={() => {
1206
- window.setTimeout(() => handleCancel(), 0)
1207
- }}
1208
- variant="destructive"
1209
- >
1210
- <i className="fa-light fa-trash-can" aria-hidden="true" />
1211
- Discard draft
1212
- </DropdownMenuItem>
1213
- </DropdownMenuContent>
1214
- </DropdownMenu>
1215
- </>
1216
- }
1217
- className="px-0 lg:px-0"
1218
- />
1219
-
1220
- {/* ── 2-column body — scrolls with the page; inspector sticky (lg+) */}
1221
- <div
1222
- className={cn(
1223
- "flex flex-col gap-8",
1224
- "lg:flex-row lg:items-start",
1225
- inspectorOpen ? "lg:gap-x-10" : "lg:gap-x-4",
1226
- )}
1523
+ {inspectorOpen ? "Hide inspector" : "Show inspector"}
1524
+ </DropdownMenuItem>
1525
+ <DropdownMenuSeparator />
1526
+ <DropdownMenuItem
1527
+ shortcut="Esc"
1528
+ onSelect={() => {
1529
+ window.setTimeout(() => handleCancel(), 0)
1530
+ }}
1531
+ variant="destructive"
1532
+ >
1533
+ <i className="fa-light fa-trash-can" aria-hidden="true" />
1534
+ Discard draft
1535
+ </DropdownMenuItem>
1536
+ </DropdownMenuContent>
1537
+ </DropdownMenu>
1538
+ </>
1539
+ }
1540
+ className="px-0 lg:px-0"
1541
+ />
1542
+ }
1543
+ maxWidthClassName="mx-auto w-full max-w-[1100px]"
1544
+ contentClassName="px-8 py-4 pb-16 flex flex-col gap-6"
1545
+ bodyClassName="min-h-0 flex-1 overflow-y-auto overscroll-y-contain"
1227
1546
  >
1228
- {/* ── Builder (no Card chrome) ─────────────────────────────── */}
1229
- <div className="flex min-w-0 flex-1 flex-col gap-7 lg:pr-2">
1547
+ {/* ── Builder (no Card chrome) direct child of the template's
1548
+ form-inspector grid; inspector lives in the `inspector` prop. */}
1549
+ <div className="flex flex-col gap-7 lg:pe-2">
1230
1550
  {/* Question prompt — a real bordered field, not an
1231
1551
  inline-editable headline. Larger heading-weight font keeps
1232
1552
  the question front-and-centre, but the visible border /
@@ -1758,297 +2078,7 @@ export function NewQuestionComposer({
1758
2078
  />
1759
2079
  ) : null}
1760
2080
  </div>
1761
-
1762
- {/* ── Inspector (right rail) — sticky while the page scrolls (lg+) */}
1763
- <aside
1764
- aria-label="Question inspector"
1765
- className={cn(
1766
- "shrink-0",
1767
- "lg:pl-2",
1768
- inspectorOpen ? "lg:w-[320px]" : "lg:w-14",
1769
- )}
1770
- >
1771
- {!inspectorOpen ? (
1772
- <div
1773
- className={cn(
1774
- "flex w-12 flex-col items-center gap-1 rounded-xl bg-[var(--secondary-panel-bg)] px-1.5 py-2 ring-1 ring-border shadow-sm",
1775
- "lg:sticky lg:top-4 lg:z-10",
1776
- )}
1777
- >
1778
- <Tip side="left" label="Show inspector">
1779
- <button
1780
- type="button"
1781
- onClick={() => setInspectorOpen(true)}
1782
- aria-label="Show inspector"
1783
- aria-expanded={false}
1784
- className={cn(
1785
- "flex size-9 shrink-0 items-center justify-center rounded-md text-sidebar-foreground transition-colors",
1786
- "hover:bg-sidebar-accent/50",
1787
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
1788
- )}
1789
- >
1790
- <i
1791
- className="fa-light fa-arrow-left-to-line text-[15px] leading-none"
1792
- aria-hidden="true"
1793
- />
1794
- </button>
1795
- </Tip>
1796
- </div>
1797
- ) : (
1798
- <div
1799
- className={cn(
1800
- "flex flex-col gap-6 rounded-xl border border-border bg-card p-4",
1801
- "lg:sticky lg:top-4 lg:z-10",
1802
- "lg:max-h-[calc(100dvh-var(--header-height)-2rem)] lg:overflow-y-auto lg:overscroll-y-contain",
1803
- )}
1804
- >
1805
- {/* Inspector header — title + collapse control. */}
1806
- <div className="flex items-center justify-between">
1807
- <p className="text-xs font-medium text-muted-foreground">
1808
- Inspector
1809
- </p>
1810
- <Tip side="bottom" label="Hide inspector">
1811
- <Button
1812
- type="button"
1813
- variant="ghost"
1814
- size="icon-sm"
1815
- onClick={() => setInspectorOpen(false)}
1816
- aria-label="Hide inspector"
1817
- aria-expanded={true}
1818
- >
1819
- <i
1820
- className="fa-light fa-arrow-right-to-line"
1821
- aria-hidden="true"
1822
- />
1823
- </Button>
1824
- </Tip>
1825
- </div>
1826
-
1827
- {/* Question format — first-time landing shows the full
1828
- `SelectionTileGrid` (matches the "File format" pattern
1829
- in `ExportDrawer`). After the author picks once it
1830
- collapses into a single selected-type tile with a
1831
- "Change" affordance that re-opens the grid. */}
1832
- <FormField
1833
- control={form.control}
1834
- name="type"
1835
- render={({ field }) => (
1836
- <FormItem>
1837
- <FormControl>
1838
- {typeChooserOpen ? (
1839
- <SelectionTileGrid
1840
- sectionLabel="Question format"
1841
- options={QUESTION_TYPE_TILES}
1842
- columns={2}
1843
- value={field.value}
1844
- onValueChange={v => changeType(v)}
1845
- interaction="radio"
1846
- idPrefix="qb-format"
1847
- itemVariant="outline"
1848
- itemMotion="pop"
1849
- />
1850
- ) : (
1851
- <div className="flex flex-col gap-2">
1852
- <Label
1853
- className="text-xs font-medium text-muted-foreground"
1854
- >
1855
- Question format
1856
- </Label>
1857
- <button
1858
- type="button"
1859
- onClick={() => setTypeChooserOpen(true)}
1860
- aria-label={`Change question format — currently ${activeType.label}`}
1861
- className={cn(
1862
- "group flex items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 text-left transition-colors",
1863
- "hover:border-foreground/30 hover:bg-muted/40",
1864
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1865
- )}
1866
- >
1867
- <span
1868
- className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--icon-disc-brand-bg)] text-[var(--icon-disc-brand-fg)]"
1869
- aria-hidden="true"
1870
- >
1871
- <i className={cn("fa-light text-base", activeType.icon)} />
1872
- </span>
1873
- <span className="flex min-w-0 flex-1 flex-col">
1874
- <span className="truncate text-sm font-medium text-foreground">
1875
- {activeType.label}
1876
- </span>
1877
- <span className="truncate text-xs text-muted-foreground">
1878
- {activeType.tileSummary ?? activeType.description}
1879
- </span>
1880
- </span>
1881
- <span
1882
- className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors group-hover:bg-muted group-hover:text-foreground"
1883
- aria-hidden="true"
1884
- >
1885
- <i className="fa-light fa-pen-to-square text-xs" />
1886
- </span>
1887
- </button>
1888
- </div>
1889
- )}
1890
- </FormControl>
1891
- {typeChooserOpen ? (
1892
- <FormDescription>{activeType.description}</FormDescription>
1893
- ) : null}
1894
- <FormMessage />
1895
- </FormItem>
1896
- )}
1897
- />
1898
-
1899
- {/* Location — compact selected tile (mirrors the
1900
- collapsed question-format card). Click opens a Command
1901
- popover with search and an "Add new folder" footer
1902
- that bridges into `QuestionBankNewFolderSheet`. */}
1903
- <FormField
1904
- control={form.control}
1905
- name="folderId"
1906
- render={({ field }) => (
1907
- <FormItem>
1908
- <Label
1909
- className="text-xs font-medium text-muted-foreground"
1910
- >
1911
- Location
1912
- </Label>
1913
- <FormControl>
1914
- <FolderPickerControl
1915
- folders={localFolders}
1916
- value={field.value}
1917
- onChange={v => field.onChange(v)}
1918
- open={folderPickerOpen}
1919
- onOpenChange={setFolderPickerOpen}
1920
- onRequestNewFolder={() => {
1921
- setFolderPickerOpen(false)
1922
- setNewFolderOpen(true)
1923
- }}
1924
- />
1925
- </FormControl>
1926
- <FormMessage />
1927
- </FormItem>
1928
- )}
1929
- />
1930
-
1931
- {/* Difficulty — meter + AI estimate + PBI + folder note.
1932
- Defaults to AI mode (the meter follows the folder
1933
- recommendation); "Override" flips to manual chips for
1934
- authors who want to lock the level themselves. */}
1935
- <FormField
1936
- control={form.control}
1937
- name="difficulty"
1938
- render={({ field }) => (
1939
- <DifficultyMeter
1940
- value={field.value}
1941
- onChange={v => field.onChange(v)}
1942
- mode={difficultyMode}
1943
- onModeChange={setDifficultyMode}
1944
- insight={difficultyInsight}
1945
- />
1946
- )}
1947
- />
1948
-
1949
- <FormField
1950
- control={form.control}
1951
- name="bloom"
1952
- render={({ field }) => (
1953
- <InspectorSection title="Bloom's taxonomy">
1954
- <ToggleGroup
1955
- type="single"
1956
- variant="outline"
1957
- size="sm"
1958
- spacing={1}
1959
- value={field.value}
1960
- onValueChange={v => field.onChange(v)}
1961
- className="flex-wrap"
1962
- >
1963
- {AUTHORING_BLOOM_OPTIONS.map(b => (
1964
- <ToggleGroupItem
1965
- key={b.value}
1966
- value={b.value}
1967
- title={b.hint}
1968
- className="rounded-full px-3"
1969
- >
1970
- {b.label}
1971
- </ToggleGroupItem>
1972
- ))}
1973
- </ToggleGroup>
1974
- </InspectorSection>
1975
- )}
1976
- />
1977
-
1978
- <FormField
1979
- control={form.control}
1980
- name="cogLevel"
1981
- render={({ field }) => (
1982
- <InspectorSection
1983
- title="NBME cognitive level"
1984
- description="Distinct from Bloom — broader buckets used by NBME item writers."
1985
- >
1986
- <ToggleGroup
1987
- type="single"
1988
- variant="outline"
1989
- size="sm"
1990
- spacing={1}
1991
- value={field.value}
1992
- onValueChange={v => field.onChange(v)}
1993
- className="flex-wrap"
1994
- >
1995
- {AUTHORING_COG_LEVEL_OPTIONS.map(c => (
1996
- <ToggleGroupItem
1997
- key={c.value}
1998
- value={c.value}
1999
- title={c.hint}
2000
- className="rounded-full px-3"
2001
- >
2002
- {c.label}
2003
- </ToggleGroupItem>
2004
- ))}
2005
- </ToggleGroup>
2006
- </InspectorSection>
2007
- )}
2008
- />
2009
-
2010
- <InspectorSection title="Tags" htmlFor="qb-tag-input">
2011
- {watchedTags.length > 0 ? (
2012
- <div className="flex flex-wrap gap-1.5">
2013
- {watchedTags.map(t => (
2014
- <Badge key={t} variant="secondary" className="gap-1.5">
2015
- <span>#{t}</span>
2016
- <Tip side="top" label={`Remove tag ${t}`}>
2017
- <button
2018
- type="button"
2019
- onClick={() => removeTag(t)}
2020
- aria-label={`Remove tag ${t}`}
2021
- className="-mr-0.5 inline-flex size-3.5 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
2022
- >
2023
- <i
2024
- className="fa-light fa-xmark text-[9px]"
2025
- aria-hidden="true"
2026
- />
2027
- </button>
2028
- </Tip>
2029
- </Badge>
2030
- ))}
2031
- </div>
2032
- ) : null}
2033
- <Input
2034
- id="qb-tag-input"
2035
- value={tagDraft}
2036
- onChange={e => setTagDraft(e.target.value)}
2037
- onKeyDown={e => {
2038
- if (e.key === "Enter" || e.key === ",") {
2039
- e.preventDefault()
2040
- commitTag()
2041
- }
2042
- }}
2043
- onBlur={commitTag}
2044
- placeholder="STEMI, antibiotics…"
2045
- className="h-8 text-xs"
2046
- />
2047
- </InspectorSection>
2048
- </div>
2049
- )}
2050
- </aside>
2051
- </div>
2081
+ </NewFocusTemplate>
2052
2082
  </form>
2053
2083
 
2054
2084
  {/* New folder — invoked from the location picker. Re-uses the
@@ -2188,7 +2218,7 @@ function OptionRow({
2188
2218
  </div>
2189
2219
 
2190
2220
  {rationaleOpen && !locked ? (
2191
- <div className="pl-10">
2221
+ <div className="ps-10">
2192
2222
  <Textarea
2193
2223
  value={option.rationale}
2194
2224
  onChange={e => onRationaleChange(e.target.value)}