@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
@@ -12,38 +12,38 @@ import { Shortcut } from "@/components/ui/dropdown-menu"
12
12
  import { useSidebar } from "@/components/ui/sidebar"
13
13
  import { useAltKeyLabel, useModKeyLabel } from "@/hooks/use-mod-key-label"
14
14
  import {
15
- DEFAULT_QUESTION_BANK_FOLDERS,
16
- type QuestionBankFolder,
17
- type QuestionBankFolderColorKey,
18
- } from "@/lib/mock/question-bank-folders"
19
- import { QUESTION_BANK_ITEMS, type QuestionBankItem } from "@/lib/mock/question-bank"
20
- import { QUESTION_BANK_HUB_ASK_LEO_PROMPTS } from "@/lib/question-bank-hub-search"
15
+ DEFAULT_LIBRARY_FOLDERS,
16
+ type LibraryFolder,
17
+ type LibraryFolderColorKey,
18
+ } from "@/lib/mock/library-folders"
19
+ import { LIBRARY_ITEMS, type LibraryItem } from "@/lib/mock/library"
20
+ import { LIBRARY_HUB_ASK_LEO_PROMPTS } from "@/lib/library-hub-search"
21
21
  import {
22
- QUESTION_BANK_LIBRARY_PATH,
23
- QUESTION_BANK_NAV_MY_AUTHOR,
24
- questionBankNavHref,
25
- } from "@/lib/question-bank-nav"
22
+ LIBRARY_ALL_PATH,
23
+ LIBRARY_NAV_MY_AUTHOR,
24
+ libraryNavHref,
25
+ } from "@/lib/library-nav"
26
26
  import { cn } from "@/lib/utils"
27
27
 
28
- const NEW_QUESTION_AUTHORING_PATH = "/question-bank/new"
28
+ const NEW_QUESTION_AUTHORING_PATH = "/library/new"
29
29
 
30
30
  const DRAFT_WITH_LEO_PROMPT =
31
- "Help me draft a new assessment question. Ask me for the topic, item type (NBME single best answer / NCLEX SATA / vignette / EMQ / T/F / short answer), Bloom level, and discipline, then propose stem, lead-in, answer options, and rationale I can paste into the authoring composer."
31
+ "Help me draft a new library item. Ask me for the category, item type (single choice / multi-select / true-false / short answer), tier, and track, then propose name, prompt, answer options, and notes I can paste into the composer."
32
32
 
33
33
  const TEMPLATE_PROMPT =
34
- "Walk me through choosing a question template (MCQ, OSCE, short answer, true/false) and produce a starter item with stem, options, rationale, and tags."
34
+ "Walk me through choosing an item template (single choice, multi-select, short answer, true / false) and produce a starter item with name, options, notes, and tags."
35
35
 
36
36
  const IMPORT_PROMPT =
37
37
  "Guide me through importing assessment questions in bulk. Ask about source format (CSV, QTI, copy/paste), then outline what columns and mappings I need."
38
38
 
39
39
  /** Rotating example queries — read like something a user would actually type into search. */
40
40
  const HUB_COMPOSER_PLACEHOLDERS = [
41
- "all the questions I used in last year's assessment",
42
- "what did we run for the spring OSCE checkout?",
43
- "Clinical folder, cardiology tagged, for third-year summative",
44
- "find QB-2024-0012 and anything like it",
45
- "that anemia vignette from the shelf-style block",
46
- "MCQs I still need before next week's diabetes station",
41
+ "items tagged with Tag 1 and Category 2",
42
+ "everything Owner A edited this month",
43
+ "Folder 1 entries marked High",
44
+ "find LIB-2026-001 and anything like it",
45
+ "drafts from the most recent reference set",
46
+ "Type 1 items I still need for the demo block",
47
47
  ] as const
48
48
 
49
49
  interface ScopeChip {
@@ -52,7 +52,7 @@ interface ScopeChip {
52
52
  href: string
53
53
  count: number
54
54
  folderGlyph: {
55
- colorKey: QuestionBankFolderColorKey
55
+ colorKey: LibraryFolderColorKey
56
56
  icon: string
57
57
  variant?: "solid" | "outline"
58
58
  }
@@ -69,33 +69,33 @@ interface CreateTile {
69
69
  shortcutKeys?: string
70
70
  }
71
71
 
72
- interface FolderTile extends QuestionBankFolder {
72
+ interface FolderTile extends LibraryFolder {
73
73
  count: number
74
74
  }
75
75
 
76
- function buildScopeChips(items: QuestionBankItem[]): ScopeChip[] {
76
+ function buildScopeChips(items: LibraryItem[]): ScopeChip[] {
77
77
  const mine = items.filter(
78
- i => i.author === QUESTION_BANK_NAV_MY_AUTHOR || i.createdBy === QUESTION_BANK_NAV_MY_AUTHOR,
78
+ i => i.author === LIBRARY_NAV_MY_AUTHOR || i.createdBy === LIBRARY_NAV_MY_AUTHOR,
79
79
  ).length
80
80
  return [
81
81
  {
82
82
  id: "all",
83
83
  label: "All",
84
84
  count: items.length,
85
- href: QUESTION_BANK_LIBRARY_PATH,
85
+ href: LIBRARY_ALL_PATH,
86
86
  folderGlyph: { colorKey: "muted", icon: "fa-layer-group", variant: "outline" },
87
87
  },
88
88
  {
89
89
  id: "my",
90
90
  label: "Mine",
91
91
  count: mine,
92
- href: questionBankNavHref({ scope: "my" }),
92
+ href: libraryNavHref({ scope: "my" }),
93
93
  folderGlyph: { colorKey: "brand", icon: "fa-user" },
94
94
  },
95
95
  ]
96
96
  }
97
97
 
98
- function buildFolderTiles(items: QuestionBankItem[], folders: QuestionBankFolder[]): FolderTile[] {
98
+ function buildFolderTiles(items: LibraryItem[], folders: LibraryFolder[]): FolderTile[] {
99
99
  const byFolder = new Map<string, number>()
100
100
  for (const i of items) byFolder.set(i.folderId, (byFolder.get(i.folderId) ?? 0) + 1)
101
101
  return folders
@@ -105,24 +105,24 @@ function buildFolderTiles(items: QuestionBankItem[], folders: QuestionBankFolder
105
105
 
106
106
  // Static derivations of immutable mock data — computed once at module load,
107
107
  // not per render of the hub. Re-derive only if the underlying mock arrays change.
108
- const HUB_SCOPES = buildScopeChips(QUESTION_BANK_ITEMS)
109
- const HUB_FOLDER_TILES = buildFolderTiles(QUESTION_BANK_ITEMS, DEFAULT_QUESTION_BANK_FOLDERS)
108
+ const HUB_SCOPES = buildScopeChips(LIBRARY_ITEMS)
109
+ const HUB_FOLDER_TILES = buildFolderTiles(LIBRARY_ITEMS, DEFAULT_LIBRARY_FOLDERS)
110
110
 
111
- function isMineQuestionBankItem(i: QuestionBankItem): boolean {
112
- return i.author === QUESTION_BANK_NAV_MY_AUTHOR || i.createdBy === QUESTION_BANK_NAV_MY_AUTHOR
111
+ function isMineLibraryItem(i: LibraryItem): boolean {
112
+ return i.author === LIBRARY_NAV_MY_AUTHOR || i.createdBy === LIBRARY_NAV_MY_AUTHOR
113
113
  }
114
114
 
115
- const HUB_MINE_RECENTS = [...QUESTION_BANK_ITEMS]
116
- .filter(isMineQuestionBankItem)
115
+ const HUB_MINE_RECENTS = [...LIBRARY_ITEMS]
116
+ .filter(isMineLibraryItem)
117
117
  .sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1))
118
118
  .slice(0, 3)
119
119
 
120
120
  const HUB_ASK_LEO_PAGE_CONTEXT = {
121
121
  title: "Question hub",
122
122
  description:
123
- "Browse and organize assessment items with AI-assisted workflows. The hub search field opens discovery results on `/question-bank/find` with your wording applied to the list; use the library’s Search in the sidebar for `/question-bank/list`. Pick a suggestion below when you want a full Ask Leo thread.",
124
- suggestions: [...QUESTION_BANK_HUB_ASK_LEO_PROMPTS],
125
- data: { surface: "question-bank-discovery-hub" as const },
123
+ "Browse and organize assessment items with AI-assisted workflows. The hub search field opens discovery results on `/library/find` with your wording applied to the list; use the library’s Search in the sidebar for `/library/list`. Pick a suggestion below when you want a full Ask Leo thread.",
124
+ suggestions: [...LIBRARY_HUB_ASK_LEO_PROMPTS],
125
+ data: { surface: "library-discovery-hub" as const },
126
126
  }
127
127
 
128
128
  function formatRelativeDate(iso: string): string {
@@ -138,7 +138,7 @@ function formatRelativeDate(iso: string): string {
138
138
  return `${Math.round(diffDays / 365)}y ago`
139
139
  }
140
140
 
141
- export function QuestionBankHubClient() {
141
+ export function LibraryHubClient() {
142
142
  const router = useRouter()
143
143
  const { openWithPrompt } = useAskLeo()
144
144
  const { setOpen: setMainSidebarOpen } = useSidebar()
@@ -162,7 +162,7 @@ export function QuestionBankHubClient() {
162
162
  )
163
163
 
164
164
  /**
165
- * Navigate to the full-page authoring composer (`/question-bank/new`).
165
+ * Navigate to the full-page authoring composer (`/library/new`).
166
166
  * Mirrors the Placements "New placement" pre-collapse: animates the sidebar
167
167
  * closed first so the user sees one smooth transition into the focused flow
168
168
  * (the route also mounts `SidebarAutoCollapse` to lock it shut while there).
@@ -178,7 +178,7 @@ export function QuestionBankHubClient() {
178
178
 
179
179
  const onHubComposerSubmit = React.useCallback(
180
180
  (message: string) => {
181
- router.push(questionBankNavHref({ scope: "all", q: message.trim(), hubFind: true }))
181
+ router.push(libraryNavHref({ scope: "all", q: message.trim(), hubFind: true }))
182
182
  },
183
183
  [router],
184
184
  )
@@ -214,7 +214,7 @@ export function QuestionBankHubClient() {
214
214
  {
215
215
  id: "template",
216
216
  label: "From template",
217
- description: "Pick MCQ, OSCE, short answer or true/false — Leo fills the scaffold.",
217
+ description: "Pick choice-style, multi-select, short answer or true / false — Leo fills the scaffold.",
218
218
  icon: "fa-clone",
219
219
  iconTint: "bg-sky-500/15 text-sky-700 dark:text-sky-300",
220
220
  onClick: () => sendLeoSuggestion(TEMPLATE_PROMPT),
@@ -334,7 +334,7 @@ export function QuestionBankHubClient() {
334
334
  Continue where you left off
335
335
  </h2>
336
336
  <Link
337
- href={questionBankNavHref({ scope: "my" })}
337
+ href={libraryNavHref({ scope: "my" })}
338
338
  className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
339
339
  >
340
340
  View all
@@ -345,7 +345,7 @@ export function QuestionBankHubClient() {
345
345
  {recents.map(item => (
346
346
  <li key={item.id}>
347
347
  <Link
348
- href={questionBankNavHref({ scope: "my" })}
348
+ href={libraryNavHref({ scope: "my" })}
349
349
  className="group flex h-full flex-col gap-3 rounded-xl border border-border bg-card p-4 transition hover:border-interactive-hover hover:bg-interactive-hover/30 hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
350
350
  >
351
351
  <div className="flex items-center gap-2">
@@ -371,7 +371,7 @@ export function QuestionBankHubClient() {
371
371
  Browse the library
372
372
  </h2>
373
373
  <Link
374
- href={QUESTION_BANK_LIBRARY_PATH}
374
+ href={LIBRARY_ALL_PATH}
375
375
  className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
376
376
  >
377
377
  Open full library
@@ -406,7 +406,7 @@ export function QuestionBankHubClient() {
406
406
  {folderTiles.map(f => (
407
407
  <div key={f.id} role="listitem" className="min-w-0">
408
408
  <Link
409
- href={questionBankNavHref({ scope: "folder", folderId: f.id })}
409
+ href={libraryNavHref({ scope: "folder", folderId: f.id })}
410
410
  className={hubFolderBrowserTileClass}
411
411
  >
412
412
  <OsFolderGlyph colorKey={f.colorKey} icon={f.icon} size="md" />
@@ -14,13 +14,13 @@ import { Tip } from "@/components/ui/tip"
14
14
  import { Kbd, KbdGroup } from "@/components/ui/kbd"
15
15
  import { cn } from "@/lib/utils"
16
16
  import {
17
- QUESTION_BANK_FOLDER_COLOR_STYLES,
18
- QUESTION_BANK_FOLDER_ICON_OPTIONS,
19
- type QuestionBankFolderColorKey,
20
- } from "@/lib/mock/question-bank-folders"
17
+ LIBRARY_FOLDER_COLOR_STYLES,
18
+ LIBRARY_FOLDER_ICON_OPTIONS,
19
+ type LibraryFolderColorKey,
20
+ } from "@/lib/mock/library-folders"
21
21
  import { OsFolderGlyph } from "@/components/data-views/os-folder-glyph"
22
22
 
23
- const COLOR_OPTIONS: QuestionBankFolderColorKey[] = [
23
+ const COLOR_OPTIONS: LibraryFolderColorKey[] = [
24
24
  "brand",
25
25
  "success",
26
26
  "warning",
@@ -38,7 +38,7 @@ function FolderTilePreview({
38
38
  className,
39
39
  }: {
40
40
  name: string
41
- colorKey: QuestionBankFolderColorKey
41
+ colorKey: LibraryFolderColorKey
42
42
  icon: string
43
43
  className?: string
44
44
  }) {
@@ -64,7 +64,7 @@ function FolderTilePreview({
64
64
  )
65
65
  }
66
66
 
67
- export interface QuestionBankNewFolderSheetProps {
67
+ export interface LibraryNewFolderSheetProps {
68
68
  open: boolean
69
69
  onOpenChange: (open: boolean) => void
70
70
  /** Parent folder id for the new folder (`null` = top level). */
@@ -75,28 +75,28 @@ export interface QuestionBankNewFolderSheetProps {
75
75
  customizingFolder?: {
76
76
  name: string
77
77
  icon: string
78
- colorKey: QuestionBankFolderColorKey
78
+ colorKey: LibraryFolderColorKey
79
79
  parentId: string | null
80
80
  } | null
81
81
  onCreated: (folder: {
82
82
  name: string
83
83
  icon: string
84
- colorKey: QuestionBankFolderColorKey
84
+ colorKey: LibraryFolderColorKey
85
85
  parentId: string | null
86
86
  }) => void
87
87
  }
88
88
 
89
- export function QuestionBankNewFolderSheet({
89
+ export function LibraryNewFolderSheet({
90
90
  open,
91
91
  onOpenChange,
92
92
  parentFolderId,
93
93
  customizingFolder,
94
94
  descriptionText = "Name, color, and icon update the preview. The folder is created in the location shown in the breadcrumb above the grid.",
95
95
  onCreated,
96
- }: QuestionBankNewFolderSheetProps) {
96
+ }: LibraryNewFolderSheetProps) {
97
97
  const [draft, setDraft] = React.useState<{
98
98
  name: string
99
- colorKey: QuestionBankFolderColorKey
99
+ colorKey: LibraryFolderColorKey
100
100
  icon: string
101
101
  }>({ name: "Untitled", colorKey: "brand", icon: "fa-folder" })
102
102
 
@@ -200,7 +200,7 @@ export function QuestionBankNewFolderSheet({
200
200
  aria-pressed={draft.colorKey === c}
201
201
  className={cn(
202
202
  "size-10 rounded-xl border-2 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
203
- QUESTION_BANK_FOLDER_COLOR_STYLES[c].tile,
203
+ LIBRARY_FOLDER_COLOR_STYLES[c].tile,
204
204
  draft.colorKey === c
205
205
  ? "ring-2 ring-ring"
206
206
  : "border-transparent opacity-85 hover:opacity-100",
@@ -214,7 +214,7 @@ export function QuestionBankNewFolderSheet({
214
214
  <div>
215
215
  <p className="mb-2 text-xs font-medium text-muted-foreground">Icon</p>
216
216
  <div className="grid max-h-48 grid-cols-5 gap-2 overflow-y-auto rounded-xl border border-border p-3">
217
- {QUESTION_BANK_FOLDER_ICON_OPTIONS.map(ic => (
217
+ {LIBRARY_FOLDER_ICON_OPTIONS.map(ic => (
218
218
  <button
219
219
  key={ic}
220
220
  type="button"
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  /**
4
- * OS-style icon folder view for Question bank — hierarchy, appearance (color + icon),
4
+ * OS-style icon folder view for Library — hierarchy, appearance (color + icon),
5
5
  * create (floating sheet drawer like `ExportDrawer` + preview), inline rename, move / delete, and move questions between folders.
6
6
  */
7
7
 
@@ -29,24 +29,24 @@ import { Input } from "@/components/ui/input"
29
29
  import { Label } from "@/components/ui/label"
30
30
  import { Tip } from "@/components/ui/tip"
31
31
  import { cn } from "@/lib/utils"
32
- import type { QuestionBankItem } from "@/lib/mock/question-bank"
32
+ import type { LibraryItem } from "@/lib/mock/library"
33
33
  import {
34
34
  collectFolderDescendantIds,
35
35
  isValidFolderMove,
36
36
  newFolderId,
37
- QUESTION_BANK_FOLDER_COLOR_STYLES,
38
- QUESTION_BANK_FOLDER_ICON_OPTIONS,
39
- type QuestionBankFolder,
40
- type QuestionBankFolderColorKey,
41
- } from "@/lib/mock/question-bank-folders"
37
+ LIBRARY_FOLDER_COLOR_STYLES,
38
+ LIBRARY_FOLDER_ICON_OPTIONS,
39
+ type LibraryFolder,
40
+ type LibraryFolderColorKey,
41
+ } from "@/lib/mock/library-folders"
42
42
  import {
43
43
  ListPageViewFrame,
44
44
  LIST_PAGE_VIEW_FRAME_MAX_WIDE,
45
45
  } from "@/components/data-views/list-page-view-frame"
46
46
  import { OsFolderGlyph } from "@/components/data-views/os-folder-glyph"
47
- import { QuestionBankNewFolderSheet } from "@/components/question-bank-new-folder-sheet"
47
+ import { LibraryNewFolderSheet } from "@/components/library-new-folder-sheet"
48
48
 
49
- const COLOR_OPTIONS: QuestionBankFolderColorKey[] = [
49
+ const COLOR_OPTIONS: LibraryFolderColorKey[] = [
50
50
  "brand",
51
51
  "success",
52
52
  "warning",
@@ -57,17 +57,17 @@ const COLOR_OPTIONS: QuestionBankFolderColorKey[] = [
57
57
  "chart3",
58
58
  ]
59
59
 
60
- export interface QuestionBankOsFolderViewProps {
61
- folders: QuestionBankFolder[]
62
- onFoldersChange: React.Dispatch<React.SetStateAction<QuestionBankFolder[]>>
63
- questions: QuestionBankItem[]
64
- onQuestionsChange: React.Dispatch<React.SetStateAction<QuestionBankItem[]>>
60
+ export interface LibraryOsFolderViewProps {
61
+ folders: LibraryFolder[]
62
+ onFoldersChange: React.Dispatch<React.SetStateAction<LibraryFolder[]>>
63
+ questions: LibraryItem[]
64
+ onQuestionsChange: React.Dispatch<React.SetStateAction<LibraryItem[]>>
65
65
  }
66
66
 
67
- function folderTrail(folders: QuestionBankFolder[], folderId: string | null): QuestionBankFolder[] {
67
+ function folderTrail(folders: LibraryFolder[], folderId: string | null): LibraryFolder[] {
68
68
  if (!folderId) return []
69
69
  const byId = new Map(folders.map(f => [f.id, f]))
70
- const trail: QuestionBankFolder[] = []
70
+ const trail: LibraryFolder[] = []
71
71
  let cur: string | null = folderId
72
72
  while (cur) {
73
73
  const f = byId.get(cur)
@@ -79,9 +79,9 @@ function folderTrail(folders: QuestionBankFolder[], folderId: string | null): Qu
79
79
  }
80
80
 
81
81
  function folderHoverCounts(
82
- folder: QuestionBankFolder,
83
- folders: QuestionBankFolder[],
84
- questions: QuestionBankItem[],
82
+ folder: LibraryFolder,
83
+ folders: LibraryFolder[],
84
+ questions: LibraryItem[],
85
85
  ) {
86
86
  const subfolders = folders.filter(f => f.parentId === folder.id).length
87
87
  const questionsInFolder = questions.filter(q => q.folderId === folder.id).length
@@ -89,10 +89,10 @@ function folderHoverCounts(
89
89
  }
90
90
 
91
91
  function validMoveTargets(
92
- folders: QuestionBankFolder[],
92
+ folders: LibraryFolder[],
93
93
  movingId: string,
94
94
  ): Array<{ id: string | null; label: string }> {
95
- const out: Array<{ id: string | null; label: string }> = [{ id: null, label: "Question bank (root)" }]
95
+ const out: Array<{ id: string | null; label: string }> = [{ id: null, label: "Library (root)" }]
96
96
  for (const f of folders) {
97
97
  if (f.id === movingId) continue
98
98
  if (!isValidFolderMove(folders, movingId, f.id)) continue
@@ -101,12 +101,12 @@ function validMoveTargets(
101
101
  return out
102
102
  }
103
103
 
104
- export function QuestionBankOsFolderView({
104
+ export function LibraryOsFolderView({
105
105
  folders,
106
106
  onFoldersChange,
107
107
  questions,
108
108
  onQuestionsChange,
109
- }: QuestionBankOsFolderViewProps) {
109
+ }: LibraryOsFolderViewProps) {
110
110
  const [currentId, setCurrentId] = React.useState<string | null>(null)
111
111
 
112
112
  const childFolders = React.useMemo(
@@ -123,13 +123,13 @@ export function QuestionBankOsFolderView({
123
123
 
124
124
  const [createFolderOpen, setCreateFolderOpen] = React.useState(false)
125
125
  const [customizeFolderOpen, setCustomizeFolderOpen] = React.useState(false)
126
- const [customizingFolder, setCustomizingFolder] = React.useState<QuestionBankFolder | null>(null)
126
+ const [customizingFolder, setCustomizingFolder] = React.useState<LibraryFolder | null>(null)
127
127
 
128
128
  const [renamingFolderId, setRenamingFolderId] = React.useState<string | null>(null)
129
129
  const [renameValue, setRenameValue] = React.useState("")
130
130
  const renameInputRef = React.useRef<HTMLInputElement>(null)
131
131
 
132
- const [appearanceDialog, setAppearanceDialog] = React.useState<QuestionBankFolder | null>(null)
132
+ const [appearanceDialog, setAppearanceDialog] = React.useState<LibraryFolder | null>(null)
133
133
  const [moveFolderId, setMoveFolderId] = React.useState<string | null>(null)
134
134
  const [deleteFolderId, setDeleteFolderId] = React.useState<string | null>(null)
135
135
 
@@ -144,7 +144,7 @@ export function QuestionBankOsFolderView({
144
144
  setCreateFolderOpen(true)
145
145
  }
146
146
 
147
- function startRename(folder: QuestionBankFolder) {
147
+ function startRename(folder: LibraryFolder) {
148
148
  setRenamingFolderId(folder.id)
149
149
  setRenameValue(folder.name)
150
150
  }
@@ -223,7 +223,7 @@ export function QuestionBankOsFolderView({
223
223
  onClick={() => setCurrentId(null)}
224
224
  className="font-sans text-sm text-muted-foreground hover:text-interactive-hover-foreground transition-colors tracking-normal"
225
225
  >
226
- Question bank
226
+ Library
227
227
  </button>
228
228
  {trail.length > 0 && (
229
229
  <i className="fa-light fa-chevron-right text-xs text-muted-foreground/50" aria-hidden="true" />
@@ -474,7 +474,7 @@ export function QuestionBankOsFolderView({
474
474
  </p>
475
475
  )}
476
476
 
477
- <QuestionBankNewFolderSheet
477
+ <LibraryNewFolderSheet
478
478
  open={createFolderOpen}
479
479
  onOpenChange={setCreateFolderOpen}
480
480
  parentFolderId={currentId}
@@ -484,7 +484,7 @@ export function QuestionBankOsFolderView({
484
484
  />
485
485
 
486
486
  {/* Customize folder */}
487
- <QuestionBankNewFolderSheet
487
+ <LibraryNewFolderSheet
488
488
  open={customizeFolderOpen}
489
489
  onOpenChange={setCustomizeFolderOpen}
490
490
  parentFolderId={customizingFolder?.parentId ?? null}
@@ -531,7 +531,7 @@ export function QuestionBankOsFolderView({
531
531
  aria-pressed={appearanceDialog.colorKey === c}
532
532
  className={cn(
533
533
  "size-9 rounded-lg border-2 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
534
- QUESTION_BANK_FOLDER_COLOR_STYLES[c].tile,
534
+ LIBRARY_FOLDER_COLOR_STYLES[c].tile,
535
535
  appearanceDialog.colorKey === c ? "ring-2 ring-ring" : "border-transparent opacity-80 hover:opacity-100",
536
536
  )}
537
537
  onClick={() =>
@@ -548,7 +548,7 @@ export function QuestionBankOsFolderView({
548
548
  <div>
549
549
  <p className="mb-2 text-xs font-medium text-muted-foreground">Icon</p>
550
550
  <div className="grid max-h-40 grid-cols-5 gap-2 overflow-y-auto rounded-md border border-border p-2">
551
- {QUESTION_BANK_FOLDER_ICON_OPTIONS.map(ic => (
551
+ {LIBRARY_FOLDER_ICON_OPTIONS.map(ic => (
552
552
  <button
553
553
  key={ic}
554
554
  type="button"
@@ -15,9 +15,9 @@ import {
15
15
  } from "@/components/ui/dropdown-menu"
16
16
  import { Tip } from "@/components/ui/tip"
17
17
  import { COLLABORATION_HEADER_ADD_LABEL } from "@/lib/collaborator-access"
18
- import { QUESTION_BANK_HEADER_COLLABORATORS } from "@/lib/mock/question-bank-header-collaborators"
18
+ import { LIBRARY_HEADER_COLLABORATORS } from "@/lib/mock/library-header-collaborators"
19
19
 
20
- export interface QuestionBankPageHeaderProps {
20
+ export interface LibraryPageHeaderProps {
21
21
  /** Scoped hub title (All / My / folder name) — keep in sync with `SiteHeader`. */
22
22
  title: string
23
23
  questionCount: number
@@ -51,7 +51,7 @@ export interface QuestionBankPageHeaderProps {
51
51
  onCustomizeFolder?: () => void
52
52
  }
53
53
 
54
- export function QuestionBankPageHeader({
54
+ export function LibraryPageHeader({
55
55
  title,
56
56
  questionCount,
57
57
  onNewQuestion,
@@ -61,7 +61,7 @@ export function QuestionBankPageHeader({
61
61
  showTitleBlock = true,
62
62
  variant = "default",
63
63
  accessInfo,
64
- collaborators = QUESTION_BANK_HEADER_COLLABORATORS,
64
+ collaborators = LIBRARY_HEADER_COLLABORATORS,
65
65
  collaboratorDisplayLimit = 3,
66
66
  onAddCollaborator = () => {},
67
67
  onCollaboratorsOpen,
@@ -71,7 +71,7 @@ export function QuestionBankPageHeader({
71
71
  subtitleOverride,
72
72
  hideActions = false,
73
73
  onCustomizeFolder,
74
- }: QuestionBankPageHeaderProps) {
74
+ }: LibraryPageHeaderProps) {
75
75
  const [moreOpen, setMoreOpen] = React.useState(false)
76
76
  const countLine =
77
77
  subtitleOverride ??
@@ -91,7 +91,7 @@ export function QuestionBankPageHeader({
91
91
  showTitleBlock={showTitleBlock}
92
92
  actions={
93
93
  hideActions ? undefined : (
94
- <div className="flex items-center gap-2" role="group" aria-label="Question bank actions">
94
+ <div className="flex items-center gap-2" role="group" aria-label="Library actions">
95
95
  {!hideNewQuestion ? (
96
96
  <Tip side="bottom" label="Create a new question (demo)">
97
97
  <Button type="button" size="lg" onClick={onNewQuestion}>
@@ -0,0 +1,8 @@
1
+ "use client"
2
+
3
+ import { SecondaryPanelHubActivator } from "@/components/templates/secondary-panel-hub-template"
4
+
5
+ /** Opens the Library secondary panel while this route is mounted. */
6
+ export function LibraryPanelActivator() {
7
+ return <SecondaryPanelHubActivator panelId="library" />
8
+ }