@exxatdesignux/ui 0.3.0 → 0.4.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 (210) hide show
  1. package/CHANGELOG.md +608 -6
  2. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +1 -1
  3. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +2 -2
  4. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -1
  5. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +2 -0
  6. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +1 -1
  7. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +3 -3
  8. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  9. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +1 -1
  10. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -1
  11. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +6 -6
  12. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -1
  13. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +2 -2
  14. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +1 -1
  15. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  16. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  17. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +7 -7
  18. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  19. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  20. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  21. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +8 -8
  22. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  23. package/consumer-extras/handbook/HANDBOOK.md +2 -0
  24. package/consumer-extras/handbook/glossary.md +2 -1
  25. package/consumer-extras/handbook/reference-implementations.md +31 -4
  26. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  27. package/consumer-extras/patterns/data-views-pattern.md +18 -16
  28. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  29. package/dist/components/data-table/index.js +2 -2
  30. package/dist/components/data-table/index.js.map +1 -1
  31. package/dist/components/data-table/pagination.js +3 -3
  32. package/dist/components/data-table/pagination.js.map +1 -1
  33. package/dist/components/data-table/use-table-state.d.ts +1 -1
  34. package/dist/components/data-table/use-table-state.js.map +1 -1
  35. package/dist/components/data-views/data-row-list.js.map +1 -1
  36. package/dist/components/data-views/finder-panel-view.d.ts +1 -1
  37. package/dist/components/data-views/finder-panel-view.js.map +1 -1
  38. package/dist/components/data-views/hub-table.d.ts +9 -3
  39. package/dist/components/data-views/hub-table.js +262 -40
  40. package/dist/components/data-views/hub-table.js.map +1 -1
  41. package/dist/components/data-views/index.js +262 -40
  42. package/dist/components/data-views/index.js.map +1 -1
  43. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +2 -2
  44. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -1
  45. package/dist/components/data-views/list-page-tree-column-header.d.ts +1 -1
  46. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -1
  47. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -1
  48. package/dist/components/data-views/os-folder-glyph.d.ts +1 -1
  49. package/dist/components/data-views/os-folder-glyph.js.map +1 -1
  50. package/dist/components/ui/avatar.d.ts +1 -1
  51. package/dist/components/ui/banner.d.ts +2 -2
  52. package/dist/components/ui/key-metrics.js.map +1 -1
  53. package/dist/index.js +136 -39
  54. package/dist/index.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/components/data-table/index.tsx +2 -2
  57. package/src/components/data-table/pagination.tsx +5 -1
  58. package/src/components/data-table/use-table-state.ts +1 -1
  59. package/src/components/data-views/data-row-list.tsx +1 -1
  60. package/src/components/data-views/finder-panel-view.tsx +2 -2
  61. package/src/components/data-views/hub-table.tsx +149 -41
  62. package/src/components/data-views/list-page-split-hub-tokens.ts +2 -2
  63. package/src/components/data-views/list-page-tree-column-header.tsx +1 -1
  64. package/src/components/data-views/os-folder-glyph.tsx +1 -1
  65. package/src/components/ui/key-metrics.tsx +1 -1
  66. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  67. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  68. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  69. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +6 -6
  70. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  71. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +5 -5
  72. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  73. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  74. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  75. package/template/AGENTS.md +43 -37
  76. package/template/app/(app)/columns/page.tsx +11 -0
  77. package/template/app/(app)/library/all/page.tsx +11 -0
  78. package/template/app/(app)/library/find/page.tsx +12 -0
  79. package/template/app/(app)/{question-bank → library}/layout.tsx +16 -16
  80. package/template/app/(app)/library/list/page.tsx +12 -0
  81. package/template/app/(app)/{question-bank → library}/new/page.tsx +10 -10
  82. package/template/app/(app)/library/page.tsx +11 -0
  83. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  84. package/template/components/ask-leo-composer.tsx +2 -2
  85. package/template/components/columns-client.tsx +158 -0
  86. package/template/components/columns-showcase.tsx +541 -0
  87. package/template/components/data-views/index.ts +32 -6
  88. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  89. package/template/components/data-views/table-cells.tsx +673 -0
  90. package/template/components/folder-details-shell.tsx +11 -11
  91. package/template/components/hub-tree-panel-view.tsx +24 -24
  92. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  93. package/template/components/{question-bank-client.tsx → library-client.tsx} +82 -82
  94. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  95. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  96. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +43 -43
  97. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +14 -14
  98. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  99. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  100. package/template/components/library-panel-activator.tsx +8 -0
  101. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +60 -60
  102. package/template/components/{question-bank-table.tsx → library-table.tsx} +97 -97
  103. package/template/components/list-hub-status-badge.tsx +2 -2
  104. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +37 -37
  105. package/template/components/sidebar/app-sidebar.tsx +61 -5
  106. package/template/components/sidebar/secondary-panel.tsx +109 -56
  107. package/template/components/sidebar/sidebar-auto-collapse.tsx +2 -2
  108. package/template/components/sidebar/sidebar-auto-open.tsx +2 -1
  109. package/template/components/table-properties/types.ts +1 -1
  110. package/template/components/templates/discovery-hub-template.tsx +1 -1
  111. package/template/components/templates/new-focus-template.tsx +2 -2
  112. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  113. package/template/components/tokens-secondary-nav.tsx +192 -0
  114. package/template/components/tokens-themes-client.tsx +476 -0
  115. package/template/components/tokens-themes-section.tsx +386 -0
  116. package/template/docs/HANDBOOK.md +187 -0
  117. package/template/docs/blueprints/README.md +1 -1
  118. package/template/docs/blueprints/board-card.md +1 -1
  119. package/template/docs/blueprints/data-table.md +2 -2
  120. package/template/docs/blueprints/list-page-template.md +3 -3
  121. package/template/docs/blueprints/page-header.md +4 -4
  122. package/template/docs/collaboration-access-pattern.md +7 -7
  123. package/template/docs/component-selection-guide.md +1 -1
  124. package/template/docs/data-views-pattern.md +18 -16
  125. package/template/docs/glossary.md +58 -0
  126. package/template/docs/kpi-flat-band-pattern.md +3 -3
  127. package/template/docs/kpi-trend-pattern.md +18 -3
  128. package/template/docs/large-dataset-strategy.md +155 -0
  129. package/template/docs/library-hub-header-pattern.md +25 -0
  130. package/template/docs/migrations/_template.md +1 -1
  131. package/template/docs/reference-implementations.md +151 -0
  132. package/template/docs/token-taxonomy.md +1 -1
  133. package/template/docs/voice-and-tone.md +262 -0
  134. package/template/hooks/use-secondary-panel-hub-nav.ts +10 -10
  135. package/template/lib/ask-leo-route-context.ts +6 -18
  136. package/template/lib/coach-mark-registry.ts +0 -16
  137. package/template/lib/command-menu-config.ts +5 -12
  138. package/template/lib/command-menu-search-data.ts +8 -39
  139. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  140. package/template/lib/library-dedicated-search.ts +19 -0
  141. package/template/lib/library-hub-search.ts +90 -0
  142. package/template/lib/library-nav.ts +477 -0
  143. package/template/lib/library-recent-searches.ts +22 -0
  144. package/template/lib/{placements-supported-views.ts → library-supported-views.ts} +2 -2
  145. package/template/lib/list-status-badges.ts +16 -104
  146. package/template/lib/mock/dashboard.ts +1 -1
  147. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  148. package/template/lib/mock/library-header-collaborators.ts +54 -0
  149. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  150. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  151. package/template/lib/mock/library.ts +249 -0
  152. package/template/lib/mock/navigation.tsx +32 -26
  153. package/template/lib/table-state-lifecycle.ts +1 -1
  154. package/template/next.config.mjs +7 -4
  155. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +0 -28
  156. package/template/app/(app)/examples/page.tsx +0 -41
  157. package/template/app/(app)/question-bank/find/page.tsx +0 -12
  158. package/template/app/(app)/question-bank/library/page.tsx +0 -11
  159. package/template/app/(app)/question-bank/list/page.tsx +0 -12
  160. package/template/app/(app)/question-bank/page.tsx +0 -11
  161. package/template/components/compliance-board-view.tsx +0 -142
  162. package/template/components/compliance-client.tsx +0 -92
  163. package/template/components/compliance-page-header.tsx +0 -89
  164. package/template/components/compliance-table.tsx +0 -468
  165. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  166. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  167. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  168. package/template/components/new-placement-back-btn.tsx +0 -28
  169. package/template/components/new-placement-form.tsx +0 -942
  170. package/template/components/placement-board-card.tsx +0 -250
  171. package/template/components/placement-detail.tsx +0 -438
  172. package/template/components/placements-board-view.tsx +0 -397
  173. package/template/components/placements-client.tsx +0 -220
  174. package/template/components/placements-list-view.tsx +0 -124
  175. package/template/components/placements-page-header.tsx +0 -166
  176. package/template/components/placements-table-cells.test.tsx +0 -22
  177. package/template/components/placements-table-cells.tsx +0 -173
  178. package/template/components/placements-table-columns.tsx +0 -210
  179. package/template/components/placements-table.tsx +0 -934
  180. package/template/components/question-bank-panel-activator.tsx +0 -8
  181. package/template/components/rotations-empty-state.tsx +0 -50
  182. package/template/components/rotations-panel-activator.tsx +0 -8
  183. package/template/components/sites-board-view.tsx +0 -67
  184. package/template/components/sites-client.tsx +0 -154
  185. package/template/components/sites-table.tsx +0 -249
  186. package/template/components/team-board-view.tsx +0 -122
  187. package/template/components/team-client.tsx +0 -100
  188. package/template/components/team-page-header.tsx +0 -92
  189. package/template/components/team-table.tsx +0 -553
  190. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  191. package/template/lib/compliance-supported-views.ts +0 -10
  192. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  193. package/template/lib/mock/compliance-kpi.ts +0 -61
  194. package/template/lib/mock/compliance.ts +0 -146
  195. package/template/lib/mock/placements-kpi.ts +0 -134
  196. package/template/lib/mock/placements.ts +0 -176
  197. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  198. package/template/lib/mock/question-bank.ts +0 -249
  199. package/template/lib/mock/sites-directory.ts +0 -16
  200. package/template/lib/mock/sites-kpi.ts +0 -25
  201. package/template/lib/mock/team-kpi.ts +0 -60
  202. package/template/lib/mock/team.ts +0 -118
  203. package/template/lib/placement-board-card-layout.ts +0 -79
  204. package/template/lib/question-bank-dedicated-search.ts +0 -19
  205. package/template/lib/question-bank-hub-search.ts +0 -90
  206. package/template/lib/question-bank-nav.ts +0 -477
  207. package/template/lib/question-bank-recent-searches.ts +0 -22
  208. package/template/lib/question-bank-supported-views.ts +0 -12
  209. package/template/lib/sites-supported-views.ts +0 -10
  210. package/template/lib/team-supported-views.ts +0 -10
@@ -11,7 +11,7 @@ alwaysApply: true
11
11
 
12
12
  1. **Shell** — Use **`ListPageBoardCard`** from **`components/data-views/list-page-board-card.tsx`** for product board cards (same **`Card` `size="sm"`** treatment as Placements).
13
13
  2. **Hierarchy** — **Title** (`ListPageBoardCardTitleRow`) → optional **avatar** (`ListPageBoardCardAvatar` on `trailing`) → **status row** (`ListPageBoardCardBadgeRow` + **`ListHubStatusBadge`** **`surface="board"`**) when the entity has status → **body** (`ListPageBoardCardBody`) with **`BoardCardTwoLineBlock`** / **`BoardCardIconRow`** (**`components/data-views/board-card-primitives.tsx`**).
14
- 3. **Status** — **Placements** (**`PLACEMENT_STATUS_*`** + **`StatusBadge`** in **`placements-table-cells.tsx`**), **Team / Compliance / Question bank** (**`ListHubStatusBadge`** + entity maps): all labels/tints/icons live in **`lib/list-status-badges.ts`**. **`surface="table"`** in grid/list rows, **`surface="board"`** on kanban cards. Prefer semantic **`LIST_HUB_STATUS_TINT_*`**. **MUST NOT** use **`uppercase`** on those chips.
14
+ 3. **Status** — **Placements** (**`PLACEMENT_STATUS_*`** + **`StatusBadge`** in **`placements-table-cells.tsx`**), **Team / Compliance / Library** (**`ListHubStatusBadge`** + entity maps): all labels/tints/icons live in **`lib/list-status-badges.ts`**. **`surface="table"`** in grid/list rows, **`surface="board"`** on kanban cards. Prefer semantic **`LIST_HUB_STATUS_TINT_*`**. **MUST NOT** use **`uppercase`** on those chips.
15
15
  4. **Simple column boards** — **`ListPageBoardTemplate`** + **`renderCard`**; compose **`ListPageBoardCard`** inside **`renderCard`**.
16
16
 
17
17
  ## MUST NOT
@@ -27,14 +27,14 @@ alwaysApply: true
27
27
 
28
28
  8. **`ListPageViewFrame`** — Non-**`DataTable`** view bodies (**folder**, **panel**, icon grids, comparable dashboard sections) **MUST** use **`ListPageViewFrame`** (and exported max-width constants) instead of copy-pasted **`mx-*` / `max-w-*`** per hub (**`exxat-list-page-view-shells.mdc`**).
29
29
 
30
- 9. **`components/data-views/`** — New **record-bearing** view layouts (**grids**, **OS folder**, **finder split**) **MUST** land as **generic** building blocks under **`data-views/`** with **`rows`**, **`getRowId`**, render props — hub **`TeamTable`** / **`QuestionBankTable`** **only** wires props and branch logic (**`AGENTS.md` §4.5**).
30
+ 9. **`components/data-views/`** — New **record-bearing** view layouts (**grids**, **OS folder**, **finder split**) **MUST** land as **generic** building blocks under **`data-views/`** with **`rows`**, **`getRowId`**, render props — hub **`TeamTable`** / **`LibraryTable`** **only** wires props and branch logic (**`AGENTS.md` §4.5**).
31
31
 
32
32
  10. **Hub client composition** — One **`*-client.tsx`** owns **`useTableState`**, passes **`tableState.rows`** into every **`viewType`** branch, and mounts **template** slots (**metrics**, **export**, **`beforeSiteHeader`**) — **MUST NOT** split the same hub across multiple clients with different row sources.
33
33
 
34
34
  ## MUST NOT
35
35
 
36
36
  - Ship alternate mock datasets per **`DataListViewType`** for the same hub without documenting them as **computed derivatives** of one canonical list.
37
- - Duplicate entity fields in inspector-only types that contradict **`QuestionBankItem`** / **`Placement`** / etc.; extend the shared interface **once**.
37
+ - Duplicate entity fields in inspector-only types that contradict **`LibraryItem`** / **`Placement`** / etc.; extend the shared interface **once**.
38
38
 
39
39
  ## See also
40
40
 
@@ -29,4 +29,4 @@ alwaysApply: false
29
29
 
30
30
  - **`exxat-page-vs-drawer.mdc`** — invite is a **sheet**, not a new route.
31
31
  - **`exxat-kbd-shortcuts.mdc`** — workflow **Cancel** / **Send invite** shortcuts on the sheet.
32
- - **`exxat-question-bank-hub-header.mdc`** — Question bank library: when URL is **folder-scoped**, **⋯ More** also includes **Customize folder** (hub client hosts **`QuestionBankNewFolderSheet`**).
32
+ - **`exxat-library-hub-header.mdc`** — Library library: when URL is **folder-scoped**, **⋯ More** also includes **Customize folder** (hub client hosts **`LibraryNewFolderSheet`**).
@@ -16,6 +16,7 @@ For **any app screen that shows a browsable, filterable grid of records** (lists
16
16
  5. **Filters:** Configure per-column `filter:` blocks in `ColumnDef` (text / select / date / number) — `HubTable` turns those into chips automatically via `columnsToFilterFields`. **MUST NOT** ship one-off filter inputs above the table that duplicate this.
17
17
  6. **Table properties:** Always reachable. `HubTable` mounts `TablePropertiesDrawerButton` in `toolbarSlot`; on **`ListPageTemplate`** pages with **table / list / board / dashboard** tabs it **MUST** also receive **`currentView`** and **`onViewChange`** (see **`apps/web/AGENTS.md` §4.2** and **`.cursor/rules/exxat-table-properties-drawer.mdc`**) so Properties matches the selected view.
18
18
  7. **Dropdown menus:** `DropdownMenuContent` uses the shared **`@exxatdesignux/ui`** default (**intrinsic `w-max`**, **`min-w-52`**, capped **`max-w`**) for view settings, row ⋯, column menus, and filter pickers — **pure CSS**, no **`ResizeObserver`**. Override only for deliberate narrow/wide rails (e.g. pagination **`w-20`**, account trigger-width, school switcher **`!w-max min-w-72 …`**). See **`docs/data-views-pattern.md`** (“Dropdown menus”).
19
+ 8. **Cell renderers MUST come from `@/components/data-views` (`table-cells.tsx`).** The DS ships **`ProgressCell`**, **`CurrencyCell`**, **`NumericCell`**, **`RatingCell`**, **`SignalBarsCell`**, **`BooleanToggleCell`**, **`AttachmentCountCell`**, **`ExternalLinkCell`**, **`RelativeTimeCell`**, **`PeopleAvatarRailCell`**, **`PillCell`**, **`TagListCell`**, and a generic **`RowActionsCell<TRow>`**. A `ColumnDef['cell']` for these patterns is a **one-liner** that calls the named cell. **MUST NOT** inline `Intl.NumberFormat`, raw `<a target="_blank">`, `[1,2,3,4,5].map(s => …)` star loops, paperclip + count chips, custom face-rail `AvatarGroup`s, or per-hub `DropdownMenu` overflow menus inside `cell:` — those are signals you're re-deriving a shipped primitive. Catalog: `apps/web/components/columns-showcase.tsx` (`/columns`). Skill: `.cursor/skills/exxat-token-economy/SKILL.md` §3.
19
20
 
20
21
  **Reference implementations:**
21
22
 
@@ -30,6 +31,7 @@ For **any app screen that shows a browsable, filterable grid of records** (lists
30
31
  - Do **not** build product list pages with `@/components/ui/table` alone, raw `<table>` markup, or third-party data grids.
31
32
  - Do **not** mount raw `<DataTable>` inside `ListPageTemplate.renderContent` — use `HubTable`. Raw `<DataTable>` does not ship the Properties drawer or filter chips; users lose discoverability.
32
33
  - Do **not** introduce a second “table component” pattern for the same product surfaces (splitting search/filters/properties across incompatible implementations).
34
+ - Do **not** inline-implement progress bars, currency formatters, rating stars, relative-time helpers, attachment chips, external-link wrappers, face rails, type pills, tag lists, or row-action dropdowns inside a `ColumnDef['cell']`. Import the named cell from `@/components/data-views` instead. New hub with novel cell needs MUST extend `table-cells.tsx` (and ship the catalog entry in `columns-showcase.tsx`), not fork inline JSX.
33
35
 
34
36
  ## Exceptions
35
37
 
@@ -18,7 +18,7 @@ Shared building blocks use **generic** `DedicatedSearch*` names under `component
18
18
 
19
19
  ## MUST NOT
20
20
 
21
- - Introduce parallel `*QuestionBankSearchLanding*` (or similar) components for another entity — extend the generic layer and compose in the hub client.
21
+ - Introduce parallel `*LibrarySearchLanding*` (or similar) components for another entity — extend the generic layer and compose in the hub client.
22
22
 
23
23
  ## See also
24
24
 
@@ -26,7 +26,7 @@ Before implementing or reviewing **list / table / board / dashboard / data-heavy
26
26
  11. **Centralized hub dataset + presentation** — **`apps/web/AGENTS.md` §4.1** / **§4.5** + **`.cursor/rules/exxat-centralized-list-dataset.mdc`** + **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** — one **`useTableState`** row bag for **all** views + **`TablePropertiesDrawer`**; **`ListPageViewFrame`** + **`data-views/`** for shared chrome; **no** parallel mock arrays per **`DataListViewType`**.
27
27
  12. **Font Awesome (product icons)** — **`.cursor/rules/exxat-fontawesome-icons.mdc`** — Kit in **`app/layout.tsx`**, **`fa-light` / `fa-solid`**, subset audit, **`aria-hidden`** on decorative **`<i>`**; pair with **`exxat-accessibility.mdc`** for icon-only controls.
28
28
  13. **Monospace system IDs** — **`apps/web/AGENTS.md` §1 (item 9)** + **`.cursor/rules/exxat-mono-ids.mdc`** + **`.cursor/skills/exxat-mono-ids/SKILL.md`** — **`font-mono tabular-nums`** on question/record keys; mono **only** the ID token in mixed lines.
29
- 14. **Primary nav → secondary panel** (Question bank) — **`apps/web/AGENTS.md` §4.6** + **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`** — **`secondaryPanel`** + **`PANELS`** + **`useAutoPanel`**; **`--secondary-panel-bg`** brand elevation (**`apps/web/docs/shell-surface-elevation-pattern.md`**); **folder URL scope** → **`exxat-question-bank-hub-header.mdc`** + **`apps/web/docs/question-bank-hub-header-pattern.md`**.
29
+ 14. **Primary nav → secondary panel** (Library) — **`apps/web/AGENTS.md` §4.6** + **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`** — **`secondaryPanel`** + **`PANELS`** + **`useAutoPanel`**; **`--secondary-panel-bg`** brand elevation (**`apps/web/docs/shell-surface-elevation-pattern.md`**); **folder URL scope** → **`exxat-library-hub-header.mdc`** + **`apps/web/docs/library-hub-header-pattern.md`**.
30
30
  15. **Collaboration & access** (shared hubs) — **`apps/web/AGENTS.md` §4.7** + **`.cursor/rules/exxat-collaboration-access.mdc`** + **`.cursor/skills/exxat-collaboration-access/SKILL.md`** — face rail, **`InviteCollaboratorsDrawer`**, **`lib/collaborator-access.ts`**.
31
31
  16. **Dedicated search** (landing vs results, `DedicatedSearch*`) — **`apps/web/AGENTS.md` §4.8** + **`.cursor/rules/exxat-dedicated-search-surfaces.mdc`** + **`.cursor/skills/exxat-dedicated-search-surfaces/SKILL.md`**.
32
32
  17. **No toast** → **`apps/web/AGENTS.md` §6.5** + **`exxat-no-toast.mdc`** — do not use **`toast()`** / Sonner / snackbars for product messaging; use banners, inline status, or dialogs.
@@ -50,7 +50,7 @@ Before implementing or reviewing **list / table / board / dashboard / data-heavy
50
50
  - **`apps/web/docs/blueprints/`** — framework-agnostic specs (start: `page-header.md`, `data-table.md`).
51
51
  - **`apps/web/docs/migrations/`** — token rename + removal history.
52
52
  - **`packages/ui/tokens/hooks-index.json`** — machine-readable token index (regenerate via `pnpm --filter @exxatdesignux/ui tokens:index`).
53
- - `.cursor/rules/exxat-data-tables.mdc`, `exxat-list-page-connected-views.mdc`, **`exxat-centralized-list-dataset.mdc`**, **`exxat-list-page-view-shells.mdc`**, **`exxat-fontawesome-icons.mdc`**, **`exxat-mono-ids.mdc`**, **`exxat-primary-nav-secondary-panel.mdc`**, **`exxat-question-bank-hub-header.mdc`**, **`exxat-collaboration-access.mdc`**, **`exxat-dedicated-search-surfaces.mdc`**, **`exxat-kpi-trends.mdc`**, **`exxat-kpi-flat-band.mdc`**, **`exxat-drawer-vs-dialog.mdc`**, **`exxat-card-vs-list-rows.mdc`**, **`exxat-kpi-max-four.mdc`**, **`exxat-reuse-before-custom.mdc`**, `exxat-table-properties-drawer.mdc`, **`exxat-board-cards.mdc`**, **`exxat-page-vs-drawer.mdc`**, **`exxat-no-toast.mdc`**, **`exxat-command-menu.mdc`**, `exxat-kbd-shortcuts.mdc`, `exxat-accessibility.mdc` at repo root; **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** for Data tab charts.
53
+ - `.cursor/rules/exxat-data-tables.mdc`, `exxat-list-page-connected-views.mdc`, **`exxat-centralized-list-dataset.mdc`**, **`exxat-list-page-view-shells.mdc`**, **`exxat-fontawesome-icons.mdc`**, **`exxat-mono-ids.mdc`**, **`exxat-primary-nav-secondary-panel.mdc`**, **`exxat-library-hub-header.mdc`**, **`exxat-collaboration-access.mdc`**, **`exxat-dedicated-search-surfaces.mdc`**, **`exxat-kpi-trends.mdc`**, **`exxat-kpi-flat-band.mdc`**, **`exxat-drawer-vs-dialog.mdc`**, **`exxat-card-vs-list-rows.mdc`**, **`exxat-kpi-max-four.mdc`**, **`exxat-reuse-before-custom.mdc`**, `exxat-table-properties-drawer.mdc`, **`exxat-board-cards.mdc`**, **`exxat-page-vs-drawer.mdc`**, **`exxat-no-toast.mdc`**, **`exxat-command-menu.mdc`**, `exxat-kbd-shortcuts.mdc`, `exxat-accessibility.mdc` at repo root; **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** for Data tab charts.
54
54
  - **`apps/web/docs/kpi-flat-band-pattern.md`**, **`apps/web/docs/shell-surface-elevation-pattern.md`** — flat KPI strip + sidebar/secondary/page OKLCH stack.
55
- - **`apps/web/docs/question-bank-hub-header-pattern.md`** — folder-scoped question bank header **Customize folder**.
55
+ - **`apps/web/docs/library-hub-header-pattern.md`** — folder-scoped library header **Customize folder**.
56
56
  - **`apps/web/docs/collaboration-access-pattern.md`** — shared hub face rail + invite sheet.
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Library library — folder-scoped hub header More menu must expose Customize folder; sheet on hub client
3
+ globs: apps/web/components/library-*.tsx, packages/ui/template/components/library-*.tsx
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Exxat DS — Library hub header (folder scope)
8
+
9
+ When the library library URL is **scoped to a folder** (`parseLibraryNav` → **`scope === "folder"`** and **`folderId`** set), users are effectively on a **“folder page”**: the hub title matches that folder, and **global** actions belong in the **`LibraryPageHeader`** **⋯ More** menu — not only on per-row or per-tile overflow menus inside a single view tab.
10
+
11
+ **Pattern doc:** **`apps/web/docs/library-hub-header-pattern.md`**. **Handbook:** **`apps/web/AGENTS.md` §4.6** (folder-scoped hub chrome).
12
+
13
+ ## MUST
14
+
15
+ 1. **`LibraryPageHeader`** — When **`navState.scope === "folder"`** and **`navState.folderId`** resolves to a row in **`folders`**, pass **`onCustomizeFolder`** so **⋯ More** includes **Customize folder** ( **`fa-wand-magic-sparkles`** + label **Customize folder** ), placed after **Invite people** (collaboration variant) and before **Export**.
16
+ 2. **Hub client** — Mount **`LibraryNewFolderSheet`** on the **hub client** (e.g. **`LibraryClient`**) next to **`ListPageTemplate`**, driven by local **`open` / `customizingFolder`** state opened from **`onCustomizeFolder`**. **MUST NOT** rely on **`LibraryTable`** alone to host the sheet when some view branches (**table**, **list**, **board**, **dashboard**) do not render that sheet — users would lose **Customize folder** on those tabs.
17
+ 3. **`onCreated`** — On save, **`setFolders`** (or equivalent) **maps** the scoped folder **`id`** to updated **`name`**, **`icon`**, **`colorKey`** — same contract as **`LibraryTable`** panel/tree customize handlers.
18
+
19
+ ## MUST NOT
20
+
21
+ - Omit **Customize folder** from the header **⋯** when the URL is folder-scoped, expecting users to find it only on secondary-nav tree rows or OS-folder tiles.
22
+ - Mount **only** one customize sheet inside **`LibraryTable`** without a **client-level** sheet when the hub uses **`ListPageTemplate`** view tabs that omit that table subtree.
23
+
24
+ ## See also
25
+
26
+ - **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`** — URL scope + secondary panel.
27
+ - **`.cursor/rules/exxat-collaboration-access.mdc`** — **`variant="collaboration"`** header + **⋯** invite pattern.
28
+ - **`lib/library-nav.ts`** — **`parseLibraryNav`**, **`LibraryNavState`**.
@@ -16,7 +16,7 @@ Use this when rendering **system identifiers** — values a user copies, searche
16
16
 
17
17
  ## SHOULD
18
18
 
19
- - Match existing hubs: **`question-bank-table.tsx`**, **`question-bank-list-view.tsx`**, **`new-question-composer.tsx`** (header subtitle), **`sites-table.tsx`** (`row.id`).
19
+ - Match existing hubs: **`library-table.tsx`**, **`library-list-view.tsx`**, **`new-library-item-form.tsx`** (header subtitle), **`sites-table.tsx`** (`row.id`).
20
20
  - Prefer **`truncate`** / **`min-w-0`** on mono IDs in tight layouts so long tokens do not blow out columns.
21
21
 
22
22
  ## MUST NOT
@@ -24,7 +24,7 @@ Dense or width-constrained surfaces **MUST NOT** squeeze email if it harms scana
24
24
  ## Table / list / drawer / inspector
25
25
 
26
26
  - **Dedicated person column** or **profile / invite / access** surfaces — show **avatar + name + email** (stacked or name with email below), consistent with **`InviteCollaboratorsDrawer`** and **`PageHeader`** collaboration variant.
27
- - **Example:** `QuestionBankTable` **Author** — `AvatarInitials`, **primary name** (`text-sm font-medium`), **muted `text-xs` email** with optional `mailto:` (row click does not navigate when the link is used).
27
+ - **Example:** `LibraryTable` **Author** — `AvatarInitials`, **primary name** (`text-sm font-medium`), **muted `text-xs` email** with optional `mailto:` (row click does not navigate when the link is used).
28
28
 
29
29
  ## Avatar rows — **never overlapping**
30
30
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Primary sidebar item opens nested SecondaryPanel (Question bank pattern)
2
+ description: Primary sidebar item opens nested SecondaryPanel (Library pattern)
3
3
  globs: apps/web/components/**/*.tsx
4
4
  alwaysApply: false
5
5
  ---
@@ -8,13 +8,13 @@ alwaysApply: false
8
8
 
9
9
  Some hubs expose **scoped navigation** (All / My / tree / filters) in a **nested panel** between the **icon rail** and **main content** — not as **primary nav child rows**.
10
10
 
11
- **Reference:** **Question bank** — **`lib/mock/navigation.tsx`** (`secondaryPanel: "question-bank"`), **`components/app-sidebar.tsx`**, **`components/secondary-panel.tsx`**, **`components/question-bank-panel-activator.tsx`**, **`components/question-bank-secondary-nav.tsx`**, **`lib/question-bank-nav.ts`** (URL scope).
11
+ **Reference:** **Library** — **`lib/mock/navigation.tsx`** (`secondaryPanel: "library"`), **`components/app-sidebar.tsx`**, **`components/secondary-panel.tsx`**, **`components/library-panel-activator.tsx`**, **`components/library-secondary-nav.tsx`**, **`lib/library-nav.ts`** (URL scope).
12
12
 
13
13
  ## MUST (wiring)
14
14
 
15
- 1. **`NavLinkItem`** — On the **primary** row that should drive the panel, set **`secondaryPanel`** to a **stable string id** (e.g. **`"question-bank"`**). Keep **`url`** pointing at the **hub route**.
15
+ 1. **`NavLinkItem`** — On the **primary** row that should drive the panel, set **`secondaryPanel`** to a **stable string id** (e.g. **`"library"`**). Keep **`url`** pointing at the **hub route**.
16
16
  2. **`SecondaryPanel` registry** — In **`components/secondary-panel.tsx`**, add **`PANELS[id]`** → component that renders **panel chrome** (title, optional search) + **your secondary nav** list. **MUST** keep ids in sync with **`NAV_PRIMARY`**.
17
- 3. **Auto-open on route** — The hub **`client`** (or layout slot) **MUST** mount **`*PanelActivator`** that calls **`useAutoPanel(id)`** with the **same id**, so deep links and first visit open the panel while the route is mounted (e.g. **`QuestionBankPanelActivator`** + **`beforeSiteHeader`** on **`QuestionBankClient`**).
17
+ 3. **Auto-open on route** — The hub **`client`** (or layout slot) **MUST** mount **`*PanelActivator`** that calls **`useAutoPanel(id)`** with the **same id**, so deep links and first visit open the panel while the route is mounted (e.g. **`LibraryPanelActivator`** + **`beforeSiteHeader`** on **`LibraryClient`**).
18
18
  4. **Same-route reopen** — **`AppSidebar`** already calls **`openPanel(secondaryPanel)`** when the user clicks the primary item **again** while already on that **`url`** (prevents no-op navigation). Secondary nav rows that stay on the same path **SHOULD** call **`openPanel`** on click where **Next.js `Link`** would not fire (same **`href`**).
19
19
 
20
20
  ## Surface elevation (brand chrome)
@@ -35,7 +35,7 @@ The nested panel sits **between** the icon rail and main content. **MUST** use t
35
35
 
36
36
  ## SHOULD
37
37
 
38
- - Drive **hub scope** from the **URL** (**`useSearchParams`** + helpers like **`parseQuestionBankNav`**) so **refresh**, **share link**, and **breadcrumbs** match the secondary list.
38
+ - Drive **hub scope** from the **URL** (**`useSearchParams`** + helpers like **`parseLibraryNav`**) so **refresh**, **share link**, and **breadcrumbs** match the secondary list.
39
39
  - Keep **panel content** **Font Awesome**-aligned with the rest of the app (**`.cursor/rules/exxat-fontawesome-icons.mdc`**).
40
40
 
41
41
  ## MUST NOT
@@ -47,6 +47,6 @@ The nested panel sits **between** the icon rail and main content. **MUST** use t
47
47
 
48
48
  - **`apps/web/AGENTS.md` §4.6** — handbook summary.
49
49
  - **`apps/web/docs/shell-surface-elevation-pattern.md`** — OKLCH tokens + product theme.
50
- - **`.cursor/rules/exxat-question-bank-hub-header.mdc`** — folder-scoped library header **⋯** → **Customize folder** + client-mounted sheet.
50
+ - **`.cursor/rules/exxat-library-hub-header.mdc`** — folder-scoped library header **⋯** → **Customize folder** + client-mounted sheet.
51
51
  - **`.cursor/rules/exxat-page-vs-drawer.mdc`** — when **drawer** vs **route**; secondary panel is **nav chrome**, not a workflow drawer.
52
52
  - **`.cursor/rules/exxat-kpi-flat-band.mdc`** — flat KPI strip (separate from panel fill).
@@ -8,7 +8,7 @@ alwaysApply: true
8
8
  ## MUST
9
9
 
10
10
  1. **Compose first** — Use existing **`components/ui/`**, **`components/data-views/`**, **`components/templates/`**, **`PageHeader`**, **`ListPageTemplate`**, **`DataTable`**, **`KeyMetrics`**, and patterns in **`AGENTS.md` §9** before writing new layout or interaction chrome.
11
- 2. **Search the codebase** — Grep or open the nearest hub (Placements, Team, Question bank) for the same UX (toolbar, drawer, metrics, board card, `ListPageViewFrame`, etc.).
11
+ 2. **Search the codebase** — Grep or open the nearest hub (Placements, Team, Library) for the same UX (toolbar, drawer, metrics, board card, `ListPageViewFrame`, etc.).
12
12
  3. **Extend in place** — Prefer adding a variant, slot, or prop to a shared component over a one-off duplicate under a single route.
13
13
 
14
14
  ## When the tool must ask the user
@@ -39,13 +39,13 @@ Import from **`@/components/data-views/list-page-board-card`**:
39
39
 
40
40
  Prefer **two-line blocks** for stacked **primary / secondary** facts so cards match Placements’ visual rhythm.
41
41
 
42
- ## Status badges (list hubs: Team, Compliance, Question bank, …)
42
+ ## Status badges (list hubs: Team, Compliance, Library, …)
43
43
 
44
44
  - **Maps (single source):** **`lib/list-status-badges.ts`** — per-entity `*_STATUS_LABEL`, `*_STATUS_BADGE_CLASS`, `*_STATUS_ICON`, plus semantic **`LIST_HUB_STATUS_TINT_*`** (success / warning / neutral / danger) for new domains.
45
45
  - **Component:** **`ListHubStatusBadge`** from **`@/components/list-hub-status-badge`** — **`surface="table"`** for **DataTable** cells and **list** rows; **`surface="board"`** inside **`ListPageBoardCardBadgeRow`**. Do not duplicate the shell classes on each page.
46
46
  - Use the **same** maps everywhere for that entity so copy and colors never drift.
47
47
  - **Do not** add **`uppercase`** or **`tracking-wide`** — **sentence / title case**, consistent with **`BoardStatusBadge`** on Placements (`placement-board-card.tsx`).
48
- - **Placements** lifecycle uses **`StatusBadge`** in **`placements-table-cells.tsx`** — thin wrapper over **`ListHubStatusBadge`** + **`PLACEMENT_STATUS_*`** in **`list-status-badges.ts`** (same visuals as Team / Question bank).
48
+ - **Placements** lifecycle uses **`StatusBadge`** in **`placements-table-cells.tsx`** — thin wrapper over **`ListHubStatusBadge`** + **`PLACEMENT_STATUS_*`** in **`list-status-badges.ts`** (same visuals as Team / Library).
49
49
 
50
50
  ## Avatar when mock has no `initials`
51
51
 
@@ -76,7 +76,7 @@ Goal: **one row model**, **one filtered bag** (`tableState.rows`), **one place f
76
76
 
77
77
  - **`components/placements-client.tsx`** + **`placements-table.tsx`** — Placements pattern.
78
78
  - **`components/team-client.tsx`** + **`team-table.tsx`**.
79
- - **`components/question-bank-table.tsx`** — multiple **`DataListViewType`** branches sharing **`tableState`** / **`folders`** / **`items`**.
79
+ - **`components/library-table.tsx`** — multiple **`DataListViewType`** branches sharing **`tableState`** / **`folders`** / **`items`**.
80
80
 
81
81
  ---
82
82
 
@@ -9,7 +9,7 @@ user-invocable: true
9
9
  **Handbook:** `apps/web/AGENTS.md` §4.7
10
10
  **Narrative:** `apps/web/docs/collaboration-access-pattern.md`
11
11
  **Cursor rule:** `.cursor/rules/exxat-collaboration-access.mdc`
12
- **Related (Question bank folder scope + ⋯ Customize folder):** `.cursor/rules/exxat-question-bank-hub-header.mdc` · `docs/question-bank-hub-header-pattern.md`
12
+ **Related (Library folder scope + ⋯ Customize folder):** `.cursor/rules/exxat-library-hub-header.mdc` · `docs/library-hub-header-pattern.md`
13
13
 
14
14
  ## Wiring checklist
15
15
 
@@ -20,7 +20,7 @@ user-invocable: true
20
20
  5. **Access maps** — `lib/collaborator-access.ts` for Owner / Editor / Commenter / Viewer, invite options, and **`COLLABORATION_HEADER_ADD_LABEL`**.
21
21
  6. **Header** — empty roster → outline **Add collaborator**; non-empty → face rail; both open the invite sheet.
22
22
  7. **Invite sheet** — `InviteCollaboratorsDrawer`: export-style **`Sheet`**, combined email + access menu, grouped roster (name → email → role tags → access badge).
23
- 8. **Question bank — folder URL scope** — When **`?scope=folder`**, **`QuestionBankPageHeader`** **⋯** also lists **Customize folder**; **`QuestionBankNewFolderSheet`** on **`QuestionBankClient`** — **`.cursor/rules/exxat-question-bank-hub-header.mdc`**, **`docs/question-bank-hub-header-pattern.md`**.
23
+ 8. **Library — folder URL scope** — When **`?scope=folder`**, **`LibraryPageHeader`** **⋯** also lists **Customize folder**; **`LibraryNewFolderSheet`** on **`LibraryClient`** — **`.cursor/rules/exxat-library-hub-header.mdc`**, **`docs/library-hub-header-pattern.md`**.
24
24
 
25
25
  ## MUST NOT
26
26
 
@@ -31,5 +31,5 @@ user-invocable: true
31
31
 
32
32
  ## Reference
33
33
 
34
- - `components/collaboration-access-flow.tsx`, `components/question-bank-page-header.tsx`, `components/question-bank-client.tsx`
34
+ - `components/collaboration-access-flow.tsx`, `components/library-page-header.tsx`, `components/library-client.tsx`
35
35
  - `components/invite-collaborators-drawer.tsx`, `components/export-drawer.tsx`
@@ -37,8 +37,8 @@ description: >-
37
37
  | Recents list | `components/dedicated-search-recents.tsx` |
38
38
  | Recents storage factory | `lib/dedicated-search-recents.ts` |
39
39
  | Optional default `q` patcher | `lib/dedicated-search-url.ts` |
40
- | Question bank adapter (placeholders + patch) | `lib/question-bank-dedicated-search.ts` |
41
- | Question bank wiring | `components/question-bank-client.tsx` |
40
+ | Library adapter (placeholders + patch) | `lib/library-dedicated-search.ts` |
41
+ | Library wiring | `components/library-client.tsx` |
42
42
 
43
43
  ## Cursor rule
44
44
 
@@ -22,7 +22,7 @@ description: >
22
22
  - **App root:** `apps/web/app/(app)/` — route group that wraps all authenticated pages
23
23
  - **Single source of truth:** `apps/web/AGENTS.md` for full prose explanations; this skill is the actionable summary
24
24
  - **Companion skills (narrow topics):** `exxat-fontawesome-icons`, `exxat-mono-ids`, `exxat-primary-nav-secondary-panel`, `exxat-centralized-list-dataset`, `exxat-list-page-view-shells`, `exxat-dedicated-search-surfaces`, `exxat-accessibility`, `exxat-board-cards`, `exxat-collaboration-access` — live under `.cursor/skills/`; vetted copies ship with **`@exxatdesignux/ui`** in `consumer-extras/cursor-skills/` after **`pnpm --filter @exxatdesignux/ui vendor:consumer-extras`**.
25
- - **Question bank folder-scoped header (rule + doc):** **`.cursor/rules/exxat-question-bank-hub-header.mdc`** and **`docs/question-bank-hub-header-pattern.md`** — pair with **`exxat-primary-nav-secondary-panel`** when URL **`scope=folder`** drives the hub title.
25
+ - **Library folder-scoped header (rule + doc):** **`.cursor/rules/exxat-library-hub-header.mdc`** and **`docs/library-hub-header-pattern.md`** — pair with **`exxat-primary-nav-secondary-panel`** when URL **`scope=folder`** drives the hub title.
26
26
  - **Consumer repos (npm install of `@exxatdesignux/ui`):** After a version bump, read **`node_modules/@exxatdesignux/ui/CHANGELOG.md`**, run **`npx --package=@exxatdesignux/ui@latest exxat-ui sync-extras`** so **`docs/exxat-ds/consumer-upgrade-checklist.md`** and Cursor skills match the tarball, and diff the host app against **`node_modules/@exxatdesignux/ui/template/`** for anything new to port (routes, re-exports, AGENTS). Use **`exxat-ui changelog`**, **`exxat-ui update`**, and **`exxat-ui doctor`** for CLI guidance.
27
27
 
28
28
  ---
@@ -105,15 +105,15 @@ A primary nav row that owns sub-routes has **three possible shapes** — pick ex
105
105
 
106
106
  | Shape | When to use | Where it lives |
107
107
  |---|---|---|
108
- | **A. Collapsible children** (e.g. Question Bank → All / My / Favorites / Folders) | Small finite child list that benefits from inline browsing (≤ 40 items, no extra page chrome). | `NavLinkItem.children` rendered by **`CollapsibleNavItem`** in `app-sidebar.tsx`. |
108
+ | **A. Collapsible children** (e.g. Library → All / My / Favorites / Folders) | Small finite child list that benefits from inline browsing (≤ 40 items, no extra page chrome). | `NavLinkItem.children` rendered by **`CollapsibleNavItem`** in `app-sidebar.tsx`. |
109
109
  | **B. Secondary panel** (separate nested rail) | Same nav row needs **scoped search / tree / metrics** alongside the hub content. | `NavLinkItem.secondaryPanel = "<id>"` + `PANELS[id]` — see companion skill `exxat-primary-nav-secondary-panel`. |
110
- | **C. Both A + B on one row** (Question Bank does this) | Most cases when a hub has a sub-list AND a rich rail. The sidebar still shows the collapsible children; clicking the parent route also opens the secondary panel via `useAutoPanel`. | Combine A + B; the active-state and animation rules in §3.2 still apply to the sidebar children. |
110
+ | **C. Both A + B on one row** (Library does this) | Most cases when a hub has a sub-list AND a rich rail. The sidebar still shows the collapsible children; clicking the parent route also opens the secondary panel via `useAutoPanel`. | Combine A + B; the active-state and animation rules in §3.2 still apply to the sidebar children. |
111
111
 
112
112
  #### Active-state rules — **the single biggest mistake to avoid**
113
113
 
114
114
  1. **Expanded sidebar (full rail):** parent stays **visually neutral** when a child is active — **never double-highlight**. `isCollapsibleParentMenuButtonActive` returns `false` if `anyChildActive` so the active child row carries `data-active` alone.
115
115
  2. **Collapsed sidebar (icon rail):** the parent icon is the **only** affordance, so it lights up when **any child** route is active. Implementation: `iconRailActive = isAnyChildActive` is fed into `SidebarMenuButton.isActive` and selects `item.iconActive` (`fa-solid`) over `item.icon` (`fa-light`).
116
- 3. **Tooltip / aria copy** in icon mode names the parent (e.g. "Question bank — open subpages"); the **popover** content lists children with their own active state so users see which sub-route is selected.
116
+ 3. **Tooltip / aria copy** in icon mode names the parent (e.g. "Library — open subpages"); the **popover** content lists children with their own active state so users see which sub-route is selected.
117
117
 
118
118
  ```tsx
119
119
  // Inside CollapsibleNavItem
@@ -296,7 +296,7 @@ Align with **`apps/web/AGENTS.md` §6.4**, **`docs/data-views-pattern.md`**, **`
296
296
  | `ColumnDef` from `@/components/data-table/types` | Column type |
297
297
  | `FilterFieldDef`, `FilterOperator`, `ConditionalRule` from `@/components/table-properties/types` | Filter types |
298
298
 
299
- **Board (kanban) cards:** Use **`ListPageBoardCard`** and related parts from **`components/data-views/list-page-board-card.tsx`**; **`BoardCardTwoLineBlock`** / **`BoardCardIconRow`** from **`board-card-primitives.tsx`**. **List hub** status (Team, Compliance, Question bank, …): maps in **`lib/list-status-badges.ts`**; render with **`ListHubStatusBadge`** (**`surface="table"`** in table/list, **`surface="board"`** on cards); semantic tints **`LIST_HUB_STATUS_TINT_*`** for new domains; no **`uppercase`**. **Placements** uses **`StatusBadge`** in **`placements-table-cells.tsx`** (wrapper over **`ListHubStatusBadge`** + **`PLACEMENT_STATUS_*`**). **Full rules:** **`apps/web/AGENTS.md` §4.4**, **`.cursor/rules/exxat-board-cards.mdc`**, **`.cursor/skills/exxat-board-cards/SKILL.md`**.
299
+ **Board (kanban) cards:** Use **`ListPageBoardCard`** and related parts from **`components/data-views/list-page-board-card.tsx`**; **`BoardCardTwoLineBlock`** / **`BoardCardIconRow`** from **`board-card-primitives.tsx`**. **List hub** status (Team, Compliance, Library, …): maps in **`lib/list-status-badges.ts`**; render with **`ListHubStatusBadge`** (**`surface="table"`** in table/list, **`surface="board"`** on cards); semantic tints **`LIST_HUB_STATUS_TINT_*`** for new domains; no **`uppercase`**. **Placements** uses **`StatusBadge`** in **`placements-table-cells.tsx`** (wrapper over **`ListHubStatusBadge`** + **`PLACEMENT_STATUS_*`**). **Full rules:** **`apps/web/AGENTS.md` §4.4**, **`.cursor/rules/exxat-board-cards.mdc`**, **`.cursor/skills/exxat-board-cards/SKILL.md`**.
300
300
 
301
301
  **Minimum required features on any data list page:**
302
302
  - Search (wire `searchable={displayOptions.showToolbarSearch}`)
@@ -401,9 +401,9 @@ Use `PageHeader` from `@/components/page-header` for the content-area header (be
401
401
 
402
402
  When a hub is **shared**, use **`PageHeader` `variant="collaboration"`**: **empty roster** → outline **Add collaborator**; **non-empty** → face rail (faces / **`+N`** open the invite sheet). **Invite people** also lives under the entity header **⋯ More** and opens **`InviteCollaboratorsDrawer`** via **`CollaborationAccessFlow`** when possible. Library access (Owner / Editor / Commenter / Viewer) comes from **`lib/collaborator-access.ts`**; directory tags (Faculty, Program coordinator, Director) use **`PageHeaderCollaborator.roles`**.
403
403
 
404
- **Question bank library — folder URL scope:** When **`?scope=folder&folderId=`** applies, **⋯ More** must also offer **Customize folder** (**`QuestionBankPageHeader`** **`onCustomizeFolder`**) and the **`QuestionBankNewFolderSheet`** must be mounted on **`QuestionBankClient`** so it works on every **`ListPageTemplate`** view tab. **`.cursor/rules/exxat-question-bank-hub-header.mdc`** · **`docs/question-bank-hub-header-pattern.md`** (app: **`apps/web/docs/...`**).
404
+ **Library library — folder URL scope:** When **`?scope=folder&folderId=`** applies, **⋯ More** must also offer **Customize folder** (**`LibraryPageHeader`** **`onCustomizeFolder`**) and the **`LibraryNewFolderSheet`** must be mounted on **`LibraryClient`** so it works on every **`ListPageTemplate`** view tab. **`.cursor/rules/exxat-library-hub-header.mdc`** · **`docs/library-hub-header-pattern.md`** (app: **`apps/web/docs/...`**).
405
405
 
406
- **Handbook:** `apps/web/AGENTS.md` §4.7 · **Doc:** `docs/collaboration-access-pattern.md` · **Skill:** `.cursor/skills/exxat-collaboration-access/SKILL.md` · **Reference:** Question bank header + client.
406
+ **Handbook:** `apps/web/AGENTS.md` §4.7 · **Doc:** `docs/collaboration-access-pattern.md` · **Skill:** `.cursor/skills/exxat-collaboration-access/SKILL.md` · **Reference:** Library header + client.
407
407
 
408
408
  ---
409
409
 
@@ -30,7 +30,7 @@ user-invocable: true
30
30
 
31
31
  - `apps/web/components/key-metrics.tsx` — `flatMetricsHairlineClass`, `flatBandStyle`
32
32
  - `apps/web/app/globals.css` — `--key-metrics-flat-*`
33
- - `question-bank-client.tsx`, `dashboard-tabs.tsx` — reference usage
33
+ - `library-client.tsx`, `dashboard-tabs.tsx` — reference usage
34
34
 
35
35
  ## Pair with
36
36
 
@@ -21,7 +21,7 @@ description: >-
21
21
  - Pass **`maxWidthClassName={LIST_PAGE_VIEW_FRAME_MAX_WIDE}`** when the view includes toolbar rows + breadcrumbs + grid.
22
22
  2. **Do not double-gutter** — If **`DataTable`** already provides horizontal inset for the **table** view, do **not** wrap that branch in **`ListPageViewFrame`**. Use the frame on **sibling** view branches (folder, panel, etc.) only.
23
23
  3. **Reuse before inventing** — Prefer **`FolderGridView`**, **`FinderPanelView`**, **`ListPageBoardTemplate`**, **`ListPageViewFrame`**. If a new pattern appears twice, **promote** it to **`components/data-views/`** with domain-agnostic props.
24
- 4. **Entity-specific → generic** — If logic is “any tree + any row type”, build **`components/data-views/<generic-name>.tsx`** and keep **`question-bank-*`** (or similar) as a thin composition + mock types.
24
+ 4. **Entity-specific → generic** — If logic is “any tree + any row type”, build **`components/data-views/<generic-name>.tsx`** and keep **`library-*`** (or similar) as a thin composition + mock types.
25
25
 
26
26
  ## Checklist
27
27
 
@@ -39,11 +39,11 @@ Always include **`tabular-nums`** with **`font-mono`** so fixed-width digits ali
39
39
 
40
40
  | Surface | File |
41
41
  |---------|------|
42
- | Question bank table | `components/question-bank-table.tsx` — `row.questionId` |
43
- | Question bank list | `components/question-bank-list-view.tsx` |
44
- | New question subtitle | `components/new-question-composer.tsx` — `questionId` in `PageHeader` subtitle |
42
+ | Library table | `components/library-table.tsx` — `row.questionId` |
43
+ | Library list | `components/library-list-view.tsx` |
44
+ | New question subtitle | `components/new-library-item-form.tsx` — `questionId` in `PageHeader` subtitle |
45
45
  | Sites record id | `components/sites-table.tsx` — `row.id` |
46
- | OS folder tiles | `components/question-bank-os-folder-view.tsx` |
46
+ | OS folder tiles | `components/library-os-folder-view.tsx` |
47
47
 
48
48
  ## Review checklist
49
49
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: exxat-primary-nav-secondary-panel
3
- description: Exxat DS pattern — one primary sidebar row opens a nested SecondaryPanel (Question bank). NavLinkItem.secondaryPanel id, PANELS registry, useAutoPanel on hub, URL scope + same useTableState rows. Use when adding hub scoped nav (All/My/tree) beside content.
3
+ description: Exxat DS pattern — one primary sidebar row opens a nested SecondaryPanel (Library). NavLinkItem.secondaryPanel id, PANELS registry, useAutoPanel on hub, URL scope + same useTableState rows. Use when adding hub scoped nav (All/My/tree) beside content.
4
4
  user-invocable: true
5
5
  ---
6
6
 
@@ -13,10 +13,10 @@ user-invocable: true
13
13
 
14
14
  1. **`lib/mock/navigation.tsx`** — set **`secondaryPanel: "<id>"`** on the primary **`NavLinkItem`**; **`url`** = hub route.
15
15
  2. **`components/secondary-panel.tsx`** — add **`PANELS["<id>"]`** → panel shell (title, optional search) + secondary nav component.
16
- 3. **Hub client** — mount **`*PanelActivator`** with **`useAutoPanel("<id>")`** (same id) for the lifetime of the route (e.g. `QuestionBankPanelActivator`).
17
- 4. **Data** — keep **one** **`useTableState`** / **`tableState.rows`**; drive scope from **URL** + small helpers (see **`lib/question-bank-nav.ts`**).
18
- 5. **Folder-scoped hub header (Question bank library)** — When **`scope === "folder"`** in the URL, **`QuestionBankPageHeader`** **⋯ More** includes **Customize folder**; mount **`QuestionBankNewFolderSheet`** on **`QuestionBankClient`** so it works on **all** **`ListPageTemplate`** view tabs — **`.cursor/rules/exxat-question-bank-hub-header.mdc`**, **`docs/question-bank-hub-header-pattern.md`**.
19
- 6. **Collapse control** — the nested rail header uses **`collapseActiveSecondaryPanel()`** (angles-left icon), not “close”, so the panel stays dismissed until **`openPanel`** runs again (nav, scope hook, or hub re-entry). Layout effects that auto-call **`openPanel`** must respect **`secondaryPanelAutoReopenSuppressed`** (see **`app/(app)/question-bank/layout.tsx`** + **`SecondaryPanelProvider`**).
16
+ 3. **Hub client** — mount **`*PanelActivator`** with **`useAutoPanel("<id>")`** (same id) for the lifetime of the route (e.g. `LibraryPanelActivator`).
17
+ 4. **Data** — keep **one** **`useTableState`** / **`tableState.rows`**; drive scope from **URL** + small helpers (see **`lib/library-nav.ts`**).
18
+ 5. **Folder-scoped hub header (Library library)** — When **`scope === "folder"`** in the URL, **`LibraryPageHeader`** **⋯ More** includes **Customize folder**; mount **`LibraryNewFolderSheet`** on **`LibraryClient`** so it works on **all** **`ListPageTemplate`** view tabs — **`.cursor/rules/exxat-library-hub-header.mdc`**, **`docs/library-hub-header-pattern.md`**.
19
+ 6. **Collapse control** — the nested rail header uses **`collapseActiveSecondaryPanel()`** (angles-left icon), not “close”, so the panel stays dismissed until **`openPanel`** runs again (nav, scope hook, or hub re-entry). Layout effects that auto-call **`openPanel`** must respect **`secondaryPanelAutoReopenSuppressed`** (see **`app/(app)/library/layout.tsx`** + **`SecondaryPanelProvider`**).
20
20
  7. **Surface elevation** — secondary panel = **level 1** (lighter than sidebar, darker than page). Use **`--secondary-panel-bg`** on **`NestedSecondaryPanelShell`**; derive from **`--brand-tint*`** per active product (**One** indigo, **Prism** rose). See **`docs/shell-surface-elevation-pattern.md`**.
21
21
 
22
22
  ## MUST NOT
@@ -30,7 +30,7 @@ user-invocable: true
30
30
 
31
31
  `SecondaryPanelProvider` reads **`useSidebarReflowZoom()`** (zoom ≥ 200% or very short viewport — same WCAG 1.4.10 signal the primary sidebar uses). On entering high zoom it sets `secondaryPanelCompact = true` so the 16 rem rail drops to the 3 rem icon variant and frees up content space. The user can still re-expand manually (via the icon rail's "Show labels" affordance or any `openPanel` trigger); the next zoom-out → zoom-in cycle re-collapses it. `openPanel` itself opens directly in compact mode when high zoom is already active so newly-navigated panels don't flash expanded.
32
32
 
33
- Custom panel content (anything you register under `PANELS[id]`) should **read `secondaryPanelCompact` from the provider context** and render an icon-only layout in that branch — `QuestionBankPanel` / `QuestionBankSecondaryNav` are the reference.
33
+ Custom panel content (anything you register under `PANELS[id]`) should **read `secondaryPanelCompact` from the provider context** and render an icon-only layout in that branch — `LibraryPanel` / `LibrarySecondaryNav` are the reference.
34
34
 
35
35
  ## Pair with
36
36
 
@@ -45,5 +45,5 @@ Custom panel content (anything you register under `PANELS[id]`) should **read `s
45
45
  - `app/globals.css` — `--secondary-panel-bg`, `--sidebar`, product **`theme-one`** / **`theme-prism`** blocks.
46
46
  - `contexts/product-context.tsx` — `accentOverrideActive`, theme class on `<html>`.
47
47
  - **`docs/shell-surface-elevation-pattern.md`**
48
- - `components/question-bank-secondary-nav.tsx` + `lib/question-bank-nav.ts`.
49
- - **Folder-scoped header customize:** `components/question-bank-page-header.tsx`, `components/question-bank-client.tsx` — **`docs/question-bank-hub-header-pattern.md`**, **`.cursor/rules/exxat-question-bank-hub-header.mdc`**.
48
+ - `components/library-secondary-nav.tsx` + `lib/library-nav.ts`.
49
+ - **Folder-scoped header customize:** `components/library-page-header.tsx`, `components/library-client.tsx` — **`docs/library-hub-header-pattern.md`**, **`.cursor/rules/exxat-library-hub-header.mdc`**.