@devxcrew/react-ui 0.0.0-stage → 0.1.7

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 (326) hide show
  1. package/README.md +85 -2
  2. package/package.json +105 -4
  3. package/src/blocks/agent-chat-workspace/agent-chat-workspace.tsx +162 -0
  4. package/src/blocks/agent-chat-workspace/index.ts +2 -0
  5. package/src/blocks/agent-provider-settings/agent-provider-settings.tsx +183 -0
  6. package/src/blocks/agent-provider-settings/index.ts +2 -0
  7. package/src/blocks/agent-task-workspace/agent-task-workspace.tsx +48 -0
  8. package/src/blocks/agent-task-workspace/index.ts +2 -0
  9. package/src/blocks/analysis-context/analysis-context.tsx +6 -0
  10. package/src/blocks/analysis-context/index.ts +1 -0
  11. package/src/blocks/app-header/app-header.tsx +159 -0
  12. package/src/blocks/app-header/index.ts +2 -0
  13. package/src/blocks/archived-chat-workspace/archived-chat-workspace.tsx +29 -0
  14. package/src/blocks/archived-chat-workspace/index.ts +2 -0
  15. package/src/blocks/auth/README.md +32 -0
  16. package/src/blocks/auth/admin-login-page.tsx +15 -0
  17. package/src/blocks/auth/admin-password-forgot-page.tsx +5 -0
  18. package/src/blocks/auth/auth-page-layout.tsx +90 -0
  19. package/src/blocks/auth/client-login-page.tsx +20 -0
  20. package/src/blocks/auth/client-password-forgot-page.tsx +5 -0
  21. package/src/blocks/auth/identity-management-desk.tsx +530 -0
  22. package/src/blocks/auth/index.ts +12 -0
  23. package/src/blocks/auth/login-page.tsx +206 -0
  24. package/src/blocks/auth/password-forgot-page.tsx +60 -0
  25. package/src/blocks/auth/portal-pages.tsx +32 -0
  26. package/src/blocks/auth/privileged-desk.tsx +55 -0
  27. package/src/blocks/auth/register-page.tsx +117 -0
  28. package/src/blocks/auth/session-boundary.tsx +296 -0
  29. package/src/blocks/auth/super-admin-login-page.tsx +15 -0
  30. package/src/blocks/auth/super-admin-password-forgot-page.tsx +5 -0
  31. package/src/blocks/blog/blog-card.tsx +156 -0
  32. package/src/blocks/blog/blog-reader.tsx +175 -0
  33. package/src/blocks/blog/blog-types.ts +47 -0
  34. package/src/blocks/blog/index.ts +3 -0
  35. package/src/blocks/chat-composer/chat-composer.tsx +140 -0
  36. package/src/blocks/chat-composer/index.ts +1 -0
  37. package/src/blocks/chat-history/chat-history.tsx +59 -0
  38. package/src/blocks/chat-history/index.ts +1 -0
  39. package/src/blocks/chat-response-progress/chat-response-progress.tsx +34 -0
  40. package/src/blocks/chat-response-progress/index.ts +2 -0
  41. package/src/blocks/chat-runtime-controls/chat-runtime-controls.tsx +56 -0
  42. package/src/blocks/chat-runtime-controls/index.ts +2 -0
  43. package/src/blocks/chat-runtime-trace/chat-runtime-trace.tsx +71 -0
  44. package/src/blocks/chat-runtime-trace/index.ts +2 -0
  45. package/src/blocks/codex-connection-settings/codex-connection-settings.tsx +42 -0
  46. package/src/blocks/codex-connection-settings/index.ts +1 -0
  47. package/src/blocks/dropzone/dropzone-file-item.tsx +113 -0
  48. package/src/blocks/dropzone/dropzone-types.ts +32 -0
  49. package/src/blocks/dropzone/dropzone.tsx +216 -0
  50. package/src/blocks/dropzone/index.ts +3 -0
  51. package/src/blocks/ecommerce/cart/cart-drawer.tsx +228 -0
  52. package/src/blocks/ecommerce/cart/cart-types.ts +27 -0
  53. package/src/blocks/ecommerce/cart/index.ts +2 -0
  54. package/src/blocks/ecommerce/categories/categories-types.ts +23 -0
  55. package/src/blocks/ecommerce/categories/category-showcase.tsx +125 -0
  56. package/src/blocks/ecommerce/categories/index.ts +2 -0
  57. package/src/blocks/ecommerce/checkout/checkout-types.ts +48 -0
  58. package/src/blocks/ecommerce/checkout/checkout-wizard.tsx +468 -0
  59. package/src/blocks/ecommerce/checkout/index.ts +2 -0
  60. package/src/blocks/ecommerce/comparison/comparison-types.ts +28 -0
  61. package/src/blocks/ecommerce/comparison/index.ts +2 -0
  62. package/src/blocks/ecommerce/comparison/product-comparison.tsx +174 -0
  63. package/src/blocks/ecommerce/coupon-wallet/coupon-wallet-types.ts +21 -0
  64. package/src/blocks/ecommerce/coupon-wallet/coupon-wallet.tsx +154 -0
  65. package/src/blocks/ecommerce/coupon-wallet/index.ts +2 -0
  66. package/src/blocks/ecommerce/delivery-tracker/delivery-tracker-types.ts +20 -0
  67. package/src/blocks/ecommerce/delivery-tracker/delivery-tracker.tsx +169 -0
  68. package/src/blocks/ecommerce/delivery-tracker/index.ts +2 -0
  69. package/src/blocks/ecommerce/index.ts +12 -0
  70. package/src/blocks/ecommerce/payment-methods/index.ts +2 -0
  71. package/src/blocks/ecommerce/payment-methods/payment-method-selector.tsx +224 -0
  72. package/src/blocks/ecommerce/payment-methods/payment-methods-types.ts +21 -0
  73. package/src/blocks/ecommerce/price-history/index.ts +2 -0
  74. package/src/blocks/ecommerce/price-history/price-history-chart.tsx +217 -0
  75. package/src/blocks/ecommerce/price-history/price-history-types.ts +16 -0
  76. package/src/blocks/ecommerce/reviews/index.ts +2 -0
  77. package/src/blocks/ecommerce/reviews/reviews-section.tsx +309 -0
  78. package/src/blocks/ecommerce/reviews/reviews-types.ts +26 -0
  79. package/src/blocks/ecommerce/wishlist/index.ts +2 -0
  80. package/src/blocks/ecommerce/wishlist/wishlist-grid.tsx +149 -0
  81. package/src/blocks/ecommerce/wishlist/wishlist-types.ts +19 -0
  82. package/src/blocks/execution-status/README.md +25 -0
  83. package/src/blocks/execution-status/execution-status.tsx +135 -0
  84. package/src/blocks/execution-status/index.ts +2 -0
  85. package/src/blocks/file-tree/file-icons.tsx +84 -0
  86. package/src/blocks/file-tree/file-tree-node.tsx +158 -0
  87. package/src/blocks/file-tree/file-tree-types.ts +33 -0
  88. package/src/blocks/file-tree/file-tree.tsx +163 -0
  89. package/src/blocks/file-tree/index.ts +5 -0
  90. package/src/blocks/filter-builder/filter-builder.tsx +169 -0
  91. package/src/blocks/filter-builder/filter-row.tsx +164 -0
  92. package/src/blocks/filter-builder/filter-types.ts +61 -0
  93. package/src/blocks/filter-builder/index.ts +12 -0
  94. package/src/blocks/footer/footer-types.ts +35 -0
  95. package/src/blocks/footer/index.ts +2 -0
  96. package/src/blocks/footer/site-footer.tsx +212 -0
  97. package/src/blocks/form/form-block.tsx +113 -0
  98. package/src/blocks/form/form-lookup-field.tsx +60 -0
  99. package/src/blocks/form/index.ts +4 -0
  100. package/src/blocks/handover-stack/handover-stack.tsx +41 -0
  101. package/src/blocks/handover-stack/index.ts +1 -0
  102. package/src/blocks/idea-handover/idea-handover-readiness.ts +41 -0
  103. package/src/blocks/idea-handover/idea-handover-workspace.tsx +150 -0
  104. package/src/blocks/idea-handover/index.ts +4 -0
  105. package/src/blocks/kanban/index.ts +11 -0
  106. package/src/blocks/kanban/kanban-board.tsx +146 -0
  107. package/src/blocks/kanban/kanban-card.tsx +149 -0
  108. package/src/blocks/kanban/kanban-column.tsx +102 -0
  109. package/src/blocks/kanban/kanban-types.ts +38 -0
  110. package/src/blocks/loader/global-loader.tsx +90 -0
  111. package/src/blocks/loader/index.ts +1 -0
  112. package/src/blocks/mascot/README.md +21 -0
  113. package/src/blocks/mascot/index.ts +2 -0
  114. package/src/blocks/mascot/mascot.tsx +202 -0
  115. package/src/blocks/master-list/README.md +21 -0
  116. package/src/blocks/master-list/index.ts +12 -0
  117. package/src/blocks/master-list/master-form.tsx +157 -0
  118. package/src/blocks/master-list/master-list-desk.tsx +370 -0
  119. package/src/blocks/master-list/master-list.tsx +189 -0
  120. package/src/blocks/master-list/types.ts +18 -0
  121. package/src/blocks/mermaid-preview/index.ts +1 -0
  122. package/src/blocks/mermaid-preview/mermaid-preview.tsx +164 -0
  123. package/src/blocks/notifications/README.md +7 -0
  124. package/src/blocks/notifications/index.ts +1 -0
  125. package/src/blocks/notifications/notification-center-page.tsx +73 -0
  126. package/src/blocks/pricing/index.ts +7 -0
  127. package/src/blocks/pricing/pricing-table.tsx +173 -0
  128. package/src/blocks/pricing/pricing-types.ts +34 -0
  129. package/src/blocks/product-card/index.ts +2 -0
  130. package/src/blocks/product-card/product-card-types.ts +35 -0
  131. package/src/blocks/product-card/product-card.tsx +183 -0
  132. package/src/blocks/table/data-table-block.tsx +313 -0
  133. package/src/blocks/table/data-table-filter-menu.tsx +48 -0
  134. package/src/blocks/table/data-table-icon-menu-trigger.tsx +44 -0
  135. package/src/blocks/table/data-table-menu-content.tsx +32 -0
  136. package/src/blocks/table/data-table-pagination.tsx +149 -0
  137. package/src/blocks/table/data-table-row-actions.tsx +53 -0
  138. package/src/blocks/table/data-table-status.tsx +42 -0
  139. package/src/blocks/table/data-table-toolbar.tsx +124 -0
  140. package/src/blocks/table/data-table-totals.tsx +31 -0
  141. package/src/blocks/table/index.ts +13 -0
  142. package/src/blocks/workspace/index.ts +19 -0
  143. package/src/blocks/workspace/workspace-action-card.tsx +73 -0
  144. package/src/blocks/workspace/workspace-application-header.tsx +67 -0
  145. package/src/blocks/workspace/workspace-entity-card.tsx +48 -0
  146. package/src/blocks/workspace/workspace-health-summary.tsx +15 -0
  147. package/src/blocks/workspace/workspace-metric-card.tsx +97 -0
  148. package/src/blocks/workspace/workspace-page-header.tsx +43 -0
  149. package/src/blocks/workspace/workspace-publish-status.tsx +15 -0
  150. package/src/blocks/workspace/workspace-route-checklist.tsx +28 -0
  151. package/src/blocks/workspace/workspace-runtime-status.tsx +48 -0
  152. package/src/blocks/workspace/workspace-section-card.tsx +42 -0
  153. package/src/components/accordion.tsx +65 -0
  154. package/src/components/alert-dialog.tsx +160 -0
  155. package/src/components/alert.tsx +68 -0
  156. package/src/components/aspect-ratio.tsx +22 -0
  157. package/src/components/attachment.tsx +195 -0
  158. package/src/components/avatar.tsx +90 -0
  159. package/src/components/badge.tsx +55 -0
  160. package/src/components/breadcrumb.tsx +100 -0
  161. package/src/components/bubble.tsx +123 -0
  162. package/src/components/button-group.tsx +79 -0
  163. package/src/components/button.tsx +71 -0
  164. package/src/components/calendar.tsx +185 -0
  165. package/src/components/card.tsx +100 -0
  166. package/src/components/carousel.tsx +229 -0
  167. package/src/components/chart.tsx +338 -0
  168. package/src/components/checkbox.tsx +27 -0
  169. package/src/components/collapsible.tsx +15 -0
  170. package/src/components/combobox.tsx +269 -0
  171. package/src/components/command.tsx +181 -0
  172. package/src/components/compact-model-switcher.tsx +128 -0
  173. package/src/components/context-menu.tsx +244 -0
  174. package/src/components/dialog.tsx +138 -0
  175. package/src/components/direction.tsx +1 -0
  176. package/src/components/drawer.tsx +208 -0
  177. package/src/components/dropdown-menu.tsx +257 -0
  178. package/src/components/empty.tsx +93 -0
  179. package/src/components/field.tsx +232 -0
  180. package/src/components/hover-card.tsx +45 -0
  181. package/src/components/input-group.tsx +146 -0
  182. package/src/components/input-otp.tsx +83 -0
  183. package/src/components/input.tsx +19 -0
  184. package/src/components/item.tsx +188 -0
  185. package/src/components/kbd.tsx +26 -0
  186. package/src/components/label.tsx +17 -0
  187. package/src/components/markdown-content.tsx +113 -0
  188. package/src/components/marker.tsx +67 -0
  189. package/src/components/menubar.tsx +267 -0
  190. package/src/components/message-scroller.tsx +91 -0
  191. package/src/components/message.tsx +84 -0
  192. package/src/components/native-select.tsx +54 -0
  193. package/src/components/navigation-menu.tsx +160 -0
  194. package/src/components/pagination.tsx +118 -0
  195. package/src/components/popover.tsx +74 -0
  196. package/src/components/progress.tsx +82 -0
  197. package/src/components/questionnaire.tsx +150 -0
  198. package/src/components/radio-group.tsx +35 -0
  199. package/src/components/resizable.tsx +41 -0
  200. package/src/components/rich-text-editor.tsx +604 -0
  201. package/src/components/scroll-area.tsx +47 -0
  202. package/src/components/select.tsx +189 -0
  203. package/src/components/separator.tsx +18 -0
  204. package/src/components/sheet.tsx +125 -0
  205. package/src/components/sidebar.tsx +693 -0
  206. package/src/components/skeleton.tsx +13 -0
  207. package/src/components/slider.tsx +51 -0
  208. package/src/components/sonner.tsx +45 -0
  209. package/src/components/sparkline.tsx +231 -0
  210. package/src/components/spinner.tsx +20 -0
  211. package/src/components/status-badge.tsx +114 -0
  212. package/src/components/switch.tsx +31 -0
  213. package/src/components/table.tsx +91 -0
  214. package/src/components/tabs.tsx +72 -0
  215. package/src/components/textarea.tsx +17 -0
  216. package/src/components/toast.tsx +222 -0
  217. package/src/components/toggle-group.tsx +85 -0
  218. package/src/components/toggle.tsx +42 -0
  219. package/src/components/tooltip.tsx +53 -0
  220. package/src/design-system/README.md +55 -0
  221. package/src/design-system/action-intents.ts +21 -0
  222. package/src/design-system/agent-manifest.ts +83 -0
  223. package/src/design-system/block-registry.ts +272 -0
  224. package/src/design-system/component-registry.ts +142 -0
  225. package/src/design-system/contracts.ts +69 -0
  226. package/src/design-system/defaults.ts +3 -0
  227. package/src/design-system/index.ts +29 -0
  228. package/src/design-system/mcp-server.ts +156 -0
  229. package/src/design-system/page-registry.ts +74 -0
  230. package/src/design-system/selection.ts +67 -0
  231. package/src/design-system/template-registry.ts +33 -0
  232. package/src/features/interface-topology/index.ts +14 -0
  233. package/src/features/interface-topology/interface-topology.types.ts +40 -0
  234. package/src/features/interface-topology/topology-control.tsx +18 -0
  235. package/src/features/interface-topology/topology-inspector.tsx +228 -0
  236. package/src/features/interface-topology/topology-marker.tsx +47 -0
  237. package/src/features/interface-topology/topology-region.tsx +44 -0
  238. package/src/features/interface-topology/use-interface-topology.ts +178 -0
  239. package/src/hooks/use-mobile.ts +19 -0
  240. package/src/hooks/use-scroll-follow.ts +22 -0
  241. package/src/index.ts +11 -0
  242. package/src/layouts/README.md +65 -0
  243. package/src/layouts/agent-workspace/agent-activity-rail.tsx +101 -0
  244. package/src/layouts/agent-workspace/agent-workspace.tsx +27 -0
  245. package/src/layouts/agent-workspace/agent-workspace.types.ts +28 -0
  246. package/src/layouts/agent-workspace/index.ts +7 -0
  247. package/src/layouts/blog-header/blog-header-types.ts +25 -0
  248. package/src/layouts/blog-header/blog-header.tsx +172 -0
  249. package/src/layouts/blog-header/index.ts +2 -0
  250. package/src/layouts/documentation-workspace/README.md +8 -0
  251. package/src/layouts/documentation-workspace/documentation-workspace.tsx +34 -0
  252. package/src/layouts/documentation-workspace/index.ts +1 -0
  253. package/src/layouts/ecommerce-header/ecommerce-actions.tsx +89 -0
  254. package/src/layouts/ecommerce-header/ecommerce-announcement.tsx +43 -0
  255. package/src/layouts/ecommerce-header/ecommerce-drawer.tsx +97 -0
  256. package/src/layouts/ecommerce-header/ecommerce-header-types.ts +59 -0
  257. package/src/layouts/ecommerce-header/ecommerce-header.tsx +47 -0
  258. package/src/layouts/ecommerce-header/ecommerce-navigation.tsx +98 -0
  259. package/src/layouts/ecommerce-header/ecommerce-search.tsx +86 -0
  260. package/src/layouts/ecommerce-header/index.ts +2 -0
  261. package/src/layouts/main-workspace/index.ts +28 -0
  262. package/src/layouts/mdi/index.ts +1 -0
  263. package/src/layouts/mdi/mdi.tsx +20 -0
  264. package/src/layouts/mdi-main/index.ts +27 -0
  265. package/src/layouts/mdi-main/mdi-app-catalog.generated.ts +159 -0
  266. package/src/layouts/mdi-main/mdi-app-catalog.ts +76 -0
  267. package/src/layouts/mdi-main/mdi-app-switcher.tsx +107 -0
  268. package/src/layouts/mdi-main/mdi-feature-settings.tsx +135 -0
  269. package/src/layouts/mdi-main/mdi-global-search.tsx +192 -0
  270. package/src/layouts/mdi-main/mdi-main.tsx +241 -0
  271. package/src/layouts/mdi-main/mdi-notifications-menu.tsx +197 -0
  272. package/src/layouts/mdi-main/mdi-profile-menu.tsx +79 -0
  273. package/src/layouts/mdi-main/mdi-sidebar-navigation.tsx +226 -0
  274. package/src/layouts/mdi-main/mdi-sidebar.tsx +110 -0
  275. package/src/layouts/mdi-main/mdi-status-bar.tsx +26 -0
  276. package/src/layouts/mdi-main/mdi-top-menu-control.ts +4 -0
  277. package/src/layouts/mdi-main/mdi-top-menu.tsx +117 -0
  278. package/src/layouts/mdi-main/mdi-topology.tsx +297 -0
  279. package/src/layouts/mdi-main/mdi-tweak-panel.tsx +104 -0
  280. package/src/layouts/mdi-main/mdi-types.ts +131 -0
  281. package/src/layouts/mdi-main/use-mdi-features.ts +36 -0
  282. package/src/layouts/mdi-main/use-mdi-sidebar-state.ts +59 -0
  283. package/src/layouts/site-header/announcement-bar.tsx +55 -0
  284. package/src/layouts/site-header/category-strip.tsx +41 -0
  285. package/src/layouts/site-header/index.ts +16 -0
  286. package/src/layouts/site-header/main-navbar.tsx +179 -0
  287. package/src/layouts/site-header/mobile-drawer.tsx +140 -0
  288. package/src/layouts/site-header/site-header-types.ts +63 -0
  289. package/src/layouts/site-header/site-header.tsx +58 -0
  290. package/src/lib/random-id.ts +19 -0
  291. package/src/lib/utils.ts +6 -0
  292. package/src/providers/pdf-provider.ts +137 -0
  293. package/src/styles/globals.css +374 -0
  294. package/src/templates/README.md +12 -0
  295. package/src/templates/dashboard-01/components/app-sidebar.tsx +177 -0
  296. package/src/templates/dashboard-01/components/chart-area-interactive.tsx +276 -0
  297. package/src/templates/dashboard-01/components/data-table-cell-viewer.tsx +213 -0
  298. package/src/templates/dashboard-01/components/data-table.tsx +577 -0
  299. package/src/templates/dashboard-01/components/nav-documents.tsx +79 -0
  300. package/src/templates/dashboard-01/components/nav-main.tsx +55 -0
  301. package/src/templates/dashboard-01/components/nav-secondary.tsx +39 -0
  302. package/src/templates/dashboard-01/components/nav-user.tsx +97 -0
  303. package/src/templates/dashboard-01/components/section-cards.tsx +99 -0
  304. package/src/templates/dashboard-01/components/site-header.tsx +14 -0
  305. package/src/templates/dashboard-01/data.json +614 -0
  306. package/src/templates/dashboard-01/index.ts +7 -0
  307. package/src/templates/documentation-sidebar/app-sidebar.tsx +95 -0
  308. package/src/templates/documentation-sidebar/index.ts +2 -0
  309. package/src/templates/sidebar-07/components/app-sidebar.tsx +175 -0
  310. package/src/templates/sidebar-07/components/nav-main.tsx +64 -0
  311. package/src/templates/sidebar-07/components/nav-projects.tsx +79 -0
  312. package/src/templates/sidebar-07/components/nav-user.tsx +105 -0
  313. package/src/templates/sidebar-07/components/team-switcher.tsx +94 -0
  314. package/src/templates/sidebar-07/index.ts +1 -0
  315. package/src/templates/ui-page/index.ts +7 -0
  316. package/src/templates/ui-page/ui-browser-frame.tsx +27 -0
  317. package/src/templates/ui-page/ui-template-code.tsx +48 -0
  318. package/src/templates/ui-page/ui-template-header.tsx +87 -0
  319. package/src/templates/ui-page/ui-template-navigation.tsx +60 -0
  320. package/src/templates/ui-page/ui-template-page.tsx +97 -0
  321. package/src/theme/index.ts +12 -0
  322. package/src/theme/theme-config.ts +30 -0
  323. package/src/theme/theme-provider.tsx +142 -0
  324. package/src/theme/theme-selector.tsx +75 -0
  325. package/src/tokens/index.ts +15 -0
  326. package/src/vite-env.d.ts +3 -0
@@ -0,0 +1,48 @@
1
+ import { Check, Copy } from 'lucide-react'
2
+ import { useEffect, useState } from 'react'
3
+ import { Button } from '../../components/button'
4
+
5
+ export function UiTemplateCode({
6
+ code,
7
+ copyLabel = 'Copy code',
8
+ }: {
9
+ code: string
10
+ copyLabel?: string
11
+ }) {
12
+ const [copyState, setCopyState] = useState<'copied' | 'failed' | 'idle'>('idle')
13
+
14
+ useEffect(() => {
15
+ if (copyState === 'idle') return
16
+ const timeout = window.setTimeout(() => setCopyState('idle'), 4000)
17
+ return () => window.clearTimeout(timeout)
18
+ }, [copyState])
19
+
20
+ async function copyCode() {
21
+ try {
22
+ await navigator.clipboard.writeText(code)
23
+ setCopyState('copied')
24
+ } catch {
25
+ setCopyState('failed')
26
+ }
27
+ }
28
+
29
+ return (
30
+ <div className="relative overflow-hidden rounded-xl border bg-zinc-950 text-zinc-100">
31
+ <div className="flex items-center justify-between border-b border-white/10 px-4 py-2.5">
32
+ <span className="font-mono text-xs text-zinc-400">tsx</span>
33
+ <Button
34
+ className="text-zinc-300 hover:bg-white/10 hover:text-white"
35
+ onClick={copyCode}
36
+ size="sm"
37
+ variant="ghost"
38
+ >
39
+ {copyState === 'copied' ? <Check /> : <Copy />}
40
+ {copyState === 'copied' ? 'Copied' : copyState === 'failed' ? 'Copy failed' : copyLabel}
41
+ </Button>
42
+ </div>
43
+ <pre className="max-h-96 overflow-auto p-5 text-[13px] leading-6">
44
+ <code>{code}</code>
45
+ </pre>
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,87 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Check, Copy } from "lucide-react";
3
+ import { Badge } from "../../components/badge";
4
+ import {
5
+ Breadcrumb,
6
+ BreadcrumbItem,
7
+ BreadcrumbList,
8
+ BreadcrumbPage,
9
+ BreadcrumbSeparator,
10
+ } from "../../components/breadcrumb";
11
+ import { Button } from "../../components/button";
12
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../components/tooltip";
13
+ import { cn } from "../../lib/utils";
14
+
15
+ export type UiTemplateKind = "Block" | "Component" | "Layout" | "Page" | "Static Page" | "Template";
16
+
17
+ export function UiTemplateHeader({
18
+ importPath,
19
+ kind,
20
+ name,
21
+ topologyLabelsVisible = false,
22
+ }: {
23
+ importPath: string;
24
+ kind: UiTemplateKind;
25
+ name: string;
26
+ topologyLabelsVisible?: boolean;
27
+ }) {
28
+ const [copyState, setCopyState] = useState<"copied" | "failed" | "idle">("idle");
29
+
30
+ useEffect(() => {
31
+ if (copyState === "idle") return;
32
+ const timeout = window.setTimeout(() => setCopyState("idle"), 2000);
33
+ return () => window.clearTimeout(timeout);
34
+ }, [copyState]);
35
+
36
+ async function copyImportPath() {
37
+ try {
38
+ await navigator.clipboard.writeText(importPath);
39
+ setCopyState("copied");
40
+ } catch {
41
+ setCopyState("failed");
42
+ }
43
+ }
44
+
45
+ const copyLabel =
46
+ copyState === "copied"
47
+ ? "Import path copied"
48
+ : copyState === "failed"
49
+ ? "Copy import path failed"
50
+ : "Copy import path";
51
+
52
+ return (
53
+ <header
54
+ className={cn(
55
+ "sticky top-0 z-20 flex min-h-14 items-center justify-between gap-4 border-b bg-background/95 pr-6 backdrop-blur lg:pr-10",
56
+ topologyLabelsVisible ? "pl-52" : "pl-6 lg:pl-10",
57
+ )}
58
+ >
59
+ <Breadcrumb>
60
+ <BreadcrumbList>
61
+ <BreadcrumbItem>
62
+ <Badge variant="secondary">{kind}</Badge>
63
+ </BreadcrumbItem>
64
+ <BreadcrumbSeparator />
65
+ <BreadcrumbItem>
66
+ <BreadcrumbPage className="font-semibold">{name}</BreadcrumbPage>
67
+ </BreadcrumbItem>
68
+ </BreadcrumbList>
69
+ </Breadcrumb>
70
+
71
+ <div className="flex min-w-0 items-center gap-1.5">
72
+ <code className="truncate rounded-md bg-muted px-2.5 py-1.5 text-sm">{importPath}</code>
73
+ <TooltipProvider>
74
+ <Tooltip>
75
+ <TooltipTrigger
76
+ render={<Button aria-label={copyLabel} size="icon-sm" variant="ghost" />}
77
+ onClick={copyImportPath}
78
+ >
79
+ {copyState === "copied" ? <Check /> : <Copy />}
80
+ </TooltipTrigger>
81
+ <TooltipContent>{copyLabel}</TooltipContent>
82
+ </Tooltip>
83
+ </TooltipProvider>
84
+ </div>
85
+ </header>
86
+ );
87
+ }
@@ -0,0 +1,60 @@
1
+ import { ArrowLeft, ArrowRight } from 'lucide-react'
2
+ import { cn } from '../../lib/utils'
3
+
4
+ export type UiTemplateNavigationItem = {
5
+ href: string
6
+ name: string
7
+ }
8
+
9
+ export function UiTemplateNavigation({
10
+ next,
11
+ previous,
12
+ }: {
13
+ next?: UiTemplateNavigationItem
14
+ previous?: UiTemplateNavigationItem
15
+ }) {
16
+ return (
17
+ <nav aria-label="Documentation navigation" className="grid gap-3 border-t pt-6 sm:grid-cols-2">
18
+ {previous ? <NavigationLink direction="previous" item={previous} /> : <span />}
19
+ {next ? <NavigationLink direction="next" item={next} /> : null}
20
+ </nav>
21
+ )
22
+ }
23
+
24
+ function NavigationLink({
25
+ direction,
26
+ item,
27
+ }: {
28
+ direction: 'next' | 'previous'
29
+ item: UiTemplateNavigationItem
30
+ }) {
31
+ const isPrevious = direction === 'previous'
32
+
33
+ return (
34
+ <a
35
+ aria-label={`${isPrevious ? 'Previous' : 'Next'}: ${item.name}`}
36
+ className={cn(
37
+ 'group flex min-h-20 items-center gap-3 rounded-md border bg-card px-4 py-3 shadow-sm',
38
+ 'transition-[transform,box-shadow,border-color] duration-200 ease-out',
39
+ 'hover:-translate-y-0.5 hover:border-foreground/20 hover:shadow-md',
40
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
41
+ 'motion-reduce:transform-none motion-reduce:transition-none',
42
+ isPrevious ? 'text-left' : 'justify-end text-right sm:col-start-2',
43
+ )}
44
+ href={item.href}
45
+ >
46
+ {isPrevious ? (
47
+ <ArrowLeft className="size-4 shrink-0 transition-transform group-hover:-translate-x-0.5 motion-reduce:transform-none" />
48
+ ) : null}
49
+ <span className="grid gap-1">
50
+ <span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
51
+ {isPrevious ? 'Previous' : 'Next'}
52
+ </span>
53
+ <span className="font-semibold text-foreground">{item.name}</span>
54
+ </span>
55
+ {!isPrevious ? (
56
+ <ArrowRight className="size-4 shrink-0 transition-transform group-hover:translate-x-0.5 motion-reduce:transform-none" />
57
+ ) : null}
58
+ </a>
59
+ )
60
+ }
@@ -0,0 +1,97 @@
1
+ import { useEffect, type ReactNode } from 'react';
2
+ import { TopologyRegion, type InterfaceTopologyController } from '../../features/interface-topology';
3
+ import { cn } from '../../lib/utils';
4
+ import { UiBrowserFrame } from './ui-browser-frame';
5
+ import { UiTemplateCode } from './ui-template-code';
6
+ import { UiTemplateHeader, type UiTemplateKind } from './ui-template-header';
7
+ import { UiTemplateNavigation, type UiTemplateNavigationItem } from './ui-template-navigation';
8
+
9
+ export type UiTemplatePageProps = {
10
+ code: string;
11
+ codeCopyLabel?: string;
12
+ importPath: string;
13
+ kind: UiTemplateKind;
14
+ name: string;
15
+ navigation?: {
16
+ next?: UiTemplateNavigationItem;
17
+ previous?: UiTemplateNavigationItem;
18
+ };
19
+ preview: ReactNode;
20
+ previewClassName?: string;
21
+ showCode?: boolean;
22
+ topology: InterfaceTopologyController;
23
+ topologyIds: {
24
+ page: string;
25
+ preview: string;
26
+ usage: string;
27
+ };
28
+ usageDescription: ReactNode;
29
+ usageTitle?: string;
30
+ };
31
+
32
+ export function UiTemplatePage({
33
+ code,
34
+ codeCopyLabel,
35
+ importPath,
36
+ kind,
37
+ name,
38
+ navigation,
39
+ preview,
40
+ previewClassName,
41
+ showCode = true,
42
+ topology,
43
+ topologyIds,
44
+ usageDescription,
45
+ usageTitle,
46
+ }: UiTemplatePageProps) {
47
+ const { setScope } = topology;
48
+
49
+ useEffect(() => {
50
+ setScope(topologyIds.preview);
51
+ return () => setScope(null);
52
+ }, [setScope, topologyIds.preview]);
53
+
54
+ return (
55
+ <TopologyRegion
56
+ as="main"
57
+ className="h-full overflow-y-auto bg-background"
58
+ id={topologyIds.page}
59
+ topology={topology}
60
+ >
61
+ <UiTemplateHeader
62
+ importPath={importPath}
63
+ kind={kind}
64
+ name={name}
65
+ topologyLabelsVisible={topology.labelsVisible}
66
+ />
67
+ <div className="grid min-w-0 gap-10 pt-12 pb-8">
68
+ <TopologyRegion
69
+ as="section"
70
+ className={cn('mx-auto w-[90%] min-w-0', previewClassName)}
71
+ id={topologyIds.preview}
72
+ topology={topology}
73
+ >
74
+ <UiBrowserFrame className={kind === 'Layout' ? 'rounded-none' : undefined} title={name}>
75
+ {preview}
76
+ </UiBrowserFrame>
77
+ </TopologyRegion>
78
+
79
+ <TopologyRegion
80
+ as="section"
81
+ className="mx-auto grid w-[90%] min-w-0 gap-5 pt-10 pb-8"
82
+ id={topologyIds.usage}
83
+ topology={topology}
84
+ >
85
+ <div className="grid gap-1.5">
86
+ <h2 className="text-xl font-semibold tracking-tight">
87
+ {usageTitle ?? `Use the shared ${name} ${kind.toLowerCase()}`}
88
+ </h2>
89
+ <div className="max-w-3xl text-sm leading-6 text-muted-foreground">{usageDescription}</div>
90
+ </div>
91
+ {showCode ? <UiTemplateCode code={code} copyLabel={codeCopyLabel} /> : null}
92
+ {navigation ? <UiTemplateNavigation next={navigation.next} previous={navigation.previous} /> : null}
93
+ </TopologyRegion>
94
+ </div>
95
+ </TopologyRegion>
96
+ );
97
+ }
@@ -0,0 +1,12 @@
1
+ export { ThemeProvider, useColorTheme, useTheme } from './theme-provider'
2
+ export { ThemeSelector } from './theme-selector'
3
+ export {
4
+ COLOR_THEME_ATTRIBUTE,
5
+ COLOR_THEME_STORAGE_KEY,
6
+ THEME_STORAGE_KEY,
7
+ colorThemes,
8
+ isColorThemeId,
9
+ isThemeMode,
10
+ themeModes,
11
+ } from './theme-config'
12
+ export type { ColorThemeId, ThemeMode } from './theme-config'
@@ -0,0 +1,30 @@
1
+ export const themeModes = [
2
+ { id: 'light', name: 'Light' },
3
+ { id: 'dark', name: 'Dark' },
4
+ { id: 'system', name: 'System' },
5
+ ] as const
6
+
7
+ export type ThemeMode = (typeof themeModes)[number]['id']
8
+
9
+ export const colorThemes = [
10
+ { id: 'neutral', name: 'Neutral', swatchClassName: 'bg-zinc-700 dark:bg-zinc-300' },
11
+ { id: 'blue', name: 'Blue', swatchClassName: 'bg-blue-600' },
12
+ { id: 'violet', name: 'Violet', swatchClassName: 'bg-violet-600' },
13
+ { id: 'emerald', name: 'Emerald', swatchClassName: 'bg-emerald-600' },
14
+ { id: 'orange', name: 'Orange', swatchClassName: 'bg-orange-600' },
15
+ { id: 'studio', name: 'Studio', swatchClassName: 'bg-[#191a1c]' },
16
+ ] as const
17
+
18
+ export type ColorThemeId = (typeof colorThemes)[number]['id']
19
+
20
+ export const COLOR_THEME_ATTRIBUTE = 'data-color-theme'
21
+ export const COLOR_THEME_STORAGE_KEY = 'codexsun.ui.color-theme'
22
+ export const THEME_STORAGE_KEY = 'codexsun.ui.theme'
23
+
24
+ export function isColorThemeId(value: string | null): value is ColorThemeId {
25
+ return colorThemes.some((theme) => theme.id === value)
26
+ }
27
+
28
+ export function isThemeMode(value: string | undefined): value is ThemeMode {
29
+ return themeModes.some((mode) => mode.id === value)
30
+ }
@@ -0,0 +1,142 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+
5
+ import {
6
+ COLOR_THEME_ATTRIBUTE,
7
+ COLOR_THEME_STORAGE_KEY,
8
+ THEME_STORAGE_KEY,
9
+ isColorThemeId,
10
+ isThemeMode,
11
+ type ColorThemeId,
12
+ type ThemeMode,
13
+ } from './theme-config'
14
+
15
+ type ColorThemeContextValue = {
16
+ colorTheme: ColorThemeId
17
+ setColorTheme: (theme: ColorThemeId) => void
18
+ }
19
+
20
+ type ThemeContextValue = {
21
+ resolvedTheme: 'dark' | 'light'
22
+ setTheme: (theme: ThemeMode) => void
23
+ theme: ThemeMode
24
+ }
25
+
26
+ type ThemeProviderProps = {
27
+ children: React.ReactNode
28
+ colorStorageKey?: string
29
+ defaultColorTheme?: ColorThemeId
30
+ defaultTheme?: ThemeMode
31
+ storageKey?: string
32
+ }
33
+
34
+ const ColorThemeContext = React.createContext<ColorThemeContextValue | null>(null)
35
+ const ThemeContext = React.createContext<ThemeContextValue | null>(null)
36
+
37
+ export function ThemeProvider({
38
+ children,
39
+ colorStorageKey = COLOR_THEME_STORAGE_KEY,
40
+ defaultColorTheme = 'neutral',
41
+ defaultTheme = 'system',
42
+ storageKey = THEME_STORAGE_KEY,
43
+ }: ThemeProviderProps) {
44
+ const [colorTheme, setColorThemeState] = React.useState<ColorThemeId>(() =>
45
+ readColorTheme(colorStorageKey, defaultColorTheme),
46
+ )
47
+ const [theme, setThemeState] = React.useState<ThemeMode>(() => readTheme(storageKey, defaultTheme))
48
+ const resolvedTheme = resolveTheme(theme)
49
+
50
+ const setColorTheme = React.useCallback(
51
+ (nextTheme: ColorThemeId) => {
52
+ setColorThemeState(nextTheme)
53
+ writeStorage(colorStorageKey, nextTheme)
54
+ },
55
+ [colorStorageKey],
56
+ )
57
+ const setTheme = React.useCallback(
58
+ (nextTheme: ThemeMode) => {
59
+ setThemeState(nextTheme)
60
+ writeStorage(storageKey, nextTheme)
61
+ },
62
+ [storageKey],
63
+ )
64
+
65
+ React.useEffect(() => {
66
+ document.documentElement.setAttribute(COLOR_THEME_ATTRIBUTE, colorTheme)
67
+ }, [colorTheme])
68
+ React.useEffect(() => {
69
+ document.documentElement.classList.remove('dark', 'light')
70
+ document.documentElement.classList.add(resolvedTheme)
71
+ document.documentElement.style.colorScheme = resolvedTheme
72
+ }, [resolvedTheme])
73
+ React.useEffect(() => subscribeToStorage(colorStorageKey, defaultColorTheme, setColorThemeState), [colorStorageKey, defaultColorTheme])
74
+ React.useEffect(() => subscribeToStorage(storageKey, defaultTheme, setThemeState), [defaultTheme, storageKey])
75
+ React.useEffect(() => subscribeToSystemTheme(theme, setThemeState), [theme])
76
+
77
+ return (
78
+ <ThemeContext.Provider value={{ resolvedTheme, setTheme, theme }}>
79
+ <ColorThemeContext.Provider value={{ colorTheme, setColorTheme }}>{children}</ColorThemeContext.Provider>
80
+ </ThemeContext.Provider>
81
+ )
82
+ }
83
+
84
+ export function useColorTheme(): ColorThemeContextValue {
85
+ const context = React.useContext(ColorThemeContext)
86
+ if (!context) throw new Error('useColorTheme must be used inside ThemeProvider.')
87
+ return context
88
+ }
89
+
90
+ export function useTheme(): ThemeContextValue {
91
+ const context = React.useContext(ThemeContext)
92
+ if (!context) throw new Error('useTheme must be used inside ThemeProvider.')
93
+ return context
94
+ }
95
+
96
+ function readColorTheme(storageKey: string, fallback: ColorThemeId): ColorThemeId {
97
+ const value = readStorage(storageKey)
98
+ return isColorThemeId(value) ? value : fallback
99
+ }
100
+
101
+ function readTheme(storageKey: string, fallback: ThemeMode): ThemeMode {
102
+ const value = readStorage(storageKey)
103
+ return isThemeMode(value ?? undefined) ? (value as ThemeMode) : fallback
104
+ }
105
+
106
+ function resolveTheme(theme: ThemeMode): 'dark' | 'light' {
107
+ if (theme !== 'system') return theme
108
+ return typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
109
+ }
110
+
111
+ function subscribeToStorage<T extends string>(storageKey: string, fallback: T, setValue: (value: T) => void) {
112
+ function sync(event: StorageEvent) {
113
+ if (event.key === storageKey) setValue((event.newValue ?? fallback) as T)
114
+ }
115
+ window.addEventListener('storage', sync)
116
+ return () => window.removeEventListener('storage', sync)
117
+ }
118
+
119
+ function subscribeToSystemTheme(theme: ThemeMode, setTheme: (theme: ThemeMode) => void) {
120
+ if (theme !== 'system') return undefined
121
+ const media = window.matchMedia('(prefers-color-scheme: dark)')
122
+ const sync = () => setTheme('system')
123
+ media.addEventListener('change', sync)
124
+ return () => media.removeEventListener('change', sync)
125
+ }
126
+
127
+ function readStorage(storageKey: string): string | null {
128
+ if (typeof window === 'undefined') return null
129
+ try {
130
+ return window.localStorage.getItem(storageKey)
131
+ } catch {
132
+ return null
133
+ }
134
+ }
135
+
136
+ function writeStorage(storageKey: string, value: string) {
137
+ try {
138
+ window.localStorage.setItem(storageKey, value)
139
+ } catch {
140
+ // Keep the active theme when browser storage is unavailable.
141
+ }
142
+ }
@@ -0,0 +1,75 @@
1
+ 'use client'
2
+
3
+ import { LaptopIcon, MoonIcon, SunIcon, type LucideIcon } from 'lucide-react'
4
+
5
+ import { cn } from '../lib/utils'
6
+ import { colorThemes, isThemeMode, themeModes, type ThemeMode } from './theme-config'
7
+ import { useColorTheme, useTheme } from './theme-provider'
8
+
9
+ const modeIcons: Record<ThemeMode, LucideIcon> = {
10
+ dark: MoonIcon,
11
+ light: SunIcon,
12
+ system: LaptopIcon,
13
+ }
14
+
15
+ export function ThemeSelector({ className }: { className?: string }) {
16
+ const { colorTheme, setColorTheme } = useColorTheme()
17
+ const { setTheme, theme } = useTheme()
18
+ const activeMode = isThemeMode(theme) ? theme : 'system'
19
+
20
+ return (
21
+ <div className={cn('grid gap-4', className)}>
22
+ <fieldset className="grid gap-2">
23
+ <legend className="text-sm font-medium">Mode</legend>
24
+ <div className="grid grid-cols-3 gap-1 rounded-lg bg-muted p-1">
25
+ {themeModes.map((mode) => {
26
+ const Icon = modeIcons[mode.id]
27
+ const active = activeMode === mode.id
28
+ return (
29
+ <button
30
+ aria-pressed={active}
31
+ className={cn(
32
+ 'flex h-8 items-center justify-center gap-1.5 rounded-md px-2 text-sm transition-colors',
33
+ active
34
+ ? 'bg-background text-foreground shadow-sm'
35
+ : 'text-muted-foreground hover:text-foreground',
36
+ )}
37
+ key={mode.id}
38
+ onClick={() => setTheme(mode.id)}
39
+ type="button"
40
+ >
41
+ <Icon className="size-3.5" />
42
+ <span>{mode.name}</span>
43
+ </button>
44
+ )
45
+ })}
46
+ </div>
47
+ </fieldset>
48
+
49
+ <fieldset className="grid gap-2">
50
+ <legend className="text-sm font-medium">Color</legend>
51
+ <div className="grid grid-cols-5 gap-1.5">
52
+ {colorThemes.map((color) => {
53
+ const active = colorTheme === color.id
54
+ return (
55
+ <button
56
+ aria-label={`${color.name} color theme`}
57
+ aria-pressed={active}
58
+ className={cn(
59
+ 'grid size-9 place-items-center rounded-lg border bg-background transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
60
+ active && 'border-primary ring-2 ring-primary/25',
61
+ )}
62
+ key={color.id}
63
+ onClick={() => setColorTheme(color.id)}
64
+ title={color.name}
65
+ type="button"
66
+ >
67
+ <span className={cn('size-4 rounded-full', color.swatchClassName)} />
68
+ </button>
69
+ )
70
+ })}
71
+ </div>
72
+ </fieldset>
73
+ </div>
74
+ )
75
+ }
@@ -0,0 +1,15 @@
1
+ export const designTokens = {
2
+ color: {
3
+ accent: '#0ea5e9',
4
+ background: '#0b1020',
5
+ foreground: '#f1f5f9',
6
+ },
7
+ radius: {
8
+ medium: '0.75rem',
9
+ small: '0.5rem',
10
+ },
11
+ space: {
12
+ medium: '1rem',
13
+ small: '0.5rem',
14
+ },
15
+ } as const
@@ -0,0 +1,3 @@
1
+ interface ImportMeta {
2
+ readonly env: Readonly<Record<string, string | undefined>>
3
+ }