@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
package/README.md CHANGED
@@ -1,3 +1,86 @@
1
- # Temporary Holding Version
1
+ # ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Own reusable React components, blocks, layouts, themes, and public exports.
4
+
5
+ Use the sibling workspace layout. Shared framework and UI keep their existing public exports and
6
+ build contracts.
7
+
8
+ ## Run
9
+
10
+ Use Node 26.10 or newer and the package manifest requirements. Clone the sibling tools and
11
+ mcp-governance repositories along with this repository.
12
+
13
+ ```powershell
14
+ npm install
15
+ npm run check
16
+ ```
17
+
18
+ ## Repository records
19
+
20
+ - `AGENTS.md`: repository instructions and ownership rules.
21
+ - `agent/SKILLS.md`: repository capabilities.
22
+ - `agent/TASK.md`: current task and status.
23
+ - `agent/PLAN.md`: next steps.
24
+ - `agent/CHANGELOG.md`: versioned changes and validation results.
25
+
26
+ ## Shared guidance
27
+
28
+ Retrieve shared documentation and rules only from `https://mcp.codexsun.com/mcp` using `npm run mcp:connect`.
29
+ A successful authenticated connection is required before repository work. Stop and report connection failures.
30
+ Do not use local guides or cached instructions as fallback. Instruction retrieval does not authorize actions.
31
+
32
+ Retrieve shared documentation and rules only from `https://mcp.codexsun.com/mcp` using `npm run mcp:connect`.
33
+ A successful authenticated connection is required before repository work. Stop and report connection failures.
34
+ Do not use local guides or cached instructions as fallback. Instruction retrieval does not authorize actions.
35
+
36
+ Configure these values with `.env.example`:
37
+
38
+ - `MCP_SERVER_URL`
39
+ - `MCP_SERVER_SECRET`
40
+ - `APP_ID`
41
+ - `APP_USER`
42
+
43
+ Keep the secret in ignored `.env` files.
44
+
45
+ ```powershell
46
+ npm run mcp:connect
47
+ npm run mcp:verify
48
+ ```
49
+
50
+ Use `mcp:connect` to retrieve instructions. Use `mcp:verify` for a strict connection test.
51
+ Connection failures do not block application work. Editor registration uses the central connection
52
+ template and depends on the editor.
53
+
54
+ ## Maintenance
55
+
56
+ ```powershell
57
+ npm run version-bump -- --dry-run
58
+ npm run version-bump -- --title "Release title" --note "Change details"
59
+ npm run check:versions
60
+ npm run fix:line-endings
61
+ npm run lines:check
62
+ npm run github:now -- --dry-run
63
+ ```
64
+
65
+ Version bumps align `package.json`, `package-lock.json`, and `agent/CHANGELOG.md`. Record changes
66
+ and validation before committing.
67
+
68
+ Commit subjects use `#<patch> - <release title>`. For example:
69
+ `#5 - Central governance and repository agent layout`.
70
+
71
+ Review the changed files before an authorized `npm run github:now`. Do not bump again when the
72
+ release version is already prepared.
73
+
74
+ ## Tools source and publication
75
+
76
+ Workspace maintenance delegates to `shared/tools`. The installed npm package remains pinned at
77
+ `0.1.3` until agent changelog support is published.
78
+
79
+ GitHub source releases use `github:now`. Npm publication requires separate authorization.
80
+
81
+ ## npm package
82
+
83
+ UI publishes TypeScript/TSX source and CSS for Vite or another TypeScript-aware React bundler. React and React DOM remain peer dependencies. Native Node cannot execute these frontend exports directly.
84
+
85
+ Run `npm run release:check`, then `npm publish --access public` from this repository.
86
+ Only public exports are supported. App manifests use npm versions.
package/package.json CHANGED
@@ -1,6 +1,107 @@
1
1
  {
2
2
  "name": "@devxcrew/react-ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.7",
4
+ "type": "module",
5
+ "exports": {
6
+ "./hooks/*": "./src/hooks/*.ts",
7
+ "./features/*": "./src/features/*/index.ts",
8
+ ".": "./src/index.ts",
9
+ "./components/*": "./src/components/*.tsx",
10
+ "./blocks/*": "./src/blocks/*/index.ts",
11
+ "./layouts/*": "./src/layouts/*/index.ts",
12
+ "./theme": "./src/theme/index.ts",
13
+ "./lib/*": "./src/lib/*.ts",
14
+ "./tokens": "./src/tokens/index.ts",
15
+ "./styles": "./src/styles/globals.css",
16
+ "./design-system": "./src/design-system/index.ts",
17
+ "./templates/*": "./src/templates/*/index.ts"
18
+ },
19
+ "peerDependencies": {
20
+ "react": ">=19",
21
+ "react-dom": ">=19"
22
+ },
23
+ "dependencies": {
24
+ "@base-ui/react": "latest",
25
+ "@dnd-kit/core": "^6.3.1",
26
+ "@dnd-kit/modifiers": "^9.0.0",
27
+ "@dnd-kit/sortable": "^10.0.0",
28
+ "@dnd-kit/utilities": "^3.2.2",
29
+ "@tabler/icons-react": "latest",
30
+ "@tanstack/react-table": "^9.1.2",
31
+ "@tiptap/core": "^3.30.2",
32
+ "@tiptap/extension-color": "^3.30.2",
33
+ "@tiptap/extension-highlight": "^3.30.2",
34
+ "@tiptap/extension-image": "^3.30.2",
35
+ "@tiptap/extension-link": "^3.30.2",
36
+ "@tiptap/extension-mention": "^3.30.2",
37
+ "@tiptap/extension-placeholder": "^3.30.2",
38
+ "@tiptap/extension-subscript": "^3.30.2",
39
+ "@tiptap/extension-superscript": "^3.30.2",
40
+ "@tiptap/extension-task-item": "^3.30.2",
41
+ "@tiptap/extension-task-list": "^3.30.2",
42
+ "@tiptap/extension-text-align": "^3.30.2",
43
+ "@tiptap/extension-text-style": "^3.30.2",
44
+ "@tiptap/extension-underline": "^3.30.2",
45
+ "@tiptap/react": "^3.30.2",
46
+ "@tiptap/starter-kit": "^3.30.2",
47
+ "@tiptap/suggestion": "^3.30.2",
48
+ "class-variance-authority": "^0.7.1",
49
+ "clsx": "^2.1.1",
50
+ "cmdk": "^1.1.1",
51
+ "embla-carousel-react": "^8.6.0",
52
+ "framer-motion": "^13.1.0",
53
+ "input-otp": "^1.5.0",
54
+ "jspdf": "^4.2.1",
55
+ "lucide-react": "^1.33.0",
56
+ "mermaid": "latest",
57
+ "react-day-picker": "^10.0.1",
58
+ "react-is": "^19.3.0",
59
+ "react-markdown": "latest",
60
+ "react-resizable-panels": "^4.12.3",
61
+ "recharts": "^3.10.1",
62
+ "remark-gfm": "latest",
63
+ "sonner": "^2.0.8",
64
+ "tailwind-merge": "^3.6.0",
65
+ "tailwindcss": "^4.3.3",
66
+ "zod": "^4.4.3"
67
+ },
68
+ "devDependencies": {
69
+ "@types/react": "^19.2.18",
70
+ "@types/react-dom": "^19.2.4",
71
+ "@devxcrew/tools": "0.1.3"
72
+ },
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "git+https://github.com/devxcrew/ui.git"
76
+ },
77
+ "scripts": {
78
+ "github:now": "node ../../shared/mcp-governance/client/repository.mjs github:now",
79
+ "version:update": "node ../../shared/mcp-governance/client/repository.mjs version:bump",
80
+ "fix:line-endings": "node ../../shared/mcp-governance/client/repository.mjs lines:fix",
81
+ "check:versions": "node ../../shared/mcp-governance/client/repository.mjs check:versions",
82
+ "version:show": "node ../../shared/mcp-governance/client/repository.mjs version:show",
83
+ "version:bump": "node ../../shared/mcp-governance/client/repository.mjs version:bump",
84
+ "changelog:append": "node ../../shared/mcp-governance/client/repository.mjs changelog:append",
85
+ "changelog:show": "node ../../shared/mcp-governance/client/repository.mjs changelog:show",
86
+ "tools:lines:fix": "node ../../shared/mcp-governance/client/repository.mjs lines:fix",
87
+ "lines:check": "node ../../shared/mcp-governance/client/repository.mjs lines:check",
88
+ "tools:check": "devxcrew-tools dependencies:order && node ../../shared/mcp-governance/client/repository.mjs check:versions && devxcrew-tools lines:check",
89
+ "check": "npm run tools:check",
90
+ "mcp:connect": "node agent/connect.mjs",
91
+ "mcp:verify": "node agent/connect.mjs --strict",
92
+ "version-bump": "node ../../shared/mcp-governance/client/repository.mjs version:bump",
93
+ "prepublishOnly": "npm run release:check",
94
+ "release:check": "npm run check && npm pack --dry-run --ignore-scripts"
95
+ },
96
+ "publishConfig": {
97
+ "access": "public",
98
+ "registry": "https://registry.npmjs.org/"
99
+ },
100
+ "files": [
101
+ "src",
102
+ "README.md"
103
+ ],
104
+ "sideEffects": [
105
+ "**/*.css"
106
+ ]
107
+ }
@@ -0,0 +1,162 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import { Bot, FileText, Layers3, MessageSquareShare } from 'lucide-react'
3
+
4
+ import { ChatComposer, type ChatComposerAttachment } from '../chat-composer'
5
+ import { ChatResponseProgress, ChatTurnDivider } from '../chat-response-progress'
6
+ import { ChatRuntimeControls } from '../chat-runtime-controls'
7
+ import { ChatRuntimeTrace, type ChatRuntimeTraceEvent } from '../chat-runtime-trace'
8
+ import { Button } from '../../components/button'
9
+ import { MessageScroller, MessageScrollerButton, MessageScrollerViewport } from '../../components/message-scroller'
10
+
11
+ export type AgentChatMessage = {
12
+ content: string
13
+ id: string
14
+ role: 'assistant' | 'error' | 'user'
15
+ trace?: readonly ChatRuntimeTraceEvent[]
16
+ }
17
+
18
+ export type AgentChatRuntime = {
19
+ connected: boolean
20
+ message: string
21
+ model: string
22
+ models: readonly string[]
23
+ provider: string
24
+ providers: readonly string[]
25
+ reasoning: string
26
+ reasoningLevels: readonly string[]
27
+ }
28
+
29
+ export type AgentChatWorkspaceProps = {
30
+ attachments?: readonly ChatComposerAttachment[]
31
+ draft: string
32
+ elapsedSeconds?: number
33
+ isRecording?: boolean
34
+ isWorking?: boolean
35
+ messages: readonly AgentChatMessage[]
36
+ onAddAttachments?: (files: File[]) => void
37
+ onDraftChange: (value: string) => void
38
+ onLongTextPaste?: (file: File) => void
39
+ onReconnect: () => void
40
+ onRemoveAttachment?: (id: string) => void
41
+ onRuntimeChange: (runtime: AgentChatRuntime) => void
42
+ onStop?: () => void
43
+ onSubmit: () => void
44
+ onVoiceToggle?: () => void
45
+ placeholder?: string
46
+ queuedSteerCount?: number
47
+ runtime: AgentChatRuntime
48
+ title?: string
49
+ }
50
+
51
+ export function AgentChatWorkspace({
52
+ attachments = [],
53
+ draft,
54
+ elapsedSeconds = 0,
55
+ isRecording = false,
56
+ isWorking = false,
57
+ messages,
58
+ onAddAttachments,
59
+ onDraftChange,
60
+ onLongTextPaste,
61
+ onReconnect,
62
+ onRemoveAttachment,
63
+ onRuntimeChange,
64
+ onStop,
65
+ onSubmit,
66
+ onVoiceToggle,
67
+ placeholder = 'Ask about a focused coding task…',
68
+ queuedSteerCount = 0,
69
+ runtime,
70
+ title = 'Start with an agent task',
71
+ }: AgentChatWorkspaceProps) {
72
+ const [canScrollToLatest, setCanScrollToLatest] = useState(false)
73
+ const messageViewportRef = useRef<HTMLDivElement>(null)
74
+ const composerRef = useRef<HTMLTextAreaElement>(null)
75
+
76
+ useEffect(() => {
77
+ composerRef.current?.focus({ preventScroll: true })
78
+ }, [])
79
+
80
+ function updateScrollPosition(): void {
81
+ const viewport = messageViewportRef.current
82
+ if (!viewport) return
83
+ setCanScrollToLatest(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > 8)
84
+ }
85
+
86
+ function scrollToLatest(): void {
87
+ messageViewportRef.current?.scrollTo({ top: messageViewportRef.current.scrollHeight, behavior: 'smooth' })
88
+ setCanScrollToLatest(false)
89
+ }
90
+
91
+ return (
92
+ <section aria-label="Agent chat" className="flex size-full min-h-0 flex-col bg-background">
93
+ <header className="grid shrink-0 gap-3 border-b border-border px-4 py-3 sm:px-6 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center">
94
+ <div className="min-w-0">
95
+ <h1 className="truncate text-sm font-semibold tracking-tight">{title}</h1>
96
+ <div className="mt-2 flex flex-wrap gap-1">
97
+ <Button className="h-7 px-2 text-xs" size="sm" variant="secondary">Explore</Button>
98
+ <Button className="h-7 px-2 text-xs" size="sm" variant="ghost">Plan</Button>
99
+ <Button className="h-7 px-2 text-xs" size="sm" variant="ghost">Execute</Button>
100
+ </div>
101
+ </div>
102
+ <div className="flex items-center justify-end gap-1">
103
+ <Button aria-label="Open chat context" size="icon-sm" variant="ghost"><Layers3 /></Button>
104
+ <Button aria-label="Open task brief" size="sm" variant="outline"><FileText /> Brief</Button>
105
+ <Button aria-label="Share chat" size="icon-sm" variant="ghost"><MessageSquareShare /></Button>
106
+ <ChatRuntimeControls
107
+ connected={runtime.connected}
108
+ message={runtime.message}
109
+ model={runtime.model}
110
+ models={runtime.models}
111
+ onModelChange={(model) => onRuntimeChange({ ...runtime, model })}
112
+ onProviderChange={(provider) => onRuntimeChange({ ...runtime, provider })}
113
+ onReasoningChange={(reasoning) => onRuntimeChange({ ...runtime, reasoning })}
114
+ onReconnect={onReconnect}
115
+ provider={runtime.provider}
116
+ providers={runtime.providers}
117
+ reasoning={runtime.reasoning}
118
+ reasoningLevels={runtime.reasoningLevels}
119
+ />
120
+ </div>
121
+ </header>
122
+
123
+ <MessageScroller>
124
+ <MessageScrollerViewport ref={messageViewportRef} className="px-6 py-7 [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-1" onScroll={updateScrollPosition}>
125
+ <div className="mx-auto flex w-4/5 flex-col gap-3">
126
+ {messages.map((message) => (
127
+ <div key={message.id} className={`group flex flex-col ${message.role === 'user' ? 'w-fit max-w-[80%] self-end' : 'w-full self-start'}`}>
128
+ <article className={message.role === 'user' ? 'rounded-sm bg-muted/60 px-4 py-3 text-foreground shadow-sm' : 'px-4 py-3'}>
129
+ <p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground">{message.role === 'assistant' ? <Bot aria-hidden="true" className="size-3.5" /> : null}{message.role === 'user' ? 'You' : message.role === 'error' ? 'Connection' : 'CodeLoop'}</p>
130
+ {message.content ? <p className="mt-1 whitespace-pre-wrap text-sm leading-6">{message.content}</p> : null}
131
+ {message.trace?.length ? <ChatRuntimeTrace className="mt-2" elapsedSeconds={elapsedSeconds} events={message.trace} isWorking={isWorking} /> : null}
132
+ </article>
133
+ {message.role === 'assistant' ? <ChatTurnDivider /> : null}
134
+ </div>
135
+ ))}
136
+ {isWorking ? <div className="px-4"><ChatResponseProgress elapsedSeconds={elapsedSeconds} label="Working" tone="orange" /></div> : null}
137
+ </div>
138
+ </MessageScrollerViewport>
139
+ {canScrollToLatest ? <MessageScrollerButton aria-label="Scroll to latest message" className="border-border shadow-sm" onClick={scrollToLatest} /> : null}
140
+ </MessageScroller>
141
+
142
+ <footer className="sticky bottom-0 z-10 shrink-0 bg-background px-6 py-4">
143
+ <ChatComposer
144
+ attachments={attachments}
145
+ isRecording={isRecording}
146
+ isWorking={isWorking}
147
+ onAddFiles={onAddAttachments}
148
+ onLongTextPaste={onLongTextPaste}
149
+ onRemoveAttachment={onRemoveAttachment}
150
+ onStop={onStop}
151
+ onSubmit={onSubmit}
152
+ onValueChange={onDraftChange}
153
+ onVoiceToggle={onVoiceToggle}
154
+ placeholder={placeholder}
155
+ queuedSteerCount={queuedSteerCount}
156
+ textareaRef={composerRef}
157
+ value={draft}
158
+ />
159
+ </footer>
160
+ </section>
161
+ )
162
+ }
@@ -0,0 +1,2 @@
1
+ export { AgentChatWorkspace } from './agent-chat-workspace'
2
+ export type { AgentChatMessage, AgentChatRuntime, AgentChatWorkspaceProps } from './agent-chat-workspace'
@@ -0,0 +1,183 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import type { LucideIcon } from 'lucide-react'
3
+ import { CheckCircle2, Eye, EyeOff } from 'lucide-react'
4
+
5
+ import { Badge } from '../../components/badge'
6
+ import { Button } from '../../components/button'
7
+ import { Input } from '../../components/input'
8
+ import { Label } from '../../components/label'
9
+ import { Separator } from '../../components/separator'
10
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../components/select'
11
+ import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle } from '../../components/sheet'
12
+ import { Switch } from '../../components/switch'
13
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/tabs'
14
+
15
+ export type AgentProviderSettingsItem = {
16
+ apiKey?: string
17
+ apiKeyConfigured?: boolean
18
+ apiKeyHint?: string
19
+ apiKeyLabel?: string
20
+ apiKeyPlaceholder?: string
21
+ connectionMessage?: string
22
+ connectionStatus?: 'connected' | 'error' | 'idle' | 'checking'
23
+ description: string
24
+ enabled: boolean
25
+ endpoint: string
26
+ icon: LucideIcon
27
+ id: string
28
+ model: string
29
+ modelOptions?: readonly string[]
30
+ name: string
31
+ }
32
+
33
+ export type AgentProviderVerificationResult = {
34
+ message: string
35
+ models?: readonly string[]
36
+ status: 'connected' | 'error'
37
+ }
38
+
39
+ export type AgentProviderSmokeTestResult = {
40
+ latencyMs: number
41
+ message: string
42
+ model?: string
43
+ response?: string
44
+ status: 'connected' | 'error'
45
+ }
46
+
47
+ export type AgentProviderSettingsProps = {
48
+ embedded?: boolean
49
+ onClose?: () => void
50
+ onOpenChange: (open: boolean) => void
51
+ onSave: (providers: readonly AgentProviderSettingsItem[]) => void | Promise<void>
52
+ onSmokeTestProvider?: (provider: AgentProviderSettingsItem, signal: AbortSignal) => Promise<AgentProviderSmokeTestResult>
53
+ onVerifyProvider?: (provider: AgentProviderSettingsItem, signal: AbortSignal) => Promise<AgentProviderVerificationResult>
54
+ open: boolean
55
+ providers: readonly AgentProviderSettingsItem[]
56
+ }
57
+
58
+ export function AgentProviderSettings({ embedded = false, onClose, onOpenChange, onSave, onSmokeTestProvider, onVerifyProvider, open, providers }: AgentProviderSettingsProps) {
59
+ const [draft, setDraft] = useState<AgentProviderSettingsItem[]>([])
60
+ const [activeProviderId, setActiveProviderId] = useState('')
61
+ const [saving, setSaving] = useState(false)
62
+ const [smokeResult, setSmokeResult] = useState<AgentProviderSmokeTestResult | undefined>()
63
+ const verificationController = useRef<AbortController | null>(null)
64
+ const smokeController = useRef<AbortController | null>(null)
65
+
66
+ useEffect(() => {
67
+ if (!open) return
68
+ setDraft(providers.map((provider) => ({ ...provider })))
69
+ setActiveProviderId((current) => current || providers[0]?.id || '')
70
+ setSmokeResult(undefined)
71
+ }, [open, providers])
72
+
73
+ const activeProvider = draft.find((provider) => provider.id === activeProviderId) ?? draft[0]
74
+
75
+ function updateProvider(changes: Partial<AgentProviderSettingsItem>): void {
76
+ if (!activeProvider) return
77
+ setDraft((items) => items.map((provider) => provider.id === activeProvider.id ? { ...provider, ...changes } : provider))
78
+ }
79
+
80
+ async function save(): Promise<void> {
81
+ setSaving(true)
82
+ try {
83
+ await onSave(draft)
84
+ onOpenChange(false)
85
+ } finally {
86
+ setSaving(false)
87
+ }
88
+ }
89
+
90
+ async function verifyProvider(provider: AgentProviderSettingsItem): Promise<void> {
91
+ if (!onVerifyProvider) return
92
+ verificationController.current?.abort()
93
+ const controller = new AbortController()
94
+ verificationController.current = controller
95
+ updateProvider({ connectionStatus: 'checking', connectionMessage: 'Checking connection…' })
96
+ try {
97
+ const result = await onVerifyProvider(provider, controller.signal)
98
+ setDraft((items) => items.map((item) => item.id === provider.id ? {
99
+ ...item,
100
+ connectionStatus: result.status,
101
+ connectionMessage: result.message,
102
+ modelOptions: result.models ?? item.modelOptions,
103
+ } : item))
104
+ } catch (error) {
105
+ const stopped = controller.signal.aborted
106
+ setDraft((items) => items.map((item) => item.id === provider.id ? { ...item, connectionStatus: stopped ? 'idle' : 'error', connectionMessage: stopped ? 'Verification stopped.' : error instanceof Error ? error.message : 'Connection verification failed.' } : item))
107
+ } finally {
108
+ if (verificationController.current === controller) verificationController.current = null
109
+ }
110
+ }
111
+
112
+ function stopVerification(): void {
113
+ const controller = verificationController.current
114
+ if (!controller) return
115
+ controller.abort()
116
+ setDraft((items) => items.map((item) => item.id === activeProviderId ? { ...item, connectionStatus: 'idle', connectionMessage: 'Verification stopped.' } : item))
117
+ verificationController.current = null
118
+ }
119
+
120
+ async function smokeTestProvider(provider: AgentProviderSettingsItem): Promise<void> {
121
+ if (!onSmokeTestProvider) return
122
+ smokeController.current?.abort()
123
+ const controller = new AbortController()
124
+ smokeController.current = controller
125
+ setSmokeResult({ latencyMs: 0, message: 'Running smoke test…', status: 'error' })
126
+ try {
127
+ setSmokeResult(await onSmokeTestProvider(provider, controller.signal))
128
+ } catch (error) {
129
+ setSmokeResult({ latencyMs: 0, message: controller.signal.aborted ? 'Smoke test stopped.' : error instanceof Error ? error.message : 'Smoke test failed.', status: 'error' })
130
+ } finally {
131
+ if (smokeController.current === controller) smokeController.current = null
132
+ }
133
+ }
134
+
135
+ const content = <>
136
+ {draft.length ? (
137
+ <Tabs className="min-h-0 flex-1" orientation="vertical" value={activeProvider?.id} onValueChange={(value) => setActiveProviderId(value)}>
138
+ <div className="flex min-h-0 flex-1 flex-col gap-6 overflow-y-auto p-4 sm:flex-row sm:p-6 lg:p-8">
139
+ <TabsList className="h-fit w-full shrink-0 flex-col items-stretch gap-1 bg-transparent p-0 sm:w-56" variant="line">
140
+ {draft.map((provider) => <TabsTrigger key={provider.id} className="min-h-10 w-full justify-start rounded-none px-3 font-normal text-muted-foreground after:hidden data-active:bg-muted/70 data-active:text-foreground data-active:before:absolute data-active:before:inset-y-0 data-active:before:left-0 data-active:before:w-0.5 data-active:before:bg-foreground" value={provider.id}><provider.icon className="size-4" />{provider.name}</TabsTrigger>)}
141
+ </TabsList>
142
+ {activeProvider ? <TabsContent className="min-w-0 flex-1 rounded-lg border p-5 sm:p-6" value={activeProvider.id}><ProviderPanel onChange={updateProvider} onSmokeTest={onSmokeTestProvider ? () => void smokeTestProvider(activeProvider) : undefined} onStop={stopVerification} onVerify={() => verifyProvider(activeProvider)} provider={activeProvider} smokeResult={smokeResult} /></TabsContent> : null}
143
+ </div>
144
+ </Tabs>
145
+ ) : <p className="p-4 text-sm text-muted-foreground">No providers are configured.</p>}
146
+ </>
147
+
148
+ if (embedded) return <section aria-label="Provider settings" className="flex size-full min-h-0 flex-col overflow-hidden bg-background">
149
+ <header className="flex shrink-0 items-start justify-between gap-4 border-b px-6 py-5 lg:px-8">
150
+ <h1 className="text-xl font-semibold tracking-tight">Provider settings</h1>
151
+ <div className="flex shrink-0 items-center gap-2"><Button variant="outline" disabled={saving} onClick={onClose}>Back to workspace</Button><Button disabled={!draft.length || saving} onClick={() => void save()}>{saving ? 'Saving…' : 'Save provider settings'}</Button></div>
152
+ </header>
153
+ <div className="mx-auto flex min-h-0 w-full max-w-7xl flex-1 flex-col">{content}</div>
154
+ </section>
155
+
156
+ return <Sheet open={open} onOpenChange={onOpenChange}>
157
+ <SheetContent className="gap-0 p-0 sm:max-w-xl" side="right">
158
+ <SheetHeader className="border-b pr-12"><SheetTitle>Provider settings</SheetTitle></SheetHeader>
159
+ {content}
160
+ <SheetFooter className="border-t"><Button variant="outline" disabled={saving} onClick={() => onOpenChange(false)}>Cancel</Button><Button disabled={!draft.length || saving} onClick={() => void save()}>{saving ? 'Saving…' : 'Save provider settings'}</Button></SheetFooter>
161
+ </SheetContent>
162
+ </Sheet>
163
+ }
164
+
165
+ function ProviderPanel({ onChange, onSmokeTest, onStop, onVerify, provider, smokeResult }: { onChange: (changes: Partial<AgentProviderSettingsItem>) => void; onSmokeTest?: () => void; onStop: () => void; onVerify: () => Promise<void>; provider: AgentProviderSettingsItem; smokeResult?: AgentProviderSmokeTestResult }) {
166
+ const [showKey, setShowKey] = useState(false)
167
+ const Icon = provider.icon
168
+
169
+ return <div className="flex flex-col gap-5">
170
+ <div className="flex items-start gap-3">
171
+ <span className="grid size-10 shrink-0 place-items-center rounded-md border bg-muted"><Icon className="size-5" /></span>
172
+ <div className="min-w-0 flex-1"><h2 className="text-base font-semibold">{provider.name}</h2></div>
173
+ <Badge variant={provider.connectionStatus === 'connected' ? 'secondary' : 'outline'}>{provider.connectionStatus === 'connected' ? 'Connected' : provider.enabled ? 'Enabled' : 'Disabled'}</Badge>
174
+ </div>
175
+ <Separator />
176
+ <div className="flex items-center justify-between gap-3"><Label htmlFor={`${provider.id}-enabled`}>Use provider</Label><Switch id={`${provider.id}-enabled`} checked={provider.enabled} onCheckedChange={(enabled) => onChange({ enabled })} /></div>
177
+ <div className="grid gap-2"><Label htmlFor={`${provider.id}-endpoint`}>Endpoint</Label><Input id={`${provider.id}-endpoint`} value={provider.endpoint} onChange={(event) => onChange({ endpoint: event.target.value })} /></div>
178
+ <div className="grid gap-2"><Label htmlFor={`${provider.id}-model`}>Default model</Label>{provider.modelOptions?.length ? <Select value={provider.model} onValueChange={(model) => onChange({ model: model ?? '' })}><SelectTrigger id={`${provider.id}-model`} className="w-full"><SelectValue placeholder="Select a model" /></SelectTrigger><SelectContent>{provider.modelOptions.map((model) => <SelectItem key={model} value={model}>{model}</SelectItem>)}</SelectContent></Select> : <Input id={`${provider.id}-model`} value={provider.model} onChange={(event) => onChange({ model: event.target.value })} />}</div>
179
+ <div className="grid gap-2"><Label htmlFor={`${provider.id}-key`}>{provider.apiKeyLabel ?? 'API key'}</Label><div className="flex gap-2"><Input id={`${provider.id}-key`} type={showKey ? 'text' : 'password'} value={provider.apiKey ?? ''} placeholder={provider.apiKeyPlaceholder ?? (provider.apiKeyConfigured ? 'Configured in runtime' : 'Not configured')} onChange={(event) => onChange({ apiKey: event.target.value, apiKeyConfigured: Boolean(event.target.value) })} /><Button aria-label={showKey ? 'Hide API key' : 'Show API key'} size="icon-sm" variant="outline" onClick={() => setShowKey((value) => !value)}>{showKey ? <EyeOff /> : <Eye />}</Button></div></div>
180
+ <div className="flex flex-wrap items-center justify-between gap-3 border-t pt-4"><div className="flex min-w-0 items-center gap-2 text-sm">{provider.connectionStatus === 'connected' ? <CheckCircle2 className="size-4 text-emerald-600" /> : null}<span className={provider.connectionStatus === 'error' ? 'text-destructive' : 'text-muted-foreground'}>{provider.connectionMessage ?? 'Not verified'}</span></div><div className="flex items-center gap-2">{onSmokeTest ? <Button disabled={provider.connectionStatus !== 'connected'} variant="outline" onClick={onSmokeTest}>{smokeResult?.message === 'Running smoke test…' ? 'Testing…' : 'Smoke test'}</Button> : null}{provider.connectionStatus === 'checking' ? <Button variant="outline" onClick={onStop}>Stop</Button> : <Button variant="outline" onClick={() => void onVerify()}>{provider.connectionStatus === 'connected' ? 'Verify again' : 'Connect and verify'}</Button>}</div></div>
181
+ {smokeResult ? <div className={`rounded-md border px-3 py-2 text-sm ${smokeResult.status === 'connected' ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-destructive/30 bg-destructive/5 text-destructive'}`} role="status"><div>{smokeResult.message}{smokeResult.latencyMs > 0 ? ` (${smokeResult.latencyMs} ms)` : ''}</div>{smokeResult.response ? <div className="mt-1 break-words font-mono text-xs">{smokeResult.response}</div> : null}</div> : null}
182
+ </div>
183
+ }
@@ -0,0 +1,2 @@
1
+ export { AgentProviderSettings } from './agent-provider-settings'
2
+ export type { AgentProviderSettingsItem, AgentProviderSettingsProps, AgentProviderSmokeTestResult, AgentProviderVerificationResult } from './agent-provider-settings'
@@ -0,0 +1,48 @@
1
+ import { ChevronLeftIcon, ClipboardListIcon, FolderKanbanIcon } from "lucide-react";
2
+ import { Button } from "../../components/button";
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/card";
4
+
5
+ export type AgentTaskWorkspaceItem = {
6
+ acceptanceCriteria: string;
7
+ briefId: string;
8
+ createdAt: string;
9
+ id: string;
10
+ projectReference: string | null;
11
+ projectScope: "project" | "all-projects";
12
+ priority: "low" | "medium" | "high";
13
+ status: string;
14
+ summary: string;
15
+ title: string;
16
+ };
17
+
18
+ export type AgentTaskWorkspaceProps = {
19
+ onBack: () => void;
20
+ onSelectTask: (id: string) => void;
21
+ selectedTaskId?: string;
22
+ tasks: readonly AgentTaskWorkspaceItem[];
23
+ };
24
+
25
+ export function AgentTaskWorkspace({ onBack, onSelectTask, selectedTaskId, tasks }: AgentTaskWorkspaceProps) {
26
+ const selectedTask = tasks.find((task) => task.id === selectedTaskId) ?? tasks[0];
27
+
28
+ return <section className="flex size-full min-h-0 flex-col bg-background">
29
+ <header className="flex shrink-0 items-center gap-3 border-b px-4 py-3 sm:px-6">
30
+ <Button size="sm" variant="ghost" onClick={onBack}><ChevronLeftIcon /> Conversation</Button>
31
+ <div className="min-w-0 flex-1"><h1 className="truncate text-sm font-semibold">Agent tasks</h1><p className="text-xs text-muted-foreground">Prepared handovers retain their final brief and project reference.</p></div>
32
+ </header>
33
+ <main className="min-h-0 flex-1 overflow-y-auto px-4 py-6 sm:px-6">
34
+ <div className="mx-auto grid w-full max-w-5xl gap-4 lg:grid-cols-[minmax(16rem,0.8fr)_minmax(0,1.4fr)]">
35
+ <div className="flex flex-col gap-2">{tasks.length ? tasks.map((task) => <Button key={task.id} className="h-auto justify-start whitespace-normal p-3 text-left" variant={task.id === selectedTask?.id ? "secondary" : "outline"} onClick={() => onSelectTask(task.id)}><ClipboardListIcon className="mt-0.5 shrink-0" /><span className="min-w-0"><span className="block truncate font-medium">{task.title}</span><span className="block text-xs font-normal text-muted-foreground">{task.status} · {projectLabel(task)}</span></span></Button>) : <p className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">No prepared tasks yet. Finalize an idea brief and hand it over here.</p>}</div>
36
+ {selectedTask ? <Card size="sm"><CardHeader><CardTitle>{selectedTask.title}</CardTitle><CardDescription>{selectedTask.status} · {selectedTask.priority} priority · prepared from final brief</CardDescription></CardHeader><CardContent className="grid gap-5 text-sm"><section><p className="mb-1 text-xs font-medium uppercase text-muted-foreground">Task summary</p><p className="whitespace-pre-wrap leading-6">{selectedTask.summary}</p></section>{selectedTask.acceptanceCriteria ? <section><p className="mb-1 text-xs font-medium uppercase text-muted-foreground">Acceptance criteria</p><p className="whitespace-pre-wrap leading-6">{selectedTask.acceptanceCriteria}</p></section> : null}<section className="grid gap-3 sm:grid-cols-2"><Meta label="Project" value={projectLabel(selectedTask)} /><Meta label="Created" value={new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(new Date(selectedTask.createdAt))} /><Meta label="Task reference" value={selectedTask.id} /><Meta label="Final brief reference" value={selectedTask.briefId} /></section></CardContent></Card> : null}
37
+ </div>
38
+ </main>
39
+ </section>;
40
+ }
41
+
42
+ function projectLabel(task: AgentTaskWorkspaceItem): string {
43
+ return task.projectScope === "all-projects" ? "All projects" : task.projectReference ?? "Project not set";
44
+ }
45
+
46
+ function Meta({ label, value }: { label: string; value: string }) {
47
+ return <div className="flex gap-2"><FolderKanbanIcon className="mt-0.5 size-4 shrink-0 text-muted-foreground" /><div className="min-w-0"><p className="text-xs text-muted-foreground">{label}</p><p className="break-all">{value}</p></div></div>;
48
+ }
@@ -0,0 +1,2 @@
1
+ export { AgentTaskWorkspace } from "./agent-task-workspace";
2
+ export type { AgentTaskWorkspaceItem, AgentTaskWorkspaceProps } from "./agent-task-workspace";
@@ -0,0 +1,6 @@
1
+ import { FolderSearchIcon, ShieldCheckIcon } from "lucide-react";
2
+
3
+ export function AnalysisContext({ root }: { root?: string | null }) {
4
+ if (!root) return null;
5
+ return <div className="mx-auto mb-3 flex w-4/5 items-center gap-2 rounded-md border border-border/70 bg-muted/30 px-3 py-2 text-xs text-muted-foreground"><FolderSearchIcon className="size-3.5 shrink-0" /><span className="min-w-0 flex-1 truncate">Read-only project analysis: {root}</span><span className="flex shrink-0 items-center gap-1"><ShieldCheckIcon className="size-3.5" /> No writes</span></div>;
6
+ }
@@ -0,0 +1 @@
1
+ export { AnalysisContext } from "./analysis-context";