canvas-ui-sdk 0.3.23 → 4.0.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 (177) hide show
  1. package/README.md +25 -5
  2. package/dist/charts.js +11 -6
  3. package/dist/charts.js.map +1 -1
  4. package/dist/index.d.ts +1233 -153
  5. package/dist/index.js +3562 -447
  6. package/dist/index.js.map +1 -1
  7. package/mcp/dist/index.js +1195 -149
  8. package/package.json +1 -1
  9. package/prompts/.cursorrules +96 -0
  10. package/prompts/.windsurfrules +96 -0
  11. package/prompts/CLAUDE.md +22 -0
  12. package/prompts/copilot-instructions.md +96 -0
  13. package/registry/blocks/activity-feed.json +12 -1
  14. package/registry/blocks/blog-cards.json +10 -2
  15. package/registry/blocks/bottom-action-bar.json +27 -0
  16. package/registry/blocks/bottom-input-chat-widget.json +9 -1
  17. package/registry/blocks/category-grid.json +10 -2
  18. package/registry/blocks/centered-hero.json +9 -1
  19. package/registry/blocks/chat-message.json +8 -1
  20. package/registry/blocks/circular-progress-bar-list.json +11 -1
  21. package/registry/blocks/confirmation-popup.json +10 -1
  22. package/registry/blocks/contact-form-popup.json +10 -1
  23. package/registry/blocks/content-dropzone.json +8 -0
  24. package/registry/blocks/content-with-image.json +9 -1
  25. package/registry/blocks/core-values-grid.json +10 -2
  26. package/registry/blocks/credit-card-display.json +9 -1
  27. package/registry/blocks/cta-banner.json +10 -2
  28. package/registry/blocks/destination-cards.json +10 -1
  29. package/registry/blocks/detail-drawer.json +10 -1
  30. package/registry/blocks/details-popup.json +10 -1
  31. package/registry/blocks/editable-list.json +29 -0
  32. package/registry/blocks/empty-state.json +10 -2
  33. package/registry/blocks/faq-accordion.json +9 -1
  34. package/registry/blocks/faqs-table.json +10 -1
  35. package/registry/blocks/feature-with-image.json +9 -1
  36. package/registry/blocks/featured-news-cards.json +10 -2
  37. package/registry/blocks/featured-places.json +10 -2
  38. package/registry/blocks/features-comparison.json +9 -1
  39. package/registry/blocks/feedback-popup.json +9 -1
  40. package/registry/blocks/filter-popover.json +8 -1
  41. package/registry/blocks/fixed-column-data-table.json +11 -1
  42. package/registry/blocks/flair-banner.json +9 -1
  43. package/registry/blocks/footer-navbar.json +9 -1
  44. package/registry/blocks/form-group.json +14 -3
  45. package/registry/blocks/form-popup.json +31 -0
  46. package/registry/blocks/gallery-section.json +10 -2
  47. package/registry/blocks/gradient-banner.json +10 -2
  48. package/registry/blocks/graph-metric-tiles.json +1 -1
  49. package/registry/blocks/grid-tiles-list.json +10 -1
  50. package/registry/blocks/hero-dark-centered.json +9 -1
  51. package/registry/blocks/hero-dark-with-image.json +9 -1
  52. package/registry/blocks/hero-fullwidth-image.json +9 -1
  53. package/registry/blocks/hero-section.json +9 -1
  54. package/registry/blocks/how-it-works.json +9 -1
  55. package/registry/blocks/image-feed-with-nested-comments.json +10 -1
  56. package/registry/blocks/image-popup.json +10 -1
  57. package/registry/blocks/invoice-popup.json +10 -1
  58. package/registry/blocks/large-image-labels-list.json +10 -1
  59. package/registry/blocks/list-popup.json +28 -0
  60. package/registry/blocks/loader.json +9 -1
  61. package/registry/blocks/login-branding-panel.json +10 -2
  62. package/registry/blocks/menu-section.json +9 -1
  63. package/registry/blocks/menufocus-template.json +9 -1
  64. package/registry/blocks/messenger-sidebar.json +11 -2
  65. package/registry/blocks/metrics-section.json +10 -2
  66. package/registry/blocks/mobile-bottom-nav.json +10 -2
  67. package/registry/blocks/monthly-calendar-widget.json +9 -1
  68. package/registry/blocks/multistep-form-popup.json +34 -0
  69. package/registry/blocks/nested-comments-table.json +9 -1
  70. package/registry/blocks/nested-data-table.json +10 -1
  71. package/registry/blocks/nps-survey-popup.json +27 -0
  72. package/registry/blocks/office-locations.json +10 -2
  73. package/registry/blocks/order-summary-sidebar.json +27 -0
  74. package/registry/blocks/page-header-section.json +9 -1
  75. package/registry/blocks/pagination.json +8 -1
  76. package/registry/blocks/participant-list.json +9 -1
  77. package/registry/blocks/persona-card.json +10 -1
  78. package/registry/blocks/personalize-feed-popup.json +27 -0
  79. package/registry/blocks/pill-tabs.json +9 -1
  80. package/registry/blocks/place-detail-panel.json +11 -1
  81. package/registry/blocks/pricing-cards.json +10 -2
  82. package/registry/blocks/pricing-cta.json +9 -1
  83. package/registry/blocks/pricing-plans-popup.json +10 -1
  84. package/registry/blocks/profile-card.json +12 -2
  85. package/registry/blocks/profile-grid-tiles-list.json +10 -1
  86. package/registry/blocks/profile-image-uploader.json +9 -1
  87. package/registry/blocks/profile-info-cards.json +10 -1
  88. package/registry/blocks/progress-bar.json +8 -1
  89. package/registry/blocks/prompt-template.json +1 -1
  90. package/registry/blocks/purchase-confirmation-popup.json +10 -1
  91. package/registry/blocks/reservation-card.json +26 -0
  92. package/registry/blocks/reviews-grid.json +10 -2
  93. package/registry/blocks/reviews-table.json +10 -1
  94. package/registry/blocks/screen-prompt-template.json +1 -1
  95. package/registry/blocks/search-bar.json +9 -2
  96. package/registry/blocks/search-sidebar.json +9 -2
  97. package/registry/blocks/settings-list-row.json +9 -1
  98. package/registry/blocks/share-project-popup.json +36 -0
  99. package/registry/blocks/sidebar-cards.json +10 -2
  100. package/registry/blocks/sidebar-profile-card.json +10 -2
  101. package/registry/blocks/slideshow-grid-tiles.json +10 -2
  102. package/registry/blocks/slideshow-popup.json +10 -1
  103. package/registry/blocks/small-edit-popup.json +29 -0
  104. package/registry/blocks/social-feed.json +10 -1
  105. package/registry/blocks/social-proof.json +9 -1
  106. package/registry/blocks/standard-data-table.json +13 -1
  107. package/registry/blocks/standard-list-with-image.json +10 -1
  108. package/registry/blocks/step-tracker.json +9 -1
  109. package/registry/blocks/store-location-map.json +9 -1
  110. package/registry/blocks/team-cards-grid.json +9 -1
  111. package/registry/blocks/team-circular-grid.json +9 -1
  112. package/registry/blocks/terms-of-service-popup.json +10 -1
  113. package/registry/blocks/testimonial-carousel.json +10 -2
  114. package/registry/blocks/tile-image-gallery.json +26 -0
  115. package/registry/blocks/title-group.json +10 -1
  116. package/registry/blocks/upvoting-posts-table.json +10 -1
  117. package/registry/blocks/vertical-how-it-works.json +9 -1
  118. package/registry/blocks/vertical-step-tracker.json +9 -1
  119. package/registry/blocks/video-chat-controls.json +9 -1
  120. package/registry/blocks/video-content-section.json +9 -1
  121. package/registry/blocks/video-playlist.json +9 -1
  122. package/registry/blocks/video-popup.json +9 -1
  123. package/registry/blocks/view-profile-popup.json +10 -1
  124. package/registry/blocks/webcam-preview.json +9 -1
  125. package/registry/hooks/use-css-variable-sync.json +10 -1
  126. package/registry/hooks/use-mobile.json +9 -1
  127. package/registry/index.json +1526 -147
  128. package/registry/layout/account-settings-shell.json +10 -1
  129. package/registry/layout/dashboard-shell.json +12 -1
  130. package/registry/layout/double-sidebar-shell.json +11 -2
  131. package/registry/layout/double-sidebar.json +9 -1
  132. package/registry/layout/header.json +10 -1
  133. package/registry/layout/icon-sidebar-shell.json +9 -1
  134. package/registry/layout/icon-sidebar.json +9 -1
  135. package/registry/layout/mobile-menu-shell.json +10 -1
  136. package/registry/layout/multistep-progressbar-shell.json +9 -1
  137. package/registry/layout/multistep-shell.json +11 -1
  138. package/registry/layout/multistep-sidebar-shell.json +10 -2
  139. package/registry/layout/project-context-shell.json +1 -1
  140. package/registry/layout/search-bar-shell.json +8 -1
  141. package/registry/layout/sidebar-nav.json +7 -1
  142. package/registry/layout/sidebar.json +9 -2
  143. package/registry/layout/standard-page-shell.json +10 -1
  144. package/registry/layout/vertical-multistep-shell.json +10 -1
  145. package/registry/ui/avatar.json +9 -1
  146. package/registry/ui/button.json +9 -1
  147. package/registry/ui/calendar.json +9 -1
  148. package/registry/ui/checkbox.json +8 -1
  149. package/registry/ui/date-input.json +9 -1
  150. package/registry/ui/dialog.json +8 -1
  151. package/registry/ui/dropdown-menu.json +8 -1
  152. package/registry/ui/file-uploader.json +9 -1
  153. package/registry/ui/image-uploader.json +9 -1
  154. package/registry/ui/input.json +8 -1
  155. package/registry/ui/label.json +8 -1
  156. package/registry/ui/line-tabs.json +9 -1
  157. package/registry/ui/multiselect-checkbox-field.json +9 -1
  158. package/registry/ui/multiselect-tags.json +9 -1
  159. package/registry/ui/popover.json +8 -1
  160. package/registry/ui/radio-group.json +9 -1
  161. package/registry/ui/range-input.json +8 -1
  162. package/registry/ui/scroll-area.json +8 -1
  163. package/registry/ui/searchbox.json +9 -1
  164. package/registry/ui/select.json +9 -1
  165. package/registry/ui/selectable-pills.json +11 -1
  166. package/registry/ui/separator.json +8 -1
  167. package/registry/ui/sheet.json +9 -1
  168. package/registry/ui/sidebar.json +8 -1
  169. package/registry/ui/skeleton.json +8 -1
  170. package/registry/ui/slider.json +10 -2
  171. package/registry/ui/switch.json +9 -1
  172. package/registry/ui/tabs.json +8 -1
  173. package/registry/ui/text-input.json +8 -1
  174. package/registry/ui/textarea.json +9 -1
  175. package/registry/ui/tooltip.json +8 -1
  176. package/registry/ui/typography.json +9 -1
  177. package/styles/tokens.reference.css +21 -0
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "account-settings-shell",
3
3
  "type": "registry:layout",
4
- "description": "Settings page layout with left sidebar navigation (Profile, Security, Payments, Notifications tabs).",
4
+ "description": "Settings page layout with left sidebar tab navigation (Profile, Security, Payments, Notifications) and right content area. Sidebar stacks above content on mobile. Full viewport height. Use for user preferences, account management, or any settings interface.",
5
+ "keywords": [
6
+ "settings",
7
+ "account",
8
+ "preferences",
9
+ "tabs",
10
+ "profile",
11
+ "security"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/account-settings-shell.tsx",
@@ -1,7 +1,18 @@
1
1
  {
2
2
  "name": "dashboard-shell",
3
3
  "type": "registry:layout",
4
- "description": "Main dashboard layout with dark sidebar (320px) and fixed header. Sidebar has collapsible sections with nav items.",
4
+ "description": "Full-screen app layout with dark sidebar (320px) and fixed header. Sidebar has collapsible sections with nav items. Hides sidebar on mobile with a slide-out menu. Full viewport height. Use for data-heavy applications with many navigation sections like admin dashboards, CRM tools, or project management.",
5
+ "keywords": [
6
+ "admin",
7
+ "dashboard",
8
+ "sidebar",
9
+ "navigation",
10
+ "crm",
11
+ "back-office",
12
+ "management",
13
+ "app"
14
+ ],
15
+ "visualWeight": "light",
5
16
  "files": [
6
17
  {
7
18
  "path": "components/layout/dashboard-shell.tsx",
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "double-sidebar-shell",
3
3
  "type": "registry:layout",
4
- "description": "Two-column sidebar layout (96px icons + 280px nav). Each column can be light/dark themed independently.",
4
+ "description": "Two-column sidebar layout (96px icon strip + 280px nav panel). Each column can be light or dark themed independently. Full viewport height. Use for complex apps with category groupings and nested navigation.",
5
+ "keywords": [
6
+ "sidebar",
7
+ "double",
8
+ "icon",
9
+ "navigation",
10
+ "categories",
11
+ "nested"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/double-sidebar-shell.tsx",
8
17
  "type": "registry:layout",
9
- "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { Header } from \"./header\";\nimport { useCSSVariableSync } from \"../../hooks/use-css-variable-sync\";\nimport { DoubleSidebar, DoubleSidebarSection, NavTab, defaultDoubleSidebarSections } from \"./double-sidebar\";\nimport { \n Sheet, \n SheetContent,\n SheetTitle,\n} from \"../ui/sheet\";\nimport { cn } from \"../../lib/utils\";\nimport { useThemeBranding } from \"../../context/theme-context\";\nimport * as VisuallyHidden from \"@radix-ui/react-visually-hidden\";\n\ninterface DoubleSidebarShellProps {\n /** Navigation sections for the double sidebar */\n sections?: DoubleSidebarSection[];\n /** Visual variant for the icon column */\n iconVariant?: \"dark\" | \"light\";\n /** Visual variant for the navigation column */\n navVariant?: \"dark\" | \"light\";\n /** Optional page header content (e.g., breadcrumbs, page title) */\n pageHeader?: React.ReactNode;\n /** Main content - the modular blocks */\n children: React.ReactNode;\n /** Callback when a tab is clicked */\n onTabClick?: (section: DoubleSidebarSection, tab: NavTab) => void;\n /** Callback when app menu (hamburger) is clicked - for future app-level menu */\n onAppMenuClick?: () => void;\n /** Additional class name for the main content area */\n contentClassName?: string;\n}\n\n/**\n * Canvas Design System - Double Sidebar Shell\n * \n * A composable page layout with a two-column sidebar that provides:\n * - Fixed header (80px)\n * - Fixed double sidebar on desktop (96px icons + 280px nav = 376px total)\n * - Floating sidebar toggle button on mobile (left edge)\n * - Mobile sheet navigation with both sidebar columns\n * - Each sidebar column can be independently themed (light/dark)\n * - Main content area with pageHeader slot and children slot for blocks\n * \n * Uses the same styling and spacing as DashboardShell for non-sidebar content.\n * \n * @example\n * ```tsx\n * <DoubleSidebarShell \n * sections={sections}\n * iconVariant=\"light\" \n * navVariant=\"light\"\n * >\n * <ContentDropzone label=\"Main content area\" />\n * </DoubleSidebarShell>\n * ```\n */\nexport function DoubleSidebarShell({\n sections = defaultDoubleSidebarSections,\n iconVariant,\n navVariant,\n pageHeader,\n children,\n onTabClick,\n onAppMenuClick,\n contentClassName,\n}: DoubleSidebarShellProps) {\n useCSSVariableSync();\n const { branding } = useThemeBranding();\n const themeSidebarMode = branding.sidebarMode ?? \"dark\";\n const effectiveIconVariant = iconVariant ?? themeSidebarMode;\n const effectiveNavVariant = navVariant ?? themeSidebarMode;\n const [sidebarOpen, setSidebarOpen] = useState(false);\n\n const handleTabClick = (section: DoubleSidebarSection, tab: NavTab) => {\n onTabClick?.(section, tab);\n // Close sidebar when tab is clicked\n setSidebarOpen(false);\n };\n\n const handleAppMenuClick = () => {\n // Placeholder for future app-level menu\n onAppMenuClick?.();\n console.log(\"App menu clicked - implement app-level mobile menu here\");\n };\n\n return (\n <div className=\"min-h-screen bg-[var(--canvas-background)]\">\n {/* Header - Fixed at top, offset on desktop to not overlap double sidebar */}\n <div className=\"fixed top-0 left-0 right-0 lg:left-[var(--double-sidebar-width)] z-40\">\n <Header onMenuClick={handleAppMenuClick} />\n </div>\n\n {/* Desktop Double Sidebar - Fixed on left, visible lg+ */}\n <div className=\"hidden lg:block fixed top-0 left-0 bottom-0 z-50 w-[var(--double-sidebar-width)]\">\n <DoubleSidebar\n sections={sections}\n iconVariant={effectiveIconVariant}\n navVariant={effectiveNavVariant}\n onTabClick={handleTabClick}\n />\n </div>\n\n {/* Mobile Sidebar Toggle Button - Floating on left edge */}\n <button\n onClick={() => setSidebarOpen(true)}\n className={cn(\n \"lg:hidden fixed left-0 z-30\",\n \"top-[calc(var(--header-height)+4px)]\",\n \"flex items-center justify-center\",\n \"size-11\",\n \"bg-[var(--canvas-background)]\",\n \"border border-l-0 border-[var(--canvas-neutral-border)]\",\n \"rounded-r-[var(--radius-xs)]\",\n \"shadow-[0px_4px_16px_0px_rgba(0,0,0,0.04)]\",\n \"transition-opacity hover:opacity-80\"\n )}\n aria-label=\"Open sidebar\"\n >\n <ChevronRight className=\"size-6 text-[var(--canvas-primary)]\" />\n </button>\n\n {/* Mobile Double Sidebar Sheet */}\n <Sheet open={sidebarOpen} onOpenChange={setSidebarOpen}>\n <SheetContent side=\"left\" className=\"p-0 w-[var(--double-sidebar-width)]\">\n <VisuallyHidden.Root>\n <SheetTitle>Navigation</SheetTitle>\n </VisuallyHidden.Root>\n <DoubleSidebar\n sections={sections}\n iconVariant={effectiveIconVariant}\n navVariant={effectiveNavVariant}\n onTabClick={handleTabClick}\n onClose={() => setSidebarOpen(false)}\n />\n </SheetContent>\n </Sheet>\n\n {/* Main Content Area - Same styling as DashboardShell */}\n <main\n className={cn(\n \"pt-[var(--header-height)]\",\n \"lg:pl-[var(--double-sidebar-width)]\",\n \"min-h-screen\"\n )}\n >\n <div \n className={cn(\n \"flex flex-col gap-[var(--spacing-xl)]\",\n \"px-[var(--spacing-xl)] lg:px-[var(--spacing-5xl)]\",\n \"pt-10 pb-[var(--spacing-5xl)]\",\n contentClassName\n )}\n >\n {/* Page Header Slot */}\n {pageHeader && (\n <section className=\"pt-0\">\n {pageHeader}\n </section>\n )}\n\n {/* Main Content Slot - Blocks go here */}\n <section className=\"flex flex-col gap-[var(--spacing-4xl)]\">\n {children}\n </section>\n </div>\n </main>\n </div>\n );\n}\n\n// Re-export types for convenience\nexport type { DoubleSidebarSection, NavTab } from \"./double-sidebar\";\n\n\n"
18
+ "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { Header } from \"./header\";\nimport { useCSSVariableSync } from \"../../hooks/use-css-variable-sync\";\nimport { DoubleSidebar, DoubleSidebarSection, NavTab, defaultDoubleSidebarSections } from \"./double-sidebar\";\nimport { \n Sheet, \n SheetContent,\n SheetTitle,\n} from \"../ui/sheet\";\nimport { cn } from \"../../lib/utils\";\nimport * as VisuallyHidden from \"@radix-ui/react-visually-hidden\";\n\ninterface DoubleSidebarShellProps {\n /** Navigation sections for the double sidebar */\n sections?: DoubleSidebarSection[];\n /** Visual variant for the icon column */\n iconVariant?: \"dark\" | \"light\";\n /** Visual variant for the navigation column */\n navVariant?: \"dark\" | \"light\";\n /** Optional page header content (e.g., breadcrumbs, page title) */\n pageHeader?: React.ReactNode;\n /** Main content - the modular blocks */\n children: React.ReactNode;\n /** Callback when a tab is clicked */\n onTabClick?: (section: DoubleSidebarSection, tab: NavTab) => void;\n /** Callback when app menu (hamburger) is clicked - for future app-level menu */\n onAppMenuClick?: () => void;\n /** Additional class name for the main content area */\n contentClassName?: string;\n}\n\n/**\n * Canvas Design System - Double Sidebar Shell\n * \n * A composable page layout with a two-column sidebar that provides:\n * - Fixed header (80px)\n * - Fixed double sidebar on desktop (96px icons + 280px nav = 376px total)\n * - Floating sidebar toggle button on mobile (left edge)\n * - Mobile sheet navigation with both sidebar columns\n * - Each sidebar column can be independently themed (light/dark)\n * - Main content area with pageHeader slot and children slot for blocks\n * \n * Uses the same styling and spacing as DashboardShell for non-sidebar content.\n * \n * @example\n * ```tsx\n * <DoubleSidebarShell \n * sections={sections}\n * iconVariant=\"light\" \n * navVariant=\"light\"\n * >\n * <ContentDropzone label=\"Main content area\" />\n * </DoubleSidebarShell>\n * ```\n */\nexport function DoubleSidebarShell({\n sections = defaultDoubleSidebarSections,\n iconVariant,\n navVariant,\n pageHeader,\n children,\n onTabClick,\n onAppMenuClick,\n contentClassName,\n}: DoubleSidebarShellProps) {\n useCSSVariableSync();\n const effectiveIconVariant = iconVariant ?? \"dark\";\n const effectiveNavVariant = navVariant ?? \"light\";\n const [sidebarOpen, setSidebarOpen] = useState(false);\n\n const handleTabClick = (section: DoubleSidebarSection, tab: NavTab) => {\n onTabClick?.(section, tab);\n // Close sidebar when tab is clicked\n setSidebarOpen(false);\n };\n\n const handleAppMenuClick = () => {\n // Placeholder for future app-level menu\n onAppMenuClick?.();\n console.log(\"App menu clicked - implement app-level mobile menu here\");\n };\n\n return (\n <div className=\"min-h-screen bg-[var(--canvas-background)]\">\n {/* Header - Fixed at top, offset on desktop to not overlap double sidebar */}\n <div className=\"fixed top-0 left-0 right-0 lg:left-[var(--double-sidebar-width)] z-40\">\n <Header onMenuClick={handleAppMenuClick} />\n </div>\n\n {/* Desktop Double Sidebar - Fixed on left, visible lg+ */}\n <div className=\"hidden lg:block fixed top-0 left-0 bottom-0 z-50 w-[var(--double-sidebar-width)]\">\n <DoubleSidebar\n sections={sections}\n iconVariant={effectiveIconVariant}\n navVariant={effectiveNavVariant}\n onTabClick={handleTabClick}\n />\n </div>\n\n {/* Mobile Sidebar Toggle Button - Floating on left edge */}\n <button\n onClick={() => setSidebarOpen(true)}\n className={cn(\n \"lg:hidden fixed left-0 z-30\",\n \"top-[calc(var(--header-height)+4px)]\",\n \"flex items-center justify-center\",\n \"size-11\",\n \"bg-[var(--canvas-background)]\",\n \"border border-l-0 border-[var(--canvas-neutral-border)]\",\n \"rounded-r-[var(--radius-xs)]\",\n \"shadow-[0px_4px_16px_0px_rgba(0,0,0,0.04)]\",\n \"transition-opacity hover:opacity-80\"\n )}\n aria-label=\"Open sidebar\"\n >\n <ChevronRight className=\"size-6 text-[var(--canvas-primary)]\" />\n </button>\n\n {/* Mobile Double Sidebar Sheet */}\n <Sheet open={sidebarOpen} onOpenChange={setSidebarOpen}>\n <SheetContent side=\"left\" className=\"p-0 w-[var(--double-sidebar-width)]\">\n <VisuallyHidden.Root>\n <SheetTitle>Navigation</SheetTitle>\n </VisuallyHidden.Root>\n <DoubleSidebar\n sections={sections}\n iconVariant={effectiveIconVariant}\n navVariant={effectiveNavVariant}\n onTabClick={handleTabClick}\n onClose={() => setSidebarOpen(false)}\n />\n </SheetContent>\n </Sheet>\n\n {/* Main Content Area - Same styling as DashboardShell */}\n <main\n className={cn(\n \"pt-[var(--header-height)]\",\n \"lg:pl-[var(--double-sidebar-width)]\",\n \"min-h-screen\"\n )}\n >\n <div \n className={cn(\n \"flex flex-col gap-[var(--spacing-xl)]\",\n \"px-[var(--spacing-xl)] lg:px-[var(--spacing-5xl)]\",\n \"pt-10 pb-[var(--spacing-5xl)]\",\n contentClassName\n )}\n >\n {/* Page Header Slot */}\n {pageHeader && (\n <section className=\"pt-0\">\n {pageHeader}\n </section>\n )}\n\n {/* Main Content Slot - Blocks go here */}\n <section className=\"flex flex-col gap-[var(--spacing-4xl)]\">\n {children}\n </section>\n </div>\n </main>\n </div>\n );\n}\n\n// Re-export types for convenience\nexport type { DoubleSidebarSection, NavTab } from \"./double-sidebar\";\n\n\n"
10
19
  }
11
20
  ],
12
21
  "dependencies": [
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "double-sidebar",
3
3
  "type": "registry:layout",
4
- "description": "Two-column sidebar with icon strip and nav panel. Used inside DoubleSidebarShell.",
4
+ "description": "Two-column sidebar with icon strip (96px) and nav panel (280px, full height). Used inside DoubleSidebarShell. Icons select categories, nav panel shows items for selected category.",
5
+ "keywords": [
6
+ "sidebar",
7
+ "double",
8
+ "icon",
9
+ "categories",
10
+ "navigation"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/layout/double-sidebar.tsx",
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "header",
3
3
  "type": "registry:layout",
4
- "description": "Top navigation bar with logo, nav links, icon cluster (search, notifications, messages, cart), and avatar dropdown.",
4
+ "description": "Responsive top navigation bar (~64px tall) with logo, nav links, icon cluster (search, notifications, messages, cart), and avatar dropdown. Collapses to hamburger menu on mobile. Light and dark variants.",
5
+ "keywords": [
6
+ "header",
7
+ "navbar",
8
+ "navigation",
9
+ "top-bar",
10
+ "logo",
11
+ "menu"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/header.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "icon-sidebar-shell",
3
3
  "type": "registry:layout",
4
- "description": "Layout with narrow icon-only sidebar (96px). Good for apps with few main sections.",
4
+ "description": "App layout with narrow icon-only sidebar (96px) and fixed header. Icons expand to tooltip labels on hover. Full viewport height. Use for apps with few main sections where you want maximum content space.",
5
+ "keywords": [
6
+ "sidebar",
7
+ "icon",
8
+ "compact",
9
+ "navigation",
10
+ "app"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/layout/icon-sidebar-shell.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "icon-sidebar",
3
3
  "type": "registry:layout",
4
- "description": "Narrow sidebar with only icons and tooltips. Used inside IconSidebarShell.",
4
+ "description": "Narrow sidebar (96px, full height) with only icons and tooltips. Used inside IconSidebarShell. Shows icon buttons with hover tooltips for labels.",
5
+ "keywords": [
6
+ "sidebar",
7
+ "icon",
8
+ "compact",
9
+ "narrow",
10
+ "navigation"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/layout/icon-sidebar.tsx",
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "mobile-menu-shell",
3
3
  "type": "registry:layout",
4
- "description": "Mobile-first layout with bottom navigation bar. Good for mobile app-like experiences.",
4
+ "description": "Mobile-first layout with sticky bottom navigation bar (Home, Search, Profile tabs). Full viewport height. Use for mobile app-like experiences or responsive apps that need bottom tab navigation.",
5
+ "keywords": [
6
+ "mobile",
7
+ "bottom",
8
+ "tabs",
9
+ "navigation",
10
+ "app",
11
+ "responsive"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/mobile-menu-shell.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "multistep-progressbar-shell",
3
3
  "type": "registry:layout",
4
- "description": "",
4
+ "description": "Alternative multi-step layout with a horizontal progress bar instead of numbered step tracker. Content area centered below. Use for simplified wizard flows where progress percentage matters more than step numbers.",
5
+ "keywords": [
6
+ "wizard",
7
+ "multistep",
8
+ "progress",
9
+ "bar",
10
+ "form"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/layout/multistep-progressbar-shell.tsx",
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "name": "multistep-shell",
3
3
  "type": "registry:layout",
4
- "description": "Multi-step wizard layout with horizontal step tracker, step title/description, and navigation buttons.",
4
+ "description": "Multi-step wizard layout with horizontal step tracker at top, step title/description, and navigation buttons. Content area centered (max 768px). Full viewport height. Use for registration flows, onboarding, or any sequential form process.",
5
+ "keywords": [
6
+ "wizard",
7
+ "multistep",
8
+ "steps",
9
+ "form",
10
+ "onboarding",
11
+ "registration",
12
+ "flow"
13
+ ],
14
+ "visualWeight": "light",
5
15
  "files": [
6
16
  {
7
17
  "path": "components/layout/multistep-shell.tsx",
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "multistep-sidebar-shell",
3
3
  "type": "registry:layout",
4
- "description": "Multi-step wizard with sidebar step list instead of horizontal tracker.",
4
+ "description": "Multi-step wizard with step list in a left sidebar instead of horizontal tracker. Full viewport height. Use when steps need descriptions or the flow has many steps.",
5
+ "keywords": [
6
+ "wizard",
7
+ "multistep",
8
+ "sidebar",
9
+ "steps",
10
+ "form"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/layout/multistep-sidebar-shell.tsx",
8
16
  "type": "registry:layout",
9
- "content": "\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Header } from \"./header\";\nimport { useCSSVariableSync } from \"../../hooks/use-css-variable-sync\";\nimport { StepTracker, Step, defaultSteps } from \"../blocks/step-tracker\";\nimport { InfoCard, LinksCard, LinkItem, defaultSupportLinks } from \"../blocks/sidebar-cards\";\nimport { Button } from \"../ui/button\";\nimport { Typography } from \"../ui/typography\";\n\ninterface MultistepSidebarShellProps {\n /** Array of step objects */\n steps?: Step[];\n /** Current active step (0-indexed) */\n currentStep?: number;\n /** Callback when a step is clicked */\n onStepClick?: (stepIndex: number) => void;\n /** Info card title */\n infoTitle?: string;\n /** Info card description */\n infoDescription?: string;\n /** Links card title */\n linksTitle?: string;\n /** Links card items */\n links?: LinkItem[];\n /** Main content */\n children: React.ReactNode;\n /** Callback when Cancel/Back is clicked */\n onCancel?: () => void;\n /** Callback when Continue/Submit is clicked */\n onContinue?: () => void;\n /** Cancel button text */\n cancelText?: string;\n /** Continue button text */\n continueText?: string;\n /** Whether Continue button is disabled */\n continueDisabled?: boolean;\n /** Callback when app menu (hamburger) is clicked */\n onAppMenuClick?: () => void;\n /** Additional class name for the main content area */\n contentClassName?: string;\n}\n\n/**\n * Canvas Design System - Multistep Sidebar Shell\n * \n * A layout for multi-step processes with a sidebar:\n * - Fixed header with logo\n * - Horizontal step tracker below header\n * - Step title and description\n * - Two-column layout:\n * - Left: Main content with navigation buttons\n * - Right: Sidebar (320px) with Info and Links cards\n * \n * @example\n * ```tsx\n * <MultistepSidebarShell\n * steps={defaultSteps}\n * currentStep={0}\n * onCancel={() => router.back()}\n * onContinue={() => setStep(step + 1)}\n * >\n * <ContentDropzone />\n * </MultistepSidebarShell>\n * ```\n */\nexport function MultistepSidebarShell({\n steps = defaultSteps,\n currentStep = 0,\n onStepClick,\n infoTitle,\n infoDescription,\n linksTitle,\n links = defaultSupportLinks,\n children,\n onCancel,\n onContinue,\n cancelText,\n continueText,\n continueDisabled = false,\n onAppMenuClick,\n contentClassName,\n}: MultistepSidebarShellProps) {\n useCSSVariableSync();\n\n const handleAppMenuClick = () => {\n onAppMenuClick?.();\n console.log(\"App menu clicked - implement app-level mobile menu here\");\n };\n\n const currentStepData = steps[currentStep];\n const isFirstStep = currentStep === 0;\n const isLastStep = currentStep === steps.length - 1;\n\n return (\n <div className=\"min-h-screen bg-[var(--canvas-background)]\">\n {/* Header - Fixed at top with logo visible */}\n <header className=\"sticky top-0 z-40\">\n <Header onMenuClick={handleAppMenuClick} showDesktopLogo />\n </header>\n\n {/* Step Tracker Section */}\n <div className=\"w-full\">\n <StepTracker\n steps={steps}\n currentStep={currentStep}\n onStepClick={onStepClick}\n />\n </div>\n\n {/* Main Content Area - Two Column Layout */}\n <main className=\"w-full\">\n <div \n className={cn(\n \"w-full max-w-[1200px] mx-auto\",\n \"px-[var(--spacing-xl)] lg:px-0\",\n \"py-10\",\n \"flex gap-10\"\n )}\n >\n {/* Left: Main Content */}\n <div className={cn(\"flex-1 min-w-0\", contentClassName)}>\n {/* Step Content Container */}\n <div className=\"flex flex-col gap-[var(--spacing-3xl)]\">\n {/* Step Title and Description */}\n <div className=\"flex flex-col gap-1\">\n <Typography variant=\"h6\" as=\"h3\">\n {currentStepData?.label || `Step ${currentStep + 1}`}\n </Typography>\n {currentStepData?.description && (\n <Typography variant=\"body-m\" color=\"muted\">\n {currentStepData.description}\n </Typography>\n )}\n </div>\n\n {/* Main Content Area */}\n <div className=\"w-full\">\n {children}\n </div>\n\n {/* Navigation Buttons */}\n <div className=\"flex gap-[var(--spacing-3xl)] items-center justify-end\">\n <Button variant=\"neutral\" onClick={onCancel}>\n {cancelText || (isFirstStep ? \"Cancel\" : \"Back\")}\n </Button>\n <Button variant=\"primary\" onClick={onContinue} disabled={continueDisabled}>\n {continueText || (isLastStep ? \"Submit\" : \"Continue\")}\n </Button>\n </div>\n </div>\n </div>\n\n {/* Right: Sidebar */}\n <aside className=\"hidden lg:flex flex-col gap-10 shrink-0\">\n <InfoCard \n title={infoTitle} \n description={infoDescription} \n />\n <LinksCard \n title={linksTitle} \n links={links} \n />\n </aside>\n </div>\n </main>\n </div>\n );\n}\n"
17
+ "content": "\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Header } from \"./header\";\nimport { useCSSVariableSync } from \"../../hooks/use-css-variable-sync\";\nimport { StepTracker, Step, defaultSteps } from \"../blocks/step-tracker\";\nimport { InfoCard, SupportLinksCard, LinkItem, defaultSupportLinks } from \"../blocks/sidebar-cards\";\nimport { Button } from \"../ui/button\";\nimport { Typography } from \"../ui/typography\";\n\ninterface MultistepSidebarShellProps {\n /** Array of step objects */\n steps?: Step[];\n /** Current active step (0-indexed) */\n currentStep?: number;\n /** Callback when a step is clicked */\n onStepClick?: (stepIndex: number) => void;\n /** Info card title */\n infoTitle?: string;\n /** Info card description */\n infoDescription?: string;\n /** Links card title */\n linksTitle?: string;\n /** Links card items */\n links?: LinkItem[];\n /** Main content */\n children: React.ReactNode;\n /** Callback when Cancel/Back is clicked */\n onCancel?: () => void;\n /** Callback when Continue/Submit is clicked */\n onContinue?: () => void;\n /** Cancel button text */\n cancelText?: string;\n /** Continue button text */\n continueText?: string;\n /** Whether Continue button is disabled */\n continueDisabled?: boolean;\n /** Callback when app menu (hamburger) is clicked */\n onAppMenuClick?: () => void;\n /** Additional class name for the main content area */\n contentClassName?: string;\n}\n\n/**\n * Canvas Design System - Multistep Sidebar Shell\n * \n * A layout for multi-step processes with a sidebar:\n * - Fixed header with logo\n * - Horizontal step tracker below header\n * - Step title and description\n * - Two-column layout:\n * - Left: Main content with navigation buttons\n * - Right: Sidebar (320px) with Info and Links cards\n * \n * @example\n * ```tsx\n * <MultistepSidebarShell\n * steps={defaultSteps}\n * currentStep={0}\n * onCancel={() => router.back()}\n * onContinue={() => setStep(step + 1)}\n * >\n * <ContentDropzone />\n * </MultistepSidebarShell>\n * ```\n */\nexport function MultistepSidebarShell({\n steps = defaultSteps,\n currentStep = 0,\n onStepClick,\n infoTitle,\n infoDescription,\n linksTitle,\n links = defaultSupportLinks,\n children,\n onCancel,\n onContinue,\n cancelText,\n continueText,\n continueDisabled = false,\n onAppMenuClick,\n contentClassName,\n}: MultistepSidebarShellProps) {\n useCSSVariableSync();\n\n const handleAppMenuClick = () => {\n onAppMenuClick?.();\n console.log(\"App menu clicked - implement app-level mobile menu here\");\n };\n\n const currentStepData = steps[currentStep];\n const isFirstStep = currentStep === 0;\n const isLastStep = currentStep === steps.length - 1;\n\n return (\n <div className=\"min-h-screen bg-[var(--canvas-background)]\">\n {/* Header - Fixed at top with logo visible */}\n <header className=\"sticky top-0 z-40\">\n <Header onMenuClick={handleAppMenuClick} showDesktopLogo />\n </header>\n\n {/* Step Tracker Section */}\n <div className=\"w-full\">\n <StepTracker\n steps={steps}\n currentStep={currentStep}\n onStepClick={onStepClick}\n />\n </div>\n\n {/* Main Content Area - Two Column Layout */}\n <main className=\"w-full\">\n <div \n className={cn(\n \"w-full max-w-[1200px] mx-auto\",\n \"px-[var(--spacing-xl)] lg:px-0\",\n \"py-10\",\n \"flex gap-10\"\n )}\n >\n {/* Left: Main Content */}\n <div className={cn(\"flex-1 min-w-0\", contentClassName)}>\n {/* Step Content Container */}\n <div className=\"flex flex-col gap-[var(--spacing-3xl)]\">\n {/* Step Title and Description */}\n <div className=\"flex flex-col gap-1\">\n <Typography variant=\"h6\" as=\"h3\">\n {currentStepData?.label || `Step ${currentStep + 1}`}\n </Typography>\n {currentStepData?.description && (\n <Typography variant=\"body-m\" color=\"muted\">\n {currentStepData.description}\n </Typography>\n )}\n </div>\n\n {/* Main Content Area */}\n <div className=\"w-full\">\n {children}\n </div>\n\n {/* Navigation Buttons */}\n <div className=\"flex gap-[var(--spacing-3xl)] items-center justify-end\">\n <Button variant=\"neutral\" onClick={onCancel}>\n {cancelText || (isFirstStep ? \"Cancel\" : \"Back\")}\n </Button>\n <Button variant=\"primary\" onClick={onContinue} disabled={continueDisabled}>\n {continueText || (isLastStep ? \"Submit\" : \"Continue\")}\n </Button>\n </div>\n </div>\n </div>\n\n {/* Right: Sidebar */}\n <aside className=\"hidden lg:flex flex-col gap-10 shrink-0\">\n <InfoCard \n title={infoTitle} \n description={infoDescription} \n />\n <SupportLinksCard\n title={linksTitle}\n links={links}\n />\n </aside>\n </div>\n </main>\n </div>\n );\n}\n"
10
18
  }
11
19
  ],
12
20
  "dependencies": [],
@@ -6,7 +6,7 @@
6
6
  {
7
7
  "path": "components/layout/project-context-shell.tsx",
8
8
  "type": "registry:layout",
9
- "content": "\"use client\";\n\nimport { ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport {\n FileText,\n Users,\n LayoutGrid,\n Wand2,\n} from \"lucide-react\";\nimport { useThemeImages, useThemeBranding } from \"../../context/theme-context\";\nimport {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette as PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n type Icon as PhosphorIcon,\n} from \"@phosphor-icons/react\";\n\n// ═══════════════════════════════════════════════════════════\n// LOGO ICON SHAPES\n// ═══════════════════════════════════════════════════════════\n\ntype IconShapeId = \"rounded\" | \"circle\" | \"square\";\n\nconst iconShapes: { id: IconShapeId; renderBackground: (bgColor: string) => React.ReactNode }[] = [\n {\n id: \"rounded\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" rx=\"10\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"circle\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <circle cx=\"16\" cy=\"16\" r=\"16\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"square\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n];\n\nconst iconMap: Record<string, PhosphorIcon> = {\n Diamond, Hexagon, Star, Lightning, Sparkle, Infinity, Code, Terminal, Cpu, Database,\n Globe, Cloud, WifiHigh, Briefcase, Buildings, Storefront, Handshake, ChartLine,\n Palette: PaletteIcon, PencilSimple, Camera, MusicNote, Lightbulb, Leaf, Tree,\n Sun, Moon, Fire, Drop, ChatCircle, Envelope, Phone, Megaphone, Heart, Shield,\n Trophy, Rocket, Target, Flag,\n};\n\nfunction resolveBrandingColor(value: string): string {\n if (!value) return \"#ffffff\";\n if (value.startsWith(\"var(\")) {\n const varName = value.replace(\"var(\", \"\").replace(\")\", \"\");\n if (typeof window !== \"undefined\") {\n const computed = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();\n return computed || \"#ffffff\";\n }\n return \"#ffffff\";\n }\n return value;\n}\n\n// ═══════════════════════════════════════════════════════════\n// TAB TYPES\n// ═══════════════════════════════════════════════════════════\n\nexport type ProjectContextTab = \"scope\" | \"personas\" | \"screens\" | \"prompts\";\n\ninterface TabConfig {\n id: ProjectContextTab;\n label: string;\n icon: typeof FileText;\n description: string;\n}\n\nconst tabs: TabConfig[] = [\n {\n id: \"scope\",\n label: \"Scope\",\n icon: FileText,\n description: \"Upload your project scope document\",\n },\n {\n id: \"personas\",\n label: \"Personas\",\n icon: Users,\n description: \"Define who you're building for\",\n },\n {\n id: \"screens\",\n label: \"Screens\",\n icon: LayoutGrid,\n description: \"Map out your product's screens and flows\",\n },\n {\n id: \"prompts\",\n label: \"Prompt Helpers\",\n icon: Wand2,\n description: \"Build prompts with existing components\",\n },\n];\n\n// ═══════════════════════════════════════════════════════════\n// SHELL COMPONENT\n// ═══════════════════════════════════════════════════════════\n\ninterface ProjectContextShellProps {\n children: ReactNode;\n activeTab: ProjectContextTab;\n onTabChange: (tab: ProjectContextTab) => void;\n}\n\nexport function ProjectContextShell({\n children,\n activeTab,\n onTabChange,\n}: ProjectContextShellProps) {\n const activeTabConfig = tabs.find((t) => t.id === activeTab);\n const themeImages = useThemeImages();\n const { branding, isMounted } = useThemeBranding();\n \n // Get logo (use light variant for this light sidebar)\n const logoUrl = themeImages.logoLight;\n const iconShape = iconShapes.find(s => s.id === branding.iconShape) || iconShapes[0];\n\n return (\n <div className=\"min-h-screen flex bg-[var(--canvas-background)]\">\n {/* Sidebar */}\n <aside className=\"w-64 border-r border-[var(--canvas-border)] bg-[var(--canvas-background)] flex flex-col shrink-0\">\n {/* Logo + Title Header - matches main header height */}\n <div className=\"px-4 border-b border-[var(--canvas-border)] flex items-center h-[97px]\">\n {/* Logo */}\n <div className={`flex items-center ${isMounted ? 'opacity-100' : 'opacity-0'}`}>\n {logoUrl ? (\n <img \n src={logoUrl} \n alt=\"Logo\" \n className=\"h-8 w-auto object-contain\"\n />\n ) : (\n // Uses CSS variables directly - no JavaScript resolution needed\n <div className=\"flex items-center\">\n <div className=\"relative size-8 shrink-0\">\n {iconShape.renderBackground(branding.bgColor || \"var(--canvas-primary)\")}\n <div className=\"absolute inset-0 flex items-center justify-center z-10\">\n {(() => {\n const IconComponent = iconMap[branding.iconName || \"Buildings\"] || Buildings;\n return <IconComponent weight=\"bold\" size={18} color={branding.iconColor || \"var(--canvas-primary-foreground)\"} />;\n })()}\n </div>\n </div>\n <span className=\"font-semibold ml-2.5 text-[var(--canvas-text)]\" style={{ fontSize: \"var(--typo-body-xl-size)\" }}>\n {branding.wordmark || \"canvas\"}\n </span>\n </div>\n )}\n </div>\n </div>\n\n <nav className=\"flex-1 p-2\">\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = activeTab === tab.id;\n\n return (\n <button\n key={tab.id}\n onClick={() => onTabChange(tab.id)}\n className={cn(\n \"cursor-pointer w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-colors mb-1\",\n isActive\n ? \"bg-[var(--canvas-surface-brand)] text-[var(--canvas-primary)]\"\n : \"text-[var(--canvas-text-muted)] hover:bg-[var(--canvas-surface)] hover:text-[var(--canvas-text)]\"\n )}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"font-medium\" style={{ fontSize: \"var(--typo-body-s-size)\" }}>{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n {/* Help section */}\n <div className=\"p-4 border-t border-[var(--canvas-border)]\">\n <div className=\"rounded-lg bg-[var(--canvas-surface)] p-3\">\n <p className=\"text-[var(--canvas-text-muted)] leading-relaxed\" style={{ fontSize: \"var(--typo-body-xs-size)\" }}>\n 💡 Use the prompt templates to generate content with Cursor AI\n </p>\n </div>\n </div>\n </aside>\n\n {/* Content Area */}\n <main className=\"flex-1 flex flex-col min-w-0 overflow-hidden\">\n {/* Tab Header */}\n <div className=\"border-b border-[var(--canvas-border)] px-8 py-6 bg-[var(--canvas-background)]\">\n {activeTabConfig && (\n <div>\n <h2 className=\"font-semibold text-[var(--canvas-text)]\" style={{ fontSize: \"var(--typo-body-xl-size)\" }}>\n {activeTabConfig.label}\n </h2>\n <p className=\"text-[var(--canvas-text-muted)]\" style={{ fontSize: \"var(--typo-body-s-size)\" }}>\n {activeTabConfig.description}\n </p>\n </div>\n )}\n </div>\n\n {/* Scrollable Content */}\n <ScrollArea className=\"flex-1\">\n <div className=\"p-8\">{children}</div>\n </ScrollArea>\n </main>\n </div>\n );\n}\n"
9
+ "content": "\"use client\";\n\nimport { ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport {\n FileText,\n Users,\n LayoutGrid,\n Wand2,\n} from \"lucide-react\";\nimport { useThemeImages, useThemeBranding } from \"../../context/theme-context\";\nimport {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette as PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n type Icon as PhosphorIcon,\n} from \"@phosphor-icons/react\";\n\n// ═══════════════════════════════════════════════════════════\n// LOGO ICON SHAPES\n// ═══════════════════════════════════════════════════════════\n\ntype IconShapeId = \"rounded\" | \"circle\" | \"square\";\n\nconst iconShapes: { id: IconShapeId; renderBackground: (bgColor: string) => React.ReactNode }[] = [\n {\n id: \"rounded\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" rx=\"10\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"circle\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <circle cx=\"16\" cy=\"16\" r=\"16\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"square\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n];\n\nconst iconMap: Record<string, PhosphorIcon> = {\n Diamond, Hexagon, Star, Lightning, Sparkle, Infinity, Code, Terminal, Cpu, Database,\n Globe, Cloud, WifiHigh, Briefcase, Buildings, Storefront, Handshake, ChartLine,\n Palette: PaletteIcon, PencilSimple, Camera, MusicNote, Lightbulb, Leaf, Tree,\n Sun, Moon, Fire, Drop, ChatCircle, Envelope, Phone, Megaphone, Heart, Shield,\n Trophy, Rocket, Target, Flag,\n};\n\nfunction resolveBrandingColor(value: string): string {\n if (!value) return \"#ffffff\";\n if (value.startsWith(\"var(\")) {\n const varName = value.replace(\"var(\", \"\").replace(\")\", \"\");\n if (typeof window !== \"undefined\") {\n const computed = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();\n return computed || \"#ffffff\";\n }\n return \"#ffffff\";\n }\n return value;\n}\n\n// ═══════════════════════════════════════════════════════════\n// TAB TYPES\n// ═══════════════════════════════════════════════════════════\n\nexport type ProjectContextTab = \"scope\" | \"personas\" | \"screens\" | \"prompts\";\n\ninterface TabConfig {\n id: ProjectContextTab;\n label: string;\n icon: typeof FileText;\n description: string;\n}\n\nconst tabs: TabConfig[] = [\n {\n id: \"scope\",\n label: \"Scope\",\n icon: FileText,\n description: \"Upload your project scope document\",\n },\n {\n id: \"personas\",\n label: \"Personas\",\n icon: Users,\n description: \"Define who you're building for\",\n },\n {\n id: \"screens\",\n label: \"Screens\",\n icon: LayoutGrid,\n description: \"Map out your product's screens and flows\",\n },\n {\n id: \"prompts\",\n label: \"Prompt Helpers\",\n icon: Wand2,\n description: \"Build prompts with existing components\",\n },\n];\n\n// ═══════════════════════════════════════════════════════════\n// SHELL COMPONENT\n// ═══════════════════════════════════════════════════════════\n\ninterface ProjectContextShellProps {\n children: ReactNode;\n activeTab: ProjectContextTab;\n onTabChange: (tab: ProjectContextTab) => void;\n}\n\nexport function ProjectContextShell({\n children,\n activeTab,\n onTabChange,\n}: ProjectContextShellProps) {\n const activeTabConfig = tabs.find((t) => t.id === activeTab);\n const themeImages = useThemeImages();\n const { branding, isMounted } = useThemeBranding();\n \n // Get logo (use light variant for this light sidebar)\n const logoUrl = themeImages.logoLight;\n const iconShape = iconShapes.find(s => s.id === branding.iconShape) || iconShapes[0];\n\n return (\n <div className=\"min-h-screen flex bg-[var(--canvas-background)]\">\n {/* Sidebar */}\n <aside className=\"w-64 border-r border-[var(--canvas-border)] bg-[var(--canvas-background)] flex flex-col shrink-0\">\n {/* Logo + Title Header - matches main header height */}\n <div className=\"px-4 border-b border-[var(--canvas-border)] flex items-center h-[97px]\">\n {/* Logo */}\n <div className={`flex items-center ${isMounted ? 'opacity-100' : 'opacity-0'}`}>\n {logoUrl ? (\n <img \n src={logoUrl} \n alt=\"Logo\" \n className=\"h-8 w-auto object-contain\"\n />\n ) : (\n // Uses CSS variables directly - no JavaScript resolution needed\n <div className=\"flex items-center\">\n <div className=\"relative size-8 shrink-0\">\n {iconShape.renderBackground(branding.bgColor || \"var(--canvas-primary)\")}\n <div className=\"absolute inset-0 flex items-center justify-center z-10\">\n {(() => {\n const IconComponent = iconMap[branding.iconName || \"Buildings\"] || Buildings;\n return <IconComponent weight=\"bold\" size={18} color={branding.iconColor || \"var(--canvas-primary-foreground)\"} />;\n })()}\n </div>\n </div>\n <span className=\"font-semibold ml-2.5 text-[var(--canvas-text)]\" style={{ fontSize: \"var(--typo-body-xl-size)\" }}>\n {branding.wordmark || \"canvas\"}\n </span>\n </div>\n )}\n </div>\n </div>\n\n <nav className=\"flex-1 p-2\">\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = activeTab === tab.id;\n\n return (\n <button\n key={tab.id}\n onClick={() => onTabChange(tab.id)}\n className={cn(\n \"cursor-pointer w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-colors mb-1\",\n isActive\n ? \"bg-[var(--canvas-surface-brand)] text-[var(--canvas-primary)]\"\n : \"text-[var(--canvas-text-muted)] hover:bg-[var(--canvas-surface)] hover:text-[var(--canvas-text)]\"\n )}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"font-medium\" style={{ fontSize: \"var(--typo-body-s-size)\" }}>{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n {/* Help section */}\n <div className=\"p-4 border-t border-[var(--canvas-border)]\">\n <div className=\"rounded-lg bg-[var(--canvas-surface)] p-3\">\n <p className=\"text-[var(--canvas-text-muted)] leading-relaxed\" style={{ fontSize: \"var(--typo-body-xs-size)\" }}>\n 💡 Use the prompt templates to generate content with AI\n </p>\n </div>\n </div>\n </aside>\n\n {/* Content Area */}\n <main className=\"flex-1 flex flex-col min-w-0 overflow-hidden\">\n {/* Tab Header */}\n <div className=\"border-b border-[var(--canvas-border)] px-8 py-6 bg-[var(--canvas-background)]\">\n {activeTabConfig && (\n <div>\n <h2 className=\"font-semibold text-[var(--canvas-text)]\" style={{ fontSize: \"var(--typo-body-xl-size)\" }}>\n {activeTabConfig.label}\n </h2>\n <p className=\"text-[var(--canvas-text-muted)]\" style={{ fontSize: \"var(--typo-body-s-size)\" }}>\n {activeTabConfig.description}\n </p>\n </div>\n )}\n </div>\n\n {/* Scrollable Content */}\n <ScrollArea className=\"flex-1\">\n <div className=\"p-8\">{children}</div>\n </ScrollArea>\n </main>\n </div>\n );\n}\n"
10
10
  }
11
11
  ],
12
12
  "dependencies": [
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "search-bar-shell",
3
3
  "type": "registry:layout",
4
- "description": "Layout with prominent search bar in header area.",
4
+ "description": "Layout with prominent search bar in header area. Centered content below. Full viewport height. Use for search-focused pages like documentation, knowledge bases, or directory search.",
5
+ "keywords": [
6
+ "search",
7
+ "documentation",
8
+ "knowledge-base",
9
+ "directory"
10
+ ],
11
+ "visualWeight": "light",
5
12
  "files": [
6
13
  {
7
14
  "path": "components/layout/search-bar-shell.tsx",
@@ -1,7 +1,13 @@
1
1
  {
2
2
  "name": "sidebar-nav",
3
3
  "type": "registry:layout",
4
- "description": "Navigation items component used inside Sidebar. Handles section titles and item rendering.",
4
+ "description": "Navigation items component used inside Sidebar. Handles section titles and item rendering with active states. Compact, fits within sidebar width.",
5
+ "keywords": [
6
+ "navigation",
7
+ "sidebar",
8
+ "menu-items"
9
+ ],
10
+ "visualWeight": "light",
5
11
  "files": [
6
12
  {
7
13
  "path": "components/layout/sidebar-nav.tsx",
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "sidebar",
3
3
  "type": "registry:layout",
4
- "description": "Dark or light sidebar with logo and collapsible nav sections. Used inside DashboardShell.",
4
+ "description": "Dark or light sidebar (320px, full height) with logo and collapsible nav sections. Used inside DashboardShell. Each section has a title and list of nav items with icons.",
5
+ "keywords": [
6
+ "sidebar",
7
+ "navigation",
8
+ "menu",
9
+ "collapsible"
10
+ ],
11
+ "visualWeight": "light",
5
12
  "files": [
6
13
  {
7
14
  "path": "components/layout/sidebar.tsx",
8
15
  "type": "registry:layout",
9
- "content": "\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { SidebarNav, NavSection, NavItem } from \"./sidebar-nav\";\nimport { useThemeImages, useThemeBranding } from \"../../context/theme-context\";\n\n// Phosphor Icons (Bold variant) - same curated set as variables-modal\nimport {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette as PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n type Icon as PhosphorIcon,\n} from \"@phosphor-icons/react\";\n\n// ============================================\n// Icon Shape Presets for Logo Creator\n// ============================================\n\ntype IconShapeId = \"rounded\" | \"circle\" | \"square\";\n\ninterface IconShape {\n id: IconShapeId;\n renderBackground: (bgColor: string) => React.ReactNode;\n}\n\nconst iconShapes: IconShape[] = [\n {\n id: \"rounded\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" rx=\"10\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"circle\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <circle cx=\"16\" cy=\"16\" r=\"16\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"square\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n];\n\n// Map icon names to components\nconst iconMap: Record<string, PhosphorIcon> = {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette: PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n};\n\n// Helper to resolve CSS variable references to actual hex colors\nfunction resolveBrandingColor(value: string): string {\n if (!value) return \"#ffffff\";\n if (value.startsWith(\"var(\")) {\n const varName = value.replace(\"var(\", \"\").replace(\")\", \"\");\n if (typeof window !== \"undefined\") {\n const computed = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();\n return computed || \"#ffffff\";\n }\n return \"#ffffff\";\n }\n return value;\n}\n\ninterface SidebarProps {\n /** Navigation sections to display */\n sections: NavSection[];\n /** Visual variant - dark for desktop, light for mobile sheet */\n variant?: \"dark\" | \"light\";\n /** Callback when a nav item or subtab is clicked */\n onItemClick?: (item: NavItem | Omit<NavItem, \"children\" | \"icon\">) => void;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Canvas Design System - Sidebar Component\n * \n * Desktop: Fixed dark sidebar (320px width) on the left\n * Mobile: Light theme sidebar rendered inside a Sheet\n */\nexport function Sidebar({ \n sections, \n variant = \"dark\", \n onItemClick,\n className \n}: SidebarProps) {\n const isDark = variant === \"dark\";\n const themeImages = useThemeImages();\n const { branding, isMounted } = useThemeBranding();\n \n // Get the appropriate logo based on variant\n const logoUrl = isDark ? themeImages.logoDark : themeImages.logoLight;\n \n // Get the icon shape renderer\n const iconShape = iconShapes.find(s => s.id === branding.iconShape) || iconShapes[0];\n \n return (\n <aside\n className={cn(\n \"flex flex-col h-full w-[var(--sidebar-width)]\",\n isDark && \"bg-[var(--canvas-sidebar-dark-bg)] border-r border-[var(--canvas-sidebar-dark-border)]\",\n !isDark && \"bg-[var(--canvas-background)] border-r border-[var(--canvas-border)]\",\n className\n )}\n >\n {/* Logo Section */}\n <div className={cn(\n \"flex items-center h-8 shrink-0\",\n \"pt-11 pb-0 pr-[var(--spacing-2xl)] pl-9\"\n )}>\n {logoUrl ? (\n // Custom logo replaces entire lockup (icon + wordmark)\n <img \n src={logoUrl} \n alt=\"Logo\" \n className={`h-8 w-auto object-contain ${isMounted ? 'opacity-100' : 'opacity-0'}`}\n />\n ) : (\n // Logo creator: dynamic icon shape + Phosphor icon + wordmark\n // Uses CSS variables directly - no JavaScript resolution needed\n <div className=\"flex items-center\">\n <div className=\"relative size-8 shrink-0\">\n {iconShape.renderBackground(branding.bgColor || \"var(--canvas-primary)\")}\n <div className=\"absolute inset-0 flex items-center justify-center z-10\">\n {(() => {\n const IconComponent = iconMap[branding.iconName || \"Buildings\"] || Buildings;\n return <IconComponent weight=\"bold\" size={18} color={branding.iconColor || \"var(--canvas-primary-foreground)\"} />;\n })()}\n </div>\n </div>\n <span className={cn(\n \"font-semibold ml-[var(--spacing-md)]\",\n isDark ? \"text-[var(--canvas-sidebar-dark-active-text)]\" : \"text-[var(--canvas-text)]\"\n )} style={{ fontSize: \"var(--typo-body-xl-size)\" }}>\n {branding.wordmark || \"canvas\"}\n </span>\n </div>\n )}\n </div>\n\n {/* Navigation */}\n <ScrollArea className=\"flex-1 px-[var(--spacing-2xl)] pb-[var(--spacing-5xl)]\">\n <SidebarNav \n sections={sections} \n variant={variant} \n onItemClick={onItemClick}\n />\n </ScrollArea>\n </aside>\n );\n}\n\n// Re-export types for convenience\nexport type { NavSection, NavItem } from \"./sidebar-nav\";\n\n"
16
+ "content": "\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { SidebarNav, NavSection, NavItem } from \"./sidebar-nav\";\nimport { useThemeImages, useThemeBranding } from \"../../context/theme-context\";\n\n// Phosphor Icons (Bold variant) - same curated set as variables-modal\nimport {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette as PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n type Icon as PhosphorIcon,\n} from \"@phosphor-icons/react\";\n\n// ============================================\n// Icon Shape Presets for Logo Creator\n// ============================================\n\ntype IconShapeId = \"rounded\" | \"circle\" | \"square\";\n\ninterface IconShape {\n id: IconShapeId;\n renderBackground: (bgColor: string) => React.ReactNode;\n}\n\nconst iconShapes: IconShape[] = [\n {\n id: \"rounded\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" rx=\"10\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"circle\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <circle cx=\"16\" cy=\"16\" r=\"16\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n {\n id: \"square\",\n renderBackground: (bgColor: string) => (\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"size-full absolute inset-0\">\n <rect width=\"32\" height=\"32\" style={{ fill: bgColor }} />\n </svg>\n ),\n },\n];\n\n// Map icon names to components\nconst iconMap: Record<string, PhosphorIcon> = {\n Diamond,\n Hexagon,\n Star,\n Lightning,\n Sparkle,\n Infinity,\n Code,\n Terminal,\n Cpu,\n Database,\n Globe,\n Cloud,\n WifiHigh,\n Briefcase,\n Buildings,\n Storefront,\n Handshake,\n ChartLine,\n Palette: PaletteIcon,\n PencilSimple,\n Camera,\n MusicNote,\n Lightbulb,\n Leaf,\n Tree,\n Sun,\n Moon,\n Fire,\n Drop,\n ChatCircle,\n Envelope,\n Phone,\n Megaphone,\n Heart,\n Shield,\n Trophy,\n Rocket,\n Target,\n Flag,\n};\n\n// Helper to resolve CSS variable references to actual hex colors\nfunction resolveBrandingColor(value: string): string {\n if (!value) return \"#ffffff\";\n if (value.startsWith(\"var(\")) {\n const varName = value.replace(\"var(\", \"\").replace(\")\", \"\");\n if (typeof window !== \"undefined\") {\n const computed = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();\n return computed || \"#ffffff\";\n }\n return \"#ffffff\";\n }\n return value;\n}\n\ninterface SidebarProps {\n /** Navigation sections to display */\n sections: NavSection[];\n /** Visual variant - dark for desktop, light for mobile sheet */\n variant?: \"dark\" | \"light\";\n /** Callback when a nav item or subtab is clicked */\n onItemClick?: (item: NavItem | Omit<NavItem, \"children\" | \"icon\">) => void;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Canvas Design System - Sidebar Component\n * \n * Desktop: Fixed dark sidebar (320px width) on the left\n * Mobile: Light theme sidebar rendered inside a Sheet\n */\nexport function Sidebar({ \n sections, \n variant = \"dark\", \n onItemClick,\n className \n}: SidebarProps) {\n const isDark = variant === \"dark\";\n const themeImages = useThemeImages();\n const { branding, isMounted } = useThemeBranding();\n \n // Get the appropriate logo based on variant\n const logoUrl = isDark ? themeImages.logoDark : themeImages.logoLight;\n \n // Get the icon shape renderer\n const iconShape = iconShapes.find(s => s.id === branding.iconShape) || iconShapes[0];\n \n return (\n <aside\n className={cn(\n \"flex flex-col h-full w-[var(--sidebar-width)]\",\n isDark && \"bg-[var(--canvas-sidebar-dark-bg)] border-r border-[var(--canvas-sidebar-dark-border)]\",\n !isDark && \"bg-[var(--canvas-background)] border-r border-[var(--canvas-border)]\",\n className\n )}\n >\n {/* Logo Section */}\n <div className={cn(\n \"flex items-center h-8 shrink-0\",\n \"pt-11 pb-0 pr-[var(--spacing-2xl)] pl-9\"\n )}>\n {logoUrl ? (\n // Custom logo replaces entire lockup (icon + wordmark)\n <img \n src={logoUrl} \n alt=\"Logo\" \n className={`h-8 w-auto object-contain ${isMounted ? 'opacity-100' : 'opacity-0'}`}\n />\n ) : (\n // Logo creator: dynamic icon shape + Phosphor icon + wordmark\n // Uses CSS variables directly - no JavaScript resolution needed\n <div className=\"flex items-center\">\n <div className=\"relative size-8 shrink-0\">\n {iconShape.renderBackground(branding.bgColor || \"var(--canvas-primary)\")}\n <div className=\"absolute inset-0 flex items-center justify-center z-10\">\n {(() => {\n const IconComponent = iconMap[branding.iconName || \"Buildings\"] || Buildings;\n return <IconComponent weight=\"bold\" size={18} color={branding.iconColor || \"var(--canvas-primary-foreground)\"} />;\n })()}\n </div>\n </div>\n <span className={cn(\n \"font-semibold ml-[var(--spacing-md)]\",\n isDark ? \"text-[var(--canvas-sidebar-dark-active-text)]\" : \"text-[var(--canvas-text)]\"\n )} style={{ fontSize: \"var(--typo-body-xl-size)\", fontFamily: \"var(--typo-body-xl-font, var(--typo-global-font))\" }}>\n {branding.wordmark || \"canvas\"}\n </span>\n </div>\n )}\n </div>\n\n {/* Navigation */}\n <ScrollArea className=\"flex-1 px-[var(--spacing-2xl)] pb-[var(--spacing-5xl)]\">\n <SidebarNav \n sections={sections} \n variant={variant} \n onItemClick={onItemClick}\n />\n </ScrollArea>\n </aside>\n );\n}\n\n// Re-export types for convenience\nexport type { NavSection, NavItem } from \"./sidebar-nav\";\n\n"
10
17
  }
11
18
  ],
12
19
  "dependencies": [
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "standard-page-shell",
3
3
  "type": "registry:layout",
4
- "description": "No-sidebar layout with centered content (max 992px). Has optional flair banner and page header with tabs.",
4
+ "description": "No-sidebar layout with centered content (max 992px). Has optional flair banner at top and page header with tabs. Full viewport height. Use for content pages, marketing pages, search results, or any page that doesn't need persistent navigation.",
5
+ "keywords": [
6
+ "centered",
7
+ "content",
8
+ "standard",
9
+ "page",
10
+ "no-sidebar",
11
+ "simple"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/standard-page-shell.tsx",
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "vertical-multistep-shell",
3
3
  "type": "registry:layout",
4
- "description": "Multi-step wizard with vertical step tracker on the left side.",
4
+ "description": "Multi-step wizard with vertical step tracker on the left side. Steps show as a vertical list with descriptions. Full viewport height. Use for compact multi-step forms.",
5
+ "keywords": [
6
+ "wizard",
7
+ "multistep",
8
+ "vertical",
9
+ "steps",
10
+ "form",
11
+ "compact"
12
+ ],
13
+ "visualWeight": "light",
5
14
  "files": [
6
15
  {
7
16
  "path": "components/layout/vertical-multistep-shell.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "avatar",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Circular avatar image with fallback initials. Built on Radix Avatar. Use for user photos, profile thumbnails, or any person/entity representation.",
5
+ "keywords": [
6
+ "avatar",
7
+ "image",
8
+ "profile",
9
+ "user",
10
+ "photo"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/avatar.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "button",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Themed button with 6 variants (primary, primary-outline, primary-neutral, neutral, neutral-delete, delete) and 4 sizes (mini, sm, default, lg). Inline element (~36-48px tall depending on size). Use for form submissions, actions, navigation, or any clickable trigger.",
5
+ "keywords": [
6
+ "button",
7
+ "action",
8
+ "submit",
9
+ "click",
10
+ "cta"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/button.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "calendar",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Month calendar widget for single or range date selection. Shows month grid with navigation arrows. Use for date pickers, scheduling inputs, or any date selection.",
5
+ "keywords": [
6
+ "calendar",
7
+ "date",
8
+ "picker",
9
+ "month",
10
+ "schedule"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/calendar.tsx",
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "checkbox",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Themed checkbox with check indicator. Built on Radix Checkbox. Use for boolean toggles or multi-select lists in forms.",
5
+ "keywords": [
6
+ "checkbox",
7
+ "toggle",
8
+ "form",
9
+ "boolean"
10
+ ],
11
+ "visualWeight": "light",
5
12
  "files": [
6
13
  {
7
14
  "path": "components/ui/checkbox.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "date-input",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Date input field with calendar icon that opens a calendar popover for date selection. Combines Input with Calendar. Use for date entry fields in forms.",
5
+ "keywords": [
6
+ "date",
7
+ "input",
8
+ "picker",
9
+ "calendar",
10
+ "form"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/date-input.tsx",
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "dialog",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Modal dialog overlay with title, description, and content area. Built on Radix Dialog. Use as the base for popup components or any modal interaction.",
5
+ "keywords": [
6
+ "dialog",
7
+ "modal",
8
+ "overlay",
9
+ "popup"
10
+ ],
11
+ "visualWeight": "light",
5
12
  "files": [
6
13
  {
7
14
  "path": "components/ui/dialog.tsx",
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "dropdown-menu",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Dropdown menu with items, labels, separators, and keyboard navigation. Built on Radix DropdownMenu. Use for action menus, context menus, or any dropdown list of actions.",
5
+ "keywords": [
6
+ "dropdown",
7
+ "menu",
8
+ "actions",
9
+ "context-menu"
10
+ ],
11
+ "visualWeight": "light",
5
12
  "files": [
6
13
  {
7
14
  "path": "components/ui/dropdown-menu.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "file-uploader",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Drag-and-drop file upload area with browse button. Shows upload progress and file previews. Use for document uploads, image uploads, or any file attachment interface.",
5
+ "keywords": [
6
+ "upload",
7
+ "file",
8
+ "drag-drop",
9
+ "attachment",
10
+ "document"
11
+ ],
12
+ "visualWeight": "light",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/file-uploader.tsx",
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "image-uploader",
3
3
  "type": "registry:ui",
4
- "description": "",
4
+ "description": "Image upload component with drag-and-drop, preview grid of uploaded images, and remove buttons. Use for multi-image uploads, gallery management, or product photo uploads.",
5
+ "keywords": [
6
+ "upload",
7
+ "image",
8
+ "drag-drop",
9
+ "gallery",
10
+ "photos"
11
+ ],
12
+ "visualWeight": "medium",
5
13
  "files": [
6
14
  {
7
15
  "path": "components/ui/image-uploader.tsx",