@exxatdesignux/ui 0.5.11 → 0.5.13
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.
- package/CHANGELOG.md +45 -0
- package/bin/cli.mjs +70 -1
- package/bin/init.mjs +18 -4
- package/bin/sync-extras.mjs +28 -4
- package/consumer-extras/README.md +41 -5
- package/consumer-extras/cursor-rules/exxat-accessibility.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-board-cards.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +4 -2
- package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-command-menu.mdc +3 -2
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +7 -0
- package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +6 -5
- package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +6 -2
- package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +4 -3
- package/consumer-extras/cursor-rules/exxat-no-image-pixel-copy.mdc +25 -14
- package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +8 -2
- package/consumer-extras/cursor-rules/exxat-no-toast.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-no-vaul.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-page-header-actions.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-sidebar-shell.mdc +13 -7
- package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-table-row-preview.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-tabs-chrome.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +6 -0
- package/consumer-extras/cursor-rules/exxat-ux-discovery-protocol.mdc +28 -0
- package/consumer-extras/cursor-rules/exxat-ux-principles.mdc +1 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-kpi-trends/SKILL.md +5 -3
- package/consumer-extras/patterns/command-menu-pattern.md +2 -2
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +1 -1
- package/consumer-extras/patterns/jobs/README.md +1 -1
- package/consumer-extras/patterns/perf-memory-pattern.md +115 -150
- package/consumer-extras/scripts/dev-guard.mjs +156 -0
- package/consumer-extras/templates/README.md +23 -0
- package/consumer-extras/templates/handoff.md +190 -0
- package/package.json +2 -3
- package/{template → template-vite}/.claude/skills/exxat-ds-skill/SKILL.md +184 -23
- package/template-vite/.cursor/rules/exxat-accessibility.mdc +40 -0
- package/template-vite/.cursor/rules/exxat-board-cards.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-breadcrumbs-no-back.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-card-vs-list-rows.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-centralized-list-dataset.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-collaboration-access.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-command-menu.mdc +5 -5
- package/template-vite/.cursor/rules/exxat-data-tables.mdc +47 -0
- package/template-vite/.cursor/rules/exxat-dedicated-search-surfaces.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-drawer-vs-dialog.mdc +23 -0
- package/template-vite/.cursor/rules/exxat-ds-agents.mdc +87 -0
- package/template-vite/.cursor/rules/exxat-fontawesome-icons.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-hub-supported-views.mdc +56 -0
- package/{template → template-vite}/.cursor/rules/exxat-kbd-shortcuts.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-kpi-flat-band.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-kpi-max-four.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-kpi-trends.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-library-hub-header.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-list-page-connected-views.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-list-page-view-shells.mdc +32 -0
- package/{template → template-vite}/.cursor/rules/exxat-mono-ids.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-nav-single-active.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-no-image-pixel-copy.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-no-slds-leakage.mdc +84 -0
- package/{template → template-vite}/.cursor/rules/exxat-no-toast.mdc +2 -2
- package/template-vite/.cursor/rules/exxat-no-vaul.mdc +26 -0
- package/template-vite/.cursor/rules/exxat-page-header-actions.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-page-vs-drawer.mdc +5 -3
- package/template-vite/.cursor/rules/exxat-person-identity-display.mdc +48 -0
- package/template-vite/.cursor/rules/exxat-primary-nav-secondary-panel.mdc +53 -0
- package/template-vite/.cursor/rules/exxat-reuse-before-custom.mdc +37 -0
- package/template-vite/.cursor/rules/exxat-sidebar-shell.mdc +41 -0
- package/template-vite/.cursor/rules/exxat-table-properties-drawer.mdc +79 -0
- package/template-vite/.cursor/rules/exxat-table-row-preview.mdc +25 -0
- package/template-vite/.cursor/rules/exxat-tabs-chrome.mdc +33 -0
- package/template-vite/.cursor/rules/exxat-token-discipline.mdc +109 -0
- package/template-vite/.cursor/rules/exxat-ux-discovery-protocol.mdc +202 -0
- package/template-vite/.cursor/rules/exxat-ux-principles.mdc +187 -0
- package/template-vite/.cursor/skills/exxat-accessibility/SKILL.md +282 -0
- package/template-vite/.cursor/skills/exxat-board-cards/SKILL.md +68 -0
- package/template-vite/.cursor/skills/exxat-card-vs-list-rows/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-centralized-list-dataset/SKILL.md +99 -0
- package/template-vite/.cursor/skills/exxat-collaboration-access/SKILL.md +35 -0
- package/template-vite/.cursor/skills/exxat-dedicated-search-surfaces/SKILL.md +45 -0
- package/template-vite/.cursor/skills/exxat-drawer-vs-dialog/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/SKILL.md +893 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/accessibility.md +142 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/coach-marks.md +169 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/data-table-pattern.md +392 -0
- package/template-vite/.cursor/skills/exxat-fontawesome-icons/SKILL.md +31 -0
- package/template-vite/.cursor/skills/exxat-kpi-flat-band/SKILL.md +38 -0
- package/template-vite/.cursor/skills/exxat-kpi-max-four/SKILL.md +19 -0
- package/template-vite/.cursor/skills/exxat-kpi-trends/SKILL.md +29 -0
- package/template-vite/.cursor/skills/exxat-list-page-view-shells/SKILL.md +36 -0
- package/template-vite/.cursor/skills/exxat-mono-ids/SKILL.md +56 -0
- package/template-vite/.cursor/skills/exxat-primary-nav-secondary-panel/SKILL.md +49 -0
- package/template-vite/.cursor/skills/exxat-senior-ux/SKILL.md +198 -0
- package/template-vite/.cursor/skills/exxat-token-economy/SKILL.md +287 -0
- package/template-vite/.cursor/skills/exxat-ux-audit/SKILL.md +303 -0
- package/{template → template-vite}/components/ask-leo-sidebar.tsx +10 -8
- package/{template → template-vite}/components/command-menu.tsx +1 -1
- package/{template → template-vite}/components/data-views/library-folder-tree-branch.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-recents.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-url-composer.tsx +1 -1
- package/{template → template-vite}/components/exxat-product-logo.tsx +3 -3
- package/{template → template-vite}/components/library-client.tsx +1 -1
- package/{template → template-vite}/components/library-hub-client.tsx +2 -2
- package/{template → template-vite}/components/library-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/library-table.tsx +35 -27
- package/{template → template-vite}/components/new-library-item-form.tsx +1 -1
- package/{template → template-vite}/components/page-breadcrumb-trail.tsx +1 -1
- package/{template → template-vite}/components/settings-client.tsx +1 -1
- package/{template → template-vite}/components/sidebar/app-sidebar.tsx +2 -2
- package/{template → template-vite}/components/sidebar/nav-main.tsx +1 -1
- package/{template → template-vite}/components/sidebar/nav-user.tsx +1 -1
- package/{template → template-vite}/components/sidebar/secondary-nav.tsx +1 -1
- package/{template → template-vite}/components/system-banner-slot.tsx +1 -1
- package/{template → template-vite}/components/templates/discovery-hub-template.tsx +2 -2
- package/{template → template-vite}/components/templates/new-focus-template.tsx +1 -1
- package/{template → template-vite}/components/tokens-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/tokens-themes-client.tsx +1 -1
- package/{template → template-vite}/hooks/use-secondary-panel-hub-nav.ts +1 -1
- package/template-vite/index.html +49 -0
- package/template-vite/lib/next-compat.tsx +98 -0
- package/{template → template-vite}/package.json +15 -27
- package/template-vite/scripts/port-next-imports.mjs +70 -0
- package/template-vite/src/App.tsx +103 -0
- package/template-vite/src/main.tsx +50 -0
- package/{template/app/(app)/error.tsx → template-vite/src/pages/_error.tsx} +12 -24
- package/{template/app/(app)/loading.tsx → template-vite/src/pages/_loading.tsx} +4 -2
- package/template-vite/src/pages/_not-found.tsx +17 -0
- package/template-vite/src/pages/dashboard.tsx +48 -0
- package/{template/app/(app)/help/page.tsx → template-vite/src/pages/help.tsx} +3 -2
- package/{template/app/(app)/library/layout.tsx → template-vite/src/pages/library/_layout.tsx} +18 -16
- package/{template/app/(app)/library/all/page.tsx → template-vite/src/pages/library/all.tsx} +1 -1
- package/{template/app/(app)/library/new/page.tsx → template-vite/src/pages/library/new.tsx} +12 -18
- package/template-vite/src/routes.tsx +72 -0
- package/template-vite/src/styles/globals.css +25 -0
- package/{template → template-vite}/tsconfig.json +5 -14
- package/template-vite/vite.config.ts +52 -0
- package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +0 -53
- package/template/.agents/skills/shadcn/SKILL.md +0 -242
- package/template/.agents/skills/shadcn/agents/openai.yml +0 -5
- package/template/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/template/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/template/.agents/skills/shadcn/cli.md +0 -257
- package/template/.agents/skills/shadcn/customization.md +0 -202
- package/template/.agents/skills/shadcn/evals/evals.json +0 -47
- package/template/.agents/skills/shadcn/mcp.md +0 -94
- package/template/.agents/skills/shadcn/rules/base-vs-radix.md +0 -306
- package/template/.agents/skills/shadcn/rules/composition.md +0 -195
- package/template/.agents/skills/shadcn/rules/forms.md +0 -192
- package/template/.agents/skills/shadcn/rules/icons.md +0 -101
- package/template/.agents/skills/shadcn/rules/styling.md +0 -162
- package/template/.cursor/rules/exxat-accessibility.mdc +0 -33
- package/template/.cursor/rules/exxat-data-tables.mdc +0 -32
- package/template/.cursor/rules/exxat-ds-agents.mdc +0 -26
- package/template/.cursor/rules/exxat-list-page-connected-views.mdc +0 -16
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +0 -40
- package/template/.nvmrc +0 -1
- package/template/.prettierignore +0 -7
- package/template/Logo/Exxat_Prism.svg +0 -39
- package/template/Logo/Exxat_one.svg +0 -36
- package/template/app/(app)/dashboard/loading.tsx +0 -18
- package/template/app/(app)/dashboard/page.tsx +0 -36
- package/template/app/(app)/layout.tsx +0 -77
- package/template/app/global-error.tsx +0 -63
- package/template/app/globals.css +0 -20
- package/template/app/layout.tsx +0 -133
- package/template/app/page.tsx +0 -9
- package/template/docs/HANDBOOK.md +0 -187
- package/template/docs/blueprints/README.md +0 -86
- package/template/docs/blueprints/_template.md +0 -91
- package/template/docs/blueprints/board-card.md +0 -123
- package/template/docs/blueprints/data-table.md +0 -139
- package/template/docs/blueprints/key-metrics.md +0 -128
- package/template/docs/blueprints/list-page-template.md +0 -123
- package/template/docs/blueprints/page-header.md +0 -130
- package/template/docs/card-vs-rows-pattern.md +0 -36
- package/template/docs/collaboration-access-pattern.md +0 -116
- package/template/docs/command-menu-pattern.md +0 -45
- package/template/docs/component-selection-guide.md +0 -224
- package/template/docs/components-audit-2026-05.md +0 -158
- package/template/docs/consumer-upgrade-checklist.md +0 -52
- package/template/docs/data-views-pattern.md +0 -185
- package/template/docs/drawer-vs-dialog-pattern.md +0 -50
- package/template/docs/glossary.md +0 -59
- package/template/docs/hub-supported-views-pattern.md +0 -53
- package/template/docs/jobs/README.md +0 -59
- package/template/docs/jobs/record-detail.md +0 -177
- package/template/docs/kpi-flat-band-pattern.md +0 -57
- package/template/docs/kpi-strip-max-four-pattern.md +0 -30
- package/template/docs/kpi-trend-pattern.md +0 -58
- package/template/docs/large-dataset-strategy.md +0 -155
- package/template/docs/library-hub-header-pattern.md +0 -25
- package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +0 -95
- package/template/docs/migrations/0002-exxat-token-namespace.md +0 -154
- package/template/docs/migrations/0003-globals-css-canonical.md +0 -110
- package/template/docs/migrations/README.md +0 -100
- package/template/docs/migrations/_template.md +0 -64
- package/template/docs/modern-saas-patterns.md +0 -165
- package/template/docs/perf-memory-pattern.md +0 -206
- package/template/docs/reference-implementations.md +0 -153
- package/template/docs/shell-surface-elevation-pattern.md +0 -52
- package/template/docs/token-taxonomy.md +0 -416
- package/template/docs/voice-and-tone.md +0 -262
- package/template/ecosystem.config.cjs +0 -32
- package/template/next.config.mjs +0 -216
- package/template/postcss.config.mjs +0 -8
- package/template/public/favicon/favicon.ico +0 -0
- package/template/tests/setup.ts +0 -26
- package/template/vitest.config.ts +0 -18
- /package/{template → template-vite}/.cursor/rules/exxat-dashboard-view-charts.mdc +0 -0
- /package/{template → template-vite}/.prettierrc +0 -0
- /package/{template → template-vite}/AGENTS.md +0 -0
- /package/{template → template-vite}/README.md +0 -0
- /package/{template → template-vite}/components/.gitkeep +0 -0
- /package/{template → template-vite}/components/ask-leo-composer.tsx +0 -0
- /package/{template → template-vite}/components/brand-color-picker.tsx +0 -0
- /package/{template → template-vite}/components/chart-area-interactive.tsx +0 -0
- /package/{template → template-vite}/components/charts-overview.tsx +0 -0
- /package/{template → template-vite}/components/collaboration-access-flow.tsx +0 -0
- /package/{template → template-vite}/components/columns-client.tsx +0 -0
- /package/{template → template-vite}/components/columns-showcase.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-promo-banner.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-quota-progress-card.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-report-charts.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-section-heading.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-tabs.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-date-calendar.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-text-value-input.tsx +0 -0
- /package/{template → template-vite}/components/data-table/index.tsx +0 -0
- /package/{template → template-vite}/components/data-table/pagination.tsx +0 -0
- /package/{template → template-vite}/components/data-table/types.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.test.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.ts +0 -0
- /package/{template → template-vite}/components/data-views/board-card-primitives.tsx +0 -0
- /package/{template → template-vite}/components/data-views/data-row-list.tsx +0 -0
- /package/{template → template-vite}/components/data-views/finder-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/folder-grid-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/hub-table.tsx +0 -0
- /package/{template → template-vite}/components/data-views/index.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-card.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-template.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-connected-view-body.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-details-placeholder.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-chrome.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-tokens.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-column-header.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-view-frame.tsx +0 -0
- /package/{template → template-vite}/components/data-views/os-folder-glyph.tsx +0 -0
- /package/{template → template-vite}/components/data-views/outline-tree-menu.tsx +0 -0
- /package/{template → template-vite}/components/data-views/table-cells.tsx +0 -0
- /package/{template → template-vite}/components/dev-chunk-load-recovery.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.test.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.tsx +0 -0
- /package/{template → template-vite}/components/folder-details-shell.tsx +0 -0
- /package/{template → template-vite}/components/form-layout-01.tsx +0 -0
- /package/{template → template-vite}/components/hub-tree-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/invite-collaborators-drawer.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics-ask-leo-bridge.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics.tsx +0 -0
- /package/{template → template-vite}/components/leo-insight-indicator.tsx +0 -0
- /package/{template → template-vite}/components/leo-typing-dots.tsx +0 -0
- /package/{template → template-vite}/components/library-board-view.tsx +0 -0
- /package/{template → template-vite}/components/library-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/library-favorite-button.tsx +0 -0
- /package/{template → template-vite}/components/library-new-folder-sheet.tsx +0 -0
- /package/{template → template-vite}/components/library-os-folder-view.tsx +0 -0
- /package/{template → template-vite}/components/library-page-header.tsx +0 -0
- /package/{template → template-vite}/components/library-panel-activator.tsx +0 -0
- /package/{template → template-vite}/components/list-hub-status-badge.tsx +0 -0
- /package/{template → template-vite}/components/list-page-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/getting-started.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/index.ts +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-01.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-02.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-03.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-04.tsx +0 -0
- /package/{template → template-vite}/components/page-header.tsx +0 -0
- /package/{template → template-vite}/components/product-switcher.tsx +0 -0
- /package/{template → template-vite}/components/product-wordmark.tsx +0 -0
- /package/{template → template-vite}/components/settings-appearance-card.tsx +0 -0
- /package/{template → template-vite}/components/settings-form-row.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/app-sidebar-dynamic.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/index.ts +0 -0
- /package/{template → template-vite}/components/sidebar/nav-documents.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/nav-secondary.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/secondary-panel.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-collapse.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-open.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-shell.tsx +0 -0
- /package/{template → template-vite}/components/site-header.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/column-row.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/draggable-list.ts +0 -0
- /package/{template → template-vite}/components/table-properties/drawer-button.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/drawer.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/filter-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/index.ts +0 -0
- /package/{template → template-vite}/components/table-properties/sort-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/types.ts +0 -0
- /package/{template → template-vite}/components/task-list-panel.tsx +0 -0
- /package/{template → template-vite}/components/task-priority-badge.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-landing-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-results-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/list-page.tsx +0 -0
- /package/{template → template-vite}/components/templates/nested-secondary-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/templates/primary-page-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/secondary-panel-hub-template.tsx +0 -0
- /package/{template → template-vite}/components/theme-color-sync.tsx +0 -0
- /package/{template → template-vite}/components/theme-provider.tsx +0 -0
- /package/{template → template-vite}/components/tinted-icon-disc.tsx +0 -0
- /package/{template → template-vite}/components/tokens-hub-auxiliary-views.tsx +0 -0
- /package/{template → template-vite}/components/tokens-themes-section.tsx +0 -0
- /package/{template → template-vite}/components/ui/accordion.tsx +0 -0
- /package/{template → template-vite}/components/ui/ai-thinking-surface.tsx +0 -0
- /package/{template → template-vite}/components/ui/alert-dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/avatar.tsx +0 -0
- /package/{template → template-vite}/components/ui/badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/banner.tsx +0 -0
- /package/{template → template-vite}/components/ui/breadcrumb.tsx +0 -0
- /package/{template → template-vite}/components/ui/button.tsx +0 -0
- /package/{template → template-vite}/components/ui/calendar.tsx +0 -0
- /package/{template → template-vite}/components/ui/card.tsx +0 -0
- /package/{template → template-vite}/components/ui/chart.tsx +0 -0
- /package/{template → template-vite}/components/ui/checkbox.tsx +0 -0
- /package/{template → template-vite}/components/ui/coach-mark.tsx +0 -0
- /package/{template → template-vite}/components/ui/collapsible.tsx +0 -0
- /package/{template → template-vite}/components/ui/command.tsx +0 -0
- /package/{template → template-vite}/components/ui/context-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/date-picker-field.tsx +0 -0
- /package/{template → template-vite}/components/ui/dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/dot-pattern.tsx +0 -0
- /package/{template → template-vite}/components/ui/drag-handle-grip.tsx +0 -0
- /package/{template → template-vite}/components/ui/dropdown-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/field.tsx +0 -0
- /package/{template → template-vite}/components/ui/form.tsx +0 -0
- /package/{template → template-vite}/components/ui/hover-card.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-mask.tsx +0 -0
- /package/{template → template-vite}/components/ui/input.tsx +0 -0
- /package/{template → template-vite}/components/ui/kbd.tsx +0 -0
- /package/{template → template-vite}/components/ui/label.tsx +0 -0
- /package/{template → template-vite}/components/ui/leo-icon.tsx +0 -0
- /package/{template → template-vite}/components/ui/payment-card-fields.tsx +0 -0
- /package/{template → template-vite}/components/ui/popover.tsx +0 -0
- /package/{template → template-vite}/components/ui/radio-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/resizable.tsx +0 -0
- /package/{template → template-vite}/components/ui/scroll-area.tsx +0 -0
- /package/{template → template-vite}/components/ui/select.tsx +0 -0
- /package/{template → template-vite}/components/ui/selection-tile-grid.tsx +0 -0
- /package/{template → template-vite}/components/ui/separator.tsx +0 -0
- /package/{template → template-vite}/components/ui/sheet.tsx +0 -0
- /package/{template → template-vite}/components/ui/sidebar.tsx +0 -0
- /package/{template → template-vite}/components/ui/skeleton.tsx +0 -0
- /package/{template → template-vite}/components/ui/slider.tsx +0 -0
- /package/{template → template-vite}/components/ui/sonner.tsx +0 -0
- /package/{template → template-vite}/components/ui/status-badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/table.tsx +0 -0
- /package/{template → template-vite}/components/ui/tabs.tsx +0 -0
- /package/{template → template-vite}/components/ui/textarea.tsx +0 -0
- /package/{template → template-vite}/components/ui/tip.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-switch.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle.tsx +0 -0
- /package/{template → template-vite}/components/ui/tooltip.tsx +0 -0
- /package/{template → template-vite}/components/ui/view-segmented-control.tsx +0 -0
- /package/{template → template-vite}/components.json +0 -0
- /package/{template → template-vite}/contexts/chart-variant-context.tsx +0 -0
- /package/{template → template-vite}/contexts/command-menu-context.tsx +0 -0
- /package/{template → template-vite}/contexts/dashboard-view-context.tsx +0 -0
- /package/{template → template-vite}/contexts/product-context.tsx +0 -0
- /package/{template → template-vite}/contexts/system-banner-context.tsx +0 -0
- /package/{template → template-vite}/eslint.config.mjs +0 -0
- /package/{template → template-vite}/fontawesome-subset.manifest.json +0 -0
- /package/{template → template-vite}/hooks/.gitkeep +0 -0
- /package/{template → template-vite}/hooks/use-app-theme.ts +0 -0
- /package/{template → template-vite}/hooks/use-coach-mark.ts +0 -0
- /package/{template → template-vite}/hooks/use-location-hash.ts +0 -0
- /package/{template → template-vite}/hooks/use-mobile.ts +0 -0
- /package/{template → template-vite}/hooks/use-mod-key-label.ts +0 -0
- /package/{template → template-vite}/hooks/use-sidebar-reflow-zoom.ts +0 -0
- /package/{template → template-vite}/lib/.gitkeep +0 -0
- /package/{template → template-vite}/lib/ask-leo-route-context.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.test.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.ts +0 -0
- /package/{template → template-vite}/lib/chart-line-dash.ts +0 -0
- /package/{template → template-vite}/lib/chunk-load-error.ts +0 -0
- /package/{template → template-vite}/lib/coach-mark-registry.ts +0 -0
- /package/{template → template-vite}/lib/collaborator-access.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-config.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-search-data.ts +0 -0
- /package/{template → template-vite}/lib/conditional-rule-match.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-customize-coach-mark.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-layout-merge.ts +0 -0
- /package/{template → template-vite}/lib/data-list-display-options.ts +0 -0
- /package/{template → template-vite}/lib/data-list-persistence.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-registry.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-surface.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view.ts +0 -0
- /package/{template → template-vite}/lib/data-view-dashboard-storage.ts +0 -0
- /package/{template → template-vite}/lib/date-filter.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-recents.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-url.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.test.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.ts +0 -0
- /package/{template → template-vite}/lib/discovery-hub.ts +0 -0
- /package/{template → template-vite}/lib/editable-target.ts +0 -0
- /package/{template → template-vite}/lib/exxat-palette.json +0 -0
- /package/{template → template-vite}/lib/exxat-palette.ts +0 -0
- /package/{template → template-vite}/lib/floating-sheet-panel.ts +0 -0
- /package/{template → template-vite}/lib/full-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/hub-connected-view-renderers.ts +0 -0
- /package/{template → template-vite}/lib/initials-from-name.ts +0 -0
- /package/{template → template-vite}/lib/library-authoring.ts +0 -0
- /package/{template → template-vite}/lib/library-dedicated-search.ts +0 -0
- /package/{template → template-vite}/lib/library-hub-search.ts +0 -0
- /package/{template → template-vite}/lib/library-nav.ts +0 -0
- /package/{template → template-vite}/lib/library-recent-searches.ts +0 -0
- /package/{template → template-vite}/lib/library-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-page-table-properties.ts +0 -0
- /package/{template → template-vite}/lib/list-status-badges.ts +0 -0
- /package/{template → template-vite}/lib/logo-dev.ts +0 -0
- /package/{template → template-vite}/lib/mailto.ts +0 -0
- /package/{template → template-vite}/lib/mock/dashboard.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-folders.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-header-collaborators.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-inspector.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-kpi.ts +0 -0
- /package/{template → template-vite}/lib/mock/library.ts +0 -0
- /package/{template → template-vite}/lib/mock/navigation.tsx +0 -0
- /package/{template → template-vite}/lib/motion-ui.ts +0 -0
- /package/{template → template-vite}/lib/product-brand.ts +0 -0
- /package/{template → template-vite}/lib/raf-throttle.ts +0 -0
- /package/{template → template-vite}/lib/row-height.ts +0 -0
- /package/{template → template-vite}/lib/sidebar-state-cookie.ts +0 -0
- /package/{template → template-vite}/lib/stock-portrait.ts +0 -0
- /package/{template → template-vite}/lib/table-state-lifecycle.ts +0 -0
- /package/{template → template-vite}/lib/utils.test.ts +0 -0
- /package/{template → template-vite}/lib/utils.ts +0 -0
- /package/{template → template-vite}/public/.gitkeep +0 -0
- /package/{template → template-vite}/public/Illustration/Rotation.svg +0 -0
- /package/{template → template-vite}/public/avatars/user.svg +0 -0
- /package/{template/public → template-vite/public/favicon}/favicon.ico +0 -0
- /package/{template/app → template-vite/public}/favicon.ico +0 -0
- /package/{template → template-vite}/public/folders/icons8-folder-windows-11.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-one.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-prism.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/emory.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/rush.svg +0 -0
- /package/{template → template-vite}/scripts/fontawesome-subset-audit.mjs +0 -0
- /package/{template → template-vite}/scripts/pm2-startup-macos.sh +0 -0
- /package/{template → template-vite}/skills-lock.json +0 -0
- /package/{template/app/(app)/columns/page.tsx → template-vite/src/pages/columns.tsx} +0 -0
- /package/{template/app/(app)/library/find/page.tsx → template-vite/src/pages/library/find.tsx} +0 -0
- /package/{template/app/(app)/library/page.tsx → template-vite/src/pages/library/index.tsx} +0 -0
- /package/{template/app/(app)/library/list/page.tsx → template-vite/src/pages/library/list.tsx} +0 -0
- /package/{template/app/(app)/settings/page.tsx → template-vite/src/pages/settings.tsx} +0 -0
- /package/{template/app/(app)/tokens-themes/page.tsx → template-vite/src/pages/tokens-themes.tsx} +0 -0
- /package/{template → template-vite}/stores/app-store.ts +0 -0
- /package/{template → template-vite}/types/react-payment-inputs.d.ts +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Do not duplicate parent navigation with a Back control when breadcrumbs exist
|
|
3
|
+
globs: apps/web/**/*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — breadcrumbs and back navigation
|
|
9
|
+
|
|
10
|
+
## MUST NOT
|
|
11
|
+
|
|
12
|
+
When a page uses **`SiteHeader`** with **`breadcrumbs`** (a visible trail such as Patterns → Library → current title), **do not** add a **“Back to …”** link or button in the page body that goes to the same parent as the breadcrumb segment. Breadcrumbs already provide hierarchy and one-click navigation up the tree.
|
|
13
|
+
|
|
14
|
+
## MAY
|
|
15
|
+
|
|
16
|
+
- Rely on **`SiteHeader`** breadcrumbs only for returning to parent routes.
|
|
17
|
+
- Use a **single** explicit back affordance on flows that **omit** breadcrumbs by design (e.g. full-screen step, modal route) where product copy requires it.
|
|
18
|
+
|
|
19
|
+
## See also
|
|
20
|
+
|
|
21
|
+
- `components/site-header.tsx`
|
|
22
|
+
- `components/templates/primary-page-template.tsx`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — when to use cards vs DataTable rows vs simple list rows.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — cards vs rows vs lists
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
1. **Dense, comparable records (10+)** — **`DataTable`** + **`ListPageTemplate`** + **`useTableState`** for primary hubs (**`exxat-data-tables.mdc`**).
|
|
12
|
+
2. **Board / tiles / visual browse** — **`ListPageBoardCard`** (and related shells), **`ListPageViewFrame`** for non-table bodies (**`exxat-board-cards.mdc`**, **`exxat-list-page-view-shells.mdc`**).
|
|
13
|
+
3. **One dataset** — Cards and tables read the **same** **`tableState.rows`**; no forked mock arrays (**`exxat-centralized-list-dataset.mdc`**).
|
|
14
|
+
|
|
15
|
+
## MUST NOT
|
|
16
|
+
|
|
17
|
+
- Replace a **primary data hub grid** with a **card wall** when users need column sort, filter chips, and export parity.
|
|
18
|
+
- Introduce a **second table stack** for the same entity.
|
|
19
|
+
|
|
20
|
+
## See also
|
|
21
|
+
|
|
22
|
+
- **`docs/card-vs-rows-pattern.md`** · **`.cursor/skills/exxat-card-vs-list-rows/SKILL.md`**
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Single source of truth for hub rows, KPIs, table properties, detail views, and shared view chrome — dataset + presentation consistency across DataListViewType surfaces. Auto-attaches when editing list-hub React files.
|
|
3
|
+
globs: apps/web/{components,lib}/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — centralized list hub dataset + presentation
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.1** and **§4.5** (connected views + view shells), **`docs/data-views-pattern.md`**, **`.cursor/rules/exxat-list-page-connected-views.mdc`**, and **`.cursor/rules/exxat-list-page-view-shells.mdc`**. This rule tightens **where data lives** and **where reusable view chrome lives** so **table, list, board, dashboard, folder, panel, tree, and inspectors** never drift.
|
|
11
|
+
|
|
12
|
+
## Single dataset
|
|
13
|
+
|
|
14
|
+
1. **Rows** — One typed collection per entity (**`lib/mock/<entity>.ts`** until API wiring). **MUST NOT** maintain a second parallel array (e.g. “tree only” or “panel only”) with overlapping IDs unless it is a **derived view** (computed from the same source via selectors).
|
|
15
|
+
|
|
16
|
+
2. **`useTableState`** — One instance per tab table; **`tableState.rows`** is the **only** filtered/sorted row bag that **list / board / dashboard / folder / panel / tree** surfaces read. **MUST NOT** bypass filters by importing raw mock arrays into alternate views.
|
|
17
|
+
|
|
18
|
+
3. **Detail / inspector / finder / split-panel bodies** — Resolve the active record by **`id`** (or stable key) against **`tableState.rows`** (or the same upstream state that feeds **`useTableState`**). **MUST NOT** hydrate inspector fields from ad-hoc literals when the row already exists on the dataset.
|
|
19
|
+
|
|
20
|
+
4. **Columns & table “properties”** — Column defs **`accessorKey` / cell renderers** map to the **same** row interface as KPI helpers and board cards. **MUST NOT** introduce incompatible duplicate TypeScript shapes per view.
|
|
21
|
+
|
|
22
|
+
5. **`TablePropertiesDrawer`** — Stays wired to the **`DataTable`** that owns **`useTableState`** (**§4.2**): **`currentView`**, **`onViewChange`**, column visibility/density — all reflect manipulation of the **same** underlying rows.
|
|
23
|
+
|
|
24
|
+
6. **Labels / status / chips** — Prefer shared maps (**`lib/list-status-badges.ts`**, **`lib/data-list-view.ts`** tiles, entity-specific maps **next to** **`lib/mock/<entity>.ts`**). **MUST NOT** fork label strings or badge colors per view file.
|
|
25
|
+
|
|
26
|
+
7. **KPIs & charts** — **`MetricItem`** / **`MetricInsight`** builders take **`tableState.rows`** (or equivalent filtered list). Same inputs as the grid after search/filters.
|
|
27
|
+
|
|
28
|
+
## Centralized presentation (layout + components)
|
|
29
|
+
|
|
30
|
+
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`**).
|
|
31
|
+
|
|
32
|
+
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**).
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
## MUST NOT
|
|
37
|
+
|
|
38
|
+
- Ship alternate mock datasets per **`DataListViewType`** for the same hub without documenting them as **computed derivatives** of one canonical list.
|
|
39
|
+
- Duplicate entity fields in inspector-only types that contradict **`LibraryItem`** / **`Placement`** / etc.; extend the shared interface **once**.
|
|
40
|
+
|
|
41
|
+
## See also
|
|
42
|
+
|
|
43
|
+
- **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** — workflow + checklist.
|
|
44
|
+
- **`.cursor/rules/exxat-table-properties-drawer.mdc`** — **`currentView`** / **`onViewChange`**.
|
|
45
|
+
- **`exxat-list-page-connected-views.mdc`** — toolbar + **`tableState.rows`**.
|
|
46
|
+
- **`exxat-list-page-view-shells.mdc`** — **`ListPageViewFrame`** + **`data-views/`** extraction.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Shared hubs — collaboration header, invite sheet, library access vs directory roles
|
|
3
|
+
globs: apps/web/components/**/*.{tsx,ts},apps/web/lib/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — collaboration & access
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.7** and **`apps/web/docs/collaboration-access-pattern.md`**.
|
|
11
|
+
|
|
12
|
+
## MUST
|
|
13
|
+
|
|
14
|
+
1. **Shared hubs** — Use **`PageHeader`** **`variant="collaboration"`** with **`collaborators`** (`PageHeaderCollaborator[]`) and optional **`accessInfo`**.
|
|
15
|
+
2. **Header chrome** — **Empty roster:** outline **Add collaborator** (`COLLABORATION_HEADER_ADD_LABEL` / **`addCollaboratorLabel`**) opens the invite sheet. **Non-empty roster:** face rail only; faces and **`+N`** open the same sheet via **`onCollaboratorsOpen`**.
|
|
16
|
+
3. **Invite entry** — **⋯ More** → **Invite people** on the entity page header; opens **`InviteCollaboratorsDrawer`** (floating sheet, same family as **`ExportDrawer`**).
|
|
17
|
+
4. **State** — Prefer **`CollaborationAccessFlow`** on hub **`*-client.tsx`**; otherwise own **`collaborators`** + **`inviteOpen`**. Successful invite updates **`collaborators`** so the header and sheet roster match.
|
|
18
|
+
5. **Library access** — Labels and invite options from **`lib/collaborator-access.ts`**; trailing roster badge = access (Owner / Editor / Commenter / Viewer).
|
|
19
|
+
6. **Directory roles** — Optional **`roles`** on **`PageHeaderCollaborator`** as **`Badge variant="outline"`** chips (Faculty, Program coordinator, Director) — **not** library access.
|
|
20
|
+
7. **Roster layout** — One bordered list with row dividers; per row: **name**, **email**, role tags, access badge — **not** one card per person.
|
|
21
|
+
8. **Invite field** — **`FieldGroup`** + **`Field`**; combined email + access row uses **`InputGroup`** + **`InputGroupInput`** + **`InputGroupAddon`** with shadcn **`Select`** (**`SelectGroup`** / **`SelectItem`**, **`position="popper"`**); persistent **`FieldDescription`** for email format; **no** **`toast()`** (**`exxat-no-toast.mdc`**).
|
|
22
|
+
|
|
23
|
+
## MUST NOT
|
|
24
|
+
|
|
25
|
+
- A second invite control **beside** a populated face rail.
|
|
26
|
+
- Fork access enums/labels per hub — extend **`collaborator-access.ts`** once.
|
|
27
|
+
- Use **`Select`** inside **`InputGroupAddon`** without **`InputGroupInput`** / **`SelectGroup`**; put email above name in the roster (order is **name → email → role tags**).
|
|
28
|
+
|
|
29
|
+
## See also
|
|
30
|
+
|
|
31
|
+
- **`exxat-page-vs-drawer.mdc`** — invite is a **sheet**, not a new route.
|
|
32
|
+
- **`exxat-kbd-shortcuts.mdc`** — workflow **Cancel** / **Send invite** shortcuts on the sheet.
|
|
33
|
+
- **`exxat-library-hub-header.mdc`** — Library library: when URL is **folder-scoped**, **⋯ More** also includes **Customize folder** (hub client hosts **`LibraryNewFolderSheet`**).
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Exxat DS — global command palette (⌘K) as search + quick AI vs Ask Leo for long answers.
|
|
3
3
|
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Exxat DS — global command palette (`CommandMenu`)
|
|
7
8
|
|
|
8
9
|
## Intent
|
|
9
10
|
|
|
10
|
-
- **`CommandMenu`** (**⌘K** / **Ctrl+K**) is **global search** (routes, library, patterns, AI starters, optional row data such as student names / question stems) — see **`AGENTS.md` §7.1** and **`docs/command-menu-pattern.md`**.
|
|
11
|
+
- **`CommandMenu`** (**⌘K** / **Ctrl+K**) is **global search** (routes, library, patterns, AI starters, optional row data such as student names / question stems) — see **`apps/web/AGENTS.md` §7.1** and **`apps/web/docs/command-menu-pattern.md`**.
|
|
11
12
|
- **Quick / lookup / short AI:** Prefer **results inside the palette** when the product can return compact answers or lightweight “research” without leaving the flow.
|
|
12
13
|
- **Long or complex answers:** **Ask Leo** side panel (**⌘⌥K** / **Ctrl+Alt+K**)—not forced into the palette.
|
|
13
14
|
|
|
14
15
|
## Implementation pointers
|
|
15
16
|
|
|
16
|
-
- Shell: `components/command-menu.tsx`; config **`buildCommandMenuConfig()`** in **`lib/command-menu-config.ts`**; optional **`dataGroups`** from **`lib/command-menu-search-data.ts`** (e.g. **`getCommandMenuSearchDataGroups()`**), wired in **`app/(app)/layout.tsx`**. Keep domain mapping out of the shell.
|
|
17
|
-
- Large indexes: set **`searchOnly: true`** on **`CommandMenuGroup`** so **`command-menu.tsx`** skips the group until the user types (avoids listing every row on open).
|
|
18
|
-
- Sidebar **“Search or ask Leo”** opens the same palette.
|
|
17
|
+
- Shell: `apps/web/components/command-menu.tsx`; config **`buildCommandMenuConfig()`** in **`apps/web/lib/command-menu-config.ts`**; optional **`dataGroups`** from **`apps/web/lib/command-menu-search-data.ts`** (e.g. **`getCommandMenuSearchDataGroups()`**), wired in **`apps/web/app/(app)/layout.tsx`**. Keep domain mapping out of the shell.
|
|
18
|
+
- Large indexes: set **`searchOnly: true`** on **`CommandMenuGroup`** so **`command-menu.tsx`** skips the group until the user types (avoids listing every row on open; cmdk shows all items when the search string is empty).
|
|
19
19
|
|
|
20
20
|
## See also
|
|
21
21
|
|
|
22
22
|
- **`apps/web/AGENTS.md` §7.1**
|
|
23
|
-
-
|
|
23
|
+
- **`exxat-kbd-shortcuts.mdc`** (⌘K vs ⌘⌥K)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — product data tables must use HubTable/DataTable with search, filters, table properties; no alternate table stacks. Auto-attaches when editing apps/web React files; ask for it explicitly when designing a list/grid surface.
|
|
3
|
+
globs: apps/web/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — data tables (mandatory pattern)
|
|
9
|
+
|
|
10
|
+
## Use one stack for product data lists
|
|
11
|
+
|
|
12
|
+
For **any app screen that shows a browsable, filterable grid of records** (directories, tokens, columns showcase, question banks, etc.):
|
|
13
|
+
|
|
14
|
+
1. **Inside `ListPageTemplate` (every primary hub + every showcase page that mounts a hub-style grid) MUST use `HubTable`** from `@/components/data-views`. `HubTable` is the **canonical** wrapper that wires `useTableState`, the toolbar (**search + filter chips + filter dropdown + sort**), the **Table properties drawer**, view-type tiles, bulk-actions, and conditional rules in one place. Pages that drop down to raw `<DataTable>` silently lose filters and Properties — **MUST NOT** ship a hub or showcase that way.
|
|
15
|
+
2. **Outside `ListPageTemplate`** (rare — e.g. embedded mini-grids, modal sub-tables, drawer body lists), use `DataTable` from `@/components/data-table` directly. Even then, prefer composing a small `toolbarSlot` with `TablePropertiesDrawerButton` whenever the grid is more than a handful of rows.
|
|
16
|
+
3. **Pagination:** Wrap with `DataTablePaginated` when server-style paging is required (Placements is the reference).
|
|
17
|
+
4. **Search:** `HubTable` wires the toolbar search automatically; only consumers that pass `searchable={false}` to `displayOptionsInit` can opt out, and they MUST justify it in the call-site comment.
|
|
18
|
+
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.
|
|
19
|
+
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.
|
|
20
|
+
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”).
|
|
21
|
+
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.
|
|
22
|
+
9. **Add view parity** — **`FULL_HUB_SUPPORTED_VIEWS`** + a real renderer per view — **`.cursor/rules/exxat-hub-supported-views.mdc`**, **`apps/web/docs/hub-supported-views-pattern.md`**.
|
|
23
|
+
|
|
24
|
+
**Reference implementations:**
|
|
25
|
+
|
|
26
|
+
- `components/library-table.tsx` + `components/library-client.tsx` — **canonical** seven-view hub (All questions).
|
|
27
|
+
- `components/columns-showcase.tsx` — cell-pattern catalog via **`LibraryTable`** (`columnDefs` + folder state); same Add view as Library.
|
|
28
|
+
- `components/tokens-themes-client.tsx` + `components/tokens-hub-auxiliary-views.tsx` — tokens hub with **`FULL_HUB_SUPPORTED_VIEWS`**.
|
|
29
|
+
|
|
30
|
+
## Do not
|
|
31
|
+
|
|
32
|
+
- Do **not** build product list pages with `@/components/ui/table` alone, raw `<table>` markup, or third-party data grids.
|
|
33
|
+
- Do **not** mount raw `<DataTable>` inside `ListPageTemplate.renderContent` — use `HubTable`. Raw `<DataTable>` does not ship the Properties drawer or filter chips; users lose discoverability.
|
|
34
|
+
- Do **not** introduce a second “table component” pattern for the same product surfaces (splitting search/filters/properties across incompatible implementations).
|
|
35
|
+
- 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.
|
|
36
|
+
- Do **not** trim **`supportedViewTypes`** to table-only or **`PRIMARY_HUB_SUPPORTED_VIEWS`** on hubs that should match Library — see **`exxat-hub-supported-views.mdc`**.
|
|
37
|
+
|
|
38
|
+
## Exceptions
|
|
39
|
+
|
|
40
|
+
- **Tiny, read-only tables inside charts or analytics cards** (e.g. chart figure captions / summary matrices) may use minimal markup when they are not primary data-list experiences — still prefer tokens and accessibility, but the full hub stack is not required there.
|
|
41
|
+
- **Drawer body and dialog sub-grids** (small, secondary, scoped to a transient flow) — raw `<DataTable>` is acceptable; still expose search if rows > ~10.
|
|
42
|
+
- **Documented narrow allowlists** in `lib/*-supported-views.ts` when product truly omits folder/panel/tree (comment required).
|
|
43
|
+
|
|
44
|
+
## See also
|
|
45
|
+
|
|
46
|
+
- **`apps/web/AGENTS.md`** — full MUST/MUST NOT, list-page template, primary hubs, checklist.
|
|
47
|
+
- **`.cursor/rules/exxat-hub-supported-views.mdc`** — Add view menu + renderer matrix.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Dedicated search surfaces — DedicatedSearch* templates, recents storage, landing-vs-results URL composer. Auto-attaches when editing search-related React routes.
|
|
3
|
+
globs: apps/web/{components,app,lib}/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — dedicated search surfaces
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.8**, **`.cursor/skills/exxat-dedicated-search-surfaces/SKILL.md`**.
|
|
11
|
+
|
|
12
|
+
## Intent
|
|
13
|
+
|
|
14
|
+
Some hubs use **one route** (or sibling routes) with **empty vs non-empty** primary search query:
|
|
15
|
+
|
|
16
|
+
- **Landing** — centered hero, composer, optional recents; no results grid (or minimal chrome).
|
|
17
|
+
- **Results** — `ListPageTemplate` + table/list driven by the same filtered row bag as the rest of the hub.
|
|
18
|
+
|
|
19
|
+
Shared building blocks use **generic** `DedicatedSearch*` names under `components/`, `components/templates/`, and `lib/dedicated-search-*.ts`. Domain code passes **`patchSearchParams`**, **recents controller**, and copy.
|
|
20
|
+
|
|
21
|
+
## MUST
|
|
22
|
+
|
|
23
|
+
- Keep **first paint** of recents **storage-free** (no `localStorage` in `useState` initial state) — see skill.
|
|
24
|
+
- Prefer **`DedicatedSearchLandingTemplate`**, **`DedicatedSearchUrlComposer`**, **`DedicatedSearchRecents`**, **`DedicatedSearchResultsHeaderChrome`**, **`DEDICATED_SEARCH_RESULTS_OUTER_CONTENT_CLASSNAME`** before copying markup into a new hub.
|
|
25
|
+
|
|
26
|
+
## MUST NOT
|
|
27
|
+
|
|
28
|
+
- Introduce parallel `*LibrarySearchLanding*` (or similar) components for another entity — extend the generic layer and compose in the hub client.
|
|
29
|
+
|
|
30
|
+
## See also
|
|
31
|
+
|
|
32
|
+
- **`exxat-list-page-connected-views.mdc`**, **`exxat-centralized-list-dataset.mdc`** — results branch still uses one row bag + `ListPageTemplate`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — drawer/sheet vs modal dialog vs route for flows and confirmations.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — drawer vs dialog
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
1. **Sheet panel** — Hub-**adjacent** work where **parent context** (list, filters, selection) stays relevant: properties, export, invites, long option lists beside the grid. Use **`Sheet`** (`showOverlay={false}`, floating inset) — **MUST NOT** add a Vaul `Drawer` primitive.
|
|
12
|
+
2. **Dialog** — **Blocking**, **short** focus: destructive confirm, legal/acknowledgment, single-step choice, alert when the user must not interact with the page behind until resolved.
|
|
13
|
+
3. **Route** — **Primary**, **long**, or **bookmarkable** flows — **`AGENTS.md` §6.4**, **`.cursor/rules/exxat-page-vs-drawer.mdc`**.
|
|
14
|
+
|
|
15
|
+
## MUST NOT
|
|
16
|
+
|
|
17
|
+
- Put **irreversible delete** only in a dismissible toast — use **dialog** (or sheet with explicit confirm) per **`exxat-no-toast.mdc`**.
|
|
18
|
+
- Use a **centered dialog** for **wide tables of export columns** when a **sheet panel** matches mental model and space.
|
|
19
|
+
|
|
20
|
+
## See also
|
|
21
|
+
|
|
22
|
+
- **`docs/drawer-vs-dialog-pattern.md`** · **`.cursor/skills/exxat-drawer-vs-dialog/SKILL.md`**
|
|
23
|
+
- **`exxat-page-vs-drawer.mdc`** (drawer vs **route**)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — follow AGENTS.md; DataTable, ListPageTemplate, primary hubs, export, Kbd
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — AI handbook (binding)
|
|
8
|
+
|
|
9
|
+
**App location:** `apps/web/` in this **pnpm monorepo**. The legacy single-folder layout (`exxat-ds/`) was migrated in 2026-05; the path no longer exists. Shared design system lives at `packages/ui/` (`@exxatdesignux/ui`).
|
|
10
|
+
|
|
11
|
+
**Read `AGENTS.md`:** `apps/web/AGENTS.md` from the monorepo root, or `./AGENTS.md` when the workspace is the `apps/web/` folder only (same file).
|
|
12
|
+
|
|
13
|
+
Before implementing or reviewing **list / table / board / dashboard / data-heavy** pages, read **AGENTS.md** (rule precedence, MUST/MUST NOT, file references, §4.1–§4.8, **§6.4** page vs drawer, **§6.5** no toast, §7.1 command palette, §13 checklist). **Before** adding **new shared UI** not covered by existing **`components/`** patterns, read **`exxat-reuse-before-custom.mdc`**.
|
|
14
|
+
|
|
15
|
+
## Top of stack — read FIRST on any design task
|
|
16
|
+
|
|
17
|
+
**`.cursor/skills/exxat-senior-ux/SKILL.md`** is the persona every other rule and skill is downstream of. It defines the **five-step protocol** (Discovery → Research → Synthesis → Build → Audit), the **brief format**, and the **push-back triggers**. Pair with:
|
|
18
|
+
|
|
19
|
+
- **`.cursor/rules/exxat-ux-discovery-protocol.mdc`** — brief-before-code gate + question bank per surface type. **`alwaysApply: true`** — fires on every design task automatically.
|
|
20
|
+
- **`.cursor/rules/exxat-ux-principles.mdc`** — 20 principles (P1–P20) split into **always-follow (P1–P8)** and **default-follow with stated reason (P9–P20)**. Every deviation MUST be named in the design brief.
|
|
21
|
+
- **`apps/web/docs/modern-saas-patterns.md`** — the 12 modern SaaS patterns (M1–M12) the DS works against; cite by `(Mx)` codes.
|
|
22
|
+
- **`apps/web/docs/jobs/`** — canonical references per **job-to-be-done** (start with `record-detail.md`). If no job doc matches, write one.
|
|
23
|
+
|
|
24
|
+
### Brief-before-code is a CHECKPOINT, not a preamble
|
|
25
|
+
|
|
26
|
+
On any task that **decides what a surface should be** — including *new* pages **and** *rebuilds / redesigns / replacements of existing pages* — the protocol is:
|
|
27
|
+
|
|
28
|
+
1. **Post the brief in chat. END THE TURN with "Ready to build — confirm or edit."**
|
|
29
|
+
2. **Wait for the user's next message.** Silence is not consent until the next user reply arrives.
|
|
30
|
+
3. **Only then** call code-mutating tools.
|
|
31
|
+
|
|
32
|
+
Treat **"rebuild X"**, **"redesign X"**, **"replace what we have"**, **"make a new version of X"**, **"from scratch"**, and **"instead of what we currently have"** identically to "create new X" — all of them make a design decision, all of them require the brief checkpoint. The only exempt edits are trivial ones (copy tweaks, single-class restyles, bug fixes, dep bumps, ESLint passes, adding a column to an existing `HubTable` without changing IA).
|
|
33
|
+
|
|
34
|
+
If your next tool call would be `write_file` / `str_replace` / `create_file` and you have not posted a brief + received user confirmation, **you are violating the protocol**. Stop, post the brief, end the turn.
|
|
35
|
+
|
|
36
|
+
**Reviewing existing work?** Load **`.cursor/skills/exxat-ux-audit/SKILL.md`** instead. It runs an EXISTING surface (route / file / component / customer-app path / screenshot) through the same P1–P20 / M1–M12 rubric and outputs a **Blocker / Issue / Nit** findings report with code citations, a fix plan, and an offer to auto-apply text-only Blocker fixes. Triggers: "audit X", "review the X page", "is this following DS?", "what's wrong with this screen", "do a UX review", or a route URL with a symptom.
|
|
37
|
+
|
|
38
|
+
## Non‑negotiables (if anything conflicts, open AGENTS.md §12–§13)
|
|
39
|
+
|
|
40
|
+
1. **Product data lists** → `DataTable` + search + shared filters + `TablePropertiesDrawer` — not raw `<table>` / ui `Table` alone / ad-hoc grids. With **`ListPageTemplate`** view tabs, pass **`currentView`** + **`onViewChange`** into **`TablePropertiesDrawer`** (**`AGENTS.md` §4.2**, **`.cursor/rules/exxat-table-properties-drawer.mdc`**).
|
|
41
|
+
2. **Main `DataTable` surface** → wrapped in **`ListPageTemplate`** (view tabs). All tab view types share **`useTableState`**; list/board/dashboard read **`tableState.rows`**. Reference: `PlacementsClient`, `TeamClient`, `TeamTable`, `ComplianceClient`.
|
|
42
|
+
3. **Do not double-indent** the toolbar/table — avoid extra `px`/`mx` wrappers around `DataTable` when it already applies horizontal inset.
|
|
43
|
+
4. **Primary hub + large/complex data** → same composition as Placements/Team: `ListPageTemplate` + metrics/export pattern as in `PlacementsClient` / `TeamClient`, not `PageHeader`-only. **Do not** ship empty or "replace later" placeholder pages for nav-linked hubs (**`AGENTS.md` §4.1**).
|
|
44
|
+
5. **Exportable pages** → filled primary CTA; **⋯** menu with Export → `ExportDrawer` pattern (see `PlacementsPageHeader`).
|
|
45
|
+
6. **Keyboard hints** → `.cursor/rules/exxat-kbd-shortcuts.mdc`; pair hints with behavior.
|
|
46
|
+
7. **Accessibility** → `.cursor/rules/exxat-accessibility.mdc` + **`apps/web/AGENTS.md` §8** + `.cursor/skills/exxat-accessibility/SKILL.md`.
|
|
47
|
+
8. **Data view dashboard charts** → **`apps/web/AGENTS.md` §4.3** + **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** (centralized storage, `chart-keyboard-selection`, `ChartFigure`).
|
|
48
|
+
9. **Board (kanban) cards** → **`apps/web/AGENTS.md` §4.4** + **`exxat-board-cards.mdc`** + **`.cursor/skills/exxat-board-cards/SKILL.md`** (`ListPageBoardCard`, **`ListHubStatusBadge`** + **`lib/list-status-badges.ts`**, primitives).
|
|
49
|
+
10. **List-page view shells** (folder / panel / centered non-table bodies) → **`apps/web/AGENTS.md` §4.5** + **`exxat-list-page-view-shells.mdc`** + **`.cursor/skills/exxat-list-page-view-shells/SKILL.md`** (`ListPageViewFrame`, **`components/data-views/`**).
|
|
50
|
+
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`**.
|
|
51
|
+
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.
|
|
52
|
+
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.
|
|
53
|
+
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`**.
|
|
54
|
+
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`**.
|
|
55
|
+
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`**.
|
|
56
|
+
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.
|
|
57
|
+
18. **KPI trend polarity** (`KeyMetrics`, `*-kpi.ts`, chart mini-metrics) — **`docs/kpi-trend-pattern.md`** + **`.cursor/rules/exxat-kpi-trends.mdc`** + **`.cursor/skills/exxat-kpi-trends/SKILL.md`** — **`trend`** follows the data; set **`trendPolarity`** when “up” is not good news (e.g. defect counts, **low PBI flags**).
|
|
58
|
+
19. **Drawer vs dialog** (same-route overlays) — **`docs/drawer-vs-dialog-pattern.md`** + **`.cursor/rules/exxat-drawer-vs-dialog.mdc`** + **`.cursor/skills/exxat-drawer-vs-dialog/SKILL.md`** — drawers preserve hub context; dialogs for **blocking** short confirms.
|
|
59
|
+
20. **Cards vs table rows** — **`docs/card-vs-rows-pattern.md`** + **`.cursor/rules/exxat-card-vs-list-rows.mdc`** + **`.cursor/skills/exxat-card-vs-list-rows/SKILL.md`** — **`DataTable`** for dense comparable hubs; cards for kanban / visual browse (**`ListPageBoardCard`**).
|
|
60
|
+
21. **KPI strip max four** — **`docs/kpi-strip-max-four-pattern.md`** + **`.cursor/rules/exxat-kpi-max-four.mdc`** + **`.cursor/skills/exxat-kpi-max-four/SKILL.md`** — **≤ 4** `MetricItem` per primary **`KeyMetrics`** strip / Data-tab key-metrics card (**`KEY_METRICS_KPI_COUNT_MAX`**).
|
|
61
|
+
22. **KPI flat band** — **`docs/kpi-flat-band-pattern.md`** + **`.cursor/rules/exxat-kpi-flat-band.mdc`** + **`.cursor/skills/exxat-kpi-flat-band/SKILL.md`** — **`variant="flat"`**: transparent cells, OKLCH brand glow only, border hairlines (no grey panel).
|
|
62
|
+
23. **Reuse before custom** — **`exxat-reuse-before-custom.mdc`** — compose from **`components/`** + **`AGENTS.md` §9**; **ask the user** before new shared primitives or bespoke widgets unless the task already approved greenfield.
|
|
63
|
+
24. **Token discipline** — **`docs/token-taxonomy.md`** + **`.cursor/rules/exxat-token-discipline.mdc`** + **`packages/ui/tokens/hooks-index.json`** (163 tokens · 36 namespaces) — no hex literals in app code; no deprecated tokens; **prefer Exxat L0** (`--exxat-color-*`, `--exxat-radius-*`, `--exxat-spacing-*`) for new code (taxonomy §2.0 · rollout `docs/migrations/0002-exxat-token-namespace.md`); legacy L1 shadcn names stay as aliases. ESLint enforces via `exxat-ds/no-hex-color` + `exxat-ds/no-deprecated-tokens` in **`@exxatdesignux/eslint-plugin`** (workspace: **`packages/eslint-plugin-exxat-ds/`**).
|
|
64
|
+
25. **No SLDS leakage** — **`.cursor/rules/exxat-no-slds-leakage.mdc`** — no `slds-*` classes, no `<lightning-*>` markup, no SLDS token names, no synthetic-shadow assumptions. ESLint catches via `exxat-ds/no-slds-classes` + `exxat-ds/no-lightning-elements` (from **`@exxatdesignux/eslint-plugin`**).
|
|
65
|
+
26. **Blueprints + selection guide** — **`apps/web/docs/blueprints/`** + **`apps/web/docs/component-selection-guide.md`** — framework-agnostic specs + decision tree across the whole DS. Start there before composing a new hub.
|
|
66
|
+
27. **Migrations** — **`apps/web/docs/migrations/`** — every deprecation gets a numbered entry (`NNNN-<slug>.md`). Token deprecations surface in `tokens/hooks-index.json` as `deprecated: true` and are lint-flagged.
|
|
67
|
+
28. **Hub supported views (Add view parity)** — **`exxat-hub-supported-views.mdc`** + **`apps/web/docs/hub-supported-views-pattern.md`** — **`FULL_HUB_SUPPORTED_VIEWS`** (seven views, same as Library); sync **`ListPageTemplate`** + **`HubTable`**; real renderers per view; **`LibraryTable`** for **`LibraryItem`** catalogs; **`tokens-hub-auxiliary-views.tsx`** for tokens.
|
|
68
|
+
29. **No Vaul** — **`exxat-no-vaul.mdc`** — side panels = **`Sheet`** only; remove **`vaul`** from consumer apps on **`@exxatdesignux/ui@0.5.3+`**.
|
|
69
|
+
30. **Tabs chrome** — **`exxat-tabs-chrome.mdc`** — hub views = **`ListPageTemplate`** toolbar (`w-max`); record tabs = **`TabsList`** `w-fit`, never full-width stretch.
|
|
70
|
+
31. **Page header actions** — **`exxat-page-header-actions.mdc`** + **`apps/web/docs/blueprints/page-header.md`** — filled primary + outline overflow; no hand-built grey buttons.
|
|
71
|
+
32. **Table row preview** — **`exxat-table-row-preview.mdc`** — **`HoverCard`** + DS badges/cells; no bespoke popover cards.
|
|
72
|
+
33. **Sidebar shell** — **`exxat-sidebar-shell.mdc`** + **`AGENTS.md` §9.1** — legacy screenshots = **IA only** (labels/routes); **MUST NOT** copy per-section colors or fork **`app-sidebar.tsx`** styling; use **`SidebarMenuButton`** + **`lib/nav-active`**.
|
|
73
|
+
34. **Uploaded images** — **`exxat-no-image-pixel-copy.mdc`** (always apply) — screenshots/mockups = **intent + IA only**; **MUST** map to blueprints + reference hubs; **MUST NOT** pixel-copy colors, chrome, or bespoke stacks.
|
|
74
|
+
|
|
75
|
+
## Also read
|
|
76
|
+
|
|
77
|
+
- **`apps/web/docs/data-views-pattern.md`** — architecture narrative for list hubs. Includes **Page vs drawer** (§6.4 in `AGENTS.md`), **board UI** (§4.4), and **KPI trend polarity** → **`apps/web/docs/kpi-trend-pattern.md`**.
|
|
78
|
+
- **`apps/web/docs/command-menu-pattern.md`** — global ⌘K palette (search + quick AI vs Ask Leo).
|
|
79
|
+
- **`apps/web/docs/token-taxonomy.md`** — naming + deprecation policy for every CSS custom property the DS ships (semantic shadcn + brand / chip / chart / interactive / sidebar / control / radius / shadow / transition families).
|
|
80
|
+
- **`apps/web/docs/component-selection-guide.md`** — top-of-funnel decision tree for picking the right composition.
|
|
81
|
+
- **`apps/web/docs/blueprints/`** — framework-agnostic specs (start: `page-header.md`, `data-table.md`).
|
|
82
|
+
- **`apps/web/docs/migrations/`** — token rename + removal history.
|
|
83
|
+
- **`packages/ui/tokens/hooks-index.json`** — machine-readable token index (regenerate via `pnpm --filter @exxatdesignux/ui tokens:index`).
|
|
84
|
+
- `.cursor/rules/exxat-data-tables.mdc`, **`exxat-hub-supported-views.mdc`**, **`exxat-no-vaul.mdc`**, **`exxat-tabs-chrome.mdc`**, **`exxat-page-header-actions.mdc`**, **`exxat-table-row-preview.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-nav-single-active.mdc`** (one active sidebar/secondary link — `nav-active` helpers), **`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.
|
|
85
|
+
- **`apps/web/docs/kpi-flat-band-pattern.md`**, **`apps/web/docs/shell-surface-elevation-pattern.md`** — flat KPI strip + sidebar/secondary/page OKLCH stack.
|
|
86
|
+
- **`apps/web/docs/library-hub-header-pattern.md`** — folder-scoped library header **Customize folder**.
|
|
87
|
+
- **`apps/web/docs/collaboration-access-pattern.md`** — shared hub face rail + invite sheet.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Font Awesome Pro icons — kit, weights, markup, subset audit, accessibility pairing
|
|
3
|
+
globs: apps/web/**/*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — Font Awesome icons
|
|
9
|
+
|
|
10
|
+
Product UI uses **Font Awesome 6 Pro** via the **Kit script** in **`apps/web/app/layout.tsx`** (not per-page ad-hoc CDNs). **Authoritative accessibility pairing** for decorative vs informational vs icon-only controls: **`.cursor/rules/exxat-accessibility.mdc`** (Case A/B/C).
|
|
11
|
+
|
|
12
|
+
## MUST
|
|
13
|
+
|
|
14
|
+
1. **Markup** — Use **`<i className="…" />`** with **`fa-*`** weight + icon classes (e.g. **`fa-light fa-folder`**, **`fa-solid fa-check`**). Put **`aria-hidden="true"`** on the **`<i>`** when the **accessible name** lives on a parent (**`Button` `aria-label`**, adjacent text, **`Tip`** on interactive controls).
|
|
15
|
+
2. **Weights** — Default inactive/ambient icons to **`fa-light`**; use **`fa-solid`** for **selected / active / strong emphasis** when the pattern already exists (e.g. sidebar rows, view tabs). Stay consistent within a surface.
|
|
16
|
+
3. **Class strings** — Prefer **static** `className` strings so **`pnpm --filter web fa:subset-audit`** (see **`apps/web/scripts/fontawesome-subset-audit.mjs`**) can discover glyphs for **Kit subsetting** (`fontawesome-subset.manifest.json` → [fontawesome.com/kits](https://fontawesome.com/kits) Icon Selection).
|
|
17
|
+
4. **Props APIs** — When a component accepts an icon, document **suffix only** (`fa-folder`) or **full classes** consistently with nearby code (**`lib/data-list-view.ts`**, **`lib/list-status-badges.ts`**, **`os-folder-glyph.tsx`**).
|
|
18
|
+
|
|
19
|
+
## SHOULD
|
|
20
|
+
|
|
21
|
+
- After adding **new** Font Awesome glyph names, run **`fa:subset-audit`** and refresh the **Kit** subset so production does not 404 missing icons.
|
|
22
|
+
- Reuse **shared** maps (**`DATA_LIST_VIEW_TILES`**, **`lib/list-status-badges.ts`**) instead of one-off **`fa-*`** strings for the same semantic concept.
|
|
23
|
+
|
|
24
|
+
## MUST NOT
|
|
25
|
+
|
|
26
|
+
- Add **second** icon systems for **product chrome** (duplicate Lucide/Phosphor/Heroicons for the same nav, table toolbar, or hub cards) when Font Awesome is already the standard for that surface.
|
|
27
|
+
- Put **meaningful** accessible names **only** on **`<i>`** without **`role`** / parent labelling — follow **Case B/C** in **`exxat-accessibility.mdc`**.
|
|
28
|
+
|
|
29
|
+
## See also
|
|
30
|
+
|
|
31
|
+
- **`apps/web/AGENTS.md`** — handbook; **§8** accessibility.
|
|
32
|
+
- **`.cursor/rules/exxat-accessibility.mdc`** — tooltips + **`aria-label`** for icon-only buttons.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: List-page hubs — FULL_HUB_SUPPORTED_VIEWS, Add view parity, and real renderers (never trimmed allowlists or placeholder list rows). Auto-attaches when editing hub clients / list-page templates.
|
|
3
|
+
globs: apps/web/components/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — hub supported views (Add view parity)
|
|
9
|
+
|
|
10
|
+
**Canonical reference:** `components/library-table.tsx` + `components/library-client.tsx` (All questions / Library).
|
|
11
|
+
|
|
12
|
+
**Registry:** `FULL_HUB_SUPPORTED_VIEWS` in `@exxatdesignux/ui/lib/data-list-view-registry` (re-export `@/lib/data-list-view-registry`).
|
|
13
|
+
|
|
14
|
+
## Default allowlist (seven views)
|
|
15
|
+
|
|
16
|
+
Every **`ListPageTemplate`** + **`HubTable`** hub that mounts a product data grid **MUST** expose the same **Add view** menu as Library unless product documents a narrower exception in the hub’s `*-supported-views.ts` file:
|
|
17
|
+
|
|
18
|
+
| # | View | Render kind | How to implement |
|
|
19
|
+
|---|------|-------------|------------------|
|
|
20
|
+
| 1 | Table | `data-table` | Default `HubTable` / custom `columnDefs` |
|
|
21
|
+
| 2 | List | `list-with-toolbar` | **`ListPageBoardCard`** `layout="row"` via `renderListRow` — same as `library-table.tsx` |
|
|
22
|
+
| 3 | Board | `board-with-toolbar` | `renderers["board-with-toolbar"]` or `renderBoardCard` + `boardGroups` |
|
|
23
|
+
| 4 | Dashboard | `dashboard-with-toolbar` | `renderers["dashboard-with-toolbar"]` with **`KeyMetrics`** / charts on `state.rows` |
|
|
24
|
+
| 5 | Folder | `folder-with-toolbar` | `renderers["folder-with-toolbar"]` (e.g. `LibraryOsFolderView`, `FolderGridView`) |
|
|
25
|
+
| 6 | List & details | `panel-with-toolbar` | `renderers["panel-with-toolbar"]` (Miller columns — see `library-table.tsx`) |
|
|
26
|
+
| 7 | Tree & details | `tree-panel-with-toolbar` | `renderers["tree-panel-with-toolbar"]` (e.g. `HubTreePanelView`) |
|
|
27
|
+
|
|
28
|
+
`HubTable` and `ListPageTemplate` default to **`FULL_HUB_SUPPORTED_VIEWS`** when `supportedViewTypes` is omitted.
|
|
29
|
+
|
|
30
|
+
**`PRIMARY_HUB_SUPPORTED_VIEWS`** (table, list, board, dashboard only) is for hubs that **document** they intentionally omit folder/panel/tree — not the default for new work.
|
|
31
|
+
|
|
32
|
+
## MUST (every new or touched hub)
|
|
33
|
+
|
|
34
|
+
1. **Sync allowlists** — Pass the **same** `supportedViewTypes` to **`ListPageTemplate`** and **`HubTable`** (or only omit on both so the default applies).
|
|
35
|
+
2. **Implement every allowed view** — Each entry in `supportedViewTypes` needs a renderer (explicit `renderers` map and/or `renderListRow` / `renderBoardCard` + `boardGroups`). Dev warns when a view is allowed but missing; users see **“does not implement X view”** if you ship the allowlist without bodies.
|
|
36
|
+
3. **List rows use DS chrome** — **`ListPageBoardCard`** (Library list pattern). **MUST NOT** ship a bare two-line `renderListRow` (title + mono id only).
|
|
37
|
+
4. **Library-shaped rows** — Hubs on **`LibraryItem`** with a custom table (e.g. Column types) **MUST** delegate non-table views to **`LibraryTable`** (`columnDefs` + `hubLabels` + `folders` state) — do not reimplement folder/panel/tree in isolation.
|
|
38
|
+
5. **Token hubs** — Use **`FULL_HUB_SUPPORTED_VIEWS`** + **`components/tokens-hub-auxiliary-views.tsx`** (`renderTokenListRow`, `buildTokensHubRenderers`) — **MUST NOT** use `supportedViewTypes={["table"]}`.
|
|
39
|
+
|
|
40
|
+
## MUST NOT
|
|
41
|
+
|
|
42
|
+
- Pass **`PRIMARY_HUB_SUPPORTED_VIEWS`** or `["table", "list", "board", "dashboard"]` on a hub that should match Library Add view parity.
|
|
43
|
+
- Pass **`supportedViewTypes={["table"]}`** on any primary hub or showcase inside **`ListPageTemplate`**.
|
|
44
|
+
- Copy **`COLUMNS_SUPPORTED_VIEWS = PRIMARY_…`** without implementing folder/panel/tree.
|
|
45
|
+
- Invent a new per-page allowlist constant without a matching renderer for every listed view.
|
|
46
|
+
|
|
47
|
+
## Narrow exceptions (document in code comment)
|
|
48
|
+
|
|
49
|
+
- Hubs that genuinely lack calendar/folder/panel/tree (e.g. legacy Placements-only four views) — export `ENTITY_SUPPORTED_VIEWS` in `lib/*-supported-views.ts` and **comment why** it is narrower than `FULL_HUB_SUPPORTED_VIEWS`.
|
|
50
|
+
- Calendar is **not** in `FULL_HUB_SUPPORTED_VIEWS`; add only when the hub implements `calendar-with-toolbar`.
|
|
51
|
+
|
|
52
|
+
## See also
|
|
53
|
+
|
|
54
|
+
- **`.cursor/rules/exxat-data-tables.mdc`**, **`exxat-list-page-connected-views.mdc`**, **`exxat-list-page-view-shells.mdc`**
|
|
55
|
+
- **`apps/web/docs/hub-supported-views-pattern.md`**
|
|
56
|
+
- **`apps/web/docs/data-views-pattern.md`**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: KeyMetrics variant flat — transparent band, brand glow only, OKLCH hairlines
|
|
3
|
+
globs: apps/web/components/**/*key-metrics*,apps/web/app/globals.css,apps/web/docs/kpi*.md
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — KPI flat band (`variant="flat"`)
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/docs/kpi-flat-band-pattern.md`**, **`.cursor/skills/exxat-kpi-flat-band/SKILL.md`**.
|
|
11
|
+
|
|
12
|
+
## MUST
|
|
13
|
+
|
|
14
|
+
1. **`KeyMetrics variant="flat"`** on list hubs / dashboard mix — **no** opaque band surface; section background is **only** `--key-metrics-flat-band-radial` (brand glow).
|
|
15
|
+
2. **Cells** — **`bg-transparent`**; hairlines via **`flatMetricsHairlineClass`** (cell **borders**, not `gap-px` grid fill).
|
|
16
|
+
3. **Four tiles** — default **4-across** verticals between columns; **2×2** hairlines only when `@container` is narrow (`@[max-width:29.99rem]`). **No** horizontal rule in 4-across layout.
|
|
17
|
+
4. **OKLCH** — `--key-metrics-flat-divider`, glow stops via `color-mix(in oklch, var(--brand-color) …)`; divider uses `var(--sidebar-border)` mix. **Do not** hardcode rose/indigo on one product for all themes.
|
|
18
|
+
5. **`--key-metrics-flat-band-shadow: none`** on flat band. **≤ 4** tiles — **`exxat-kpi-max-four.mdc`**.
|
|
19
|
+
|
|
20
|
+
## MUST NOT
|
|
21
|
+
|
|
22
|
+
- Stack linear gradients, `bg-background` on cells, or `gap-px` + tinted grid background on flat KPI (reads as a grey/lavender **box**).
|
|
23
|
+
- Use **`variant="card"`** for **`ListPageTemplate`** metrics when the strip should sit on the page canvas.
|
|
24
|
+
- Reintroduce **`lg:border-l`** on insight column when `variant="flat"` (insight card ring is enough).
|
|
25
|
+
|
|
26
|
+
## See also
|
|
27
|
+
|
|
28
|
+
- **`exxat-kpi-max-four.mdc`**, **`exxat-kpi-trends.mdc`**, **`exxat-list-page-connected-views.mdc`**
|
|
29
|
+
- **`exxat-primary-nav-secondary-panel.mdc`** — shell elevation (separate from KPI band)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — at most four KPI tiles on primary hub strips and key-metrics dashboard cards.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — KPI strip (max four)
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
1. **Cap visible KPIs at four** on **`ListPageTemplate`** metric strips and on **Data tab** **`key-metrics`** dashboard cards — align with **`KEY_METRICS_KPI_COUNT_MAX`** in **`lib/dashboard-layout-merge.ts`** and **`clampKeyMetricsKpiCount`** when persisting layout.
|
|
12
|
+
2. **KPI helpers** (`*-kpi.ts`) return **≤ 4** `MetricItem` entries for those surfaces, or **`.slice(0, 4)`** after prioritization.
|
|
13
|
+
3. **Overflow narrative** — Push secondary stats into **`MetricInsight`**, charts, or another section — not a fifth headline tile.
|
|
14
|
+
|
|
15
|
+
## MUST NOT
|
|
16
|
+
|
|
17
|
+
- Render **five or more** KPI tiles in a single **`KeyMetrics`** row/band meant as the **primary** summary for a hub or the **key-metrics** card.
|
|
18
|
+
- Raise **`KEY_METRICS_KPI_COUNT_MAX`** without design-system review.
|
|
19
|
+
|
|
20
|
+
## See also
|
|
21
|
+
|
|
22
|
+
- **`docs/kpi-strip-max-four-pattern.md`** · **`docs/kpi-trend-pattern.md`** · **`docs/kpi-flat-band-pattern.md`** · **`.cursor/skills/exxat-kpi-max-four/SKILL.md`**
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — KPI deltas and trend arrows must be contextual; use trendPolarity when “up” is not good news.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — KPI trends (`KeyMetrics` + chart mini-metrics)
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
1. **`trend` matches reality** — `up` / `down` / `neutral` reflects the **signed change** in the metric; arrows are not “spin for optimism”.
|
|
12
|
+
2. **Set `trendPolarity` on `MetricItem`** when an increase is **not** favorable:
|
|
13
|
+
- **`lower_is_better`** — defect / error / overdue / **low PBI or quality-flag counts**, cost or time when the product goal is to **reduce**.
|
|
14
|
+
- **`informational`** — volume or mix where **direction is not** inherently good or bad (library size, % split between categories); tints stay **muted**, arrows still show direction.
|
|
15
|
+
3. **Default** — Omitting `trendPolarity` means **`higher_is_better`** (legacy): up = positive tint, down = negative tint.
|
|
16
|
+
4. **`delta` is a count, `description` is a caption.** **`delta`** (next to the arrow) is a **numeric change** like `"+5"`, `"-3"`, `"+12%"`. Contextual prose like `"left + right"`, `"vs last week"`, `"across 4 sites"` goes in **`MetricItem.description`** which renders **below** the value row (muted, small). **MUST NOT** stuff prose into **`delta`**.
|
|
17
|
+
5. **Hide the trend chip when there is nothing to say.** If **`trend === "neutral"`** **and** **`delta`** is empty (`""` / `0` / unset), `KeyMetrics` suppresses the chip entirely — no `—` placeholder. Use **`description`** for the supporting caption instead. Only show the chip when the metric has a real direction (`up` / `down`) or a real count to surface.
|
|
18
|
+
6. **Contextual copy** — `label` + `value` + (`delta` when present) + period header should read as one sentence; avoid orphan deltas.
|
|
19
|
+
7. **Accessibility** — Do not rely on colour alone; keep delta text + icon; chip `aria-label` comes from **`metricTrendAriaQualifier`** in **`components/key-metrics.tsx`**.
|
|
20
|
+
|
|
21
|
+
## MUST NOT
|
|
22
|
+
|
|
23
|
+
- Paint an **up** arrow with the “good” tint when the metric is **worse** when it goes up (unless **`trendPolarity: "lower_is_better"`** is set so “up” correctly tints **destructive**).
|
|
24
|
+
- Use **`ResizeObserver`** or JS layout measurement **only** to pick trend colours — polarity is a **product** decision, not a layout one.
|
|
25
|
+
- Render an **empty `—`** trend chip just to keep the layout symmetric. If a metric has no comparison this period, leave **`delta`** empty and let **`KeyMetrics`** hide the chip.
|
|
26
|
+
- Put words like **"left + right"**, **"hidden"**, **"shown"**, **"vs last week"** in **`delta`** — they are not deltas. Put them in **`description`**.
|
|
27
|
+
|
|
28
|
+
## See also
|
|
29
|
+
|
|
30
|
+
- **`docs/kpi-trend-pattern.md`** (app) — narrative + table of examples.
|
|
31
|
+
- **`.cursor/skills/exxat-kpi-trends/SKILL.md`** — checklist for new hubs.
|
|
32
|
+
- **`components/key-metrics.tsx`** — `MetricTrendPolarity`, `metricTrendTone`, `metricTrendAriaQualifier`.
|
|
@@ -0,0 +1,29 @@
|
|
|
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-vite/components/library-*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — Library hub header (folder scope)
|
|
9
|
+
|
|
10
|
+
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.
|
|
11
|
+
|
|
12
|
+
**Pattern doc:** **`apps/web/docs/library-hub-header-pattern.md`**. **Handbook:** **`apps/web/AGENTS.md` §4.6** (folder-scoped hub chrome).
|
|
13
|
+
|
|
14
|
+
## MUST
|
|
15
|
+
|
|
16
|
+
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**.
|
|
17
|
+
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.
|
|
18
|
+
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.
|
|
19
|
+
|
|
20
|
+
## MUST NOT
|
|
21
|
+
|
|
22
|
+
- 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.
|
|
23
|
+
- Mount **only** one customize sheet inside **`LibraryTable`** without a **client-level** sheet when the hub uses **`ListPageTemplate`** view tabs that omit that table subtree.
|
|
24
|
+
|
|
25
|
+
## See also
|
|
26
|
+
|
|
27
|
+
- **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`** — URL scope + secondary panel.
|
|
28
|
+
- **`.cursor/rules/exxat-collaboration-access.mdc`** — **`variant="collaboration"`** header + **⋯** invite pattern.
|
|
29
|
+
- **`lib/library-nav.ts`** — **`parseLibraryNav`**, **`LibraryNavState`**.
|