@exxatdesignux/ui 0.3.0 → 0.4.1

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 (214) hide show
  1. package/CHANGELOG.md +701 -6
  2. package/README.md +138 -0
  3. package/bin/init.mjs +134 -31
  4. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +1 -1
  5. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +2 -2
  6. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -1
  7. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +2 -0
  8. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +1 -1
  9. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +3 -3
  10. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  11. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +1 -1
  12. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -1
  13. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +6 -6
  14. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -1
  15. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +2 -2
  16. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +1 -1
  17. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  18. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  19. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +7 -7
  20. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  21. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  22. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  23. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +8 -8
  24. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  25. package/consumer-extras/handbook/HANDBOOK.md +2 -0
  26. package/consumer-extras/handbook/glossary.md +2 -1
  27. package/consumer-extras/handbook/reference-implementations.md +31 -4
  28. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  29. package/consumer-extras/patterns/data-views-pattern.md +18 -16
  30. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  31. package/dist/components/data-table/index.js +2 -2
  32. package/dist/components/data-table/index.js.map +1 -1
  33. package/dist/components/data-table/pagination.js +3 -3
  34. package/dist/components/data-table/pagination.js.map +1 -1
  35. package/dist/components/data-table/use-table-state.d.ts +1 -1
  36. package/dist/components/data-table/use-table-state.js.map +1 -1
  37. package/dist/components/data-views/data-row-list.js.map +1 -1
  38. package/dist/components/data-views/finder-panel-view.d.ts +1 -1
  39. package/dist/components/data-views/finder-panel-view.js.map +1 -1
  40. package/dist/components/data-views/hub-table.d.ts +9 -3
  41. package/dist/components/data-views/hub-table.js +262 -40
  42. package/dist/components/data-views/hub-table.js.map +1 -1
  43. package/dist/components/data-views/index.js +262 -40
  44. package/dist/components/data-views/index.js.map +1 -1
  45. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +2 -2
  46. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -1
  47. package/dist/components/data-views/list-page-tree-column-header.d.ts +1 -1
  48. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -1
  49. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -1
  50. package/dist/components/data-views/os-folder-glyph.d.ts +1 -1
  51. package/dist/components/data-views/os-folder-glyph.js.map +1 -1
  52. package/dist/components/ui/avatar.d.ts +1 -1
  53. package/dist/components/ui/key-metrics.js.map +1 -1
  54. package/dist/index.js +136 -39
  55. package/dist/index.js.map +1 -1
  56. package/package.json +3 -2
  57. package/src/components/data-table/index.tsx +2 -2
  58. package/src/components/data-table/pagination.tsx +5 -1
  59. package/src/components/data-table/use-table-state.ts +1 -1
  60. package/src/components/data-views/data-row-list.tsx +1 -1
  61. package/src/components/data-views/finder-panel-view.tsx +2 -2
  62. package/src/components/data-views/hub-table.tsx +149 -41
  63. package/src/components/data-views/list-page-split-hub-tokens.ts +2 -2
  64. package/src/components/data-views/list-page-tree-column-header.tsx +1 -1
  65. package/src/components/data-views/os-folder-glyph.tsx +1 -1
  66. package/src/components/ui/key-metrics.tsx +1 -1
  67. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  68. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  69. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  70. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +6 -6
  71. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  72. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +5 -5
  73. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  74. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  75. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  76. package/template/AGENTS.md +43 -37
  77. package/template/app/(app)/columns/page.tsx +11 -0
  78. package/template/app/(app)/library/all/page.tsx +11 -0
  79. package/template/app/(app)/library/find/page.tsx +12 -0
  80. package/template/app/(app)/{question-bank → library}/layout.tsx +16 -16
  81. package/template/app/(app)/library/list/page.tsx +12 -0
  82. package/template/app/(app)/{question-bank → library}/new/page.tsx +10 -10
  83. package/template/app/(app)/library/page.tsx +11 -0
  84. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  85. package/template/components/ask-leo-composer.tsx +2 -2
  86. package/template/components/columns-client.tsx +158 -0
  87. package/template/components/columns-showcase.tsx +541 -0
  88. package/template/components/data-views/index.ts +32 -6
  89. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  90. package/template/components/data-views/table-cells.tsx +673 -0
  91. package/template/components/folder-details-shell.tsx +11 -11
  92. package/template/components/hub-tree-panel-view.tsx +24 -24
  93. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  94. package/template/components/{question-bank-client.tsx → library-client.tsx} +82 -82
  95. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  96. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  97. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +43 -43
  98. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +14 -14
  99. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  100. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  101. package/template/components/library-panel-activator.tsx +8 -0
  102. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +60 -60
  103. package/template/components/{question-bank-table.tsx → library-table.tsx} +97 -97
  104. package/template/components/list-hub-status-badge.tsx +2 -2
  105. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +37 -37
  106. package/template/components/sidebar/app-sidebar.tsx +61 -5
  107. package/template/components/sidebar/secondary-panel.tsx +109 -56
  108. package/template/components/sidebar/sidebar-auto-collapse.tsx +2 -2
  109. package/template/components/sidebar/sidebar-auto-open.tsx +2 -1
  110. package/template/components/table-properties/types.ts +1 -1
  111. package/template/components/templates/discovery-hub-template.tsx +1 -1
  112. package/template/components/templates/new-focus-template.tsx +2 -2
  113. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  114. package/template/components/tokens-secondary-nav.tsx +192 -0
  115. package/template/components/tokens-themes-client.tsx +476 -0
  116. package/template/components/tokens-themes-section.tsx +386 -0
  117. package/template/docs/HANDBOOK.md +187 -0
  118. package/template/docs/blueprints/README.md +1 -1
  119. package/template/docs/blueprints/board-card.md +1 -1
  120. package/template/docs/blueprints/data-table.md +2 -2
  121. package/template/docs/blueprints/list-page-template.md +3 -3
  122. package/template/docs/blueprints/page-header.md +4 -4
  123. package/template/docs/collaboration-access-pattern.md +7 -7
  124. package/template/docs/component-selection-guide.md +1 -1
  125. package/template/docs/data-views-pattern.md +18 -16
  126. package/template/docs/glossary.md +58 -0
  127. package/template/docs/kpi-flat-band-pattern.md +3 -3
  128. package/template/docs/kpi-trend-pattern.md +18 -3
  129. package/template/docs/large-dataset-strategy.md +155 -0
  130. package/template/docs/library-hub-header-pattern.md +25 -0
  131. package/template/docs/migrations/_template.md +1 -1
  132. package/template/docs/reference-implementations.md +151 -0
  133. package/template/docs/token-taxonomy.md +1 -1
  134. package/template/docs/voice-and-tone.md +262 -0
  135. package/template/eslint.config.mjs +9 -39
  136. package/template/hooks/use-secondary-panel-hub-nav.ts +10 -10
  137. package/template/lib/ask-leo-route-context.ts +6 -18
  138. package/template/lib/coach-mark-registry.ts +0 -16
  139. package/template/lib/command-menu-config.ts +5 -12
  140. package/template/lib/command-menu-search-data.ts +8 -39
  141. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  142. package/template/lib/library-dedicated-search.ts +19 -0
  143. package/template/lib/library-hub-search.ts +90 -0
  144. package/template/lib/library-nav.ts +477 -0
  145. package/template/lib/library-recent-searches.ts +22 -0
  146. package/template/lib/{placements-supported-views.ts → library-supported-views.ts} +2 -2
  147. package/template/lib/list-status-badges.ts +16 -104
  148. package/template/lib/mock/dashboard.ts +1 -1
  149. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  150. package/template/lib/mock/library-header-collaborators.ts +54 -0
  151. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  152. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  153. package/template/lib/mock/library.ts +249 -0
  154. package/template/lib/mock/navigation.tsx +32 -26
  155. package/template/lib/table-state-lifecycle.ts +1 -1
  156. package/template/next.config.mjs +7 -4
  157. package/template/package.json +0 -1
  158. package/tokens/hooks-index.json +2874 -0
  159. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +0 -28
  160. package/template/app/(app)/examples/page.tsx +0 -41
  161. package/template/app/(app)/question-bank/find/page.tsx +0 -12
  162. package/template/app/(app)/question-bank/library/page.tsx +0 -11
  163. package/template/app/(app)/question-bank/list/page.tsx +0 -12
  164. package/template/app/(app)/question-bank/page.tsx +0 -11
  165. package/template/components/compliance-board-view.tsx +0 -142
  166. package/template/components/compliance-client.tsx +0 -92
  167. package/template/components/compliance-page-header.tsx +0 -89
  168. package/template/components/compliance-table.tsx +0 -468
  169. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  170. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  171. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  172. package/template/components/new-placement-back-btn.tsx +0 -28
  173. package/template/components/new-placement-form.tsx +0 -942
  174. package/template/components/placement-board-card.tsx +0 -250
  175. package/template/components/placement-detail.tsx +0 -438
  176. package/template/components/placements-board-view.tsx +0 -397
  177. package/template/components/placements-client.tsx +0 -220
  178. package/template/components/placements-list-view.tsx +0 -124
  179. package/template/components/placements-page-header.tsx +0 -166
  180. package/template/components/placements-table-cells.test.tsx +0 -22
  181. package/template/components/placements-table-cells.tsx +0 -173
  182. package/template/components/placements-table-columns.tsx +0 -210
  183. package/template/components/placements-table.tsx +0 -934
  184. package/template/components/question-bank-panel-activator.tsx +0 -8
  185. package/template/components/rotations-empty-state.tsx +0 -50
  186. package/template/components/rotations-panel-activator.tsx +0 -8
  187. package/template/components/sites-board-view.tsx +0 -67
  188. package/template/components/sites-client.tsx +0 -154
  189. package/template/components/sites-table.tsx +0 -249
  190. package/template/components/team-board-view.tsx +0 -122
  191. package/template/components/team-client.tsx +0 -100
  192. package/template/components/team-page-header.tsx +0 -92
  193. package/template/components/team-table.tsx +0 -553
  194. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  195. package/template/lib/compliance-supported-views.ts +0 -10
  196. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  197. package/template/lib/mock/compliance-kpi.ts +0 -61
  198. package/template/lib/mock/compliance.ts +0 -146
  199. package/template/lib/mock/placements-kpi.ts +0 -134
  200. package/template/lib/mock/placements.ts +0 -176
  201. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  202. package/template/lib/mock/question-bank.ts +0 -249
  203. package/template/lib/mock/sites-directory.ts +0 -16
  204. package/template/lib/mock/sites-kpi.ts +0 -25
  205. package/template/lib/mock/team-kpi.ts +0 -60
  206. package/template/lib/mock/team.ts +0 -118
  207. package/template/lib/placement-board-card-layout.ts +0 -79
  208. package/template/lib/question-bank-dedicated-search.ts +0 -19
  209. package/template/lib/question-bank-hub-search.ts +0 -90
  210. package/template/lib/question-bank-nav.ts +0 -477
  211. package/template/lib/question-bank-recent-searches.ts +0 -22
  212. package/template/lib/question-bank-supported-views.ts +0 -12
  213. package/template/lib/sites-supported-views.ts +0 -10
  214. package/template/lib/team-supported-views.ts +0 -10
@@ -1,9 +1,9 @@
1
1
  "use client"
2
2
 
3
3
  /**
4
- * NewQuestionComposer — single-page authoring for the question bank.
4
+ * NewLibraryItemForm — single-page authoring for the library.
5
5
  *
6
- * IA (matches the rest of the question bank surfaces):
6
+ * IA (matches the rest of the library surfaces):
7
7
  *
8
8
  * ├─ PageHeader (title + actions; parent trail is in `SiteHeader`)
9
9
  * │ · "New question" + "V1 · Last updated …" subtitle
@@ -18,7 +18,7 @@
18
18
  * │ └─ Inspector (right, bg-card panel)
19
19
  * │ · Question format (SelectionTileGrid → compact)
20
20
  * │ · Location (folder SelectionTileGrid)
21
- * │ · Difficulty / Bloom / NBME (chips)
21
+ * │ · Level / Tier / Cognitive (chips)
22
22
  * │ · Tags (Input + Badge list)
23
23
  * │ Sidebar-style collapse (⌘⌥]) — collapsed rail mimics
24
24
  * │ `NestedSecondaryPanelShell` icon mode.
@@ -88,7 +88,7 @@ import {
88
88
  CommandList,
89
89
  CommandSeparator,
90
90
  } from "@/components/ui/command"
91
- import { QuestionBankNewFolderSheet } from "@/components/question-bank-new-folder-sheet"
91
+ import { LibraryNewFolderSheet } from "@/components/library-new-folder-sheet"
92
92
 
93
93
  import {
94
94
  AUTHORING_QUESTION_TYPES,
@@ -103,13 +103,13 @@ import {
103
103
  AUTHORING_RATIONALE_PLACEHOLDER,
104
104
  authoringQuestionType,
105
105
  type AuthoringQuestionType,
106
- } from "@/lib/question-bank-authoring"
106
+ } from "@/lib/library-authoring"
107
107
  import {
108
- DEFAULT_QUESTION_BANK_FOLDERS,
108
+ DEFAULT_LIBRARY_FOLDERS,
109
109
  newFolderId,
110
- type QuestionBankFolder,
111
- type QuestionBankFolderColorKey,
112
- } from "@/lib/mock/question-bank-folders"
110
+ type LibraryFolder,
111
+ type LibraryFolderColorKey,
112
+ } from "@/lib/mock/library-folders"
113
113
 
114
114
  // ─────────────────────────────────────────────────────────────────────────────
115
115
  // Schema
@@ -330,7 +330,7 @@ function buildInitialFillBlankAnswers(): QuestionFormValues["fillBlankAnswers"]
330
330
  return [{ id: "blk-init-1", accepted: "" }]
331
331
  }
332
332
 
333
- function folderBreadcrumb(folderId: string, folders: QuestionBankFolder[]): string {
333
+ function folderBreadcrumb(folderId: string, folders: LibraryFolder[]): string {
334
334
  const f = folders.find(x => x.id === folderId)
335
335
  if (!f) return ""
336
336
  if (f.parentId == null) return f.name
@@ -351,7 +351,7 @@ function difficultyToPercent(value: "easy" | "medium" | "hard"): number {
351
351
  * build this comes from analytics; for the mock it is a stable seed so
352
352
  * the inspector reads as if the AI had crunched historical data.
353
353
  */
354
- function difficultyInsightForFolder(folder: QuestionBankFolder | undefined): {
354
+ function difficultyInsightForFolder(folder: LibraryFolder | undefined): {
355
355
  /** Predicted level based on the content the author is writing. */
356
356
  recommendation: "easy" | "medium" | "hard"
357
357
  /** Contextual note shown under the meter (e.g. folder distribution). */
@@ -452,11 +452,11 @@ function BuilderSection({
452
452
  // Compact selected tile — same visual rhythm as the collapsed
453
453
  // "Question format" card. Clicking the tile opens a Command popover with
454
454
  // search, full folder list, and an "Add new folder" footer that bridges to
455
- // `QuestionBankNewFolderSheet`. Visuals reuse the folder-color tokens from
456
- // `lib/mock/question-bank-folders.ts` so the inspector reads the same as
457
- // the rest of the question bank.
455
+ // `LibraryNewFolderSheet`. Visuals reuse the folder-color tokens from
456
+ // `lib/mock/library-folders.ts` so the inspector reads the same as
457
+ // the rest of the library.
458
458
 
459
- const FOLDER_TINT_BG: Record<QuestionBankFolderColorKey, string> = {
459
+ const FOLDER_TINT_BG: Record<LibraryFolderColorKey, string> = {
460
460
  brand: "bg-[var(--icon-disc-brand-bg)] text-[var(--icon-disc-brand-fg)]",
461
461
  success: "bg-[var(--icon-disc-chart-2-bg)] text-[var(--icon-disc-chart-2-fg)]",
462
462
  warning: "bg-[var(--icon-disc-chart-4-bg)] text-[var(--icon-disc-chart-4-fg)]",
@@ -468,7 +468,7 @@ const FOLDER_TINT_BG: Record<QuestionBankFolderColorKey, string> = {
468
468
  }
469
469
 
470
470
  interface FolderPickerControlProps {
471
- folders: QuestionBankFolder[]
471
+ folders: LibraryFolder[]
472
472
  value: string
473
473
  onChange: (id: string) => void
474
474
  open: boolean
@@ -479,14 +479,14 @@ interface FolderPickerControlProps {
479
479
  /** Build a tree-ordered array: parents first, then their children
480
480
  indented beneath. Each entry carries a `depth` for left-padding. */
481
481
  function buildFolderTree(
482
- folders: QuestionBankFolder[],
483
- ): Array<{ folder: QuestionBankFolder; depth: number }> {
482
+ folders: LibraryFolder[],
483
+ ): Array<{ folder: LibraryFolder; depth: number }> {
484
484
  const roots = folders.filter(f => f.parentId == null)
485
485
  const childrenOf = (parentId: string) =>
486
486
  folders.filter(f => f.parentId === parentId)
487
487
 
488
- const out: Array<{ folder: QuestionBankFolder; depth: number }> = []
489
- function walk(parent: QuestionBankFolder, depth: number) {
488
+ const out: Array<{ folder: LibraryFolder; depth: number }> = []
489
+ function walk(parent: LibraryFolder, depth: number) {
490
490
  out.push({ folder: parent, depth })
491
491
  for (const child of childrenOf(parent.id)) {
492
492
  walk(child, depth + 1)
@@ -655,7 +655,7 @@ function DifficultyMeter({
655
655
  : "bg-destructive"
656
656
 
657
657
  const levelLabel =
658
- value === "easy" ? "Easy" : value === "hard" ? "Hard" : "Medium"
658
+ value === "easy" ? "Low" : value === "hard" ? "High" : "Normal"
659
659
 
660
660
  return (
661
661
  <section className="flex flex-col gap-3">
@@ -782,15 +782,15 @@ interface NewQuestionComposerProps {
782
782
  backHref: string
783
783
  /** Label displayed in the `SiteHeader` back-icon (e.g. "Back to Favorites"). */
784
784
  backLabel?: string
785
- folders?: QuestionBankFolder[]
785
+ folders?: LibraryFolder[]
786
786
  }
787
787
 
788
- export function NewQuestionComposer({
788
+ export function NewLibraryItemForm({
789
789
  draftQuestionId,
790
790
  defaultFolderId,
791
791
  backHref,
792
792
  backLabel = "Back",
793
- folders = DEFAULT_QUESTION_BANK_FOLDERS,
793
+ folders = DEFAULT_LIBRARY_FOLDERS,
794
794
  }: NewQuestionComposerProps) {
795
795
  const router = useRouter()
796
796
  const [submitting, setSubmitting] = React.useState(false)
@@ -804,7 +804,7 @@ export function NewQuestionComposer({
804
804
  /** Local folder list — extended in-place when the author adds one
805
805
  from the location picker so the new entry is selectable without
806
806
  a page navigation. */
807
- const [localFolders, setLocalFolders] = React.useState<QuestionBankFolder[]>(folders)
807
+ const [localFolders, setLocalFolders] = React.useState<LibraryFolder[]>(folders)
808
808
  React.useEffect(() => {
809
809
  setLocalFolders(prev =>
810
810
  prev.length === folders.length && prev.every((f, i) => f.id === folders[i]?.id)
@@ -1243,7 +1243,7 @@ export function NewQuestionComposer({
1243
1243
  {/* Location — compact selected tile (mirrors the
1244
1244
  collapsed question-format card). Click opens a Command
1245
1245
  popover with search and an "Add new folder" footer
1246
- that bridges into `QuestionBankNewFolderSheet`. */}
1246
+ that bridges into `LibraryNewFolderSheet`. */}
1247
1247
  <FormField
1248
1248
  control={form.control}
1249
1249
  name="folderId"
@@ -1324,8 +1324,8 @@ export function NewQuestionComposer({
1324
1324
  name="cogLevel"
1325
1325
  render={({ field }) => (
1326
1326
  <InspectorSection
1327
- title="NBME cognitive level"
1328
- description="Distinct from Bloom broader buckets used by NBME item writers."
1327
+ title="Cognitive level"
1328
+ description="Broader bucket used for analytics separate from the tier above."
1329
1329
  >
1330
1330
  <ToggleGroup
1331
1331
  type="single"
@@ -1594,7 +1594,7 @@ export function NewQuestionComposer({
1594
1594
  )}
1595
1595
  />
1596
1596
 
1597
- {/* MCQ family + True/False — bordered OptionRows. */}
1597
+ {/* Choice family + True/False — bordered OptionRows. */}
1598
1598
  {showOptionsBlock ? (
1599
1599
  <FormField
1600
1600
  control={form.control}
@@ -1607,7 +1607,7 @@ export function NewQuestionComposer({
1607
1607
  >
1608
1608
  <p className="text-xs text-muted-foreground">
1609
1609
  {isMulti
1610
- ? "NCLEX-style — mark every correct response."
1610
+ ? "Mark every correct response."
1611
1611
  : isTrueFalse
1612
1612
  ? "Mark whether the statement is True or False."
1613
1613
  : "Mark the single best answer. Distractors should be plausible — same length and grammar as the correct option."}
@@ -1942,10 +1942,10 @@ export function NewQuestionComposer({
1942
1942
  Image hotspot authoring
1943
1943
  </p>
1944
1944
  <p className="mt-1 text-xs text-muted-foreground">
1945
- Upload an anatomy diagram, x-ray, or ECG, then draw the correct
1946
- region(s). Image upload + region drawing tools arrive in the
1947
- next phase — for now, describe the expected target in the
1948
- explanation below.
1945
+ Upload an image, then draw the correct region(s). Image
1946
+ upload + region drawing tools arrive in the next phase —
1947
+ for now, describe the expected target in the explanation
1948
+ below.
1949
1949
  </p>
1950
1950
  </div>
1951
1951
  <Button type="button" variant="outline" size="sm" disabled>
@@ -2082,17 +2082,17 @@ export function NewQuestionComposer({
2082
2082
  </form>
2083
2083
 
2084
2084
  {/* New folder — invoked from the location picker. Re-uses the
2085
- shared `QuestionBankNewFolderSheet` (same shell as the folder
2085
+ shared `LibraryNewFolderSheet` (same shell as the folder
2086
2086
  hub) so the surface stays consistent. The created folder is
2087
2087
  appended to `localFolders` and immediately selected. */}
2088
- <QuestionBankNewFolderSheet
2088
+ <LibraryNewFolderSheet
2089
2089
  open={newFolderOpen}
2090
2090
  onOpenChange={setNewFolderOpen}
2091
2091
  parentFolderId={null}
2092
2092
  descriptionText="Drafts created from this composer can land in the new folder right away."
2093
2093
  onCreated={f => {
2094
2094
  const id = newFolderId()
2095
- const created: QuestionBankFolder = { id, ...f }
2095
+ const created: LibraryFolder = { id, ...f }
2096
2096
  setLocalFolders(prev => [...prev, created])
2097
2097
  form.setValue("folderId", id, { shouldDirty: true, shouldValidate: false })
2098
2098
  }}
@@ -105,6 +105,53 @@ function normalizedLocationHash(locationHash: string): string {
105
105
  return locationHash.startsWith("#") ? locationHash.slice(1) : locationHash
106
106
  }
107
107
 
108
+ /**
109
+ * Path → set of hash fragments claimed by *another* nav item at the same path.
110
+ *
111
+ * Why: several rows deliberately share a route and disambiguate via `#fragment`.
112
+ * Example: `Tokens & themes → /settings#appearance` and `Settings → /settings`
113
+ * both render under the same page. Without a registry, when the user is on
114
+ * `/settings#appearance` the no-fragment "Settings" row matches by path-equality
115
+ * (line below: `pathname === pathOnly`) and lights up too — so *both* rows
116
+ * appear active.
117
+ *
118
+ * The registry is computed once from the static nav config and consulted by
119
+ * `isNavActive` to make the no-fragment item defer when a hash-bearing sibling
120
+ * claims the current `location.hash`.
121
+ */
122
+ const NAV_HASH_CLAIMS: ReadonlyMap<string, ReadonlySet<string>> = (() => {
123
+ const map = new Map<string, Set<string>>()
124
+ const record = (url: string) => {
125
+ const p = navUrlPath(url)
126
+ const f = navUrlFragment(url)
127
+ if (!p || f === null) return
128
+ let set = map.get(p)
129
+ if (!set) { set = new Set<string>(); map.set(p, set) }
130
+ set.add(f)
131
+ }
132
+ const walk = (items: ReadonlyArray<{ url?: string; children?: ReadonlyArray<{ url?: string; children?: unknown }> }>) => {
133
+ for (const it of items) {
134
+ if (typeof it.url === "string") record(it.url)
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ if (Array.isArray((it as any).children)) walk((it as any).children)
137
+ }
138
+ }
139
+ walk(NAV_PRIMARY)
140
+ walk(NAV_DOCUMENTS)
141
+ walk(NAV_QUICK_ACTIONS)
142
+ walk(NAV_SECONDARY)
143
+ return map
144
+ })()
145
+
146
+ /** True when another nav item at the same path claims the current location hash. */
147
+ function navHasMoreSpecificMatch(pathname: string, locationHash: string): boolean {
148
+ const claims = NAV_HASH_CLAIMS.get(pathname)
149
+ if (!claims) return false
150
+ const h = normalizedLocationHash(locationHash)
151
+ if (h === "") return false
152
+ return claims.has(h)
153
+ }
154
+
108
155
  /**
109
156
  * Whether `pathname` (+ optional `location.hash`) matches a sidebar `href`.
110
157
  * When several links share the same path (e.g. `/settings`), disambiguate with `#fragment`
@@ -129,9 +176,18 @@ function isNavActive(pathname: string, url: string, locationHash = ""): boolean
129
176
  return pathname === pathOnly && h === frag
130
177
  }
131
178
 
132
- if (pathOnly === "/") return pathname === "/" && h === ""
133
- /** Exact path match ignore `location.hash` when the nav `href` has no `#…` fragment (QB view tabs use hash). */
134
- if (pathname === pathOnly) return true
179
+ if (pathOnly === "/") {
180
+ if (pathname !== "/" || h !== "") return false
181
+ return !navHasMoreSpecificMatch(pathname, locationHash)
182
+ }
183
+ /**
184
+ * Exact path match — ignore `location.hash` when the nav `href` has no `#…` fragment (QB view tabs use hash).
185
+ * EXCEPTION: when another nav item at the same path claims the current hash
186
+ * (e.g. `Tokens & themes → /settings#appearance` while we're evaluating
187
+ * `Settings → /settings`), defer to that more-specific row — otherwise both
188
+ * would light up. See `NAV_HASH_CLAIMS`.
189
+ */
190
+ if (pathname === pathOnly) return !navHasMoreSpecificMatch(pathname, locationHash)
135
191
  // Design system library — active on hub and detail routes.
136
192
  if (pathOnly === "/library") {
137
193
  return pathname.startsWith("/library/")
@@ -167,7 +223,7 @@ function isCollapsibleChildActive(
167
223
 
168
224
  if (!isNavActive(pathname, child.url, locationHash)) return false
169
225
 
170
- /** Hub entry (`/question-bank`) must not stay “active” on `/question-bank/library` etc. */
226
+ /** Hub entry (`/library`) must not stay “active” on `/library/all` etc. */
171
227
  if (parent.primaryHubChildKey && child.key === parent.primaryHubChildKey) {
172
228
  const hubPath = navUrlPath(parent.url)
173
229
  if (hubPath) {
@@ -298,7 +354,7 @@ function CollapsibleNavItem({ item, pathname }: { item: NavLinkItem; pathname: s
298
354
  const iconRailCollapsed = state === "collapsed" && !isMobile
299
355
  // In the icon rail the parent icon is the ONLY visible thing for this item
300
356
  // (no sub-list, no labels) — so it must reflect "I'm somewhere inside this
301
- // section" by lighting up on any descendant route (e.g. `/question-bank/library`),
357
+ // section" by lighting up on any descendant route (e.g. `/library/all`),
302
358
  // not only on the parent URL itself. In the expanded view we keep the
303
359
  // parent neutral and let the active child row carry `data-active` (see
304
360
  // `isCollapsibleParentMenuButtonActive`).
@@ -4,28 +4,29 @@
4
4
  * SecondaryPanel — nested rail between the primary icon sidebar and content.
5
5
  * Full width shows hub scope nav; **compact** matches the primary sidebar icon rail (`w-12`).
6
6
  *
7
- * Chrome uses {@link NestedSecondaryPanelShell}. Question bank body stays in
8
- * `question-bank-secondary-nav.tsx` (domain-specific), not duplicated here.
7
+ * Chrome uses {@link NestedSecondaryPanelShell}. Library body stays in
8
+ * `library-secondary-nav.tsx` (domain-specific), not duplicated here.
9
9
  */
10
10
 
11
11
  import * as React from "react"
12
12
  import { useSidebar } from "@/components/ui/sidebar"
13
13
  import { Tip } from "@/components/ui/tip"
14
14
  import { Button } from "@/components/ui/button"
15
- import { QuestionBankSecondaryNav } from "@/components/question-bank-secondary-nav"
15
+ import { LibrarySecondaryNav } from "@/components/library-secondary-nav"
16
+ import { TokensSecondaryNav } from "@/components/tokens-secondary-nav"
16
17
  import { NestedSecondaryPanelShell } from "@/components/templates/nested-secondary-panel-shell"
17
18
  import { useSidebarReflowZoom } from "@/hooks/use-sidebar-reflow-zoom"
18
- import type { QuestionBankItem } from "@/lib/mock/question-bank"
19
- import type { QuestionBankFolder } from "@/lib/mock/question-bank-folders"
20
-
21
- export type QuestionBankFolderBridge = {
22
- folders: QuestionBankFolder[]
23
- onFoldersChange: React.Dispatch<React.SetStateAction<QuestionBankFolder[]>>
24
- items: QuestionBankItem[]
25
- onItemsChange: React.Dispatch<React.SetStateAction<QuestionBankItem[]>>
19
+ import type { LibraryItem } from "@/lib/mock/library"
20
+ import type { LibraryFolder } from "@/lib/mock/library-folders"
21
+
22
+ export type LibraryFolderBridge = {
23
+ folders: LibraryFolder[]
24
+ onFoldersChange: React.Dispatch<React.SetStateAction<LibraryFolder[]>>
25
+ items: LibraryItem[]
26
+ onItemsChange: React.Dispatch<React.SetStateAction<LibraryItem[]>>
26
27
  }
27
28
 
28
- export type QuestionBankAccessBridge = {
29
+ export type LibraryAccessBridge = {
29
30
  openManageAccess: () => void
30
31
  }
31
32
 
@@ -36,11 +37,18 @@ export type QuestionBankAccessBridge = {
36
37
  export type ClosePanelOptions = {
37
38
  /**
38
39
  * Main app sidebar after the secondary panel closes.
39
- * - `leave` (default): only clear the active panel — preserves expanded/collapsed primary rail (⌘B, cookie).
40
- * - `expand`: full primary rail use only when product explicitly restores the wide rail after dismiss.
41
- * - `collapse`: icon rail matches routes that keep the primary rail compact with the panel closed.
40
+ * - `restore` (default): snap back to the user's saved preference
41
+ * (`sidebar_state_v2` cookie, or `defaultOpen` if unset). Use this when
42
+ * leaving a page whose secondary panel caused an incidental collapse the
43
+ * rail returns to the state the user explicitly set elsewhere via ⌘B / the
44
+ * sidebar button.
45
+ * - `leave`: only clear the active panel — keep the rail in whatever state it
46
+ * was in (rare; prefer `restore`).
47
+ * - `expand`: force the full primary rail open. Use only when the product
48
+ * explicitly wants the wide rail after dismiss.
49
+ * - `collapse`: keep the icon rail. Use when the next route also wants compact.
42
50
  */
43
- mainSidebar?: "expand" | "collapse" | "leave"
51
+ mainSidebar?: "restore" | "expand" | "collapse" | "leave"
44
52
  }
45
53
 
46
54
  interface SecondaryPanelContextValue {
@@ -54,12 +62,12 @@ interface SecondaryPanelContextValue {
54
62
  secondaryPanelCompact: boolean
55
63
  /** Narrow icon rail (primary-sidebar-style); keeps {@link activePanel} mounted. */
56
64
  collapseActiveSecondaryPanel: () => void
57
- /** Question bank folder tree shared with the secondary nav while the hub is mounted. */
58
- questionBankFolderBridge: QuestionBankFolderBridge | null
59
- setQuestionBankFolderBridge: (bridge: QuestionBankFolderBridge | null) => void
65
+ /** Library folder tree shared with the secondary nav while the hub is mounted. */
66
+ libraryFolderBridge: LibraryFolderBridge | null
67
+ setLibraryFolderBridge: (bridge: LibraryFolderBridge | null) => void
60
68
  /** Opens the hub collaborators sheet from the secondary nav. */
61
- questionBankAccessBridge: QuestionBankAccessBridge | null
62
- setQuestionBankAccessBridge: (bridge: QuestionBankAccessBridge | null) => void
69
+ libraryAccessBridge: LibraryAccessBridge | null
70
+ setLibraryAccessBridge: (bridge: LibraryAccessBridge | null) => void
63
71
  }
64
72
 
65
73
  const SecondaryPanelContext = React.createContext<SecondaryPanelContextValue>({
@@ -68,10 +76,10 @@ const SecondaryPanelContext = React.createContext<SecondaryPanelContextValue>({
68
76
  closePanel: () => {},
69
77
  secondaryPanelCompact: false,
70
78
  collapseActiveSecondaryPanel: () => {},
71
- questionBankFolderBridge: null,
72
- setQuestionBankFolderBridge: () => {},
73
- questionBankAccessBridge: null,
74
- setQuestionBankAccessBridge: () => {},
79
+ libraryFolderBridge: null,
80
+ setLibraryFolderBridge: () => {},
81
+ libraryAccessBridge: null,
82
+ setLibraryAccessBridge: () => {},
75
83
  })
76
84
 
77
85
  export function useSecondaryPanel() {
@@ -81,11 +89,11 @@ export function useSecondaryPanel() {
81
89
  export function SecondaryPanelProvider({ children }: { children: React.ReactNode }) {
82
90
  const [activePanel, setActivePanel] = React.useState<string | null>(null)
83
91
  const [secondaryPanelCompact, setSecondaryPanelCompact] = React.useState(false)
84
- const [questionBankFolderBridge, setQuestionBankFolderBridge] =
85
- React.useState<QuestionBankFolderBridge | null>(null)
86
- const [questionBankAccessBridge, setQuestionBankAccessBridge] =
87
- React.useState<QuestionBankAccessBridge | null>(null)
88
- const { setOpen } = useSidebar()
92
+ const [libraryFolderBridge, setLibraryFolderBridge] =
93
+ React.useState<LibraryFolderBridge | null>(null)
94
+ const [libraryAccessBridge, setLibraryAccessBridge] =
95
+ React.useState<LibraryAccessBridge | null>(null)
96
+ const { setOpen, restoreSavedOpen } = useSidebar()
89
97
 
90
98
  /**
91
99
  * Browser zoom ≥ 200% (or very short viewport) — same `useSidebarReflowZoom`
@@ -122,14 +130,25 @@ export function SecondaryPanelProvider({ children }: { children: React.ReactNode
122
130
  const closePanel = React.useCallback((opts?: ClosePanelOptions) => {
123
131
  setSecondaryPanelCompact(false)
124
132
  setActivePanel(null)
125
- const mainSidebar = opts?.mainSidebar ?? "leave"
133
+ /**
134
+ * Default = `restore`. `openPanel` collapsed the rail with `persist: false`
135
+ * (the saved cookie is untouched), so on close we ask the sidebar context
136
+ * to re-apply whatever the user truly saved. This makes the visual collapse
137
+ * scoped to the page that needed it — leave the page → rail comes back to
138
+ * the user's preference.
139
+ */
140
+ const mainSidebar = opts?.mainSidebar ?? "restore"
126
141
  if (mainSidebar === "leave") return
142
+ if (mainSidebar === "restore") {
143
+ restoreSavedOpen()
144
+ return
145
+ }
127
146
  if (mainSidebar === "collapse") {
128
147
  setOpen(false, { persist: false })
129
- } else {
130
- setOpen(true, { persist: false }) // expand main sidebar back
148
+ return
131
149
  }
132
- }, [setOpen])
150
+ setOpen(true, { persist: false }) // mainSidebar === "expand"
151
+ }, [restoreSavedOpen, setOpen])
133
152
 
134
153
  const collapseActiveSecondaryPanel = React.useCallback(() => {
135
154
  setSecondaryPanelCompact(true)
@@ -142,10 +161,10 @@ export function SecondaryPanelProvider({ children }: { children: React.ReactNode
142
161
  closePanel,
143
162
  secondaryPanelCompact,
144
163
  collapseActiveSecondaryPanel,
145
- questionBankFolderBridge,
146
- setQuestionBankFolderBridge,
147
- questionBankAccessBridge,
148
- setQuestionBankAccessBridge,
164
+ libraryFolderBridge,
165
+ setLibraryFolderBridge,
166
+ libraryAccessBridge,
167
+ setLibraryAccessBridge,
149
168
  }),
150
169
  [
151
170
  activePanel,
@@ -153,8 +172,8 @@ export function SecondaryPanelProvider({ children }: { children: React.ReactNode
153
172
  closePanel,
154
173
  secondaryPanelCompact,
155
174
  collapseActiveSecondaryPanel,
156
- questionBankFolderBridge,
157
- questionBankAccessBridge,
175
+ libraryFolderBridge,
176
+ libraryAccessBridge,
158
177
  ],
159
178
  )
160
179
 
@@ -169,11 +188,11 @@ export function SecondaryPanelProvider({ children }: { children: React.ReactNode
169
188
  // SecondaryPanel — the actual rendered panel
170
189
  // ─────────────────────────────────────────────────────────────────────────────
171
190
 
172
- function QuestionBankPanel() {
191
+ function LibraryPanel() {
173
192
  const { collapseActiveSecondaryPanel, secondaryPanelCompact } = useSecondaryPanel()
174
193
 
175
194
  if (secondaryPanelCompact) {
176
- return <QuestionBankSecondaryNav />
195
+ return <LibrarySecondaryNav />
177
196
  }
178
197
 
179
198
  return (
@@ -197,14 +216,48 @@ function QuestionBankPanel() {
197
216
  </Button>
198
217
  </Tip>
199
218
  </div>
200
- <QuestionBankSecondaryNav />
219
+ <LibrarySecondaryNav />
220
+ </>
221
+ )
222
+ }
223
+
224
+ function TokensPanel() {
225
+ const { collapseActiveSecondaryPanel, secondaryPanelCompact } = useSecondaryPanel()
226
+
227
+ if (secondaryPanelCompact) {
228
+ return <TokensSecondaryNav />
229
+ }
230
+
231
+ return (
232
+ <>
233
+ <div className="flex items-center justify-between gap-2 px-4 pt-4 pb-2">
234
+ <h2
235
+ className="text-xl font-semibold leading-tight text-sidebar-foreground"
236
+ style={{ fontFamily: "var(--font-heading)" }}
237
+ >
238
+ Tokens
239
+ </h2>
240
+ <Tip label="Collapse to icons" side="bottom">
241
+ <Button
242
+ type="button"
243
+ size="icon"
244
+ variant="ghost"
245
+ onClick={() => collapseActiveSecondaryPanel()}
246
+ aria-label="Collapse to icons"
247
+ >
248
+ <i className="fa-light fa-angles-left" aria-hidden="true" />
249
+ </Button>
250
+ </Tip>
251
+ </div>
252
+ <TokensSecondaryNav />
201
253
  </>
202
254
  )
203
255
  }
204
256
 
205
257
  /** Register panel components by id when a route opts into `secondaryPanel` in nav. */
206
258
  const PANELS: Record<string, React.FC> = {
207
- "question-bank": QuestionBankPanel,
259
+ "library": LibraryPanel,
260
+ tokens: TokensPanel,
208
261
  }
209
262
 
210
263
  export function SecondaryPanel() {
@@ -234,30 +287,30 @@ export function useAutoPanel(panelId: string) {
234
287
  }, [panelId])
235
288
  }
236
289
 
237
- /** Sync hub folder state into the question bank secondary nav while the route is mounted. */
238
- export function QuestionBankFolderBridge({
290
+ /** Sync hub folder state into the library secondary nav while the route is mounted. */
291
+ export function LibraryFolderBridge({
239
292
  folders,
240
293
  onFoldersChange,
241
294
  items,
242
295
  onItemsChange,
243
- }: QuestionBankFolderBridge) {
244
- const { setQuestionBankFolderBridge } = useSecondaryPanel()
296
+ }: LibraryFolderBridge) {
297
+ const { setLibraryFolderBridge } = useSecondaryPanel()
245
298
 
246
299
  React.useEffect(() => {
247
- setQuestionBankFolderBridge({ folders, onFoldersChange, items, onItemsChange })
248
- return () => setQuestionBankFolderBridge(null)
249
- }, [folders, onFoldersChange, items, onItemsChange, setQuestionBankFolderBridge])
300
+ setLibraryFolderBridge({ folders, onFoldersChange, items, onItemsChange })
301
+ return () => setLibraryFolderBridge(null)
302
+ }, [folders, onFoldersChange, items, onItemsChange, setLibraryFolderBridge])
250
303
 
251
304
  return null
252
305
  }
253
306
 
254
- export function QuestionBankAccessBridge({ openManageAccess }: QuestionBankAccessBridge) {
255
- const { setQuestionBankAccessBridge } = useSecondaryPanel()
307
+ export function LibraryAccessBridge({ openManageAccess }: LibraryAccessBridge) {
308
+ const { setLibraryAccessBridge } = useSecondaryPanel()
256
309
 
257
310
  React.useEffect(() => {
258
- setQuestionBankAccessBridge({ openManageAccess })
259
- return () => setQuestionBankAccessBridge(null)
260
- }, [openManageAccess, setQuestionBankAccessBridge])
311
+ setLibraryAccessBridge({ openManageAccess })
312
+ return () => setLibraryAccessBridge(null)
313
+ }, [openManageAccess, setLibraryAccessBridge])
261
314
 
262
315
  return null
263
316
  }
@@ -5,8 +5,8 @@ import { useSidebar } from "@/components/ui/sidebar"
5
5
 
6
6
  /**
7
7
  * Collapses the sidebar on mount. Restores previous state on unmount.
8
- * Used on the new-placement page so the sidebar collapses when entering
9
- * and reverts to whatever it was when leaving.
8
+ * Use on focused full-page forms / wizards so the sidebar collapses when
9
+ * entering and reverts to whatever it was when leaving.
10
10
  *
11
11
  * Both transitions pass `{ persist: false }` so the visual collapse never
12
12
  * overwrites the user's saved sidebar preference (the `sidebar_state_v2`
@@ -3,7 +3,8 @@
3
3
  import { useEffect, useRef } from "react"
4
4
  import { useSidebar } from "@/components/ui/sidebar"
5
5
 
6
- /** Reopens the sidebar once when the data-list page mounts (after returning from new-placement). */
6
+ /** Reopens the sidebar once on mount pair with `SidebarAutoCollapse` on focused
7
+ * full-page forms / wizards so leaving the form restores the sidebar. */
7
8
  export function SidebarAutoOpen() {
8
9
  const { setOpen } = useSidebar()
9
10
  const ran = useRef(false)
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * `FILTER_FIELDS` and `COLUMNS` below are **product data**, not
10
10
  * design-system primitives — they encode the columns + filter operators
11
- * of the Placements hub. Other hubs (Team, Compliance, Question bank)
11
+ * of the Placements hub. Other hubs (Team, Compliance, Library)
12
12
  * compose their own arrays and pass them into the drawer.
13
13
  */
14
14
 
@@ -78,7 +78,7 @@ const DiscoveryHubSearchRow = React.memo(function DiscoveryHubSearchRow({
78
78
 
79
79
  /**
80
80
  * Discovery hub — centered command-style natural language search, create CTA, and Ask Leo promo.
81
- * Hubs can pass `groups` from a domain module (e.g. Ask Leo prompt suggestions from `lib/question-bank-hub-search.ts`).
81
+ * Hubs can pass `groups` from a domain module (e.g. Ask Leo prompt suggestions from `lib/library-hub-search.ts`).
82
82
  */
83
83
  export function DiscoveryHubTemplate({
84
84
  title,
@@ -39,7 +39,7 @@
39
39
  * </form>
40
40
  * ```
41
41
  *
42
- * See `new-placement-form.tsx` and `new-question-composer.tsx` for canonical usage.
42
+ * See `new-placement-form.tsx` and `new-library-item-form.tsx` for canonical usage.
43
43
  *
44
44
  * The template owns:
45
45
  * • Page chrome (`PrimaryPageTemplate` underneath: `SidebarInset`, `SiteHeader`).
@@ -49,7 +49,7 @@
49
49
  * ⌘⌥← back, plain Enter submit on final step).
50
50
  * • For `form-inspector`: 2-column scaffold with sticky inspector rail.
51
51
  *
52
- * WCAG 2.1 AA — same rules as `new-placement-form` / `new-question-composer`:
52
+ * WCAG 2.1 AA — same rules as `new-placement-form` / `new-library-item-form`:
53
53
  * ✓ Hero `<h1>` carries the page title (only one h1 per route).
54
54
  * ✓ Step indicator uses `aria-current="step"` and visible labels (1.3.1).
55
55
  * ✓ Focus moves to step content when step changes (2.4.3).
@@ -24,7 +24,7 @@ export interface SecondaryPanelHubTemplateProps
24
24
  /**
25
25
  * Primary hub shell with optional bridges + `PrimaryPageTemplate` body.
26
26
  * Mount `useAutoPanel` / `SecondaryPanelHubActivator` on a parent layout that stays mounted across
27
- * hub child routes (see `app/(app)/question-bank/layout.tsx`) so the panel does not close between navigations.
27
+ * hub child routes (see `app/(app)/library/layout.tsx`) so the panel does not close between navigations.
28
28
  *
29
29
  * Pair with `useSecondaryPanelHubNav` and hub-specific `lib/*-nav.ts` helpers for URL scope.
30
30
  */