@superblocksteam/vite-plugin-file-sync 2.0.115-next.0 → 2.0.115-next.1
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/dist/ai-service/agent/middleware.d.ts.map +1 -1
- package/dist/ai-service/agent/middleware.js +19 -0
- package/dist/ai-service/agent/middleware.js.map +1 -1
- package/dist/ai-service/agent/prompts/api-prompts.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/api-prompts.js +13 -17
- package/dist/ai-service/agent/prompts/api-prompts.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +22 -3
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/subagents/coding/prompt-builder.d.ts +16 -0
- package/dist/ai-service/agent/subagents/coding/prompt-builder.d.ts.map +1 -0
- package/dist/ai-service/agent/subagents/coding/prompt-builder.js +30 -0
- package/dist/ai-service/agent/subagents/coding/prompt-builder.js.map +1 -0
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.d.ts +29 -0
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.d.ts.map +1 -0
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.js +108 -0
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.js.map +1 -0
- package/dist/ai-service/agent/subagents/types.d.ts +9 -1
- package/dist/ai-service/agent/subagents/types.d.ts.map +1 -1
- package/dist/ai-service/agent/subagents/types.js +8 -0
- package/dist/ai-service/agent/subagents/types.js.map +1 -1
- package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
- package/dist/ai-service/agent/tool-message-utils.js +5 -0
- package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/api-validation-orchestrator.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/api-validation-orchestrator.js +2 -1
- package/dist/ai-service/agent/tools/apis/api-validation-orchestrator.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts +6 -0
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js +138 -0
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js.map +1 -0
- package/dist/ai-service/agent/tools/build-finalize.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-finalize.js +39 -15
- package/dist/ai-service/agent/tools/build-finalize.js.map +1 -1
- package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +6 -3
- package/dist/ai-service/agent/tools/build-manage-checklist.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-manage-checklist.js +116 -49
- package/dist/ai-service/agent/tools/build-manage-checklist.js.map +1 -1
- package/dist/ai-service/agent/tools/index.d.ts +2 -0
- package/dist/ai-service/agent/tools/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/index.js +2 -0
- package/dist/ai-service/agent/tools/index.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +3 -0
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.js +330 -204
- package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +40 -2
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/access-control.d.ts +1 -1
- package/dist/ai-service/agent/tools2/access-control.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/access-control.js +15 -4
- package/dist/ai-service/agent/tools2/access-control.js.map +1 -1
- package/dist/ai-service/agent/tools2/registry.d.ts +3 -0
- package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/registry.js +50 -8
- package/dist/ai-service/agent/tools2/registry.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/ask-multi-choice.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/ask-multi-choice.js +3 -2
- package/dist/ai-service/agent/tools2/tools/ask-multi-choice.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/ask-searchable-dropdown.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/ask-searchable-dropdown.js +2 -1
- package/dist/ai-service/agent/tools2/tools/ask-searchable-dropdown.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/deploy-service.d.ts +16 -0
- package/dist/ai-service/agent/tools2/tools/deploy-service.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/deploy-service.js +135 -0
- package/dist/ai-service/agent/tools2/tools/deploy-service.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/download-attachments.d.ts +33 -0
- package/dist/ai-service/agent/tools2/tools/download-attachments.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/download-attachments.js +308 -0
- package/dist/ai-service/agent/tools2/tools/download-attachments.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +8 -4
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/git.d.ts +57 -1
- package/dist/ai-service/agent/tools2/tools/git.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/git.js +275 -8
- package/dist/ai-service/agent/tools2/tools/git.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/index.d.ts +2 -1
- package/dist/ai-service/agent/tools2/tools/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/index.js +2 -1
- package/dist/ai-service/agent/tools2/tools/index.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/list-attachments.d.ts +4 -1
- package/dist/ai-service/agent/tools2/tools/list-attachments.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/list-attachments.js +20 -4
- package/dist/ai-service/agent/tools2/tools/list-attachments.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/spawn-coding-subagents.d.ts +28 -0
- package/dist/ai-service/agent/tools2/tools/spawn-coding-subagents.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/spawn-coding-subagents.js +152 -0
- package/dist/ai-service/agent/tools2/tools/spawn-coding-subagents.js.map +1 -0
- package/dist/ai-service/agent/tools2/types.d.ts +1 -18
- package/dist/ai-service/agent/tools2/types.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/types.js.map +1 -1
- package/dist/ai-service/agent/utils.d.ts.map +1 -1
- package/dist/ai-service/agent/utils.js +22 -5
- package/dist/ai-service/agent/utils.js.map +1 -1
- package/dist/ai-service/app-interface/file-system-interface.d.ts +0 -4
- package/dist/ai-service/app-interface/file-system-interface.d.ts.map +1 -1
- package/dist/ai-service/app-interface/file-system-interface.js +0 -21
- package/dist/ai-service/app-interface/file-system-interface.js.map +1 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.d.ts +11 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.d.ts.map +1 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.js +29 -7
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.js.map +1 -1
- package/dist/ai-service/app-interface/shell.d.ts +6 -0
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +8 -0
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/app-skills/helpers.d.ts.map +1 -1
- package/dist/ai-service/app-skills/helpers.js +4 -2
- package/dist/ai-service/app-skills/helpers.js.map +1 -1
- package/dist/ai-service/attachments/store.d.ts +18 -2
- package/dist/ai-service/attachments/store.d.ts.map +1 -1
- package/dist/ai-service/attachments/store.js +102 -9
- package/dist/ai-service/attachments/store.js.map +1 -1
- package/dist/ai-service/attachments/uploaded-content-part.d.ts +5 -2
- package/dist/ai-service/attachments/uploaded-content-part.d.ts.map +1 -1
- package/dist/ai-service/attachments/uploaded-content-part.js +32 -0
- package/dist/ai-service/attachments/uploaded-content-part.js.map +1 -1
- package/dist/ai-service/chat/chat-session-store.d.ts +2 -2
- package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-session-store.js +45 -243
- package/dist/ai-service/chat/chat-session-store.js.map +1 -1
- package/dist/ai-service/chat/utils.d.ts +6 -0
- package/dist/ai-service/chat/utils.d.ts.map +1 -1
- package/dist/ai-service/chat/utils.js +47 -0
- package/dist/ai-service/chat/utils.js.map +1 -1
- package/dist/ai-service/features.d.ts +4 -0
- package/dist/ai-service/features.d.ts.map +1 -1
- package/dist/ai-service/features.js +4 -0
- package/dist/ai-service/features.js.map +1 -1
- package/dist/ai-service/index.d.ts +36 -11
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +228 -68
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/judge/tools/playwright-action.d.ts +1 -1
- package/dist/ai-service/llm/client.d.ts.map +1 -1
- package/dist/ai-service/llm/client.js +4 -2
- package/dist/ai-service/llm/client.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/retry-notification.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/retry-notification.js +4 -2
- package/dist/ai-service/llm/stream/observers/retry-notification.js.map +1 -1
- package/dist/ai-service/prompt-builder-service/classifiers/prompt-interpret-task.d.ts.map +1 -1
- package/dist/ai-service/prompt-builder-service/classifiers/prompt-interpret-task.js +1 -16
- package/dist/ai-service/prompt-builder-service/classifiers/prompt-interpret-task.js.map +1 -1
- package/dist/ai-service/prompt-builder-service/types.d.ts +2 -13
- package/dist/ai-service/prompt-builder-service/types.d.ts.map +1 -1
- package/dist/ai-service/prompt-builder-service/types.js.map +1 -1
- package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/_registry.generated.js +6 -0
- package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-migration/references/focused-debug.generated.d.ts +2 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/focused-debug.generated.d.ts.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/focused-debug.generated.js +58 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/focused-debug.generated.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/yaml-block-mapping.generated.d.ts +2 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/yaml-block-mapping.generated.d.ts.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/yaml-block-mapping.generated.js +107 -0
- package/dist/ai-service/skills/system/superblocks-migration/references/yaml-block-mapping.generated.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts +2 -0
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js +137 -0
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js.map +1 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts +26 -13
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js +12 -7
- package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +51 -27
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/awaiting-user.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/awaiting-user.js +15 -6
- package/dist/ai-service/state-machine/handlers/awaiting-user.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/idle.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/idle.js +4 -2
- package/dist/ai-service/state-machine/handlers/idle.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +48 -15
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/post-processing.d.ts +1 -1
- package/dist/ai-service/state-machine/handlers/post-processing.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/post-processing.js +23 -151
- package/dist/ai-service/state-machine/handlers/post-processing.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js +16 -8
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/change-info.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/change-info.js +2 -15
- package/dist/ai-service/state-machine/helpers/change-info.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/peer.d.ts +3 -3
- package/dist/ai-service/state-machine/helpers/peer.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/peer.js +9 -23
- package/dist/ai-service/state-machine/helpers/peer.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +52 -0
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -0
- package/dist/ai-service/state-machine/helpers/stable-peer.js +141 -0
- package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -0
- package/dist/ai-service/state-machine/mocks.d.ts.map +1 -1
- package/dist/ai-service/state-machine/mocks.js +0 -17
- package/dist/ai-service/state-machine/mocks.js.map +1 -1
- package/dist/ai-service/types.d.ts +2 -21
- package/dist/ai-service/types.d.ts.map +1 -1
- package/dist/ai-service/types.js.map +1 -1
- package/dist/ai-service/util/archive-extractors.d.ts +52 -0
- package/dist/ai-service/util/archive-extractors.d.ts.map +1 -0
- package/dist/ai-service/util/archive-extractors.js +278 -0
- package/dist/ai-service/util/archive-extractors.js.map +1 -0
- package/dist/codegen.d.ts +1 -22
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +1 -117
- package/dist/codegen.js.map +1 -1
- package/dist/components-manager.d.ts +0 -6
- package/dist/components-manager.d.ts.map +1 -1
- package/dist/components-manager.js +0 -27
- package/dist/components-manager.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +47 -94
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-manager.d.ts +2 -15
- package/dist/file-system-manager.d.ts.map +1 -1
- package/dist/file-system-manager.js +16 -128
- package/dist/file-system-manager.js.map +1 -1
- package/dist/git-service/live-branch.d.ts.map +1 -1
- package/dist/git-service/live-branch.js +15 -1
- package/dist/git-service/live-branch.js.map +1 -1
- package/dist/lock-service/index.d.ts.map +1 -1
- package/dist/lock-service/index.js +8 -5
- package/dist/lock-service/index.js.map +1 -1
- package/dist/migration/get-fullstack-template-dir.d.ts +37 -0
- package/dist/migration/get-fullstack-template-dir.d.ts.map +1 -0
- package/dist/migration/get-fullstack-template-dir.js +99 -0
- package/dist/migration/get-fullstack-template-dir.js.map +1 -0
- package/dist/migration/migration-checklist.d.ts +60 -0
- package/dist/migration/migration-checklist.d.ts.map +1 -0
- package/dist/migration/migration-checklist.js +230 -0
- package/dist/migration/migration-checklist.js.map +1 -0
- package/dist/migration/migration-routes.d.ts +23 -0
- package/dist/migration/migration-routes.d.ts.map +1 -0
- package/dist/migration/migration-routes.js +533 -0
- package/dist/migration/migration-routes.js.map +1 -0
- package/dist/migration/restructure.d.ts +76 -0
- package/dist/migration/restructure.d.ts.map +1 -0
- package/dist/migration/restructure.js +446 -0
- package/dist/migration/restructure.js.map +1 -0
- package/dist/migration/translation-prompt.d.ts +22 -0
- package/dist/migration/translation-prompt.d.ts.map +1 -0
- package/dist/migration/translation-prompt.js +27 -0
- package/dist/migration/translation-prompt.js.map +1 -0
- package/dist/migration-templates/app-fullstack/client/App.tsx +17 -0
- package/dist/migration-templates/app-fullstack/client/components/common/sonner.tsx +20 -0
- package/dist/migration-templates/app-fullstack/client/components/hooks/use-active-page.ts +73 -0
- package/dist/migration-templates/app-fullstack/client/components/hooks/use-mobile.ts +21 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/accordion.tsx +197 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/area-chart.tsx +432 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/aspect-ratio.tsx +45 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/avatar.tsx +117 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/badge.tsx +143 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/bar-chart.tsx +390 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/breadcrumb.tsx +259 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/button.tsx +182 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/calendar.tsx +300 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/card.tsx +129 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/chart.tsx +403 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/chat.tsx +2303 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/checkbox.tsx +97 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/code-block-content.tsx +66 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/date-range-picker.tsx +396 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/dialog.tsx +223 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/dropdown-menu.tsx +284 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/file-dropzone.tsx +395 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/file-input.tsx +166 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/hover-card.tsx +162 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/icon.tsx +133 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/image.tsx +68 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/input.tsx +219 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/label.tsx +55 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/line-chart.tsx +380 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/link.tsx +139 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/navigation-menu.tsx +345 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/pagination.tsx +192 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/pie-chart.tsx +295 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/popover.tsx +162 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/progress.tsx +69 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/radar-chart.tsx +386 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/radial-chart.tsx +402 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/scroll-area.tsx +86 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/select.tsx +229 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/separator.tsx +62 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/sheet.tsx +234 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/sidebar.tsx +974 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/skeleton.tsx +13 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/slider.tsx +198 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/switch.tsx +95 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/table.tsx +145 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/tabs.tsx +87 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/textarea.tsx +148 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/toggle-group.tsx +225 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/toggle.tsx +150 -0
- package/dist/migration-templates/app-fullstack/client/components/ui/tooltip.tsx +175 -0
- package/dist/migration-templates/app-fullstack/client/hooks/useApi.ts +25 -0
- package/dist/migration-templates/app-fullstack/client/hooks/useApiData.ts +10 -0
- package/dist/migration-templates/app-fullstack/client/index.css +131 -0
- package/dist/migration-templates/app-fullstack/client/lib/executeApi.ts +9 -0
- package/dist/migration-templates/app-fullstack/client/lib/utils.ts +6 -0
- package/dist/migration-templates/app-fullstack/client/pages/Page1/index.tsx +3 -0
- package/dist/migration-templates/app-fullstack/client/root.tsx +17 -0
- package/dist/migration-templates/app-fullstack/client/router.tsx +48 -0
- package/dist/migration-templates/app-fullstack/client/types/node.d.ts +9 -0
- package/dist/migration-templates/app-fullstack/components.json +21 -0
- package/dist/migration-templates/app-fullstack/eslint.config.js +108 -0
- package/dist/migration-templates/app-fullstack/package.json +89 -0
- package/dist/migration-templates/app-fullstack/server/.gitkeep +0 -0
- package/dist/migration-templates/app-fullstack/server/apis/index.ts +22 -0
- package/dist/migration-templates/app-fullstack/server/index.ts +2 -0
- package/dist/migration-templates/app-fullstack/tsconfig.client.json +27 -0
- package/dist/migration-templates/app-fullstack/tsconfig.eslint.json +8 -0
- package/dist/migration-templates/app-fullstack/tsconfig.json +25 -0
- package/dist/migration-templates/app-fullstack/tsconfig.server.json +13 -0
- package/dist/migration-templates/app-fullstack/vite.config.ts +17 -0
- package/dist/parsing/imports.d.ts +0 -16
- package/dist/parsing/imports.d.ts.map +1 -1
- package/dist/parsing/imports.js +0 -68
- package/dist/parsing/imports.js.map +1 -1
- package/dist/parsing/jsx.d.ts +0 -2
- package/dist/parsing/jsx.d.ts.map +1 -1
- package/dist/parsing/jsx.js +0 -62
- package/dist/parsing/jsx.js.map +1 -1
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +109 -32
- package/dist/socket-manager.js.map +1 -1
- package/dist/source-tracker.d.ts +7 -85
- package/dist/source-tracker.d.ts.map +1 -1
- package/dist/source-tracker.js +19 -695
- package/dist/source-tracker.js.map +1 -1
- package/dist/sync-service/index.d.ts +79 -2
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +139 -10
- package/dist/sync-service/index.js.map +1 -1
- package/package.json +11 -9
- package/dist/ai-service/artifacts/bolt.d.ts +0 -8
- package/dist/ai-service/artifacts/bolt.d.ts.map +0 -1
- package/dist/ai-service/artifacts/bolt.js +0 -92
- package/dist/ai-service/artifacts/bolt.js.map +0 -1
- package/dist/ai-service/result-buffer/bolt.d.ts +0 -18
- package/dist/ai-service/result-buffer/bolt.d.ts.map +0 -1
- package/dist/ai-service/result-buffer/bolt.js +0 -100
- package/dist/ai-service/result-buffer/bolt.js.map +0 -1
- package/dist/ai-service/result-buffer/shared.d.ts +0 -17
- package/dist/ai-service/result-buffer/shared.d.ts.map +0 -1
- package/dist/ai-service/result-buffer/shared.js +0 -7
- package/dist/ai-service/result-buffer/shared.js.map +0 -1
- package/dist/inject-no-select.d.ts +0 -15
- package/dist/inject-no-select.d.ts.map +0 -1
- package/dist/inject-no-select.js +0 -175
- package/dist/inject-no-select.js.map +0 -1
|
@@ -0,0 +1,2303 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
4
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
5
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
6
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
7
|
+
import type { UIMessage } from "ai";
|
|
8
|
+
import type { ChatStatus } from "ai";
|
|
9
|
+
import type { ToolUIPart } from "ai";
|
|
10
|
+
import useEmblaCarousel, {
|
|
11
|
+
type UseEmblaCarouselType,
|
|
12
|
+
} from "embla-carousel-react";
|
|
13
|
+
import {
|
|
14
|
+
ArrowDownIcon,
|
|
15
|
+
CheckIcon,
|
|
16
|
+
CopyIcon,
|
|
17
|
+
SendIcon,
|
|
18
|
+
Loader2Icon,
|
|
19
|
+
SquareIcon,
|
|
20
|
+
XIcon,
|
|
21
|
+
} from "lucide-react";
|
|
22
|
+
import {
|
|
23
|
+
ChevronRightIcon,
|
|
24
|
+
ChevronLeftIcon,
|
|
25
|
+
ArrowLeft,
|
|
26
|
+
ArrowRight,
|
|
27
|
+
BrainIcon,
|
|
28
|
+
ChevronDownIcon,
|
|
29
|
+
BookIcon,
|
|
30
|
+
SearchIcon,
|
|
31
|
+
WrenchIcon,
|
|
32
|
+
CheckCircleIcon,
|
|
33
|
+
CircleIcon,
|
|
34
|
+
ClockIcon,
|
|
35
|
+
XCircleIcon,
|
|
36
|
+
ArrowLeftIcon,
|
|
37
|
+
ArrowRightIcon,
|
|
38
|
+
} from "lucide-react";
|
|
39
|
+
import type React from "react";
|
|
40
|
+
import type {
|
|
41
|
+
ComponentProps,
|
|
42
|
+
HTMLAttributes,
|
|
43
|
+
KeyboardEventHandler,
|
|
44
|
+
ComponentPropsWithRef,
|
|
45
|
+
ReactNode,
|
|
46
|
+
} from "react";
|
|
47
|
+
import {
|
|
48
|
+
useCallback,
|
|
49
|
+
Children,
|
|
50
|
+
useEffect,
|
|
51
|
+
useState,
|
|
52
|
+
useRef,
|
|
53
|
+
createContext,
|
|
54
|
+
useContext,
|
|
55
|
+
useMemo,
|
|
56
|
+
memo,
|
|
57
|
+
lazy,
|
|
58
|
+
Suspense,
|
|
59
|
+
} from "react";
|
|
60
|
+
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
61
|
+
|
|
62
|
+
import { registerComponent } from "@superblocksteam/library";
|
|
63
|
+
import {
|
|
64
|
+
Prop,
|
|
65
|
+
Section,
|
|
66
|
+
PropsCategory,
|
|
67
|
+
type PropertiesPanelDefinition,
|
|
68
|
+
type EditorConfig,
|
|
69
|
+
} from "@superblocksteam/library";
|
|
70
|
+
|
|
71
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
72
|
+
import { Badge } from "@/components/ui/badge";
|
|
73
|
+
import { Button } from "@/components/ui/button";
|
|
74
|
+
import {
|
|
75
|
+
Select,
|
|
76
|
+
SelectContent,
|
|
77
|
+
SelectItem,
|
|
78
|
+
SelectTrigger,
|
|
79
|
+
SelectValue,
|
|
80
|
+
} from "@/components/ui/select";
|
|
81
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
82
|
+
import { cn } from "@/lib/utils";
|
|
83
|
+
// Lazy load syntax highlighter to avoid loading large bundle when Chat is imported but not rendered
|
|
84
|
+
// We intentionally diverge from shadcn here so we can increase loading performance in edit mode
|
|
85
|
+
const LazyCodeBlockContent = lazy(() => import("./code-block-content"));
|
|
86
|
+
|
|
87
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
88
|
+
import { Streamdown } from "streamdown";
|
|
89
|
+
|
|
90
|
+
// ===================================
|
|
91
|
+
// COLLAPSIBLE COMPONENTS
|
|
92
|
+
// ===================================
|
|
93
|
+
|
|
94
|
+
function Collapsible({
|
|
95
|
+
...props
|
|
96
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
97
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function CollapsibleTrigger({
|
|
101
|
+
...props
|
|
102
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
103
|
+
return (
|
|
104
|
+
<CollapsiblePrimitive.CollapsibleTrigger
|
|
105
|
+
data-slot="collapsible-trigger"
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function CollapsibleContent({
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
114
|
+
return (
|
|
115
|
+
<CollapsiblePrimitive.CollapsibleContent
|
|
116
|
+
data-slot="collapsible-content"
|
|
117
|
+
{...props}
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ===================================
|
|
123
|
+
// TOOLTIP COMPONENTS
|
|
124
|
+
// ===================================
|
|
125
|
+
|
|
126
|
+
function TooltipProvider({
|
|
127
|
+
delayDuration = 0,
|
|
128
|
+
...props
|
|
129
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
130
|
+
return (
|
|
131
|
+
<TooltipPrimitive.Provider
|
|
132
|
+
data-slot="tooltip-provider"
|
|
133
|
+
delayDuration={delayDuration}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function Tooltip({
|
|
140
|
+
...props
|
|
141
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
142
|
+
return (
|
|
143
|
+
<TooltipProvider>
|
|
144
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
145
|
+
</TooltipProvider>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function TooltipTrigger({
|
|
150
|
+
...props
|
|
151
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
152
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function TooltipContent({
|
|
156
|
+
className,
|
|
157
|
+
sideOffset = 0,
|
|
158
|
+
children,
|
|
159
|
+
...props
|
|
160
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
161
|
+
return (
|
|
162
|
+
<TooltipPrimitive.Portal>
|
|
163
|
+
<TooltipPrimitive.Content
|
|
164
|
+
data-slot="tooltip-content"
|
|
165
|
+
sideOffset={sideOffset}
|
|
166
|
+
className={cn(
|
|
167
|
+
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
168
|
+
className,
|
|
169
|
+
)}
|
|
170
|
+
{...props}
|
|
171
|
+
>
|
|
172
|
+
{children}
|
|
173
|
+
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
174
|
+
</TooltipPrimitive.Content>
|
|
175
|
+
</TooltipPrimitive.Portal>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ===================================
|
|
180
|
+
// HOVER CARD COMPONENTS
|
|
181
|
+
// ===================================
|
|
182
|
+
|
|
183
|
+
function HoverCard({
|
|
184
|
+
...props
|
|
185
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
|
186
|
+
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function HoverCardTrigger({
|
|
190
|
+
...props
|
|
191
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
|
192
|
+
return (
|
|
193
|
+
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function HoverCardContent({
|
|
198
|
+
className,
|
|
199
|
+
align = "center",
|
|
200
|
+
sideOffset = 4,
|
|
201
|
+
...props
|
|
202
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
|
203
|
+
return (
|
|
204
|
+
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
|
205
|
+
<HoverCardPrimitive.Content
|
|
206
|
+
data-slot="hover-card-content"
|
|
207
|
+
align={align}
|
|
208
|
+
sideOffset={sideOffset}
|
|
209
|
+
className={cn(
|
|
210
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
211
|
+
className,
|
|
212
|
+
)}
|
|
213
|
+
{...props}
|
|
214
|
+
/>
|
|
215
|
+
</HoverCardPrimitive.Portal>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ===================================
|
|
220
|
+
// SCROLL AREA COMPONENTS
|
|
221
|
+
// ===================================
|
|
222
|
+
|
|
223
|
+
function ScrollArea({
|
|
224
|
+
className,
|
|
225
|
+
children,
|
|
226
|
+
...props
|
|
227
|
+
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
|
228
|
+
return (
|
|
229
|
+
<ScrollAreaPrimitive.Root
|
|
230
|
+
data-slot="scroll-area"
|
|
231
|
+
className={cn("relative", className)}
|
|
232
|
+
{...props}
|
|
233
|
+
>
|
|
234
|
+
<ScrollAreaPrimitive.Viewport
|
|
235
|
+
data-slot="scroll-area-viewport"
|
|
236
|
+
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
|
237
|
+
>
|
|
238
|
+
{children}
|
|
239
|
+
</ScrollAreaPrimitive.Viewport>
|
|
240
|
+
<ScrollBar />
|
|
241
|
+
<ScrollAreaPrimitive.Corner />
|
|
242
|
+
</ScrollAreaPrimitive.Root>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function ScrollBar({
|
|
247
|
+
className,
|
|
248
|
+
orientation = "vertical",
|
|
249
|
+
...props
|
|
250
|
+
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
|
251
|
+
return (
|
|
252
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
253
|
+
data-slot="scroll-area-scrollbar"
|
|
254
|
+
orientation={orientation}
|
|
255
|
+
className={cn(
|
|
256
|
+
"flex touch-none p-px transition-colors select-none",
|
|
257
|
+
orientation === "vertical" &&
|
|
258
|
+
"h-full w-2.5 border-l border-l-transparent",
|
|
259
|
+
orientation === "horizontal" &&
|
|
260
|
+
"h-2.5 flex-col border-t border-t-transparent",
|
|
261
|
+
className,
|
|
262
|
+
)}
|
|
263
|
+
{...props}
|
|
264
|
+
>
|
|
265
|
+
<ScrollAreaPrimitive.ScrollAreaThumb
|
|
266
|
+
data-slot="scroll-area-thumb"
|
|
267
|
+
className="bg-border relative flex-1 rounded-full"
|
|
268
|
+
/>
|
|
269
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ===================================
|
|
274
|
+
// CAROUSEL COMPONENTS
|
|
275
|
+
// ===================================
|
|
276
|
+
|
|
277
|
+
type CarouselApi = UseEmblaCarouselType[1];
|
|
278
|
+
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
|
279
|
+
type CarouselOptions = UseCarouselParameters[0];
|
|
280
|
+
type CarouselPlugin = UseCarouselParameters[1];
|
|
281
|
+
|
|
282
|
+
interface CarouselProps {
|
|
283
|
+
opts?: CarouselOptions;
|
|
284
|
+
plugins?: CarouselPlugin;
|
|
285
|
+
orientation?: "horizontal" | "vertical";
|
|
286
|
+
setApi?: (api: CarouselApi) => void;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
type CarouselContextProps = {
|
|
290
|
+
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
|
|
291
|
+
api: ReturnType<typeof useEmblaCarousel>[1];
|
|
292
|
+
scrollPrev: () => void;
|
|
293
|
+
scrollNext: () => void;
|
|
294
|
+
canScrollPrev: boolean;
|
|
295
|
+
canScrollNext: boolean;
|
|
296
|
+
} & CarouselProps;
|
|
297
|
+
|
|
298
|
+
const CarouselContext = createContext<CarouselContextProps | null>(null);
|
|
299
|
+
|
|
300
|
+
function useCarousel() {
|
|
301
|
+
const context = useContext(CarouselContext);
|
|
302
|
+
|
|
303
|
+
if (!context) {
|
|
304
|
+
throw new Error("useCarousel must be used within a <Carousel />");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return context;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function Carousel({
|
|
311
|
+
orientation = "horizontal",
|
|
312
|
+
opts,
|
|
313
|
+
setApi,
|
|
314
|
+
plugins,
|
|
315
|
+
className,
|
|
316
|
+
children,
|
|
317
|
+
...props
|
|
318
|
+
}: React.ComponentProps<"div"> & CarouselProps) {
|
|
319
|
+
const [carouselRef, api] = useEmblaCarousel(
|
|
320
|
+
{
|
|
321
|
+
...opts,
|
|
322
|
+
axis: orientation === "horizontal" ? "x" : "y",
|
|
323
|
+
},
|
|
324
|
+
plugins,
|
|
325
|
+
);
|
|
326
|
+
const [canScrollPrev, setCanScrollPrev] = useState(false);
|
|
327
|
+
const [canScrollNext, setCanScrollNext] = useState(false);
|
|
328
|
+
const isInitializedRef = useRef(false);
|
|
329
|
+
|
|
330
|
+
const scrollPrev = useCallback(() => {
|
|
331
|
+
api?.scrollPrev();
|
|
332
|
+
}, [api]);
|
|
333
|
+
|
|
334
|
+
const scrollNext = useCallback(() => {
|
|
335
|
+
api?.scrollNext();
|
|
336
|
+
}, [api]);
|
|
337
|
+
|
|
338
|
+
const handleKeyDown = useCallback(
|
|
339
|
+
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
340
|
+
if (event.key === "ArrowLeft") {
|
|
341
|
+
event.preventDefault();
|
|
342
|
+
scrollPrev();
|
|
343
|
+
} else if (event.key === "ArrowRight") {
|
|
344
|
+
event.preventDefault();
|
|
345
|
+
scrollNext();
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
[scrollPrev, scrollNext],
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (!api || !setApi) return;
|
|
353
|
+
setApi(api);
|
|
354
|
+
}, [api, setApi]);
|
|
355
|
+
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
if (!api) return;
|
|
358
|
+
|
|
359
|
+
const handleSelect = () => {
|
|
360
|
+
setCanScrollPrev(api.canScrollPrev());
|
|
361
|
+
setCanScrollNext(api.canScrollNext());
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// Initialize state when API first becomes available
|
|
365
|
+
if (!isInitializedRef.current) {
|
|
366
|
+
handleSelect();
|
|
367
|
+
isInitializedRef.current = true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
api.on("reInit", handleSelect);
|
|
371
|
+
api.on("select", handleSelect);
|
|
372
|
+
|
|
373
|
+
return () => {
|
|
374
|
+
api.off("reInit", handleSelect);
|
|
375
|
+
api.off("select", handleSelect);
|
|
376
|
+
};
|
|
377
|
+
}, [api]);
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
<CarouselContext.Provider
|
|
381
|
+
value={{
|
|
382
|
+
carouselRef,
|
|
383
|
+
api: api,
|
|
384
|
+
opts,
|
|
385
|
+
orientation:
|
|
386
|
+
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
|
387
|
+
scrollPrev,
|
|
388
|
+
scrollNext,
|
|
389
|
+
canScrollPrev,
|
|
390
|
+
canScrollNext,
|
|
391
|
+
}}
|
|
392
|
+
>
|
|
393
|
+
<div
|
|
394
|
+
onKeyDownCapture={handleKeyDown}
|
|
395
|
+
className={cn("relative", className)}
|
|
396
|
+
role="region"
|
|
397
|
+
aria-roledescription="carousel"
|
|
398
|
+
data-slot="carousel"
|
|
399
|
+
{...props}
|
|
400
|
+
>
|
|
401
|
+
{children}
|
|
402
|
+
</div>
|
|
403
|
+
</CarouselContext.Provider>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
408
|
+
const { carouselRef, orientation } = useCarousel();
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<div
|
|
412
|
+
ref={carouselRef}
|
|
413
|
+
className="overflow-hidden"
|
|
414
|
+
data-slot="carousel-content"
|
|
415
|
+
>
|
|
416
|
+
<div
|
|
417
|
+
className={cn(
|
|
418
|
+
"flex",
|
|
419
|
+
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
|
420
|
+
className,
|
|
421
|
+
)}
|
|
422
|
+
{...props}
|
|
423
|
+
/>
|
|
424
|
+
</div>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
429
|
+
const { orientation } = useCarousel();
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<div
|
|
433
|
+
role="group"
|
|
434
|
+
aria-roledescription="slide"
|
|
435
|
+
data-slot="carousel-item"
|
|
436
|
+
className={cn(
|
|
437
|
+
"min-w-0 shrink-0 grow-0 basis-full",
|
|
438
|
+
orientation === "horizontal" ? "pl-4" : "pt-4",
|
|
439
|
+
className,
|
|
440
|
+
)}
|
|
441
|
+
{...props}
|
|
442
|
+
/>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function CarouselPrevious({
|
|
447
|
+
className,
|
|
448
|
+
variant = "outline",
|
|
449
|
+
size = "icon",
|
|
450
|
+
...props
|
|
451
|
+
}: React.ComponentProps<typeof Button>) {
|
|
452
|
+
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
453
|
+
|
|
454
|
+
return (
|
|
455
|
+
<Button
|
|
456
|
+
data-slot="carousel-previous"
|
|
457
|
+
variant={variant}
|
|
458
|
+
size={size}
|
|
459
|
+
className={cn(
|
|
460
|
+
"absolute size-8 rounded-full",
|
|
461
|
+
orientation === "horizontal"
|
|
462
|
+
? "top-1/2 -left-12 -translate-y-1/2"
|
|
463
|
+
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
464
|
+
className,
|
|
465
|
+
)}
|
|
466
|
+
disabled={!canScrollPrev}
|
|
467
|
+
onClick={scrollPrev}
|
|
468
|
+
{...props}
|
|
469
|
+
>
|
|
470
|
+
<ArrowLeft />
|
|
471
|
+
<span className="sr-only">Previous slide</span>
|
|
472
|
+
</Button>
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function CarouselNext({
|
|
477
|
+
className,
|
|
478
|
+
variant = "outline",
|
|
479
|
+
size = "icon",
|
|
480
|
+
...props
|
|
481
|
+
}: React.ComponentProps<typeof Button>) {
|
|
482
|
+
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
<Button
|
|
486
|
+
data-slot="carousel-next"
|
|
487
|
+
variant={variant}
|
|
488
|
+
size={size}
|
|
489
|
+
className={cn(
|
|
490
|
+
"absolute size-8 rounded-full",
|
|
491
|
+
orientation === "horizontal"
|
|
492
|
+
? "top-1/2 -right-12 -translate-y-1/2"
|
|
493
|
+
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
494
|
+
className,
|
|
495
|
+
)}
|
|
496
|
+
disabled={!canScrollNext}
|
|
497
|
+
onClick={scrollNext}
|
|
498
|
+
{...props}
|
|
499
|
+
>
|
|
500
|
+
<ArrowRight />
|
|
501
|
+
<span className="sr-only">Next slide</span>
|
|
502
|
+
</Button>
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ===================================
|
|
507
|
+
// CONVERSATION COMPONENTS
|
|
508
|
+
// ===================================
|
|
509
|
+
|
|
510
|
+
type ConversationProps = ComponentProps<typeof StickToBottom>;
|
|
511
|
+
|
|
512
|
+
const Conversation = ({ className, ...props }: ConversationProps) => (
|
|
513
|
+
<StickToBottom
|
|
514
|
+
className={cn("relative flex-1 overflow-y-auto", className)}
|
|
515
|
+
initial="smooth"
|
|
516
|
+
resize="smooth"
|
|
517
|
+
role="log"
|
|
518
|
+
{...props}
|
|
519
|
+
/>
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
|
|
523
|
+
|
|
524
|
+
const ConversationContent = ({
|
|
525
|
+
className,
|
|
526
|
+
...props
|
|
527
|
+
}: ConversationContentProps) => (
|
|
528
|
+
<StickToBottom.Content className={cn("p-4", className)} {...props} />
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
type ConversationScrollButtonProps = ComponentProps<typeof Button>;
|
|
532
|
+
|
|
533
|
+
const ConversationScrollButton = ({
|
|
534
|
+
className,
|
|
535
|
+
...props
|
|
536
|
+
}: ConversationScrollButtonProps) => {
|
|
537
|
+
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
538
|
+
|
|
539
|
+
const handleScrollToBottom = useCallback(() => {
|
|
540
|
+
void scrollToBottom();
|
|
541
|
+
}, [scrollToBottom]);
|
|
542
|
+
|
|
543
|
+
return (
|
|
544
|
+
!isAtBottom && (
|
|
545
|
+
<Button
|
|
546
|
+
className={cn(
|
|
547
|
+
"absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full",
|
|
548
|
+
className,
|
|
549
|
+
)}
|
|
550
|
+
onClick={handleScrollToBottom}
|
|
551
|
+
size="icon"
|
|
552
|
+
type="button"
|
|
553
|
+
variant="outline"
|
|
554
|
+
{...props}
|
|
555
|
+
>
|
|
556
|
+
<ArrowDownIcon className="size-4" />
|
|
557
|
+
</Button>
|
|
558
|
+
)
|
|
559
|
+
);
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// ===================================
|
|
563
|
+
// MESSAGE COMPONENTS
|
|
564
|
+
// ===================================
|
|
565
|
+
|
|
566
|
+
type MessageProps = HTMLAttributes<HTMLDivElement> & {
|
|
567
|
+
from: UIMessage["role"];
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const Message = ({ className, from, ...props }: MessageProps) => (
|
|
571
|
+
<div
|
|
572
|
+
className={cn(
|
|
573
|
+
"group flex w-full items-end justify-end gap-2 py-4",
|
|
574
|
+
from === "user" ? "is-user" : "is-assistant flex-row-reverse justify-end",
|
|
575
|
+
"[&>div]:max-w-[80%]",
|
|
576
|
+
className,
|
|
577
|
+
)}
|
|
578
|
+
{...props}
|
|
579
|
+
/>
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
type MessageContentProps = HTMLAttributes<HTMLDivElement>;
|
|
583
|
+
|
|
584
|
+
const MessageContent = ({
|
|
585
|
+
children,
|
|
586
|
+
className,
|
|
587
|
+
...props
|
|
588
|
+
}: MessageContentProps) => (
|
|
589
|
+
<div
|
|
590
|
+
className={cn(
|
|
591
|
+
"flex flex-col gap-2 overflow-hidden rounded-lg px-4 py-3 text-foreground text-sm",
|
|
592
|
+
"group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground",
|
|
593
|
+
"group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground",
|
|
594
|
+
"is-user:dark",
|
|
595
|
+
className,
|
|
596
|
+
)}
|
|
597
|
+
{...props}
|
|
598
|
+
>
|
|
599
|
+
{children}
|
|
600
|
+
</div>
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
type MessageAvatarProps = ComponentProps<typeof Avatar> & {
|
|
604
|
+
src: string;
|
|
605
|
+
name?: string;
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const MessageAvatar = ({
|
|
609
|
+
src,
|
|
610
|
+
name,
|
|
611
|
+
className,
|
|
612
|
+
...props
|
|
613
|
+
}: MessageAvatarProps) => (
|
|
614
|
+
<Avatar className={cn("size-8 ring-1 ring-border", className)} {...props}>
|
|
615
|
+
<AvatarImage alt="" className="mt-0 mb-0" src={src} />
|
|
616
|
+
<AvatarFallback>{name?.slice(0, 2) || "ME"}</AvatarFallback>
|
|
617
|
+
</Avatar>
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
// ===================================
|
|
621
|
+
// PROMPT INPUT COMPONENTS
|
|
622
|
+
// ===================================
|
|
623
|
+
|
|
624
|
+
type PromptInputProps = HTMLAttributes<HTMLFormElement>;
|
|
625
|
+
|
|
626
|
+
const PromptInput = ({ className, ...props }: PromptInputProps) => (
|
|
627
|
+
<form
|
|
628
|
+
className={cn(
|
|
629
|
+
"w-full divide-y overflow-hidden rounded-xl border bg-background shadow-sm",
|
|
630
|
+
className,
|
|
631
|
+
)}
|
|
632
|
+
{...props}
|
|
633
|
+
/>
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
type PromptInputTextareaProps = ComponentProps<typeof Textarea> & {
|
|
637
|
+
minHeight?: number;
|
|
638
|
+
maxHeight?: number;
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const PromptInputTextarea = ({
|
|
642
|
+
onChange,
|
|
643
|
+
className,
|
|
644
|
+
placeholder = "What would you like to know?",
|
|
645
|
+
...props
|
|
646
|
+
}: PromptInputTextareaProps) => {
|
|
647
|
+
const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
|
|
648
|
+
if (e.key === "Enter") {
|
|
649
|
+
// Don't submit if IME composition is in progress
|
|
650
|
+
if (e.nativeEvent.isComposing) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (e.shiftKey) {
|
|
655
|
+
// Allow newline
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Submit on Enter (without Shift)
|
|
660
|
+
e.preventDefault();
|
|
661
|
+
const form = e.currentTarget.form;
|
|
662
|
+
if (form) {
|
|
663
|
+
form.requestSubmit();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
return (
|
|
669
|
+
<Textarea
|
|
670
|
+
className={cn(
|
|
671
|
+
"w-full resize-none rounded-none border-none p-3 shadow-none outline-none ring-0",
|
|
672
|
+
"field-sizing-content max-h-[6lh] bg-transparent dark:bg-transparent",
|
|
673
|
+
"focus-visible:ring-0",
|
|
674
|
+
className,
|
|
675
|
+
)}
|
|
676
|
+
name="message"
|
|
677
|
+
onChange={(e) => {
|
|
678
|
+
onChange?.(e);
|
|
679
|
+
}}
|
|
680
|
+
onKeyDown={handleKeyDown}
|
|
681
|
+
placeholder={placeholder}
|
|
682
|
+
{...props}
|
|
683
|
+
/>
|
|
684
|
+
);
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
type PromptInputToolbarProps = HTMLAttributes<HTMLDivElement>;
|
|
688
|
+
|
|
689
|
+
const PromptInputToolbar = ({
|
|
690
|
+
className,
|
|
691
|
+
...props
|
|
692
|
+
}: PromptInputToolbarProps) => (
|
|
693
|
+
<div
|
|
694
|
+
className={cn("flex items-center justify-between p-1", className)}
|
|
695
|
+
{...props}
|
|
696
|
+
/>
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;
|
|
700
|
+
|
|
701
|
+
const PromptInputTools = ({ className, ...props }: PromptInputToolsProps) => (
|
|
702
|
+
<div
|
|
703
|
+
className={cn(
|
|
704
|
+
"flex items-center gap-1",
|
|
705
|
+
"[&_button:first-child]:rounded-bl-xl",
|
|
706
|
+
className,
|
|
707
|
+
)}
|
|
708
|
+
{...props}
|
|
709
|
+
/>
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
type PromptInputButtonProps = ComponentProps<typeof Button>;
|
|
713
|
+
|
|
714
|
+
const PromptInputButton = ({
|
|
715
|
+
variant = "ghost",
|
|
716
|
+
className,
|
|
717
|
+
size,
|
|
718
|
+
...props
|
|
719
|
+
}: PromptInputButtonProps) => {
|
|
720
|
+
const newSize =
|
|
721
|
+
(size ?? Children.count(props.children) > 1) ? "default" : "icon";
|
|
722
|
+
|
|
723
|
+
return (
|
|
724
|
+
<Button
|
|
725
|
+
className={cn(
|
|
726
|
+
"shrink-0 gap-1.5 rounded-lg",
|
|
727
|
+
variant === "ghost" && "text-muted-foreground",
|
|
728
|
+
newSize === "default" && "px-3",
|
|
729
|
+
className,
|
|
730
|
+
)}
|
|
731
|
+
size={newSize}
|
|
732
|
+
type="button"
|
|
733
|
+
variant={variant}
|
|
734
|
+
{...props}
|
|
735
|
+
/>
|
|
736
|
+
);
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
type PromptInputSubmitProps = ComponentProps<typeof Button> & {
|
|
740
|
+
status?: ChatStatus;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
const PromptInputSubmit = ({
|
|
744
|
+
className,
|
|
745
|
+
variant = "default",
|
|
746
|
+
size = "icon",
|
|
747
|
+
status,
|
|
748
|
+
children,
|
|
749
|
+
...props
|
|
750
|
+
}: PromptInputSubmitProps) => {
|
|
751
|
+
let Icon = <SendIcon className="size-4" />;
|
|
752
|
+
|
|
753
|
+
if (status === "submitted") {
|
|
754
|
+
Icon = <Loader2Icon className="size-4 animate-spin" />;
|
|
755
|
+
} else if (status === "streaming") {
|
|
756
|
+
Icon = <SquareIcon className="size-4" />;
|
|
757
|
+
} else if (status === "error") {
|
|
758
|
+
Icon = <XIcon className="size-4" />;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return (
|
|
762
|
+
<Button
|
|
763
|
+
className={cn("gap-1.5 rounded-lg", className)}
|
|
764
|
+
size={size}
|
|
765
|
+
type="submit"
|
|
766
|
+
variant={variant}
|
|
767
|
+
{...props}
|
|
768
|
+
>
|
|
769
|
+
{children ?? Icon}
|
|
770
|
+
</Button>
|
|
771
|
+
);
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
type PromptInputModelSelectProps = ComponentProps<typeof Select>;
|
|
775
|
+
|
|
776
|
+
const PromptInputModelSelect = (props: PromptInputModelSelectProps) => (
|
|
777
|
+
<Select {...props} />
|
|
778
|
+
);
|
|
779
|
+
|
|
780
|
+
type PromptInputModelSelectTriggerProps = ComponentProps<typeof SelectTrigger>;
|
|
781
|
+
|
|
782
|
+
const PromptInputModelSelectTrigger = ({
|
|
783
|
+
className,
|
|
784
|
+
...props
|
|
785
|
+
}: PromptInputModelSelectTriggerProps) => (
|
|
786
|
+
<SelectTrigger
|
|
787
|
+
className={cn(
|
|
788
|
+
"border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors",
|
|
789
|
+
'hover:bg-accent hover:text-foreground [&[aria-expanded="true"]]:bg-accent [&[aria-expanded="true"]]:text-foreground',
|
|
790
|
+
className,
|
|
791
|
+
)}
|
|
792
|
+
{...props}
|
|
793
|
+
/>
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
type PromptInputModelSelectContentProps = ComponentProps<typeof SelectContent>;
|
|
797
|
+
|
|
798
|
+
const PromptInputModelSelectContent = ({
|
|
799
|
+
className,
|
|
800
|
+
...props
|
|
801
|
+
}: PromptInputModelSelectContentProps) => (
|
|
802
|
+
<SelectContent className={cn(className)} {...props} />
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
type PromptInputModelSelectItemProps = ComponentProps<typeof SelectItem>;
|
|
806
|
+
|
|
807
|
+
const PromptInputModelSelectItem = ({
|
|
808
|
+
className,
|
|
809
|
+
...props
|
|
810
|
+
}: PromptInputModelSelectItemProps) => (
|
|
811
|
+
<SelectItem className={cn(className)} {...props} />
|
|
812
|
+
);
|
|
813
|
+
|
|
814
|
+
type PromptInputModelSelectValueProps = ComponentProps<typeof SelectValue>;
|
|
815
|
+
|
|
816
|
+
const PromptInputModelSelectValue = ({
|
|
817
|
+
className,
|
|
818
|
+
...props
|
|
819
|
+
}: PromptInputModelSelectValueProps) => (
|
|
820
|
+
<SelectValue className={cn(className)} {...props} />
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
// ===================================
|
|
824
|
+
// SUGGESTION COMPONENTS
|
|
825
|
+
// ===================================
|
|
826
|
+
|
|
827
|
+
type SuggestionsProps = ComponentProps<typeof ScrollArea>;
|
|
828
|
+
|
|
829
|
+
const Suggestions = ({ className, children, ...props }: SuggestionsProps) => (
|
|
830
|
+
<ScrollArea className="w-full overflow-x-auto whitespace-nowrap" {...props}>
|
|
831
|
+
<div className={cn("flex w-max flex-nowrap items-center gap-2", className)}>
|
|
832
|
+
{children}
|
|
833
|
+
</div>
|
|
834
|
+
<ScrollBar className="hidden" orientation="horizontal" />
|
|
835
|
+
</ScrollArea>
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
type SuggestionProps = Omit<ComponentProps<typeof Button>, "onClick"> & {
|
|
839
|
+
suggestion: string;
|
|
840
|
+
onClick?: (suggestion: string) => void;
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
const Suggestion = ({
|
|
844
|
+
suggestion,
|
|
845
|
+
onClick,
|
|
846
|
+
className,
|
|
847
|
+
variant = "outline",
|
|
848
|
+
size = "sm",
|
|
849
|
+
children,
|
|
850
|
+
...props
|
|
851
|
+
}: SuggestionProps) => {
|
|
852
|
+
const handleClick = () => {
|
|
853
|
+
onClick?.(suggestion);
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
return (
|
|
857
|
+
<Button
|
|
858
|
+
className={cn("cursor-pointer rounded-full px-4", className)}
|
|
859
|
+
onClick={handleClick}
|
|
860
|
+
size={size}
|
|
861
|
+
type="button"
|
|
862
|
+
variant={variant}
|
|
863
|
+
{...props}
|
|
864
|
+
>
|
|
865
|
+
{children || suggestion}
|
|
866
|
+
</Button>
|
|
867
|
+
);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
// ===================================
|
|
871
|
+
// ACTIONS COMPONENTS
|
|
872
|
+
// ===================================
|
|
873
|
+
|
|
874
|
+
type ActionsProps = ComponentProps<"div">;
|
|
875
|
+
|
|
876
|
+
const Actions = ({ className, children, ...props }: ActionsProps) => (
|
|
877
|
+
<div className={cn("flex items-center gap-1", className)} {...props}>
|
|
878
|
+
{children}
|
|
879
|
+
</div>
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
type ActionProps = ComponentProps<typeof Button> & {
|
|
883
|
+
tooltip?: string;
|
|
884
|
+
label?: string;
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const Action = ({
|
|
888
|
+
tooltip,
|
|
889
|
+
children,
|
|
890
|
+
label,
|
|
891
|
+
className,
|
|
892
|
+
variant = "ghost",
|
|
893
|
+
size = "sm",
|
|
894
|
+
...props
|
|
895
|
+
}: ActionProps) => {
|
|
896
|
+
const button = (
|
|
897
|
+
<Button
|
|
898
|
+
className={cn(
|
|
899
|
+
"size-9 p-1.5 text-muted-foreground hover:text-foreground relative",
|
|
900
|
+
className,
|
|
901
|
+
)}
|
|
902
|
+
size={size}
|
|
903
|
+
type="button"
|
|
904
|
+
variant={variant}
|
|
905
|
+
{...props}
|
|
906
|
+
>
|
|
907
|
+
{children}
|
|
908
|
+
<span className="sr-only">{label || tooltip}</span>
|
|
909
|
+
</Button>
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
if (tooltip) {
|
|
913
|
+
return (
|
|
914
|
+
<TooltipProvider>
|
|
915
|
+
<Tooltip>
|
|
916
|
+
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
|
917
|
+
<TooltipContent>
|
|
918
|
+
<p>{tooltip}</p>
|
|
919
|
+
</TooltipContent>
|
|
920
|
+
</Tooltip>
|
|
921
|
+
</TooltipProvider>
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return button;
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
// ===================================
|
|
929
|
+
// BRANCH COMPONENTS
|
|
930
|
+
// ===================================
|
|
931
|
+
|
|
932
|
+
interface BranchContextType {
|
|
933
|
+
currentBranch: number;
|
|
934
|
+
totalBranches: number;
|
|
935
|
+
goToPrevious: () => void;
|
|
936
|
+
goToNext: () => void;
|
|
937
|
+
branches: React.ReactElement[];
|
|
938
|
+
setBranches: (branches: React.ReactElement[]) => void;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const BranchContext = createContext<BranchContextType | null>(null);
|
|
942
|
+
|
|
943
|
+
const useBranch = () => {
|
|
944
|
+
const context = useContext(BranchContext);
|
|
945
|
+
|
|
946
|
+
if (!context) {
|
|
947
|
+
throw new Error("Branch components must be used within Branch");
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return context;
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
type BranchProps = HTMLAttributes<HTMLDivElement> & {
|
|
954
|
+
defaultBranch?: number;
|
|
955
|
+
onBranchChange?: (branchIndex: number) => void;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
const Branch = ({
|
|
959
|
+
defaultBranch = 0,
|
|
960
|
+
onBranchChange,
|
|
961
|
+
className,
|
|
962
|
+
...props
|
|
963
|
+
}: BranchProps) => {
|
|
964
|
+
const [currentBranch, setCurrentBranch] = useState(defaultBranch);
|
|
965
|
+
const [branches, setBranches] = useState<React.ReactElement[]>([]);
|
|
966
|
+
|
|
967
|
+
const handleBranchChange = (newBranch: number) => {
|
|
968
|
+
setCurrentBranch(newBranch);
|
|
969
|
+
onBranchChange?.(newBranch);
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
const goToPrevious = () => {
|
|
973
|
+
const newBranch =
|
|
974
|
+
currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
|
|
975
|
+
handleBranchChange(newBranch);
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
const goToNext = () => {
|
|
979
|
+
const newBranch =
|
|
980
|
+
currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
|
|
981
|
+
handleBranchChange(newBranch);
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
const contextValue: BranchContextType = {
|
|
985
|
+
currentBranch,
|
|
986
|
+
totalBranches: branches.length,
|
|
987
|
+
goToPrevious,
|
|
988
|
+
goToNext,
|
|
989
|
+
branches,
|
|
990
|
+
setBranches,
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
return (
|
|
994
|
+
<BranchContext.Provider value={contextValue}>
|
|
995
|
+
<div
|
|
996
|
+
className={cn("grid w-full gap-2 [&>div]:pb-0", className)}
|
|
997
|
+
{...props}
|
|
998
|
+
/>
|
|
999
|
+
</BranchContext.Provider>
|
|
1000
|
+
);
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
type BranchMessagesProps = HTMLAttributes<HTMLDivElement>;
|
|
1004
|
+
|
|
1005
|
+
const BranchMessages = ({ children, ...props }: BranchMessagesProps) => {
|
|
1006
|
+
const { currentBranch, setBranches, branches } = useBranch();
|
|
1007
|
+
const childrenArray = useMemo(
|
|
1008
|
+
() => (Array.isArray(children) ? children : [children]),
|
|
1009
|
+
[children],
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
// Use useEffect to update branches when they change
|
|
1013
|
+
useEffect(() => {
|
|
1014
|
+
if (branches.length !== childrenArray.length) {
|
|
1015
|
+
setBranches(childrenArray);
|
|
1016
|
+
}
|
|
1017
|
+
}, [childrenArray, branches, setBranches]);
|
|
1018
|
+
|
|
1019
|
+
return childrenArray.map((branch, index) => (
|
|
1020
|
+
<div
|
|
1021
|
+
className={cn(
|
|
1022
|
+
"grid gap-2 overflow-hidden [&>div]:pb-0",
|
|
1023
|
+
index === currentBranch ? "block" : "hidden",
|
|
1024
|
+
)}
|
|
1025
|
+
key={branch.key}
|
|
1026
|
+
{...props}
|
|
1027
|
+
>
|
|
1028
|
+
{branch}
|
|
1029
|
+
</div>
|
|
1030
|
+
));
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
type BranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
|
|
1034
|
+
from: UIMessage["role"];
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
const BranchSelector = ({ className, from, ...props }: BranchSelectorProps) => {
|
|
1038
|
+
const { totalBranches } = useBranch();
|
|
1039
|
+
|
|
1040
|
+
// Don't render if there's only one branch
|
|
1041
|
+
if (totalBranches <= 1) {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
return (
|
|
1046
|
+
<div
|
|
1047
|
+
className={cn(
|
|
1048
|
+
"flex items-center gap-2 self-end px-10",
|
|
1049
|
+
from === "assistant" ? "justify-start" : "justify-end",
|
|
1050
|
+
className,
|
|
1051
|
+
)}
|
|
1052
|
+
{...props}
|
|
1053
|
+
/>
|
|
1054
|
+
);
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
type BranchPreviousProps = ComponentProps<typeof Button>;
|
|
1058
|
+
|
|
1059
|
+
const BranchPrevious = ({
|
|
1060
|
+
className,
|
|
1061
|
+
children,
|
|
1062
|
+
...props
|
|
1063
|
+
}: BranchPreviousProps) => {
|
|
1064
|
+
const { goToPrevious, totalBranches } = useBranch();
|
|
1065
|
+
|
|
1066
|
+
return (
|
|
1067
|
+
<Button
|
|
1068
|
+
aria-label="Previous branch"
|
|
1069
|
+
className={cn(
|
|
1070
|
+
"size-7 shrink-0 rounded-full text-muted-foreground transition-colors",
|
|
1071
|
+
"hover:bg-accent hover:text-foreground",
|
|
1072
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1073
|
+
className,
|
|
1074
|
+
)}
|
|
1075
|
+
disabled={totalBranches <= 1}
|
|
1076
|
+
onClick={goToPrevious}
|
|
1077
|
+
size="icon"
|
|
1078
|
+
type="button"
|
|
1079
|
+
variant="ghost"
|
|
1080
|
+
{...props}
|
|
1081
|
+
>
|
|
1082
|
+
{children ?? <ChevronLeftIcon size={14} />}
|
|
1083
|
+
</Button>
|
|
1084
|
+
);
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
type BranchNextProps = ComponentProps<typeof Button>;
|
|
1088
|
+
|
|
1089
|
+
const BranchNext = ({ className, children, ...props }: BranchNextProps) => {
|
|
1090
|
+
const { goToNext, totalBranches } = useBranch();
|
|
1091
|
+
|
|
1092
|
+
return (
|
|
1093
|
+
<Button
|
|
1094
|
+
aria-label="Next branch"
|
|
1095
|
+
className={cn(
|
|
1096
|
+
"size-7 shrink-0 rounded-full text-muted-foreground transition-colors",
|
|
1097
|
+
"hover:bg-accent hover:text-foreground",
|
|
1098
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1099
|
+
className,
|
|
1100
|
+
)}
|
|
1101
|
+
disabled={totalBranches <= 1}
|
|
1102
|
+
onClick={goToNext}
|
|
1103
|
+
size="icon"
|
|
1104
|
+
type="button"
|
|
1105
|
+
variant="ghost"
|
|
1106
|
+
{...props}
|
|
1107
|
+
>
|
|
1108
|
+
{children ?? <ChevronRightIcon size={14} />}
|
|
1109
|
+
</Button>
|
|
1110
|
+
);
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
type BranchPageProps = HTMLAttributes<HTMLSpanElement>;
|
|
1114
|
+
|
|
1115
|
+
const BranchPage = ({ className, ...props }: BranchPageProps) => {
|
|
1116
|
+
const { currentBranch, totalBranches } = useBranch();
|
|
1117
|
+
|
|
1118
|
+
return (
|
|
1119
|
+
<span
|
|
1120
|
+
className={cn(
|
|
1121
|
+
"font-medium text-muted-foreground text-xs tabular-nums",
|
|
1122
|
+
className,
|
|
1123
|
+
)}
|
|
1124
|
+
{...props}
|
|
1125
|
+
>
|
|
1126
|
+
{currentBranch + 1} of {totalBranches}
|
|
1127
|
+
</span>
|
|
1128
|
+
);
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
// ===================================
|
|
1132
|
+
// CODE BLOCK COMPONENTS
|
|
1133
|
+
// ===================================
|
|
1134
|
+
|
|
1135
|
+
interface CodeBlockContextType {
|
|
1136
|
+
code: string;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const CodeBlockContext = createContext<CodeBlockContextType>({
|
|
1140
|
+
code: "",
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
|
|
1144
|
+
code: string;
|
|
1145
|
+
language: string;
|
|
1146
|
+
showLineNumbers?: boolean;
|
|
1147
|
+
children?: ReactNode;
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
const CodeBlock = ({
|
|
1151
|
+
code,
|
|
1152
|
+
language,
|
|
1153
|
+
showLineNumbers = false,
|
|
1154
|
+
className,
|
|
1155
|
+
children,
|
|
1156
|
+
...props
|
|
1157
|
+
}: CodeBlockProps) => (
|
|
1158
|
+
<CodeBlockContext.Provider value={{ code }}>
|
|
1159
|
+
<div
|
|
1160
|
+
className={cn(
|
|
1161
|
+
"relative w-full overflow-hidden rounded-md border bg-background text-foreground",
|
|
1162
|
+
className,
|
|
1163
|
+
)}
|
|
1164
|
+
{...props}
|
|
1165
|
+
>
|
|
1166
|
+
<div className="relative">
|
|
1167
|
+
<Suspense fallback={null}>
|
|
1168
|
+
<LazyCodeBlockContent
|
|
1169
|
+
code={code}
|
|
1170
|
+
language={language}
|
|
1171
|
+
showLineNumbers={showLineNumbers}
|
|
1172
|
+
/>
|
|
1173
|
+
</Suspense>
|
|
1174
|
+
{children && (
|
|
1175
|
+
<div className="absolute top-2 right-2 flex items-center gap-2">
|
|
1176
|
+
{children}
|
|
1177
|
+
</div>
|
|
1178
|
+
)}
|
|
1179
|
+
</div>
|
|
1180
|
+
</div>
|
|
1181
|
+
</CodeBlockContext.Provider>
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {
|
|
1185
|
+
onCopy?: () => void;
|
|
1186
|
+
onError?: (error: Error) => void;
|
|
1187
|
+
timeout?: number;
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
const CodeBlockCopyButton = ({
|
|
1191
|
+
onCopy,
|
|
1192
|
+
onError,
|
|
1193
|
+
timeout = 2000,
|
|
1194
|
+
children,
|
|
1195
|
+
className,
|
|
1196
|
+
...props
|
|
1197
|
+
}: CodeBlockCopyButtonProps) => {
|
|
1198
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
1199
|
+
const { code } = useContext(CodeBlockContext);
|
|
1200
|
+
|
|
1201
|
+
const copyToClipboard = async () => {
|
|
1202
|
+
if (typeof window === "undefined" || !navigator.clipboard.writeText) {
|
|
1203
|
+
onError?.(new Error("Clipboard API not available"));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
try {
|
|
1208
|
+
await navigator.clipboard.writeText(code);
|
|
1209
|
+
setIsCopied(true);
|
|
1210
|
+
onCopy?.();
|
|
1211
|
+
setTimeout(() => setIsCopied(false), timeout);
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
onError?.(error as Error);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
1218
|
+
|
|
1219
|
+
return (
|
|
1220
|
+
<Button
|
|
1221
|
+
className={cn("shrink-0", className)}
|
|
1222
|
+
onClick={() => void copyToClipboard()}
|
|
1223
|
+
size="icon"
|
|
1224
|
+
variant="ghost"
|
|
1225
|
+
{...props}
|
|
1226
|
+
>
|
|
1227
|
+
{children ?? <Icon size={14} />}
|
|
1228
|
+
</Button>
|
|
1229
|
+
);
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// ===================================
|
|
1233
|
+
// LOADER COMPONENT
|
|
1234
|
+
// ===================================
|
|
1235
|
+
|
|
1236
|
+
interface LoaderIconProps {
|
|
1237
|
+
size?: number;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
const LoaderIcon = ({ size = 16 }: LoaderIconProps) => (
|
|
1241
|
+
<svg
|
|
1242
|
+
height={size}
|
|
1243
|
+
strokeLinejoin="round"
|
|
1244
|
+
style={{ color: "currentcolor" }}
|
|
1245
|
+
viewBox="0 0 16 16"
|
|
1246
|
+
width={size}
|
|
1247
|
+
>
|
|
1248
|
+
<title>Loader</title>
|
|
1249
|
+
<g clipPath="url(#clip0_2393_1490)">
|
|
1250
|
+
<path d="M8 0V4" stroke="currentColor" strokeWidth="1.5" />
|
|
1251
|
+
<path
|
|
1252
|
+
d="M8 16V12"
|
|
1253
|
+
opacity="0.5"
|
|
1254
|
+
stroke="currentColor"
|
|
1255
|
+
strokeWidth="1.5"
|
|
1256
|
+
/>
|
|
1257
|
+
<path
|
|
1258
|
+
d="M3.29773 1.52783L5.64887 4.7639"
|
|
1259
|
+
opacity="0.9"
|
|
1260
|
+
stroke="currentColor"
|
|
1261
|
+
strokeWidth="1.5"
|
|
1262
|
+
/>
|
|
1263
|
+
<path
|
|
1264
|
+
d="M12.7023 1.52783L10.3511 4.7639"
|
|
1265
|
+
opacity="0.1"
|
|
1266
|
+
stroke="currentColor"
|
|
1267
|
+
strokeWidth="1.5"
|
|
1268
|
+
/>
|
|
1269
|
+
<path
|
|
1270
|
+
d="M12.7023 14.472L10.3511 11.236"
|
|
1271
|
+
opacity="0.4"
|
|
1272
|
+
stroke="currentColor"
|
|
1273
|
+
strokeWidth="1.5"
|
|
1274
|
+
/>
|
|
1275
|
+
<path
|
|
1276
|
+
d="M3.29773 14.472L5.64887 11.236"
|
|
1277
|
+
opacity="0.6"
|
|
1278
|
+
stroke="currentColor"
|
|
1279
|
+
strokeWidth="1.5"
|
|
1280
|
+
/>
|
|
1281
|
+
<path
|
|
1282
|
+
d="M15.6085 5.52783L11.8043 6.7639"
|
|
1283
|
+
opacity="0.2"
|
|
1284
|
+
stroke="currentColor"
|
|
1285
|
+
strokeWidth="1.5"
|
|
1286
|
+
/>
|
|
1287
|
+
<path
|
|
1288
|
+
d="M0.391602 10.472L4.19583 9.23598"
|
|
1289
|
+
opacity="0.7"
|
|
1290
|
+
stroke="currentColor"
|
|
1291
|
+
strokeWidth="1.5"
|
|
1292
|
+
/>
|
|
1293
|
+
<path
|
|
1294
|
+
d="M15.6085 10.4722L11.8043 9.2361"
|
|
1295
|
+
opacity="0.3"
|
|
1296
|
+
stroke="currentColor"
|
|
1297
|
+
strokeWidth="1.5"
|
|
1298
|
+
/>
|
|
1299
|
+
<path
|
|
1300
|
+
d="M0.391602 5.52783L4.19583 6.7639"
|
|
1301
|
+
opacity="0.8"
|
|
1302
|
+
stroke="currentColor"
|
|
1303
|
+
strokeWidth="1.5"
|
|
1304
|
+
/>
|
|
1305
|
+
</g>
|
|
1306
|
+
<defs>
|
|
1307
|
+
<clipPath id="clip0_2393_1490">
|
|
1308
|
+
<rect fill="white" height="16" width="16" />
|
|
1309
|
+
</clipPath>
|
|
1310
|
+
</defs>
|
|
1311
|
+
</svg>
|
|
1312
|
+
);
|
|
1313
|
+
|
|
1314
|
+
type LoaderProps = HTMLAttributes<HTMLDivElement> & {
|
|
1315
|
+
size?: number;
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
const Loader = ({ className, size = 16, ...props }: LoaderProps) => (
|
|
1319
|
+
<div
|
|
1320
|
+
className={cn(
|
|
1321
|
+
"inline-flex animate-spin items-center justify-center",
|
|
1322
|
+
className,
|
|
1323
|
+
)}
|
|
1324
|
+
{...props}
|
|
1325
|
+
>
|
|
1326
|
+
<LoaderIcon size={size} />
|
|
1327
|
+
</div>
|
|
1328
|
+
);
|
|
1329
|
+
|
|
1330
|
+
// ===================================
|
|
1331
|
+
// RESPONSE COMPONENT
|
|
1332
|
+
// ===================================
|
|
1333
|
+
|
|
1334
|
+
type ResponseProps = ComponentProps<typeof Streamdown>;
|
|
1335
|
+
|
|
1336
|
+
const Response = memo(
|
|
1337
|
+
({ className, ...props }: ResponseProps) => (
|
|
1338
|
+
<Streamdown
|
|
1339
|
+
className={cn(
|
|
1340
|
+
"size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
|
|
1341
|
+
className,
|
|
1342
|
+
)}
|
|
1343
|
+
{...props}
|
|
1344
|
+
/>
|
|
1345
|
+
),
|
|
1346
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children,
|
|
1347
|
+
);
|
|
1348
|
+
|
|
1349
|
+
Response.displayName = "Response";
|
|
1350
|
+
|
|
1351
|
+
// ===================================
|
|
1352
|
+
// REASONING COMPONENTS
|
|
1353
|
+
// ===================================
|
|
1354
|
+
|
|
1355
|
+
interface ReasoningContextValue {
|
|
1356
|
+
isStreaming: boolean;
|
|
1357
|
+
isOpen: boolean;
|
|
1358
|
+
setIsOpen: (open: boolean) => void;
|
|
1359
|
+
duration: number;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const ReasoningContext = createContext<ReasoningContextValue | null>(null);
|
|
1363
|
+
|
|
1364
|
+
const useReasoning = () => {
|
|
1365
|
+
const context = useContext(ReasoningContext);
|
|
1366
|
+
if (!context) {
|
|
1367
|
+
throw new Error("Reasoning components must be used within Reasoning");
|
|
1368
|
+
}
|
|
1369
|
+
return context;
|
|
1370
|
+
};
|
|
1371
|
+
|
|
1372
|
+
type ReasoningProps = Omit<
|
|
1373
|
+
ComponentProps<typeof Collapsible>,
|
|
1374
|
+
"onOpenChange"
|
|
1375
|
+
> & {
|
|
1376
|
+
isStreaming?: boolean;
|
|
1377
|
+
open?: boolean;
|
|
1378
|
+
defaultOpen?: boolean;
|
|
1379
|
+
onOpenChange?: (open: boolean) => void;
|
|
1380
|
+
duration?: number;
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
const AUTO_CLOSE_DELAY = 1000;
|
|
1384
|
+
const MS_IN_S = 1000;
|
|
1385
|
+
|
|
1386
|
+
const Reasoning = memo(
|
|
1387
|
+
({
|
|
1388
|
+
className,
|
|
1389
|
+
isStreaming = false,
|
|
1390
|
+
open,
|
|
1391
|
+
defaultOpen = true,
|
|
1392
|
+
onOpenChange,
|
|
1393
|
+
duration: durationProp,
|
|
1394
|
+
children,
|
|
1395
|
+
...props
|
|
1396
|
+
}: ReasoningProps) => {
|
|
1397
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
1398
|
+
prop: open,
|
|
1399
|
+
defaultProp: defaultOpen,
|
|
1400
|
+
onChange: onOpenChange,
|
|
1401
|
+
});
|
|
1402
|
+
const [duration, setDuration] = useControllableState({
|
|
1403
|
+
prop: durationProp,
|
|
1404
|
+
defaultProp: 0,
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false);
|
|
1408
|
+
const startTimeRef = useRef<number | null>(null);
|
|
1409
|
+
|
|
1410
|
+
// Track duration when streaming starts and ends
|
|
1411
|
+
useEffect(() => {
|
|
1412
|
+
if (isStreaming && startTimeRef.current === null) {
|
|
1413
|
+
startTimeRef.current = Date.now();
|
|
1414
|
+
} else if (!isStreaming && startTimeRef.current !== null) {
|
|
1415
|
+
setDuration(Math.round((Date.now() - startTimeRef.current) / MS_IN_S));
|
|
1416
|
+
startTimeRef.current = null;
|
|
1417
|
+
}
|
|
1418
|
+
}, [isStreaming, setDuration]);
|
|
1419
|
+
|
|
1420
|
+
// Auto-open when streaming starts, auto-close when streaming ends (once only)
|
|
1421
|
+
useEffect(() => {
|
|
1422
|
+
if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) {
|
|
1423
|
+
// Add a small delay before closing to allow user to see the content
|
|
1424
|
+
const timer = setTimeout(() => {
|
|
1425
|
+
setIsOpen(false);
|
|
1426
|
+
setHasAutoClosedRef(true);
|
|
1427
|
+
}, AUTO_CLOSE_DELAY);
|
|
1428
|
+
|
|
1429
|
+
return () => clearTimeout(timer);
|
|
1430
|
+
}
|
|
1431
|
+
}, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]);
|
|
1432
|
+
|
|
1433
|
+
const handleOpenChange = (newOpen: boolean) => {
|
|
1434
|
+
setIsOpen(newOpen);
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
return (
|
|
1438
|
+
<ReasoningContext.Provider
|
|
1439
|
+
value={{ isStreaming, isOpen, setIsOpen, duration }}
|
|
1440
|
+
>
|
|
1441
|
+
<Collapsible
|
|
1442
|
+
className={cn("not-prose mb-4", className)}
|
|
1443
|
+
onOpenChange={handleOpenChange}
|
|
1444
|
+
open={isOpen}
|
|
1445
|
+
{...props}
|
|
1446
|
+
>
|
|
1447
|
+
{children}
|
|
1448
|
+
</Collapsible>
|
|
1449
|
+
</ReasoningContext.Provider>
|
|
1450
|
+
);
|
|
1451
|
+
},
|
|
1452
|
+
);
|
|
1453
|
+
|
|
1454
|
+
type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger>;
|
|
1455
|
+
|
|
1456
|
+
const ReasoningTrigger = memo(
|
|
1457
|
+
({ className, children, ...props }: ReasoningTriggerProps) => {
|
|
1458
|
+
const { isStreaming, isOpen, duration } = useReasoning();
|
|
1459
|
+
|
|
1460
|
+
return (
|
|
1461
|
+
<CollapsibleTrigger
|
|
1462
|
+
className={cn(
|
|
1463
|
+
"flex items-center gap-2 text-muted-foreground text-sm",
|
|
1464
|
+
className,
|
|
1465
|
+
)}
|
|
1466
|
+
{...props}
|
|
1467
|
+
>
|
|
1468
|
+
{children ?? (
|
|
1469
|
+
<>
|
|
1470
|
+
<BrainIcon className="size-4" />
|
|
1471
|
+
{isStreaming || duration === 0 ? (
|
|
1472
|
+
<p>Thinking...</p>
|
|
1473
|
+
) : (
|
|
1474
|
+
<p>Thought for {duration} seconds</p>
|
|
1475
|
+
)}
|
|
1476
|
+
<ChevronDownIcon
|
|
1477
|
+
className={cn(
|
|
1478
|
+
"size-4 text-muted-foreground transition-transform",
|
|
1479
|
+
isOpen ? "rotate-180" : "rotate-0",
|
|
1480
|
+
)}
|
|
1481
|
+
/>
|
|
1482
|
+
</>
|
|
1483
|
+
)}
|
|
1484
|
+
</CollapsibleTrigger>
|
|
1485
|
+
);
|
|
1486
|
+
},
|
|
1487
|
+
);
|
|
1488
|
+
|
|
1489
|
+
type ReasoningContentProps = Omit<
|
|
1490
|
+
ComponentProps<typeof CollapsibleContent>,
|
|
1491
|
+
"children"
|
|
1492
|
+
> & {
|
|
1493
|
+
children: string;
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
const ReasoningContent = memo(
|
|
1497
|
+
({ className, children, ...props }: ReasoningContentProps) => (
|
|
1498
|
+
<CollapsibleContent
|
|
1499
|
+
className={cn(
|
|
1500
|
+
"mt-4 text-sm",
|
|
1501
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1502
|
+
className,
|
|
1503
|
+
)}
|
|
1504
|
+
{...props}
|
|
1505
|
+
>
|
|
1506
|
+
<Response className="grid gap-2">{children}</Response>
|
|
1507
|
+
</CollapsibleContent>
|
|
1508
|
+
),
|
|
1509
|
+
);
|
|
1510
|
+
|
|
1511
|
+
Reasoning.displayName = "Reasoning";
|
|
1512
|
+
ReasoningTrigger.displayName = "ReasoningTrigger";
|
|
1513
|
+
ReasoningContent.displayName = "ReasoningContent";
|
|
1514
|
+
|
|
1515
|
+
// ===================================
|
|
1516
|
+
// SOURCES COMPONENTS
|
|
1517
|
+
// ===================================
|
|
1518
|
+
|
|
1519
|
+
type SourcesProps = ComponentPropsWithRef<"div">;
|
|
1520
|
+
|
|
1521
|
+
const Sources = ({ className, ...props }: SourcesProps) => (
|
|
1522
|
+
<Collapsible
|
|
1523
|
+
className={cn("not-prose mb-4 text-primary text-xs", className)}
|
|
1524
|
+
{...props}
|
|
1525
|
+
/>
|
|
1526
|
+
);
|
|
1527
|
+
|
|
1528
|
+
type SourcesTriggerProps = Omit<
|
|
1529
|
+
ComponentProps<typeof CollapsibleTrigger>,
|
|
1530
|
+
"count"
|
|
1531
|
+
> & {
|
|
1532
|
+
count: number;
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
const SourcesTrigger = ({ count, children, ...props }: SourcesTriggerProps) => (
|
|
1536
|
+
<CollapsibleTrigger className="flex items-center gap-2" {...props}>
|
|
1537
|
+
{children ?? (
|
|
1538
|
+
<>
|
|
1539
|
+
<p className="font-medium">Used {count} sources</p>
|
|
1540
|
+
<ChevronDownIcon className="h-4 w-4" />
|
|
1541
|
+
</>
|
|
1542
|
+
)}
|
|
1543
|
+
</CollapsibleTrigger>
|
|
1544
|
+
);
|
|
1545
|
+
|
|
1546
|
+
type SourcesContentProps = ComponentProps<typeof CollapsibleContent>;
|
|
1547
|
+
|
|
1548
|
+
const SourcesContent = ({ className, ...props }: SourcesContentProps) => (
|
|
1549
|
+
<CollapsibleContent
|
|
1550
|
+
className={cn(
|
|
1551
|
+
"mt-3 flex w-fit flex-col gap-2",
|
|
1552
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1553
|
+
className,
|
|
1554
|
+
)}
|
|
1555
|
+
{...props}
|
|
1556
|
+
/>
|
|
1557
|
+
);
|
|
1558
|
+
|
|
1559
|
+
type SourceProps = ComponentProps<"a">;
|
|
1560
|
+
|
|
1561
|
+
const Source = ({ href, title, children, ...props }: SourceProps) => (
|
|
1562
|
+
<a
|
|
1563
|
+
className="flex items-center gap-2"
|
|
1564
|
+
href={href}
|
|
1565
|
+
rel="noreferrer"
|
|
1566
|
+
target="_blank"
|
|
1567
|
+
{...props}
|
|
1568
|
+
>
|
|
1569
|
+
{children ?? (
|
|
1570
|
+
<>
|
|
1571
|
+
<BookIcon className="h-4 w-4" />
|
|
1572
|
+
<span className="block font-medium">{title}</span>
|
|
1573
|
+
</>
|
|
1574
|
+
)}
|
|
1575
|
+
</a>
|
|
1576
|
+
);
|
|
1577
|
+
|
|
1578
|
+
// ===================================
|
|
1579
|
+
// TASK COMPONENTS
|
|
1580
|
+
// ===================================
|
|
1581
|
+
|
|
1582
|
+
type TaskItemFileProps = ComponentProps<"div">;
|
|
1583
|
+
|
|
1584
|
+
const TaskItemFile = ({ children, className, ...props }: TaskItemFileProps) => (
|
|
1585
|
+
<div
|
|
1586
|
+
className={cn(
|
|
1587
|
+
"inline-flex items-center gap-1 rounded-md border bg-secondary px-1.5 py-0.5 text-foreground text-xs",
|
|
1588
|
+
className,
|
|
1589
|
+
)}
|
|
1590
|
+
{...props}
|
|
1591
|
+
>
|
|
1592
|
+
{children}
|
|
1593
|
+
</div>
|
|
1594
|
+
);
|
|
1595
|
+
|
|
1596
|
+
type TaskItemProps = ComponentProps<"div">;
|
|
1597
|
+
|
|
1598
|
+
const TaskItem = ({ children, className, ...props }: TaskItemProps) => (
|
|
1599
|
+
<div className={cn("text-muted-foreground text-sm", className)} {...props}>
|
|
1600
|
+
{children}
|
|
1601
|
+
</div>
|
|
1602
|
+
);
|
|
1603
|
+
|
|
1604
|
+
type TaskProps = ComponentProps<typeof Collapsible>;
|
|
1605
|
+
|
|
1606
|
+
const Task = ({ defaultOpen = true, className, ...props }: TaskProps) => (
|
|
1607
|
+
<Collapsible
|
|
1608
|
+
className={cn(
|
|
1609
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1610
|
+
className,
|
|
1611
|
+
)}
|
|
1612
|
+
defaultOpen={defaultOpen}
|
|
1613
|
+
{...props}
|
|
1614
|
+
/>
|
|
1615
|
+
);
|
|
1616
|
+
|
|
1617
|
+
type TaskTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {
|
|
1618
|
+
title: string;
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
const TaskTrigger = ({
|
|
1622
|
+
children,
|
|
1623
|
+
className,
|
|
1624
|
+
title,
|
|
1625
|
+
...props
|
|
1626
|
+
}: TaskTriggerProps) => (
|
|
1627
|
+
<CollapsibleTrigger asChild className={cn("group", className)} {...props}>
|
|
1628
|
+
{children ?? (
|
|
1629
|
+
<div className="flex cursor-pointer items-center gap-2 text-muted-foreground hover:text-foreground">
|
|
1630
|
+
<SearchIcon className="size-4" />
|
|
1631
|
+
<p className="text-sm">{title}</p>
|
|
1632
|
+
<ChevronDownIcon className="size-4 transition-transform group-data-[state=open]:rotate-180" />
|
|
1633
|
+
</div>
|
|
1634
|
+
)}
|
|
1635
|
+
</CollapsibleTrigger>
|
|
1636
|
+
);
|
|
1637
|
+
|
|
1638
|
+
type TaskContentProps = ComponentProps<typeof CollapsibleContent>;
|
|
1639
|
+
|
|
1640
|
+
const TaskContent = ({ children, className, ...props }: TaskContentProps) => (
|
|
1641
|
+
<CollapsibleContent
|
|
1642
|
+
className={cn(
|
|
1643
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1644
|
+
className,
|
|
1645
|
+
)}
|
|
1646
|
+
{...props}
|
|
1647
|
+
>
|
|
1648
|
+
<div className="mt-4 space-y-2 border-muted border-l-2 pl-4">
|
|
1649
|
+
{children}
|
|
1650
|
+
</div>
|
|
1651
|
+
</CollapsibleContent>
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
// ===================================
|
|
1655
|
+
// TOOL COMPONENTS
|
|
1656
|
+
// ===================================
|
|
1657
|
+
|
|
1658
|
+
type ToolProps = ComponentProps<typeof Collapsible>;
|
|
1659
|
+
|
|
1660
|
+
const Tool = ({ className, ...props }: ToolProps) => (
|
|
1661
|
+
<Collapsible
|
|
1662
|
+
className={cn("not-prose mb-4 w-full rounded-md border", className)}
|
|
1663
|
+
{...props}
|
|
1664
|
+
/>
|
|
1665
|
+
);
|
|
1666
|
+
|
|
1667
|
+
interface ToolHeaderProps {
|
|
1668
|
+
type: ToolUIPart["type"];
|
|
1669
|
+
state: ToolUIPart["state"];
|
|
1670
|
+
className?: string;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
const getStatusBadge = (status: ToolUIPart["state"]) => {
|
|
1674
|
+
const labels = {
|
|
1675
|
+
"input-streaming": "Pending",
|
|
1676
|
+
"input-available": "Running",
|
|
1677
|
+
"approval-requested": "Awaiting approval",
|
|
1678
|
+
"approval-responded": "Running",
|
|
1679
|
+
"output-available": "Completed",
|
|
1680
|
+
"output-error": "Error",
|
|
1681
|
+
"output-denied": "Denied",
|
|
1682
|
+
} as const;
|
|
1683
|
+
|
|
1684
|
+
const icons = {
|
|
1685
|
+
"input-streaming": <CircleIcon className="size-4" />,
|
|
1686
|
+
"input-available": <ClockIcon className="size-4 animate-pulse" />,
|
|
1687
|
+
"approval-requested": <ClockIcon className="size-4" />,
|
|
1688
|
+
"approval-responded": <ClockIcon className="size-4 animate-pulse" />,
|
|
1689
|
+
"output-available": <CheckCircleIcon className="size-4 text-green-600" />,
|
|
1690
|
+
"output-error": <XCircleIcon className="size-4 text-red-600" />,
|
|
1691
|
+
"output-denied": <XCircleIcon className="size-4 text-amber-600" />,
|
|
1692
|
+
} as const;
|
|
1693
|
+
|
|
1694
|
+
return (
|
|
1695
|
+
<Badge className="gap-1.5 rounded-full text-xs" variant="secondary">
|
|
1696
|
+
{icons[status]}
|
|
1697
|
+
{labels[status]}
|
|
1698
|
+
</Badge>
|
|
1699
|
+
);
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
const ToolHeader = ({ className, type, state, ...props }: ToolHeaderProps) => (
|
|
1703
|
+
<CollapsibleTrigger
|
|
1704
|
+
className={cn(
|
|
1705
|
+
"flex w-full items-center justify-between gap-4 p-3",
|
|
1706
|
+
className,
|
|
1707
|
+
)}
|
|
1708
|
+
{...props}
|
|
1709
|
+
>
|
|
1710
|
+
<div className="flex items-center gap-2">
|
|
1711
|
+
<WrenchIcon className="size-4 text-muted-foreground" />
|
|
1712
|
+
<span className="font-medium text-sm">{type}</span>
|
|
1713
|
+
{getStatusBadge(state)}
|
|
1714
|
+
</div>
|
|
1715
|
+
<ChevronDownIcon className="size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" />
|
|
1716
|
+
</CollapsibleTrigger>
|
|
1717
|
+
);
|
|
1718
|
+
|
|
1719
|
+
type ToolContentProps = ComponentProps<typeof CollapsibleContent>;
|
|
1720
|
+
|
|
1721
|
+
const ToolContent = ({ className, ...props }: ToolContentProps) => (
|
|
1722
|
+
<CollapsibleContent
|
|
1723
|
+
className={cn(
|
|
1724
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1725
|
+
className,
|
|
1726
|
+
)}
|
|
1727
|
+
{...props}
|
|
1728
|
+
/>
|
|
1729
|
+
);
|
|
1730
|
+
|
|
1731
|
+
type ToolInputProps = ComponentProps<"div"> & {
|
|
1732
|
+
input: ToolUIPart["input"];
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
const ToolInput = ({ className, input, ...props }: ToolInputProps) => (
|
|
1736
|
+
<div className={cn("space-y-2 overflow-hidden p-4", className)} {...props}>
|
|
1737
|
+
<h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
|
|
1738
|
+
Parameters
|
|
1739
|
+
</h4>
|
|
1740
|
+
<div className="rounded-md bg-muted/50">
|
|
1741
|
+
<CodeBlock code={JSON.stringify(input, null, 2)} language="json" />
|
|
1742
|
+
</div>
|
|
1743
|
+
</div>
|
|
1744
|
+
);
|
|
1745
|
+
|
|
1746
|
+
type ToolOutputProps = ComponentProps<"div"> & {
|
|
1747
|
+
output: ReactNode;
|
|
1748
|
+
errorText: ToolUIPart["errorText"];
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
const ToolOutput = ({
|
|
1752
|
+
className,
|
|
1753
|
+
output,
|
|
1754
|
+
errorText,
|
|
1755
|
+
...props
|
|
1756
|
+
}: ToolOutputProps) => {
|
|
1757
|
+
if (!(output || errorText)) {
|
|
1758
|
+
return null;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
return (
|
|
1762
|
+
<div className={cn("space-y-2 p-4", className)} {...props}>
|
|
1763
|
+
<h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
|
|
1764
|
+
{errorText ? "Error" : "Result"}
|
|
1765
|
+
</h4>
|
|
1766
|
+
<div
|
|
1767
|
+
className={cn(
|
|
1768
|
+
"overflow-x-auto rounded-md text-xs [&_table]:w-full",
|
|
1769
|
+
errorText
|
|
1770
|
+
? "bg-destructive/10 text-destructive"
|
|
1771
|
+
: "bg-muted/50 text-foreground",
|
|
1772
|
+
)}
|
|
1773
|
+
>
|
|
1774
|
+
{errorText && <div>{errorText}</div>}
|
|
1775
|
+
{output && <div>{output}</div>}
|
|
1776
|
+
</div>
|
|
1777
|
+
</div>
|
|
1778
|
+
);
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
// ===================================
|
|
1782
|
+
// INLINE CITATION COMPONENTS
|
|
1783
|
+
// ===================================
|
|
1784
|
+
|
|
1785
|
+
type InlineCitationProps = ComponentProps<"span">;
|
|
1786
|
+
|
|
1787
|
+
const InlineCitation = ({ className, ...props }: InlineCitationProps) => (
|
|
1788
|
+
<span
|
|
1789
|
+
className={cn("group inline items-center gap-1", className)}
|
|
1790
|
+
{...props}
|
|
1791
|
+
/>
|
|
1792
|
+
);
|
|
1793
|
+
|
|
1794
|
+
type InlineCitationTextProps = ComponentProps<"span">;
|
|
1795
|
+
|
|
1796
|
+
const InlineCitationText = ({
|
|
1797
|
+
className,
|
|
1798
|
+
...props
|
|
1799
|
+
}: InlineCitationTextProps) => (
|
|
1800
|
+
<span
|
|
1801
|
+
className={cn("transition-colors group-hover:bg-accent", className)}
|
|
1802
|
+
{...props}
|
|
1803
|
+
/>
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
type InlineCitationCardProps = ComponentProps<typeof HoverCard>;
|
|
1807
|
+
|
|
1808
|
+
const InlineCitationCard = (props: InlineCitationCardProps) => (
|
|
1809
|
+
<HoverCard closeDelay={0} openDelay={0} {...props} />
|
|
1810
|
+
);
|
|
1811
|
+
|
|
1812
|
+
type InlineCitationCardTriggerProps = ComponentProps<typeof Badge> & {
|
|
1813
|
+
sources: string[];
|
|
1814
|
+
};
|
|
1815
|
+
|
|
1816
|
+
const InlineCitationCardTrigger = ({
|
|
1817
|
+
sources,
|
|
1818
|
+
className,
|
|
1819
|
+
...props
|
|
1820
|
+
}: InlineCitationCardTriggerProps) => (
|
|
1821
|
+
<HoverCardTrigger asChild>
|
|
1822
|
+
<Badge
|
|
1823
|
+
className={cn("ml-1 rounded-full", className)}
|
|
1824
|
+
variant="secondary"
|
|
1825
|
+
{...props}
|
|
1826
|
+
>
|
|
1827
|
+
{sources.length ? (
|
|
1828
|
+
<>
|
|
1829
|
+
{new URL(sources[0]).hostname}{" "}
|
|
1830
|
+
{sources.length > 1 && `+${sources.length - 1}`}
|
|
1831
|
+
</>
|
|
1832
|
+
) : (
|
|
1833
|
+
"unknown"
|
|
1834
|
+
)}
|
|
1835
|
+
</Badge>
|
|
1836
|
+
</HoverCardTrigger>
|
|
1837
|
+
);
|
|
1838
|
+
|
|
1839
|
+
type InlineCitationCardBodyProps = ComponentPropsWithRef<"div">;
|
|
1840
|
+
|
|
1841
|
+
const InlineCitationCardBody = ({
|
|
1842
|
+
className,
|
|
1843
|
+
...props
|
|
1844
|
+
}: InlineCitationCardBodyProps) => (
|
|
1845
|
+
<HoverCardContent className={cn("relative w-80 p-0", className)} {...props} />
|
|
1846
|
+
);
|
|
1847
|
+
|
|
1848
|
+
const CarouselApiContext = createContext<CarouselApi | undefined>(undefined);
|
|
1849
|
+
|
|
1850
|
+
const useCarouselApi = () => {
|
|
1851
|
+
const context = useContext(CarouselApiContext);
|
|
1852
|
+
return context;
|
|
1853
|
+
};
|
|
1854
|
+
|
|
1855
|
+
type InlineCitationCarouselProps = ComponentProps<typeof Carousel>;
|
|
1856
|
+
|
|
1857
|
+
const InlineCitationCarousel = ({
|
|
1858
|
+
className,
|
|
1859
|
+
children,
|
|
1860
|
+
...props
|
|
1861
|
+
}: InlineCitationCarouselProps) => {
|
|
1862
|
+
const [api, setApi] = useState<CarouselApi>();
|
|
1863
|
+
|
|
1864
|
+
return (
|
|
1865
|
+
<CarouselApiContext.Provider value={api}>
|
|
1866
|
+
<Carousel className={cn("w-full", className)} setApi={setApi} {...props}>
|
|
1867
|
+
{children}
|
|
1868
|
+
</Carousel>
|
|
1869
|
+
</CarouselApiContext.Provider>
|
|
1870
|
+
);
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
type InlineCitationCarouselContentProps = ComponentProps<"div">;
|
|
1874
|
+
|
|
1875
|
+
const InlineCitationCarouselContent = (
|
|
1876
|
+
props: InlineCitationCarouselContentProps,
|
|
1877
|
+
) => <CarouselContent {...props} />;
|
|
1878
|
+
|
|
1879
|
+
type InlineCitationCarouselItemProps = ComponentProps<"div">;
|
|
1880
|
+
|
|
1881
|
+
const InlineCitationCarouselItem = ({
|
|
1882
|
+
className,
|
|
1883
|
+
...props
|
|
1884
|
+
}: InlineCitationCarouselItemProps) => (
|
|
1885
|
+
<CarouselItem
|
|
1886
|
+
className={cn("w-full space-y-2 p-4 pl-8", className)}
|
|
1887
|
+
{...props}
|
|
1888
|
+
/>
|
|
1889
|
+
);
|
|
1890
|
+
|
|
1891
|
+
type InlineCitationCarouselHeaderProps = ComponentProps<"div">;
|
|
1892
|
+
|
|
1893
|
+
const InlineCitationCarouselHeader = ({
|
|
1894
|
+
className,
|
|
1895
|
+
...props
|
|
1896
|
+
}: InlineCitationCarouselHeaderProps) => (
|
|
1897
|
+
<div
|
|
1898
|
+
className={cn(
|
|
1899
|
+
"flex items-center justify-between gap-2 rounded-t-md bg-secondary p-2",
|
|
1900
|
+
className,
|
|
1901
|
+
)}
|
|
1902
|
+
{...props}
|
|
1903
|
+
/>
|
|
1904
|
+
);
|
|
1905
|
+
|
|
1906
|
+
type InlineCitationCarouselIndexProps = ComponentProps<"div">;
|
|
1907
|
+
|
|
1908
|
+
const InlineCitationCarouselIndex = ({
|
|
1909
|
+
children,
|
|
1910
|
+
className,
|
|
1911
|
+
...props
|
|
1912
|
+
}: InlineCitationCarouselIndexProps) => {
|
|
1913
|
+
const api = useCarouselApi();
|
|
1914
|
+
const [current, setCurrent] = useState(0);
|
|
1915
|
+
const isInitializedRef = useRef(false);
|
|
1916
|
+
|
|
1917
|
+
const count = api?.scrollSnapList().length ?? 0;
|
|
1918
|
+
|
|
1919
|
+
useEffect(() => {
|
|
1920
|
+
if (!api) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
const updateCurrent = () => {
|
|
1925
|
+
setCurrent(api.selectedScrollSnap() + 1);
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
// Initialize state when API first becomes available
|
|
1929
|
+
if (!isInitializedRef.current) {
|
|
1930
|
+
updateCurrent();
|
|
1931
|
+
isInitializedRef.current = true;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
api.on("select", updateCurrent);
|
|
1935
|
+
api.on("reInit", updateCurrent);
|
|
1936
|
+
|
|
1937
|
+
return () => {
|
|
1938
|
+
api.off("select", updateCurrent);
|
|
1939
|
+
api.off("reInit", updateCurrent);
|
|
1940
|
+
};
|
|
1941
|
+
}, [api]);
|
|
1942
|
+
|
|
1943
|
+
return (
|
|
1944
|
+
<div
|
|
1945
|
+
className={cn(
|
|
1946
|
+
"flex flex-1 items-center justify-end px-3 py-1 text-muted-foreground text-xs",
|
|
1947
|
+
className,
|
|
1948
|
+
)}
|
|
1949
|
+
{...props}
|
|
1950
|
+
>
|
|
1951
|
+
{children ?? `${current}/${count}`}
|
|
1952
|
+
</div>
|
|
1953
|
+
);
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
type InlineCitationCarouselPrevProps = ComponentProps<"button">;
|
|
1957
|
+
|
|
1958
|
+
const InlineCitationCarouselPrev = ({
|
|
1959
|
+
className,
|
|
1960
|
+
...props
|
|
1961
|
+
}: InlineCitationCarouselPrevProps) => {
|
|
1962
|
+
const api = useCarouselApi();
|
|
1963
|
+
|
|
1964
|
+
const handleClick = useCallback(() => {
|
|
1965
|
+
if (api) {
|
|
1966
|
+
api.scrollPrev();
|
|
1967
|
+
}
|
|
1968
|
+
}, [api]);
|
|
1969
|
+
|
|
1970
|
+
return (
|
|
1971
|
+
<button
|
|
1972
|
+
aria-label="Previous"
|
|
1973
|
+
className={cn("shrink-0", className)}
|
|
1974
|
+
onClick={handleClick}
|
|
1975
|
+
type="button"
|
|
1976
|
+
{...props}
|
|
1977
|
+
>
|
|
1978
|
+
<ArrowLeftIcon className="size-4 text-muted-foreground" />
|
|
1979
|
+
</button>
|
|
1980
|
+
);
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
type InlineCitationCarouselNextProps = ComponentProps<"button">;
|
|
1984
|
+
|
|
1985
|
+
const InlineCitationCarouselNext = ({
|
|
1986
|
+
className,
|
|
1987
|
+
...props
|
|
1988
|
+
}: InlineCitationCarouselNextProps) => {
|
|
1989
|
+
const api = useCarouselApi();
|
|
1990
|
+
|
|
1991
|
+
const handleClick = useCallback(() => {
|
|
1992
|
+
if (api) {
|
|
1993
|
+
api.scrollNext();
|
|
1994
|
+
}
|
|
1995
|
+
}, [api]);
|
|
1996
|
+
|
|
1997
|
+
return (
|
|
1998
|
+
<button
|
|
1999
|
+
aria-label="Next"
|
|
2000
|
+
className={cn("shrink-0", className)}
|
|
2001
|
+
onClick={handleClick}
|
|
2002
|
+
type="button"
|
|
2003
|
+
{...props}
|
|
2004
|
+
>
|
|
2005
|
+
<ArrowRightIcon className="size-4 text-muted-foreground" />
|
|
2006
|
+
</button>
|
|
2007
|
+
);
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
type InlineCitationSourceProps = ComponentProps<"div"> & {
|
|
2011
|
+
title?: string;
|
|
2012
|
+
url?: string;
|
|
2013
|
+
description?: string;
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
const InlineCitationSource = ({
|
|
2017
|
+
title,
|
|
2018
|
+
url,
|
|
2019
|
+
description,
|
|
2020
|
+
className,
|
|
2021
|
+
children,
|
|
2022
|
+
...props
|
|
2023
|
+
}: InlineCitationSourceProps) => (
|
|
2024
|
+
<div className={cn("space-y-1", className)} {...props}>
|
|
2025
|
+
{title && (
|
|
2026
|
+
<h4 className="truncate font-medium text-sm leading-tight">{title}</h4>
|
|
2027
|
+
)}
|
|
2028
|
+
{url && (
|
|
2029
|
+
<p className="truncate break-all text-muted-foreground text-xs">{url}</p>
|
|
2030
|
+
)}
|
|
2031
|
+
{description && (
|
|
2032
|
+
<p className="line-clamp-3 text-muted-foreground text-sm leading-relaxed">
|
|
2033
|
+
{description}
|
|
2034
|
+
</p>
|
|
2035
|
+
)}
|
|
2036
|
+
{children}
|
|
2037
|
+
</div>
|
|
2038
|
+
);
|
|
2039
|
+
|
|
2040
|
+
type InlineCitationQuoteProps = ComponentProps<"blockquote">;
|
|
2041
|
+
|
|
2042
|
+
const InlineCitationQuote = ({
|
|
2043
|
+
children,
|
|
2044
|
+
className,
|
|
2045
|
+
...props
|
|
2046
|
+
}: InlineCitationQuoteProps) => (
|
|
2047
|
+
<blockquote
|
|
2048
|
+
className={cn(
|
|
2049
|
+
"border-muted border-l-2 pl-3 text-muted-foreground text-sm italic",
|
|
2050
|
+
className,
|
|
2051
|
+
)}
|
|
2052
|
+
{...props}
|
|
2053
|
+
>
|
|
2054
|
+
{children}
|
|
2055
|
+
</blockquote>
|
|
2056
|
+
);
|
|
2057
|
+
|
|
2058
|
+
// ===================================
|
|
2059
|
+
// MAIN CHAT COMPONENT
|
|
2060
|
+
// ===================================
|
|
2061
|
+
|
|
2062
|
+
interface MessageItem {
|
|
2063
|
+
id: string;
|
|
2064
|
+
role: "user" | "assistant";
|
|
2065
|
+
text: string;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
interface ChatComponentProps {
|
|
2069
|
+
className?: string;
|
|
2070
|
+
messages?: MessageItem[];
|
|
2071
|
+
status?: "submitted" | "streaming" | "ready" | "error";
|
|
2072
|
+
suggestions?: string[];
|
|
2073
|
+
inputValue?: string;
|
|
2074
|
+
onSuggestionClick?: (text: string) => void;
|
|
2075
|
+
onSubmit?: (text?: string) => void;
|
|
2076
|
+
onInputChange?: (text: string) => void;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
const Chat = ({
|
|
2080
|
+
className,
|
|
2081
|
+
messages,
|
|
2082
|
+
status,
|
|
2083
|
+
suggestions,
|
|
2084
|
+
inputValue,
|
|
2085
|
+
onSuggestionClick,
|
|
2086
|
+
onSubmit,
|
|
2087
|
+
onInputChange,
|
|
2088
|
+
}: ChatComponentProps) => {
|
|
2089
|
+
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
2090
|
+
event.preventDefault();
|
|
2091
|
+
event.stopPropagation();
|
|
2092
|
+
void onSubmit?.(inputValue);
|
|
2093
|
+
};
|
|
2094
|
+
|
|
2095
|
+
const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
2096
|
+
const value = event.target.value;
|
|
2097
|
+
void onInputChange?.(value);
|
|
2098
|
+
};
|
|
2099
|
+
|
|
2100
|
+
return (
|
|
2101
|
+
<div className={cn("flex flex-col", className)}>
|
|
2102
|
+
<Conversation className="h-full">
|
|
2103
|
+
<ConversationContent>
|
|
2104
|
+
{messages?.map((message: MessageItem) => (
|
|
2105
|
+
<div key={message.id}>
|
|
2106
|
+
<Message from={message.role} key={message.id}>
|
|
2107
|
+
<MessageContent>{message.text}</MessageContent>
|
|
2108
|
+
</Message>
|
|
2109
|
+
</div>
|
|
2110
|
+
))}
|
|
2111
|
+
</ConversationContent>
|
|
2112
|
+
<ConversationScrollButton />
|
|
2113
|
+
</Conversation>
|
|
2114
|
+
<Suggestions>
|
|
2115
|
+
{suggestions?.map((suggestion: string) => (
|
|
2116
|
+
<Suggestion
|
|
2117
|
+
key={suggestion}
|
|
2118
|
+
onClick={onSuggestionClick}
|
|
2119
|
+
suggestion={suggestion}
|
|
2120
|
+
/>
|
|
2121
|
+
))}
|
|
2122
|
+
</Suggestions>
|
|
2123
|
+
<PromptInput onSubmit={handleSubmit} className="mt-4">
|
|
2124
|
+
<PromptInputTextarea onChange={handleInputChange} value={inputValue} />
|
|
2125
|
+
<PromptInputToolbar>
|
|
2126
|
+
<PromptInputTools></PromptInputTools>
|
|
2127
|
+
<PromptInputSubmit disabled={!inputValue} status={status} />
|
|
2128
|
+
</PromptInputToolbar>
|
|
2129
|
+
</PromptInput>
|
|
2130
|
+
</div>
|
|
2131
|
+
);
|
|
2132
|
+
};
|
|
2133
|
+
|
|
2134
|
+
// Properties Definition
|
|
2135
|
+
const propertiesDefinition: PropertiesPanelDefinition<ChatComponentProps> = {
|
|
2136
|
+
general: Section.category(PropsCategory.Content).children({
|
|
2137
|
+
inputValue: Prop.string().propertiesPanel({
|
|
2138
|
+
label: "Input value",
|
|
2139
|
+
controlType: "INPUT_TEXT",
|
|
2140
|
+
description: "The controlled value of the chat input",
|
|
2141
|
+
isRemovable: true,
|
|
2142
|
+
visibility: "SHOW_NAME",
|
|
2143
|
+
}),
|
|
2144
|
+
messages: Prop.array<MessageItem>()
|
|
2145
|
+
.propertiesPanel({
|
|
2146
|
+
label: "Messages",
|
|
2147
|
+
controlType: "FUNCTION_CODE_EDITOR",
|
|
2148
|
+
description: "Array of messages to display in the chat",
|
|
2149
|
+
})
|
|
2150
|
+
.docs({
|
|
2151
|
+
description: "Array of messages to display in the chat",
|
|
2152
|
+
}),
|
|
2153
|
+
suggestions: Prop.array<string>()
|
|
2154
|
+
.propertiesPanel({
|
|
2155
|
+
label: "Suggestions",
|
|
2156
|
+
controlType: "FUNCTION_CODE_EDITOR",
|
|
2157
|
+
description: "Array of suggestions to display in the chat",
|
|
2158
|
+
})
|
|
2159
|
+
.docs({
|
|
2160
|
+
description: "Array of suggestions to display in the chat",
|
|
2161
|
+
}),
|
|
2162
|
+
status: Prop.string<
|
|
2163
|
+
"submitted" | "streaming" | "ready" | "error"
|
|
2164
|
+
>().propertiesPanel({
|
|
2165
|
+
label: "Status",
|
|
2166
|
+
controlType: "DROP_DOWN",
|
|
2167
|
+
options: [
|
|
2168
|
+
{ label: "Submitted", value: "submitted" },
|
|
2169
|
+
{ label: "Streaming", value: "streaming" },
|
|
2170
|
+
{ label: "Ready", value: "ready" },
|
|
2171
|
+
{ label: "Error", value: "error" },
|
|
2172
|
+
],
|
|
2173
|
+
}),
|
|
2174
|
+
}),
|
|
2175
|
+
events: Section.category(PropsCategory.EventHandlers).children({
|
|
2176
|
+
onSubmit: Prop.eventHandler().propertiesPanel({
|
|
2177
|
+
label: "onSubmit",
|
|
2178
|
+
description: "Triggered when the user submits a message",
|
|
2179
|
+
computedArgs: [
|
|
2180
|
+
{
|
|
2181
|
+
name: "text",
|
|
2182
|
+
type: "string",
|
|
2183
|
+
description: "The message to be submitted",
|
|
2184
|
+
},
|
|
2185
|
+
],
|
|
2186
|
+
}),
|
|
2187
|
+
onInputChange: Prop.eventHandler().propertiesPanel({
|
|
2188
|
+
label: "onInputChange",
|
|
2189
|
+
description: "Triggered when the user changes the input",
|
|
2190
|
+
computedArgs: [
|
|
2191
|
+
{
|
|
2192
|
+
name: "text",
|
|
2193
|
+
type: "string",
|
|
2194
|
+
description: "The input value",
|
|
2195
|
+
},
|
|
2196
|
+
],
|
|
2197
|
+
}),
|
|
2198
|
+
onSuggestionClick: Prop.eventHandler().propertiesPanel({
|
|
2199
|
+
label: "onSuggestionClick",
|
|
2200
|
+
description: "Triggered when the user clicks a suggestion",
|
|
2201
|
+
computedArgs: [
|
|
2202
|
+
{
|
|
2203
|
+
name: "text",
|
|
2204
|
+
type: "string",
|
|
2205
|
+
description: "The suggestion text",
|
|
2206
|
+
},
|
|
2207
|
+
],
|
|
2208
|
+
}),
|
|
2209
|
+
}),
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
// Editor Configuration
|
|
2213
|
+
const editorConfig: EditorConfig = {
|
|
2214
|
+
icon: "custom", // TODO: add icon
|
|
2215
|
+
description: "A chat component",
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2218
|
+
// Registration
|
|
2219
|
+
registerComponent(Chat, propertiesDefinition).editorConfig(editorConfig);
|
|
2220
|
+
|
|
2221
|
+
export {
|
|
2222
|
+
Chat,
|
|
2223
|
+
Conversation,
|
|
2224
|
+
ConversationContent,
|
|
2225
|
+
ConversationScrollButton,
|
|
2226
|
+
Message,
|
|
2227
|
+
MessageContent,
|
|
2228
|
+
MessageAvatar,
|
|
2229
|
+
PromptInput,
|
|
2230
|
+
PromptInputTextarea,
|
|
2231
|
+
PromptInputToolbar,
|
|
2232
|
+
PromptInputTools,
|
|
2233
|
+
PromptInputButton,
|
|
2234
|
+
PromptInputSubmit,
|
|
2235
|
+
PromptInputModelSelect,
|
|
2236
|
+
PromptInputModelSelectTrigger,
|
|
2237
|
+
PromptInputModelSelectContent,
|
|
2238
|
+
PromptInputModelSelectItem,
|
|
2239
|
+
PromptInputModelSelectValue,
|
|
2240
|
+
Suggestions,
|
|
2241
|
+
Suggestion,
|
|
2242
|
+
Actions,
|
|
2243
|
+
Action,
|
|
2244
|
+
Branch,
|
|
2245
|
+
BranchMessages,
|
|
2246
|
+
BranchSelector,
|
|
2247
|
+
BranchPrevious,
|
|
2248
|
+
BranchNext,
|
|
2249
|
+
BranchPage,
|
|
2250
|
+
Carousel,
|
|
2251
|
+
CarouselContent,
|
|
2252
|
+
CarouselItem,
|
|
2253
|
+
CarouselPrevious,
|
|
2254
|
+
CarouselNext,
|
|
2255
|
+
CodeBlock,
|
|
2256
|
+
CodeBlockCopyButton,
|
|
2257
|
+
Collapsible,
|
|
2258
|
+
CollapsibleTrigger,
|
|
2259
|
+
CollapsibleContent,
|
|
2260
|
+
HoverCard,
|
|
2261
|
+
HoverCardTrigger,
|
|
2262
|
+
HoverCardContent,
|
|
2263
|
+
InlineCitation,
|
|
2264
|
+
InlineCitationText,
|
|
2265
|
+
InlineCitationCard,
|
|
2266
|
+
InlineCitationCardTrigger,
|
|
2267
|
+
InlineCitationCardBody,
|
|
2268
|
+
InlineCitationCarousel,
|
|
2269
|
+
InlineCitationCarouselContent,
|
|
2270
|
+
InlineCitationCarouselItem,
|
|
2271
|
+
InlineCitationCarouselHeader,
|
|
2272
|
+
InlineCitationCarouselIndex,
|
|
2273
|
+
InlineCitationCarouselPrev,
|
|
2274
|
+
InlineCitationCarouselNext,
|
|
2275
|
+
InlineCitationSource,
|
|
2276
|
+
InlineCitationQuote,
|
|
2277
|
+
Loader,
|
|
2278
|
+
Reasoning,
|
|
2279
|
+
ReasoningTrigger,
|
|
2280
|
+
ReasoningContent,
|
|
2281
|
+
Response,
|
|
2282
|
+
ScrollArea,
|
|
2283
|
+
ScrollBar,
|
|
2284
|
+
Sources,
|
|
2285
|
+
SourcesTrigger,
|
|
2286
|
+
SourcesContent,
|
|
2287
|
+
Source,
|
|
2288
|
+
Task,
|
|
2289
|
+
TaskTrigger,
|
|
2290
|
+
TaskContent,
|
|
2291
|
+
TaskItem,
|
|
2292
|
+
TaskItemFile,
|
|
2293
|
+
Tool,
|
|
2294
|
+
ToolHeader,
|
|
2295
|
+
ToolContent,
|
|
2296
|
+
ToolInput,
|
|
2297
|
+
ToolOutput,
|
|
2298
|
+
Tooltip,
|
|
2299
|
+
TooltipTrigger,
|
|
2300
|
+
TooltipContent,
|
|
2301
|
+
TooltipProvider,
|
|
2302
|
+
type CarouselApi,
|
|
2303
|
+
};
|