@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.
- package/README.md +85 -2
- package/package.json +105 -4
- package/src/blocks/agent-chat-workspace/agent-chat-workspace.tsx +162 -0
- package/src/blocks/agent-chat-workspace/index.ts +2 -0
- package/src/blocks/agent-provider-settings/agent-provider-settings.tsx +183 -0
- package/src/blocks/agent-provider-settings/index.ts +2 -0
- package/src/blocks/agent-task-workspace/agent-task-workspace.tsx +48 -0
- package/src/blocks/agent-task-workspace/index.ts +2 -0
- package/src/blocks/analysis-context/analysis-context.tsx +6 -0
- package/src/blocks/analysis-context/index.ts +1 -0
- package/src/blocks/app-header/app-header.tsx +159 -0
- package/src/blocks/app-header/index.ts +2 -0
- package/src/blocks/archived-chat-workspace/archived-chat-workspace.tsx +29 -0
- package/src/blocks/archived-chat-workspace/index.ts +2 -0
- package/src/blocks/auth/README.md +32 -0
- package/src/blocks/auth/admin-login-page.tsx +15 -0
- package/src/blocks/auth/admin-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/auth-page-layout.tsx +90 -0
- package/src/blocks/auth/client-login-page.tsx +20 -0
- package/src/blocks/auth/client-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/identity-management-desk.tsx +530 -0
- package/src/blocks/auth/index.ts +12 -0
- package/src/blocks/auth/login-page.tsx +206 -0
- package/src/blocks/auth/password-forgot-page.tsx +60 -0
- package/src/blocks/auth/portal-pages.tsx +32 -0
- package/src/blocks/auth/privileged-desk.tsx +55 -0
- package/src/blocks/auth/register-page.tsx +117 -0
- package/src/blocks/auth/session-boundary.tsx +296 -0
- package/src/blocks/auth/super-admin-login-page.tsx +15 -0
- package/src/blocks/auth/super-admin-password-forgot-page.tsx +5 -0
- package/src/blocks/blog/blog-card.tsx +156 -0
- package/src/blocks/blog/blog-reader.tsx +175 -0
- package/src/blocks/blog/blog-types.ts +47 -0
- package/src/blocks/blog/index.ts +3 -0
- package/src/blocks/chat-composer/chat-composer.tsx +140 -0
- package/src/blocks/chat-composer/index.ts +1 -0
- package/src/blocks/chat-history/chat-history.tsx +59 -0
- package/src/blocks/chat-history/index.ts +1 -0
- package/src/blocks/chat-response-progress/chat-response-progress.tsx +34 -0
- package/src/blocks/chat-response-progress/index.ts +2 -0
- package/src/blocks/chat-runtime-controls/chat-runtime-controls.tsx +56 -0
- package/src/blocks/chat-runtime-controls/index.ts +2 -0
- package/src/blocks/chat-runtime-trace/chat-runtime-trace.tsx +71 -0
- package/src/blocks/chat-runtime-trace/index.ts +2 -0
- package/src/blocks/codex-connection-settings/codex-connection-settings.tsx +42 -0
- package/src/blocks/codex-connection-settings/index.ts +1 -0
- package/src/blocks/dropzone/dropzone-file-item.tsx +113 -0
- package/src/blocks/dropzone/dropzone-types.ts +32 -0
- package/src/blocks/dropzone/dropzone.tsx +216 -0
- package/src/blocks/dropzone/index.ts +3 -0
- package/src/blocks/ecommerce/cart/cart-drawer.tsx +228 -0
- package/src/blocks/ecommerce/cart/cart-types.ts +27 -0
- package/src/blocks/ecommerce/cart/index.ts +2 -0
- package/src/blocks/ecommerce/categories/categories-types.ts +23 -0
- package/src/blocks/ecommerce/categories/category-showcase.tsx +125 -0
- package/src/blocks/ecommerce/categories/index.ts +2 -0
- package/src/blocks/ecommerce/checkout/checkout-types.ts +48 -0
- package/src/blocks/ecommerce/checkout/checkout-wizard.tsx +468 -0
- package/src/blocks/ecommerce/checkout/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/comparison-types.ts +28 -0
- package/src/blocks/ecommerce/comparison/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/product-comparison.tsx +174 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet-types.ts +21 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet.tsx +154 -0
- package/src/blocks/ecommerce/coupon-wallet/index.ts +2 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker-types.ts +20 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker.tsx +169 -0
- package/src/blocks/ecommerce/delivery-tracker/index.ts +2 -0
- package/src/blocks/ecommerce/index.ts +12 -0
- package/src/blocks/ecommerce/payment-methods/index.ts +2 -0
- package/src/blocks/ecommerce/payment-methods/payment-method-selector.tsx +224 -0
- package/src/blocks/ecommerce/payment-methods/payment-methods-types.ts +21 -0
- package/src/blocks/ecommerce/price-history/index.ts +2 -0
- package/src/blocks/ecommerce/price-history/price-history-chart.tsx +217 -0
- package/src/blocks/ecommerce/price-history/price-history-types.ts +16 -0
- package/src/blocks/ecommerce/reviews/index.ts +2 -0
- package/src/blocks/ecommerce/reviews/reviews-section.tsx +309 -0
- package/src/blocks/ecommerce/reviews/reviews-types.ts +26 -0
- package/src/blocks/ecommerce/wishlist/index.ts +2 -0
- package/src/blocks/ecommerce/wishlist/wishlist-grid.tsx +149 -0
- package/src/blocks/ecommerce/wishlist/wishlist-types.ts +19 -0
- package/src/blocks/execution-status/README.md +25 -0
- package/src/blocks/execution-status/execution-status.tsx +135 -0
- package/src/blocks/execution-status/index.ts +2 -0
- package/src/blocks/file-tree/file-icons.tsx +84 -0
- package/src/blocks/file-tree/file-tree-node.tsx +158 -0
- package/src/blocks/file-tree/file-tree-types.ts +33 -0
- package/src/blocks/file-tree/file-tree.tsx +163 -0
- package/src/blocks/file-tree/index.ts +5 -0
- package/src/blocks/filter-builder/filter-builder.tsx +169 -0
- package/src/blocks/filter-builder/filter-row.tsx +164 -0
- package/src/blocks/filter-builder/filter-types.ts +61 -0
- package/src/blocks/filter-builder/index.ts +12 -0
- package/src/blocks/footer/footer-types.ts +35 -0
- package/src/blocks/footer/index.ts +2 -0
- package/src/blocks/footer/site-footer.tsx +212 -0
- package/src/blocks/form/form-block.tsx +113 -0
- package/src/blocks/form/form-lookup-field.tsx +60 -0
- package/src/blocks/form/index.ts +4 -0
- package/src/blocks/handover-stack/handover-stack.tsx +41 -0
- package/src/blocks/handover-stack/index.ts +1 -0
- package/src/blocks/idea-handover/idea-handover-readiness.ts +41 -0
- package/src/blocks/idea-handover/idea-handover-workspace.tsx +150 -0
- package/src/blocks/idea-handover/index.ts +4 -0
- package/src/blocks/kanban/index.ts +11 -0
- package/src/blocks/kanban/kanban-board.tsx +146 -0
- package/src/blocks/kanban/kanban-card.tsx +149 -0
- package/src/blocks/kanban/kanban-column.tsx +102 -0
- package/src/blocks/kanban/kanban-types.ts +38 -0
- package/src/blocks/loader/global-loader.tsx +90 -0
- package/src/blocks/loader/index.ts +1 -0
- package/src/blocks/mascot/README.md +21 -0
- package/src/blocks/mascot/index.ts +2 -0
- package/src/blocks/mascot/mascot.tsx +202 -0
- package/src/blocks/master-list/README.md +21 -0
- package/src/blocks/master-list/index.ts +12 -0
- package/src/blocks/master-list/master-form.tsx +157 -0
- package/src/blocks/master-list/master-list-desk.tsx +370 -0
- package/src/blocks/master-list/master-list.tsx +189 -0
- package/src/blocks/master-list/types.ts +18 -0
- package/src/blocks/mermaid-preview/index.ts +1 -0
- package/src/blocks/mermaid-preview/mermaid-preview.tsx +164 -0
- package/src/blocks/notifications/README.md +7 -0
- package/src/blocks/notifications/index.ts +1 -0
- package/src/blocks/notifications/notification-center-page.tsx +73 -0
- package/src/blocks/pricing/index.ts +7 -0
- package/src/blocks/pricing/pricing-table.tsx +173 -0
- package/src/blocks/pricing/pricing-types.ts +34 -0
- package/src/blocks/product-card/index.ts +2 -0
- package/src/blocks/product-card/product-card-types.ts +35 -0
- package/src/blocks/product-card/product-card.tsx +183 -0
- package/src/blocks/table/data-table-block.tsx +313 -0
- package/src/blocks/table/data-table-filter-menu.tsx +48 -0
- package/src/blocks/table/data-table-icon-menu-trigger.tsx +44 -0
- package/src/blocks/table/data-table-menu-content.tsx +32 -0
- package/src/blocks/table/data-table-pagination.tsx +149 -0
- package/src/blocks/table/data-table-row-actions.tsx +53 -0
- package/src/blocks/table/data-table-status.tsx +42 -0
- package/src/blocks/table/data-table-toolbar.tsx +124 -0
- package/src/blocks/table/data-table-totals.tsx +31 -0
- package/src/blocks/table/index.ts +13 -0
- package/src/blocks/workspace/index.ts +19 -0
- package/src/blocks/workspace/workspace-action-card.tsx +73 -0
- package/src/blocks/workspace/workspace-application-header.tsx +67 -0
- package/src/blocks/workspace/workspace-entity-card.tsx +48 -0
- package/src/blocks/workspace/workspace-health-summary.tsx +15 -0
- package/src/blocks/workspace/workspace-metric-card.tsx +97 -0
- package/src/blocks/workspace/workspace-page-header.tsx +43 -0
- package/src/blocks/workspace/workspace-publish-status.tsx +15 -0
- package/src/blocks/workspace/workspace-route-checklist.tsx +28 -0
- package/src/blocks/workspace/workspace-runtime-status.tsx +48 -0
- package/src/blocks/workspace/workspace-section-card.tsx +42 -0
- package/src/components/accordion.tsx +65 -0
- package/src/components/alert-dialog.tsx +160 -0
- package/src/components/alert.tsx +68 -0
- package/src/components/aspect-ratio.tsx +22 -0
- package/src/components/attachment.tsx +195 -0
- package/src/components/avatar.tsx +90 -0
- package/src/components/badge.tsx +55 -0
- package/src/components/breadcrumb.tsx +100 -0
- package/src/components/bubble.tsx +123 -0
- package/src/components/button-group.tsx +79 -0
- package/src/components/button.tsx +71 -0
- package/src/components/calendar.tsx +185 -0
- package/src/components/card.tsx +100 -0
- package/src/components/carousel.tsx +229 -0
- package/src/components/chart.tsx +338 -0
- package/src/components/checkbox.tsx +27 -0
- package/src/components/collapsible.tsx +15 -0
- package/src/components/combobox.tsx +269 -0
- package/src/components/command.tsx +181 -0
- package/src/components/compact-model-switcher.tsx +128 -0
- package/src/components/context-menu.tsx +244 -0
- package/src/components/dialog.tsx +138 -0
- package/src/components/direction.tsx +1 -0
- package/src/components/drawer.tsx +208 -0
- package/src/components/dropdown-menu.tsx +257 -0
- package/src/components/empty.tsx +93 -0
- package/src/components/field.tsx +232 -0
- package/src/components/hover-card.tsx +45 -0
- package/src/components/input-group.tsx +146 -0
- package/src/components/input-otp.tsx +83 -0
- package/src/components/input.tsx +19 -0
- package/src/components/item.tsx +188 -0
- package/src/components/kbd.tsx +26 -0
- package/src/components/label.tsx +17 -0
- package/src/components/markdown-content.tsx +113 -0
- package/src/components/marker.tsx +67 -0
- package/src/components/menubar.tsx +267 -0
- package/src/components/message-scroller.tsx +91 -0
- package/src/components/message.tsx +84 -0
- package/src/components/native-select.tsx +54 -0
- package/src/components/navigation-menu.tsx +160 -0
- package/src/components/pagination.tsx +118 -0
- package/src/components/popover.tsx +74 -0
- package/src/components/progress.tsx +82 -0
- package/src/components/questionnaire.tsx +150 -0
- package/src/components/radio-group.tsx +35 -0
- package/src/components/resizable.tsx +41 -0
- package/src/components/rich-text-editor.tsx +604 -0
- package/src/components/scroll-area.tsx +47 -0
- package/src/components/select.tsx +189 -0
- package/src/components/separator.tsx +18 -0
- package/src/components/sheet.tsx +125 -0
- package/src/components/sidebar.tsx +693 -0
- package/src/components/skeleton.tsx +13 -0
- package/src/components/slider.tsx +51 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/sparkline.tsx +231 -0
- package/src/components/spinner.tsx +20 -0
- package/src/components/status-badge.tsx +114 -0
- package/src/components/switch.tsx +31 -0
- package/src/components/table.tsx +91 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/textarea.tsx +17 -0
- package/src/components/toast.tsx +222 -0
- package/src/components/toggle-group.tsx +85 -0
- package/src/components/toggle.tsx +42 -0
- package/src/components/tooltip.tsx +53 -0
- package/src/design-system/README.md +55 -0
- package/src/design-system/action-intents.ts +21 -0
- package/src/design-system/agent-manifest.ts +83 -0
- package/src/design-system/block-registry.ts +272 -0
- package/src/design-system/component-registry.ts +142 -0
- package/src/design-system/contracts.ts +69 -0
- package/src/design-system/defaults.ts +3 -0
- package/src/design-system/index.ts +29 -0
- package/src/design-system/mcp-server.ts +156 -0
- package/src/design-system/page-registry.ts +74 -0
- package/src/design-system/selection.ts +67 -0
- package/src/design-system/template-registry.ts +33 -0
- package/src/features/interface-topology/index.ts +14 -0
- package/src/features/interface-topology/interface-topology.types.ts +40 -0
- package/src/features/interface-topology/topology-control.tsx +18 -0
- package/src/features/interface-topology/topology-inspector.tsx +228 -0
- package/src/features/interface-topology/topology-marker.tsx +47 -0
- package/src/features/interface-topology/topology-region.tsx +44 -0
- package/src/features/interface-topology/use-interface-topology.ts +178 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-scroll-follow.ts +22 -0
- package/src/index.ts +11 -0
- package/src/layouts/README.md +65 -0
- package/src/layouts/agent-workspace/agent-activity-rail.tsx +101 -0
- package/src/layouts/agent-workspace/agent-workspace.tsx +27 -0
- package/src/layouts/agent-workspace/agent-workspace.types.ts +28 -0
- package/src/layouts/agent-workspace/index.ts +7 -0
- package/src/layouts/blog-header/blog-header-types.ts +25 -0
- package/src/layouts/blog-header/blog-header.tsx +172 -0
- package/src/layouts/blog-header/index.ts +2 -0
- package/src/layouts/documentation-workspace/README.md +8 -0
- package/src/layouts/documentation-workspace/documentation-workspace.tsx +34 -0
- package/src/layouts/documentation-workspace/index.ts +1 -0
- package/src/layouts/ecommerce-header/ecommerce-actions.tsx +89 -0
- package/src/layouts/ecommerce-header/ecommerce-announcement.tsx +43 -0
- package/src/layouts/ecommerce-header/ecommerce-drawer.tsx +97 -0
- package/src/layouts/ecommerce-header/ecommerce-header-types.ts +59 -0
- package/src/layouts/ecommerce-header/ecommerce-header.tsx +47 -0
- package/src/layouts/ecommerce-header/ecommerce-navigation.tsx +98 -0
- package/src/layouts/ecommerce-header/ecommerce-search.tsx +86 -0
- package/src/layouts/ecommerce-header/index.ts +2 -0
- package/src/layouts/main-workspace/index.ts +28 -0
- package/src/layouts/mdi/index.ts +1 -0
- package/src/layouts/mdi/mdi.tsx +20 -0
- package/src/layouts/mdi-main/index.ts +27 -0
- package/src/layouts/mdi-main/mdi-app-catalog.generated.ts +159 -0
- package/src/layouts/mdi-main/mdi-app-catalog.ts +76 -0
- package/src/layouts/mdi-main/mdi-app-switcher.tsx +107 -0
- package/src/layouts/mdi-main/mdi-feature-settings.tsx +135 -0
- package/src/layouts/mdi-main/mdi-global-search.tsx +192 -0
- package/src/layouts/mdi-main/mdi-main.tsx +241 -0
- package/src/layouts/mdi-main/mdi-notifications-menu.tsx +197 -0
- package/src/layouts/mdi-main/mdi-profile-menu.tsx +79 -0
- package/src/layouts/mdi-main/mdi-sidebar-navigation.tsx +226 -0
- package/src/layouts/mdi-main/mdi-sidebar.tsx +110 -0
- package/src/layouts/mdi-main/mdi-status-bar.tsx +26 -0
- package/src/layouts/mdi-main/mdi-top-menu-control.ts +4 -0
- package/src/layouts/mdi-main/mdi-top-menu.tsx +117 -0
- package/src/layouts/mdi-main/mdi-topology.tsx +297 -0
- package/src/layouts/mdi-main/mdi-tweak-panel.tsx +104 -0
- package/src/layouts/mdi-main/mdi-types.ts +131 -0
- package/src/layouts/mdi-main/use-mdi-features.ts +36 -0
- package/src/layouts/mdi-main/use-mdi-sidebar-state.ts +59 -0
- package/src/layouts/site-header/announcement-bar.tsx +55 -0
- package/src/layouts/site-header/category-strip.tsx +41 -0
- package/src/layouts/site-header/index.ts +16 -0
- package/src/layouts/site-header/main-navbar.tsx +179 -0
- package/src/layouts/site-header/mobile-drawer.tsx +140 -0
- package/src/layouts/site-header/site-header-types.ts +63 -0
- package/src/layouts/site-header/site-header.tsx +58 -0
- package/src/lib/random-id.ts +19 -0
- package/src/lib/utils.ts +6 -0
- package/src/providers/pdf-provider.ts +137 -0
- package/src/styles/globals.css +374 -0
- package/src/templates/README.md +12 -0
- package/src/templates/dashboard-01/components/app-sidebar.tsx +177 -0
- package/src/templates/dashboard-01/components/chart-area-interactive.tsx +276 -0
- package/src/templates/dashboard-01/components/data-table-cell-viewer.tsx +213 -0
- package/src/templates/dashboard-01/components/data-table.tsx +577 -0
- package/src/templates/dashboard-01/components/nav-documents.tsx +79 -0
- package/src/templates/dashboard-01/components/nav-main.tsx +55 -0
- package/src/templates/dashboard-01/components/nav-secondary.tsx +39 -0
- package/src/templates/dashboard-01/components/nav-user.tsx +97 -0
- package/src/templates/dashboard-01/components/section-cards.tsx +99 -0
- package/src/templates/dashboard-01/components/site-header.tsx +14 -0
- package/src/templates/dashboard-01/data.json +614 -0
- package/src/templates/dashboard-01/index.ts +7 -0
- package/src/templates/documentation-sidebar/app-sidebar.tsx +95 -0
- package/src/templates/documentation-sidebar/index.ts +2 -0
- package/src/templates/sidebar-07/components/app-sidebar.tsx +175 -0
- package/src/templates/sidebar-07/components/nav-main.tsx +64 -0
- package/src/templates/sidebar-07/components/nav-projects.tsx +79 -0
- package/src/templates/sidebar-07/components/nav-user.tsx +105 -0
- package/src/templates/sidebar-07/components/team-switcher.tsx +94 -0
- package/src/templates/sidebar-07/index.ts +1 -0
- package/src/templates/ui-page/index.ts +7 -0
- package/src/templates/ui-page/ui-browser-frame.tsx +27 -0
- package/src/templates/ui-page/ui-template-code.tsx +48 -0
- package/src/templates/ui-page/ui-template-header.tsx +87 -0
- package/src/templates/ui-page/ui-template-navigation.tsx +60 -0
- package/src/templates/ui-page/ui-template-page.tsx +97 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/theme-config.ts +30 -0
- package/src/theme/theme-provider.tsx +142 -0
- package/src/theme/theme-selector.tsx +75 -0
- package/src/tokens/index.ts +15 -0
- package/src/vite-env.d.ts +3 -0
package/README.md
CHANGED
|
@@ -1,3 +1,86 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ui
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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,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,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,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";
|