veryfront 0.1.997 → 0.1.998
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/esm/cli/templates/manifest.js +1 -1
- package/esm/deno.d.ts +7 -0
- package/esm/deno.js +11 -3
- package/esm/deps/esm.sh/react-dom@19.2.4.d.ts +2 -0
- package/esm/deps/esm.sh/react-dom@19.2.4.d.ts.map +1 -0
- package/esm/deps/esm.sh/react-dom@19.2.4.js +3 -0
- package/esm/react/react-dom.d.ts +3 -0
- package/esm/react/react-dom.d.ts.map +1 -0
- package/esm/react/react-dom.js +2 -0
- package/esm/src/agent/ag-ui/host-support.d.ts.map +1 -1
- package/esm/src/agent/ag-ui/host-support.js +12 -0
- package/esm/src/agent/react/index.d.ts +4 -2
- package/esm/src/agent/react/index.d.ts.map +1 -1
- package/esm/src/agent/react/index.js +2 -1
- package/esm/src/agent/react/use-agent-metadata.d.ts +5 -0
- package/esm/src/agent/react/use-agent-metadata.d.ts.map +1 -1
- package/esm/src/agent/react/use-agent-metadata.js +18 -9
- package/esm/src/agent/react/use-agents.d.ts +33 -0
- package/esm/src/agent/react/use-agents.d.ts.map +1 -0
- package/esm/src/agent/react/use-agents.js +72 -0
- package/esm/src/agent/react/use-chat/index.d.ts +1 -1
- package/esm/src/agent/react/use-chat/index.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/types.d.ts +3 -2
- package/esm/src/agent/react/use-chat/types.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/use-chat.d.ts +12 -1
- package/esm/src/agent/react/use-chat/use-chat.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/use-chat.js +47 -7
- package/esm/src/agent/react/use-chat/utils.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/utils.js +4 -0
- package/esm/src/agent/runtime/message-adapter.d.ts.map +1 -1
- package/esm/src/agent/runtime/message-adapter.js +3 -1
- package/esm/src/agent/runtime/text-generation-runtime-message-converter.d.ts.map +1 -1
- package/esm/src/agent/runtime/text-generation-runtime-message-converter.js +6 -2
- package/esm/src/chat/index.d.ts +17 -7
- package/esm/src/chat/index.d.ts.map +1 -1
- package/esm/src/chat/index.js +33 -7
- package/esm/src/chat/protocol.d.ts +13 -1
- package/esm/src/chat/protocol.d.ts.map +1 -1
- package/esm/src/chat/upload-handler.d.ts +55 -0
- package/esm/src/chat/upload-handler.d.ts.map +1 -0
- package/esm/src/chat/upload-handler.js +180 -0
- package/esm/src/discovery/import-rewriter.d.ts +1 -1
- package/esm/src/discovery/import-rewriter.d.ts.map +1 -1
- package/esm/src/discovery/import-rewriter.js +2 -0
- package/esm/src/discovery/transpiler.d.ts.map +1 -1
- package/esm/src/discovery/transpiler.js +2 -0
- package/esm/src/react/components/chat/agent-card.d.ts +84 -11
- package/esm/src/react/components/chat/agent-card.d.ts.map +1 -1
- package/esm/src/react/components/chat/agent-card.js +151 -57
- package/esm/src/react/components/chat/agent-picker.d.ts +166 -0
- package/esm/src/react/components/chat/agent-picker.d.ts.map +1 -0
- package/esm/src/react/components/chat/agent-picker.js +183 -0
- package/esm/src/react/components/chat/chat/components/animations.js +1 -1
- package/esm/src/react/components/chat/chat/components/attachment-pill.d.ts +125 -3
- package/esm/src/react/components/chat/chat/components/attachment-pill.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/attachment-pill.js +209 -25
- package/esm/src/react/components/chat/chat/components/attachments-panel.d.ts +149 -0
- package/esm/src/react/components/chat/chat/components/attachments-panel.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/components/attachments-panel.js +187 -0
- package/esm/src/react/components/chat/chat/components/branch-picker.d.ts +13 -2
- package/esm/src/react/components/chat/chat/components/branch-picker.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/branch-picker.js +6 -8
- package/esm/src/react/components/chat/chat/components/chat-messages-skeleton.d.ts +20 -0
- package/esm/src/react/components/chat/chat/components/chat-messages-skeleton.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/components/chat-messages-skeleton.js +31 -0
- package/esm/src/react/components/chat/chat/components/code-block.d.ts +9 -1
- package/esm/src/react/components/chat/chat/components/code-block.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/code-block.js +14 -6
- package/esm/src/react/components/chat/chat/components/drop-zone.d.ts +11 -4
- package/esm/src/react/components/chat/chat/components/drop-zone.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/drop-zone.js +10 -8
- package/esm/src/react/components/chat/chat/components/empty-state.d.ts +2 -1
- package/esm/src/react/components/chat/chat/components/empty-state.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/empty-state.js +7 -8
- package/esm/src/react/components/chat/chat/components/inference-badge.d.ts +6 -2
- package/esm/src/react/components/chat/chat/components/inference-badge.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/inference-badge.js +5 -4
- package/esm/src/react/components/chat/chat/components/inline-citation.d.ts +9 -1
- package/esm/src/react/components/chat/chat/components/inline-citation.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/inline-citation.js +19 -20
- package/esm/src/react/components/chat/chat/components/message-actions.d.ts +28 -5
- package/esm/src/react/components/chat/chat/components/message-actions.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/message-actions.js +24 -36
- package/esm/src/react/components/chat/chat/components/message-edit-form.d.ts +7 -2
- package/esm/src/react/components/chat/chat/components/message-edit-form.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/message-edit-form.js +5 -5
- package/esm/src/react/components/chat/chat/components/message-feedback.d.ts +11 -1
- package/esm/src/react/components/chat/chat/components/message-feedback.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/message-feedback.js +9 -11
- package/esm/src/react/components/chat/chat/components/quick-actions.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/quick-actions.js +3 -1
- package/esm/src/react/components/chat/chat/components/reasoning.d.ts +66 -3
- package/esm/src/react/components/chat/chat/components/reasoning.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/reasoning.js +95 -21
- package/esm/src/react/components/chat/chat/components/sidebar.d.ts +178 -12
- package/esm/src/react/components/chat/chat/components/sidebar.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/sidebar.js +228 -45
- package/esm/src/react/components/chat/chat/components/skill-badge.d.ts +6 -1
- package/esm/src/react/components/chat/chat/components/skill-badge.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/skill-badge.js +6 -5
- package/esm/src/react/components/chat/chat/components/skill-tool.d.ts +37 -0
- package/esm/src/react/components/chat/chat/components/skill-tool.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/components/skill-tool.js +46 -0
- package/esm/src/react/components/chat/chat/components/sources.d.ts +55 -3
- package/esm/src/react/components/chat/chat/components/sources.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/sources.js +50 -16
- package/esm/src/react/components/chat/chat/components/step-indicator.d.ts +42 -3
- package/esm/src/react/components/chat/chat/components/step-indicator.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/step-indicator.js +55 -12
- package/esm/src/react/components/chat/chat/components/tab-switcher.d.ts +1 -1
- package/esm/src/react/components/chat/chat/components/tab-switcher.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/tab-switcher.js +3 -3
- package/esm/src/react/components/chat/chat/components/tool-ui.d.ts +96 -3
- package/esm/src/react/components/chat/chat/components/tool-ui.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/components/tool-ui.js +158 -36
- package/esm/src/react/components/chat/chat/composition/api.d.ts +4 -2
- package/esm/src/react/components/chat/chat/composition/api.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/api.js +4 -2
- package/esm/src/react/components/chat/chat/composition/chat-composer.d.ts +112 -5
- package/esm/src/react/components/chat/chat/composition/chat-composer.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/chat-composer.js +251 -61
- package/esm/src/react/components/chat/chat/composition/chat-empty-state.d.ts +78 -0
- package/esm/src/react/components/chat/chat/composition/chat-empty-state.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/composition/chat-empty-state.js +63 -0
- package/esm/src/react/components/chat/chat/composition/chat-empty.d.ts +4 -1
- package/esm/src/react/components/chat/chat/composition/chat-empty.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/chat-empty.js +11 -10
- package/esm/src/react/components/chat/chat/composition/chat-message-list.d.ts +11 -3
- package/esm/src/react/components/chat/chat/composition/chat-message-list.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/chat-message-list.js +81 -111
- package/esm/src/react/components/chat/chat/composition/chat-root.d.ts +4 -0
- package/esm/src/react/components/chat/chat/composition/chat-root.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/chat-root.js +3 -1
- package/esm/src/react/components/chat/chat/composition/error-banner.d.ts +4 -0
- package/esm/src/react/components/chat/chat/composition/error-banner.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/error-banner.js +10 -7
- package/esm/src/react/components/chat/chat/composition/message.d.ts +123 -5
- package/esm/src/react/components/chat/chat/composition/message.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/message.js +332 -53
- package/esm/src/react/components/chat/chat/composition/model-avatar.d.ts +6 -1
- package/esm/src/react/components/chat/chat/composition/model-avatar.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/composition/model-avatar.js +3 -4
- package/esm/src/react/components/chat/chat/composition/pending-message.d.ts +31 -0
- package/esm/src/react/components/chat/chat/composition/pending-message.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/composition/pending-message.js +31 -0
- package/esm/src/react/components/chat/chat/contexts/chat-context.d.ts +4 -0
- package/esm/src/react/components/chat/chat/contexts/chat-context.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/contexts/composer-context.d.ts +1 -0
- package/esm/src/react/components/chat/chat/contexts/composer-context.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/contexts/conversations-context.d.ts +29 -0
- package/esm/src/react/components/chat/chat/contexts/conversations-context.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/contexts/conversations-context.js +39 -0
- package/esm/src/react/components/chat/chat/contexts/index.d.ts +1 -1
- package/esm/src/react/components/chat/chat/contexts/index.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/contexts/index.js +1 -1
- package/esm/src/react/components/chat/chat/contexts/message-context.d.ts +2 -0
- package/esm/src/react/components/chat/chat/contexts/message-context.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/hooks/use-clipboard.d.ts +10 -0
- package/esm/src/react/components/chat/chat/hooks/use-clipboard.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-clipboard.js +34 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversation.d.ts +21 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversation.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversation.js +54 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversations.d.ts +67 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversations.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-conversations.js +317 -0
- package/esm/src/react/components/chat/chat/hooks/use-drop-zone.d.ts +27 -0
- package/esm/src/react/components/chat/chat/hooks/use-drop-zone.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-drop-zone.js +50 -0
- package/esm/src/react/components/chat/chat/hooks/use-stick-to-bottom.d.ts +49 -0
- package/esm/src/react/components/chat/chat/hooks/use-stick-to-bottom.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-stick-to-bottom.js +120 -0
- package/esm/src/react/components/chat/chat/hooks/use-upload.d.ts +34 -0
- package/esm/src/react/components/chat/chat/hooks/use-upload.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-upload.js +147 -0
- package/esm/src/react/components/chat/chat/hooks/use-uploads-registry.d.ts +37 -0
- package/esm/src/react/components/chat/chat/hooks/use-uploads-registry.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/hooks/use-uploads-registry.js +178 -0
- package/esm/src/react/components/chat/chat/index.d.ts +171 -31
- package/esm/src/react/components/chat/chat/index.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/index.js +371 -77
- package/esm/src/react/components/chat/chat/persistence/conversation-store.d.ts +63 -0
- package/esm/src/react/components/chat/chat/persistence/conversation-store.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/persistence/conversation-store.js +1 -0
- package/esm/src/react/components/chat/chat/persistence/local-conversation-store.d.ts +13 -0
- package/esm/src/react/components/chat/chat/persistence/local-conversation-store.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/persistence/local-conversation-store.js +102 -0
- package/esm/src/react/components/chat/chat/persistence/memory-conversation-store.d.ts +11 -0
- package/esm/src/react/components/chat/chat/persistence/memory-conversation-store.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat/persistence/memory-conversation-store.js +33 -0
- package/esm/src/react/components/chat/chat/utils/message-parts.d.ts +4 -1
- package/esm/src/react/components/chat/chat/utils/message-parts.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat/utils/message-parts.js +4 -0
- package/esm/src/react/components/chat/chat-actions.d.ts +164 -0
- package/esm/src/react/components/chat/chat-actions.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat-actions.js +191 -0
- package/esm/src/react/components/chat/chat-agent-picker.d.ts +50 -0
- package/esm/src/react/components/chat/chat-agent-picker.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat-agent-picker.js +40 -0
- package/esm/src/react/components/chat/chat-theme-scope.d.ts +26 -0
- package/esm/src/react/components/chat/chat-theme-scope.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat-theme-scope.js +27 -0
- package/esm/src/react/components/chat/chat-tokens-style.d.ts +21 -0
- package/esm/src/react/components/chat/chat-tokens-style.d.ts.map +1 -0
- package/esm/src/react/components/chat/chat-tokens-style.js +24 -0
- package/esm/src/react/components/chat/chat.d.ts +5 -5
- package/esm/src/react/components/chat/chat.d.ts.map +1 -1
- package/esm/src/react/components/chat/chat.js +5 -5
- package/esm/src/react/components/chat/color-mode.d.ts +46 -0
- package/esm/src/react/components/chat/color-mode.d.ts.map +1 -0
- package/esm/src/react/components/chat/color-mode.js +123 -0
- package/esm/src/react/components/chat/error-boundary.js +2 -2
- package/esm/src/react/components/chat/icons/index.d.ts +8 -0
- package/esm/src/react/components/chat/icons/index.d.ts.map +1 -1
- package/esm/src/react/components/chat/icons/index.js +64 -0
- package/esm/src/react/components/chat/markdown.d.ts +24 -1
- package/esm/src/react/components/chat/markdown.d.ts.map +1 -1
- package/esm/src/react/components/chat/markdown.js +122 -38
- package/esm/src/react/components/chat/model-selector.d.ts +130 -8
- package/esm/src/react/components/chat/model-selector.d.ts.map +1 -1
- package/esm/src/react/components/chat/model-selector.js +148 -121
- package/esm/src/react/components/chat/theme.d.ts +9 -12
- package/esm/src/react/components/chat/theme.d.ts.map +1 -1
- package/esm/src/react/components/chat/theme.js +149 -93
- package/esm/src/react/components/chat/ui/alert.d.ts +30 -0
- package/esm/src/react/components/chat/ui/alert.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/alert.js +35 -0
- package/esm/src/react/components/chat/ui/app-shell.d.ts +155 -0
- package/esm/src/react/components/chat/ui/app-shell.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/app-shell.js +276 -0
- package/esm/src/react/components/chat/ui/avatar.d.ts +24 -0
- package/esm/src/react/components/chat/ui/avatar.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/avatar.js +44 -0
- package/esm/src/react/components/chat/ui/badge.d.ts +24 -0
- package/esm/src/react/components/chat/ui/badge.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/badge.js +30 -0
- package/esm/src/react/components/chat/ui/button.d.ts +44 -0
- package/esm/src/react/components/chat/ui/button.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/button.js +108 -0
- package/esm/src/react/components/chat/ui/card.d.ts +42 -0
- package/esm/src/react/components/chat/ui/card.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/card.js +55 -0
- package/esm/src/react/components/chat/ui/checkbox.d.ts +28 -0
- package/esm/src/react/components/chat/ui/checkbox.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/checkbox.js +36 -0
- package/esm/src/react/components/chat/ui/code-block.d.ts +89 -0
- package/esm/src/react/components/chat/ui/code-block.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/code-block.js +277 -0
- package/esm/src/react/components/chat/ui/collapsible.d.ts +30 -0
- package/esm/src/react/components/chat/ui/collapsible.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/collapsible.js +44 -0
- package/esm/src/react/components/chat/ui/command.d.ts +65 -0
- package/esm/src/react/components/chat/ui/command.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/command.js +121 -0
- package/esm/src/react/components/chat/ui/cva.d.ts +36 -0
- package/esm/src/react/components/chat/ui/cva.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/cva.js +45 -0
- package/esm/src/react/components/chat/ui/dialog.d.ts +55 -0
- package/esm/src/react/components/chat/ui/dialog.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/dialog.js +116 -0
- package/esm/src/react/components/chat/ui/drawer.d.ts +41 -0
- package/esm/src/react/components/chat/ui/drawer.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/drawer.js +92 -0
- package/esm/src/react/components/chat/ui/dropdown-menu.d.ts +63 -0
- package/esm/src/react/components/chat/ui/dropdown-menu.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/dropdown-menu.js +76 -0
- package/esm/src/react/components/chat/ui/file-type.d.ts +26 -0
- package/esm/src/react/components/chat/ui/file-type.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/file-type.js +235 -0
- package/esm/src/react/components/chat/ui/floating.d.ts +29 -0
- package/esm/src/react/components/chat/ui/floating.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/floating.js +90 -0
- package/esm/src/react/components/chat/ui/icon-button.d.ts +19 -0
- package/esm/src/react/components/chat/ui/icon-button.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/icon-button.js +20 -0
- package/esm/src/react/components/chat/ui/index.d.ts +43 -0
- package/esm/src/react/components/chat/ui/index.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/index.js +42 -0
- package/esm/src/react/components/chat/ui/input.d.ts +27 -0
- package/esm/src/react/components/chat/ui/input.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/input.js +41 -0
- package/esm/src/react/components/chat/ui/label.d.ts +29 -0
- package/esm/src/react/components/chat/ui/label.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/label.js +40 -0
- package/esm/src/react/components/chat/ui/list.d.ts +47 -0
- package/esm/src/react/components/chat/ui/list.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/list.js +40 -0
- package/esm/src/react/components/chat/ui/pill.d.ts +26 -0
- package/esm/src/react/components/chat/ui/pill.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/pill.js +39 -0
- package/esm/src/react/components/chat/ui/popover.d.ts +47 -0
- package/esm/src/react/components/chat/ui/popover.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/popover.js +68 -0
- package/esm/src/react/components/chat/ui/progress-bar.d.ts +20 -0
- package/esm/src/react/components/chat/ui/progress-bar.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/progress-bar.js +31 -0
- package/esm/src/react/components/chat/ui/radio.d.ts +25 -0
- package/esm/src/react/components/chat/ui/radio.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/radio.js +29 -0
- package/esm/src/react/components/chat/ui/scroll-fade.d.ts +23 -0
- package/esm/src/react/components/chat/ui/scroll-fade.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/scroll-fade.js +40 -0
- package/esm/src/react/components/chat/ui/select.d.ts +64 -0
- package/esm/src/react/components/chat/ui/select.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/select.js +148 -0
- package/esm/src/react/components/chat/ui/shimmer.d.ts +25 -0
- package/esm/src/react/components/chat/ui/shimmer.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/shimmer.js +24 -0
- package/esm/src/react/components/chat/ui/skeleton.d.ts +14 -0
- package/esm/src/react/components/chat/ui/skeleton.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/skeleton.js +12 -0
- package/esm/src/react/components/chat/ui/slot.d.ts +21 -0
- package/esm/src/react/components/chat/ui/slot.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/slot.js +70 -0
- package/esm/src/react/components/chat/ui/status.d.ts +28 -0
- package/esm/src/react/components/chat/ui/status.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/status.js +23 -0
- package/esm/src/react/components/chat/ui/switch.d.ts +34 -0
- package/esm/src/react/components/chat/ui/switch.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/switch.js +58 -0
- package/esm/src/react/components/chat/ui/tabs.d.ts +44 -0
- package/esm/src/react/components/chat/ui/tabs.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/tabs.js +51 -0
- package/esm/src/react/components/chat/ui/tag.d.ts +22 -0
- package/esm/src/react/components/chat/ui/tag.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/tag.js +27 -0
- package/esm/src/react/components/chat/ui/textarea.d.ts +22 -0
- package/esm/src/react/components/chat/ui/textarea.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/textarea.js +35 -0
- package/esm/src/react/components/chat/ui/tooltip.d.ts +40 -0
- package/esm/src/react/components/chat/ui/tooltip.d.ts.map +1 -0
- package/esm/src/react/components/chat/ui/tooltip.js +132 -0
- package/esm/src/react/primitives/input-box.d.ts.map +1 -1
- package/esm/src/react/primitives/input-box.js +17 -4
- package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/framework-candidates.generated.js +9437 -1064
- package/esm/src/server/handlers/dev/projects/html-shell.d.ts +1 -1
- package/esm/src/server/handlers/dev/projects/html-shell.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/projects/html-shell.js +1 -12
- package/esm/src/server/handlers/request/public-agents-list.handler.d.ts +18 -0
- package/esm/src/server/handlers/request/public-agents-list.handler.d.ts.map +1 -0
- package/esm/src/server/handlers/request/public-agents-list.handler.js +44 -0
- package/esm/src/server/runtime-handler/index.d.ts +1 -1
- package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/index.js +3 -0
- package/esm/src/studio/bridge/bridge-bundle.generated.d.ts +1 -1
- package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
- package/esm/src/studio/bridge/bridge-bundle.generated.js +3 -2
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/esm/src/workflow/blob/local-storage.d.ts +32 -0
- package/esm/src/workflow/blob/local-storage.d.ts.map +1 -0
- package/esm/src/workflow/blob/local-storage.js +179 -0
- package/esm/src/workflow/blob/types.d.ts +5 -0
- package/esm/src/workflow/blob/types.d.ts.map +1 -1
- package/esm/src/workflow/blob/veryfront-cloud-storage.d.ts +11 -0
- package/esm/src/workflow/blob/veryfront-cloud-storage.d.ts.map +1 -1
- package/esm/src/workflow/blob/veryfront-cloud-storage.js +36 -0
- package/package.json +5 -1
- package/esm/src/react/components/chat/chat/components/uploads-panel.d.ts +0 -20
- package/esm/src/react/components/chat/chat/components/uploads-panel.d.ts.map +0 -1
- package/esm/src/react/components/chat/chat/components/uploads-panel.js +0 -49
- package/esm/src/react/components/chat/chat/contexts/thread-list-context.d.ts +0 -28
- package/esm/src/react/components/chat/chat/contexts/thread-list-context.d.ts.map +0 -1
- package/esm/src/react/components/chat/chat/contexts/thread-list-context.js +0 -27
- package/esm/src/react/components/chat/chat/hooks/use-threads.d.ts +0 -28
- package/esm/src/react/components/chat/chat/hooks/use-threads.d.ts.map +0 -1
- package/esm/src/react/components/chat/chat/hooks/use-threads.js +0 -151
- package/esm/src/react/components/chat/chat-with-sidebar.d.ts +0 -110
- package/esm/src/react/components/chat/chat-with-sidebar.d.ts.map +0 -1
- package/esm/src/react/components/chat/chat-with-sidebar.js +0 -167
- package/esm/src/react/components/chat/message.d.ts +0 -42
- package/esm/src/react/components/chat/message.d.ts.map +0 -1
- package/esm/src/react/components/chat/message.js +0 -69
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
2
|
+
import { dirname, join } from "../../platform/compat/path/index.js";
|
|
3
|
+
import { createFileSystem } from "../../platform/compat/fs.js";
|
|
4
|
+
import { agentLogger } from "../../utils/index.js";
|
|
5
|
+
import { INVALID_ARGUMENT } from "../../errors/index.js";
|
|
6
|
+
const logger = agentLogger.component("local-blob-storage");
|
|
7
|
+
export class LocalBlobStorage {
|
|
8
|
+
rootDir;
|
|
9
|
+
baseUrl;
|
|
10
|
+
fs;
|
|
11
|
+
now;
|
|
12
|
+
constructor(rootDir, baseUrl, options) {
|
|
13
|
+
this.rootDir = rootDir;
|
|
14
|
+
this.baseUrl = baseUrl;
|
|
15
|
+
this.fs = createFileSystem();
|
|
16
|
+
this.now = options?.now ?? (() => new Date());
|
|
17
|
+
}
|
|
18
|
+
getPath(id) {
|
|
19
|
+
// Partition by first 2 chars to avoid too many files in one dir
|
|
20
|
+
const prefix = id.slice(0, 2);
|
|
21
|
+
return join(this.rootDir, prefix, id);
|
|
22
|
+
}
|
|
23
|
+
getMetadataPath(id) {
|
|
24
|
+
return `${this.getPath(id)}.meta.json`;
|
|
25
|
+
}
|
|
26
|
+
async put(data, options = {}) {
|
|
27
|
+
const id = options.id ?? dntShim.crypto.randomUUID();
|
|
28
|
+
const filePath = this.getPath(id);
|
|
29
|
+
const metaPath = this.getMetadataPath(id);
|
|
30
|
+
await this.fs.mkdir(dirname(filePath), { recursive: true });
|
|
31
|
+
const { bytes, size } = await this.normalizeToBytes(data);
|
|
32
|
+
await this.fs.writeFile(filePath, bytes);
|
|
33
|
+
const createdAt = this.now();
|
|
34
|
+
const expiresAt = options.ttl ? new Date(createdAt.getTime() + options.ttl * 1000) : undefined;
|
|
35
|
+
const ref = {
|
|
36
|
+
__kind: "blob",
|
|
37
|
+
id,
|
|
38
|
+
size,
|
|
39
|
+
mimeType: options.mimeType ?? "application/octet-stream",
|
|
40
|
+
createdAt,
|
|
41
|
+
expiresAt,
|
|
42
|
+
metadata: options.metadata,
|
|
43
|
+
url: this.baseUrl ? `${this.baseUrl}/${id}` : undefined,
|
|
44
|
+
};
|
|
45
|
+
await this.fs.writeTextFile(metaPath, JSON.stringify(ref));
|
|
46
|
+
return ref;
|
|
47
|
+
}
|
|
48
|
+
async normalizeToBytes(data) {
|
|
49
|
+
if (typeof data === "string") {
|
|
50
|
+
const bytes = new TextEncoder().encode(data);
|
|
51
|
+
return { bytes, size: bytes.length };
|
|
52
|
+
}
|
|
53
|
+
if (data instanceof Uint8Array)
|
|
54
|
+
return { bytes: data, size: data.length };
|
|
55
|
+
if (data instanceof Blob) {
|
|
56
|
+
const bytes = new Uint8Array(await data.arrayBuffer());
|
|
57
|
+
return { bytes, size: data.size };
|
|
58
|
+
}
|
|
59
|
+
if (data instanceof ReadableStream) {
|
|
60
|
+
// Normalize stream to bytes for cross-runtime compatibility
|
|
61
|
+
const bytes = new Uint8Array(await new Response(data).arrayBuffer());
|
|
62
|
+
return { bytes, size: bytes.length };
|
|
63
|
+
}
|
|
64
|
+
throw INVALID_ARGUMENT.create({ detail: "Unsupported data type for LocalBlobStorage" });
|
|
65
|
+
}
|
|
66
|
+
async getStream(id) {
|
|
67
|
+
const bytes = await this.getBytes(id);
|
|
68
|
+
if (!bytes)
|
|
69
|
+
return null;
|
|
70
|
+
// Create a minimal cross-runtime ReadableStream from the bytes
|
|
71
|
+
return new ReadableStream({
|
|
72
|
+
start(controller) {
|
|
73
|
+
controller.enqueue(bytes);
|
|
74
|
+
controller.close();
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async getText(id) {
|
|
79
|
+
try {
|
|
80
|
+
return await this.fs.readTextFile(this.getPath(id));
|
|
81
|
+
}
|
|
82
|
+
catch (_) {
|
|
83
|
+
/* expected: file not found returns null */
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async getBytes(id) {
|
|
88
|
+
try {
|
|
89
|
+
return await this.fs.readFile(this.getPath(id));
|
|
90
|
+
}
|
|
91
|
+
catch (_) {
|
|
92
|
+
/* expected: file not found returns null */
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async delete(id) {
|
|
97
|
+
try {
|
|
98
|
+
await this.fs.remove(this.getPath(id));
|
|
99
|
+
await this.fs.remove(this.getMetadataPath(id));
|
|
100
|
+
}
|
|
101
|
+
catch (_) {
|
|
102
|
+
/* expected: file may not exist during cleanup */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async exists(id) {
|
|
106
|
+
return this.fs.exists(this.getPath(id));
|
|
107
|
+
}
|
|
108
|
+
async stat(id) {
|
|
109
|
+
try {
|
|
110
|
+
const json = await this.fs.readTextFile(this.getMetadataPath(id));
|
|
111
|
+
const data = JSON.parse(json);
|
|
112
|
+
return {
|
|
113
|
+
...data,
|
|
114
|
+
createdAt: new Date(data.createdAt),
|
|
115
|
+
expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (_) {
|
|
119
|
+
/* expected: metadata file not found or invalid JSON */
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Enumerate every stored (non-expired) blob, newest first. Walks the same
|
|
125
|
+
* two-char-prefix partitions as {@link cleanupExpiredBlobs}, reading each
|
|
126
|
+
* `.meta.json` sidecar. Used by the chat upload handler's list endpoint.
|
|
127
|
+
*/
|
|
128
|
+
async list() {
|
|
129
|
+
const now = this.now();
|
|
130
|
+
const refs = [];
|
|
131
|
+
for (let i = 0; i < 256; i++) {
|
|
132
|
+
const prefix = i.toString(16).padStart(2, "0");
|
|
133
|
+
const prefixDir = join(this.rootDir, prefix);
|
|
134
|
+
try {
|
|
135
|
+
for await (const entry of this.fs.readDir(prefixDir)) {
|
|
136
|
+
if (!entry.isFile || !entry.name.endsWith(".meta.json"))
|
|
137
|
+
continue;
|
|
138
|
+
const id = entry.name.replace(".meta.json", "");
|
|
139
|
+
const ref = await this.stat(id);
|
|
140
|
+
if (!ref)
|
|
141
|
+
continue;
|
|
142
|
+
if (ref.expiresAt && ref.expiresAt < now)
|
|
143
|
+
continue;
|
|
144
|
+
refs.push(ref);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (_) {
|
|
148
|
+
/* expected: partition directory may not exist yet */
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return refs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Cleans up all expired blobs from storage.
|
|
155
|
+
* This method should typically be run periodically by an external process.
|
|
156
|
+
*/
|
|
157
|
+
async cleanupExpiredBlobs() {
|
|
158
|
+
const now = this.now();
|
|
159
|
+
for (let i = 0; i < 256; i++) {
|
|
160
|
+
const prefix = i.toString(16).padStart(2, "0");
|
|
161
|
+
const prefixDir = join(this.rootDir, prefix);
|
|
162
|
+
try {
|
|
163
|
+
for await (const entry of this.fs.readDir(prefixDir)) {
|
|
164
|
+
if (!entry.isFile || !entry.name.endsWith(".meta.json"))
|
|
165
|
+
continue;
|
|
166
|
+
const id = entry.name.replace(".meta.json", "");
|
|
167
|
+
const blobRef = await this.stat(id);
|
|
168
|
+
if (!blobRef?.expiresAt || blobRef.expiresAt >= now)
|
|
169
|
+
continue;
|
|
170
|
+
logger.debug(`Deleting expired blob: ${id}`);
|
|
171
|
+
await this.delete(id);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (_) {
|
|
175
|
+
/* expected: directory may not exist yet */
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -27,5 +27,10 @@ export interface BlobStorage {
|
|
|
27
27
|
delete(id: string): Promise<void>;
|
|
28
28
|
exists(id: string): Promise<boolean>;
|
|
29
29
|
stat(id: string): Promise<BlobRef | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Enumerate stored blobs, newest first. Optional — only backends that can
|
|
32
|
+
* cheaply list (e.g. local disk) implement it; callers must feature-detect.
|
|
33
|
+
*/
|
|
34
|
+
list?(): Promise<BlobRef[]>;
|
|
30
35
|
}
|
|
31
36
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/blob/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CACD,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,cAAc,EACjD,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/blob/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CACD,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,cAAc,EACjD,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1C;;;OAGG;IACH,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CAC7B"}
|
|
@@ -25,6 +25,17 @@ export declare class VeryfrontCloudBlobStorage implements BlobStorage {
|
|
|
25
25
|
delete(id: string): Promise<void>;
|
|
26
26
|
exists(id: string): Promise<boolean>;
|
|
27
27
|
stat(id: string): Promise<BlobRef | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Enumerate blobs under this store's prefix, newest first. The list endpoint
|
|
30
|
+
* (`GET /projects/{slug}/uploads`) carries only paths — the original filename
|
|
31
|
+
* lives in each blob's sidecar — so ids are recovered from the `.blob` data
|
|
32
|
+
* paths and enriched via {@link stat} (one extra request per blob). Suitable
|
|
33
|
+
* for an uploads panel; not a hot path.
|
|
34
|
+
*
|
|
35
|
+
* Note: a single page is fetched. If the project accumulates more uploads than
|
|
36
|
+
* one page, pagination will need wiring here.
|
|
37
|
+
*/
|
|
38
|
+
list(): Promise<BlobRef[]>;
|
|
28
39
|
private resolveConfig;
|
|
29
40
|
private assertSafeBlobId;
|
|
30
41
|
private getDataPath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"veryfront-cloud-storage.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/blob/veryfront-cloud-storage.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"veryfront-cloud-storage.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/blob/veryfront-cloud-storage.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiEzE,MAAM,WAAW,+BAA+B;IAC9C,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AA6FD,qBAAa,yBAA0B,YAAW,WAAW;IAC3D,OAAO,CAAC,MAAM,CAAkC;gBAEpC,MAAM,GAAE,+BAAoC;IAIlD,GAAG,CACP,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,cAAc,EACjD,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC;IAkEb,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAKrD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAM3C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAMhD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA0B/C;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IA2BhC,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,eAAe;YAKT,UAAU;YA2CV,iBAAiB;YAkBjB,YAAY;YAgBZ,cAAc;YAwBd,cAAc;YAuBd,kBAAkB;YASlB,WAAW;CAwC1B"}
|
|
@@ -30,6 +30,9 @@ const getUploadSignedUrlResponseSchema = defineSchema((v) => v.object({
|
|
|
30
30
|
signed_url: v.string().url(),
|
|
31
31
|
expires_at: v.string(),
|
|
32
32
|
}));
|
|
33
|
+
const getUploadListResponseSchema = defineSchema((v) => v.object({
|
|
34
|
+
data: v.array(v.object({ path: v.string().optional() }).passthrough()).optional(),
|
|
35
|
+
}));
|
|
33
36
|
const getBlobMetadataSchema = defineSchema((v) => v.object({
|
|
34
37
|
version: v.literal(1),
|
|
35
38
|
id: v.string(),
|
|
@@ -42,6 +45,7 @@ const getBlobMetadataSchema = defineSchema((v) => v.object({
|
|
|
42
45
|
const UploadCreateResponseSchema = lazySchema(getUploadCreateResponseSchema);
|
|
43
46
|
const UploadMetadataResponseSchema = lazySchema(getUploadMetadataResponseSchema);
|
|
44
47
|
const UploadSignedUrlResponseSchema = lazySchema(getUploadSignedUrlResponseSchema);
|
|
48
|
+
const UploadListResponseSchema = lazySchema(getUploadListResponseSchema);
|
|
45
49
|
const BlobMetadataSchema = lazySchema(getBlobMetadataSchema);
|
|
46
50
|
function normalizePrefix(prefix) {
|
|
47
51
|
const value = (prefix ?? DEFAULT_PREFIX).trim().replace(/^\/+/, "");
|
|
@@ -216,6 +220,38 @@ export class VeryfrontCloudBlobStorage {
|
|
|
216
220
|
const ref = mapUploadMetadataToRef(upload, id);
|
|
217
221
|
return await attachSignedUrl(ref, dataPath, resolved, this.getDownloadUrl.bind(this));
|
|
218
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Enumerate blobs under this store's prefix, newest first. The list endpoint
|
|
225
|
+
* (`GET /projects/{slug}/uploads`) carries only paths — the original filename
|
|
226
|
+
* lives in each blob's sidecar — so ids are recovered from the `.blob` data
|
|
227
|
+
* paths and enriched via {@link stat} (one extra request per blob). Suitable
|
|
228
|
+
* for an uploads panel; not a hot path.
|
|
229
|
+
*
|
|
230
|
+
* Note: a single page is fetched. If the project accumulates more uploads than
|
|
231
|
+
* one page, pagination will need wiring here.
|
|
232
|
+
*/
|
|
233
|
+
async list() {
|
|
234
|
+
const resolved = this.resolveConfig();
|
|
235
|
+
const raw = await this.requestJson("GET", `/projects/${encodeURIComponent(resolved.projectSlug)}/uploads`, resolved, { headers: { Accept: "application/json" }, allowNotFound: true });
|
|
236
|
+
if (!raw)
|
|
237
|
+
return [];
|
|
238
|
+
const parsed = UploadListResponseSchema.parse(raw);
|
|
239
|
+
const ids = [];
|
|
240
|
+
for (const item of parsed.data ?? []) {
|
|
241
|
+
const path = item.path;
|
|
242
|
+
// Only the data objects under our prefix — skip `.meta.json` sidecars and
|
|
243
|
+
// anything another store namespaced elsewhere.
|
|
244
|
+
if (!path || !path.startsWith(resolved.prefix) || !path.endsWith(DATA_SUFFIX))
|
|
245
|
+
continue;
|
|
246
|
+
const id = path.slice(resolved.prefix.length, path.length - DATA_SUFFIX.length);
|
|
247
|
+
if (SAFE_BLOB_ID_PATTERN.test(id))
|
|
248
|
+
ids.push(id);
|
|
249
|
+
}
|
|
250
|
+
const refs = await Promise.all(ids.map((id) => this.stat(id)));
|
|
251
|
+
return refs
|
|
252
|
+
.filter((ref) => ref !== null)
|
|
253
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
254
|
+
}
|
|
219
255
|
resolveConfig() {
|
|
220
256
|
const apiBaseUrl = this.config.apiBaseUrl ?? getVeryfrontCloudBootstrap().apiBaseUrl;
|
|
221
257
|
const apiToken = this.config.apiToken ?? getVeryfrontCloudAuthToken();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.998",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -61,6 +61,10 @@
|
|
|
61
61
|
"import": "./esm/src/chat/types.js",
|
|
62
62
|
"types": "./esm/src/chat/types.d.ts"
|
|
63
63
|
},
|
|
64
|
+
"./chat/uploads": {
|
|
65
|
+
"import": "./esm/src/chat/upload-handler.js",
|
|
66
|
+
"types": "./esm/src/chat/upload-handler.d.ts"
|
|
67
|
+
},
|
|
64
68
|
"./markdown": {
|
|
65
69
|
"import": "./esm/src/markdown/index.js",
|
|
66
70
|
"types": "./esm/src/markdown/index.d.ts"
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from "../../../../../../react/react.js";
|
|
2
|
-
/** Public API contract for uploaded file. */
|
|
3
|
-
export interface UploadedFile {
|
|
4
|
-
id: string;
|
|
5
|
-
name: string;
|
|
6
|
-
size?: number;
|
|
7
|
-
type?: string;
|
|
8
|
-
url?: string;
|
|
9
|
-
}
|
|
10
|
-
/** Props accepted by uploads panel. */
|
|
11
|
-
export interface UploadsPanelProps {
|
|
12
|
-
uploads?: UploadedFile[];
|
|
13
|
-
onRemoveUpload?: (id: string) => void;
|
|
14
|
-
onAttach?: (files: FileList) => void;
|
|
15
|
-
attachAccept?: string;
|
|
16
|
-
className?: string;
|
|
17
|
-
}
|
|
18
|
-
/** Render uploads panel. */
|
|
19
|
-
export declare function UploadsPanel({ uploads, onRemoveUpload, onAttach, attachAccept, className, }: UploadsPanelProps): React.ReactElement;
|
|
20
|
-
//# sourceMappingURL=uploads-panel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uploads-panel.d.ts","sourceRoot":"","sources":["../../../../../../../src/src/react/components/chat/chat/components/uploads-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,kCAAkC,CAAC;AAI1D,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,4BAA4B;AAC5B,wBAAgB,YAAY,CAAC,EAC3B,OAAY,EACZ,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,SAAS,GACV,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CA8GxC"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import * as React from "../../../../../../react/react.js";
|
|
2
|
-
import { cn } from "../../theme.js";
|
|
3
|
-
import { FileTextIcon, TrashIcon } from "../../icons/index.js";
|
|
4
|
-
function formatFileSize(bytes) {
|
|
5
|
-
if (bytes < 1024)
|
|
6
|
-
return `${bytes} B`;
|
|
7
|
-
if (bytes < 1024 * 1024)
|
|
8
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
9
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
10
|
-
}
|
|
11
|
-
/** Render uploads panel. */
|
|
12
|
-
export function UploadsPanel({ uploads = [], onRemoveUpload, onAttach, attachAccept, className, }) {
|
|
13
|
-
const fileInputRef = React.useRef(null);
|
|
14
|
-
return (React.createElement("div", { className: cn("flex flex-col h-full", className) },
|
|
15
|
-
React.createElement("div", { className: "flex-1 overflow-y-auto px-4 py-4" }, uploads.length === 0
|
|
16
|
-
? (React.createElement("div", { className: "flex flex-col items-center justify-center h-full text-center" },
|
|
17
|
-
React.createElement("h1", { className: "text-2xl font-medium text-[var(--foreground)] tracking-tight" }, "No files uploaded"),
|
|
18
|
-
React.createElement("p", { className: "mt-2 max-w-sm text-sm text-[var(--muted-foreground)] leading-relaxed" }, "Upload files to start asking questions"),
|
|
19
|
-
onAttach && (React.createElement("button", { type: "button", onClick: () => fileInputRef.current?.click(), className: "mt-4 px-4 py-2 text-sm font-medium rounded-full bg-[var(--primary)] text-[var(--primary-foreground)] hover:opacity-90 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2" }, "Upload Files"))))
|
|
20
|
-
: (React.createElement("div", { className: "max-w-2xl mx-auto space-y-1.5" },
|
|
21
|
-
uploads.map((doc) => (React.createElement("div", { key: doc.id, className: "flex items-center gap-3 rounded-lg px-3 py-2.5 group hover:bg-[var(--accent)] transition-colors" },
|
|
22
|
-
React.createElement("div", { className: "shrink-0 size-8 rounded-lg bg-[var(--accent)] flex items-center justify-center text-[var(--muted-foreground)]" },
|
|
23
|
-
React.createElement(FileTextIcon, { className: "size-4" })),
|
|
24
|
-
React.createElement("div", { className: "flex-1 min-w-0" },
|
|
25
|
-
doc.url
|
|
26
|
-
? (React.createElement("a", { href: doc.url, target: "_blank", rel: "noreferrer", className: "block truncate text-sm text-[var(--card-foreground)] hover:underline" }, doc.name))
|
|
27
|
-
: (React.createElement("p", { className: "text-sm text-[var(--card-foreground)] truncate" }, doc.name)),
|
|
28
|
-
doc.size != null && (React.createElement("p", { className: "text-xs text-[var(--muted-foreground)]" }, formatFileSize(doc.size)))),
|
|
29
|
-
onRemoveUpload && (React.createElement("button", { type: "button", onClick: () => onRemoveUpload(doc.id), className: "shrink-0 p-1.5 rounded-md text-[var(--muted-foreground)] hover:text-[var(--destructive)] hover:bg-[var(--destructive)]/10 transition-colors opacity-0 group-hover:opacity-100", "aria-label": `Remove ${doc.name}` },
|
|
30
|
-
React.createElement(TrashIcon, { className: "size-4" })))))),
|
|
31
|
-
onAttach && (React.createElement("button", { type: "button", onClick: () => fileInputRef.current?.click(), className: "w-full flex items-center justify-center gap-2 rounded-lg px-3 py-2.5 text-sm text-[var(--muted-foreground)] hover:bg-[var(--accent)] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2" },
|
|
32
|
-
React.createElement("span", { className: "text-xs" }, "+"),
|
|
33
|
-
"Upload more files"))))),
|
|
34
|
-
onAttach && (React.createElement("input", { ref: fileInputRef, type: "file", accept: attachAccept, multiple: true, "aria-label": "Upload file", onChange: (e) => {
|
|
35
|
-
if (e.target.files?.length)
|
|
36
|
-
onAttach(e.target.files);
|
|
37
|
-
e.target.value = "";
|
|
38
|
-
}, style: {
|
|
39
|
-
position: "absolute",
|
|
40
|
-
width: 1,
|
|
41
|
-
height: 1,
|
|
42
|
-
padding: 0,
|
|
43
|
-
margin: -1,
|
|
44
|
-
overflow: "hidden",
|
|
45
|
-
clip: "rect(0,0,0,0)",
|
|
46
|
-
whiteSpace: "nowrap",
|
|
47
|
-
border: 0,
|
|
48
|
-
} }))));
|
|
49
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ThreadListContext — Thread navigation state for multi-conversation UIs.
|
|
3
|
-
*
|
|
4
|
-
* Provided by ThreadList.Root or ChatWithSidebar.
|
|
5
|
-
* Consumed by thread list items, create/delete buttons, etc.
|
|
6
|
-
*
|
|
7
|
-
* @module react/components/chat/contexts/thread-list-context
|
|
8
|
-
*/
|
|
9
|
-
import * as React from "../../../../../../react/react.js";
|
|
10
|
-
import type { Thread } from "../hooks/use-threads.js";
|
|
11
|
-
/** Public API contract for thread list context value. */
|
|
12
|
-
export interface ThreadListContextValue {
|
|
13
|
-
threads: Thread[];
|
|
14
|
-
activeThreadId: string | null;
|
|
15
|
-
activeThread: Thread | null;
|
|
16
|
-
createThread: () => Thread;
|
|
17
|
-
selectThread: (id: string) => void;
|
|
18
|
-
deleteThread: (id: string) => void;
|
|
19
|
-
renameThread: (id: string, title: string) => void;
|
|
20
|
-
updateThread: (id: string, updates: Partial<Pick<Thread, "title" | "messages">>) => void;
|
|
21
|
-
}
|
|
22
|
-
/** Context for use thread list. */
|
|
23
|
-
export declare function useThreadListContext(): ThreadListContextValue;
|
|
24
|
-
/** React hook for thread list context optional. */
|
|
25
|
-
export declare function useThreadListContextOptional(): ThreadListContextValue | null;
|
|
26
|
-
/** Render thread list context provider. */
|
|
27
|
-
export declare const ThreadListContextProvider: React.Provider<ThreadListContextValue | null>;
|
|
28
|
-
//# sourceMappingURL=thread-list-context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"thread-list-context.d.ts","sourceRoot":"","sources":["../../../../../../../src/src/react/components/chat/chat/contexts/thread-list-context.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,MAAM,kCAAkC,CAAC;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,yDAAyD;AACzD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAG5B,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC,KAAK,IAAI,CAAC;CAC1F;AAID,mCAAmC;AACnC,wBAAgB,oBAAoB,IAAI,sBAAsB,CAQ7D;AAED,mDAAmD;AACnD,wBAAgB,4BAA4B,IAAI,sBAAsB,GAAG,IAAI,CAE5E;AAED,2CAA2C;AAC3C,eAAO,MAAM,yBAAyB,+CAA6B,CAAC"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ThreadListContext — Thread navigation state for multi-conversation UIs.
|
|
3
|
-
*
|
|
4
|
-
* Provided by ThreadList.Root or ChatWithSidebar.
|
|
5
|
-
* Consumed by thread list items, create/delete buttons, etc.
|
|
6
|
-
*
|
|
7
|
-
* @module react/components/chat/contexts/thread-list-context
|
|
8
|
-
*/
|
|
9
|
-
import * as React from "../../../../../../react/react.js";
|
|
10
|
-
import { COMPONENT_ERROR } from "../../../../../errors/error-registry.js";
|
|
11
|
-
const ThreadListContext = React.createContext(null);
|
|
12
|
-
/** Context for use thread list. */
|
|
13
|
-
export function useThreadListContext() {
|
|
14
|
-
const context = React.useContext(ThreadListContext);
|
|
15
|
-
if (!context) {
|
|
16
|
-
throw COMPONENT_ERROR.create({
|
|
17
|
-
detail: "useThreadListContext must be used within a ThreadList provider",
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
return context;
|
|
21
|
-
}
|
|
22
|
-
/** React hook for thread list context optional. */
|
|
23
|
-
export function useThreadListContextOptional() {
|
|
24
|
-
return React.useContext(ThreadListContext);
|
|
25
|
-
}
|
|
26
|
-
/** Render thread list context provider. */
|
|
27
|
-
export const ThreadListContextProvider = ThreadListContext.Provider;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { ChatMessage } from "../../../../../agent/react/index.js";
|
|
2
|
-
/** Public API contract for thread. */
|
|
3
|
-
export interface Thread {
|
|
4
|
-
id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
messages: ChatMessage[];
|
|
7
|
-
createdAt: number;
|
|
8
|
-
updatedAt: number;
|
|
9
|
-
}
|
|
10
|
-
/** Options accepted by use threads. */
|
|
11
|
-
export interface UseThreadsOptions {
|
|
12
|
-
/** localStorage key prefix. Default: "vf-threads" */
|
|
13
|
-
storageKey?: string;
|
|
14
|
-
}
|
|
15
|
-
/** Result returned from use threads. */
|
|
16
|
-
export interface UseThreadsResult {
|
|
17
|
-
threads: Thread[];
|
|
18
|
-
activeThreadId: string | null;
|
|
19
|
-
activeThread: Thread | null;
|
|
20
|
-
createThread: () => Thread;
|
|
21
|
-
selectThread: (id: string) => void;
|
|
22
|
-
deleteThread: (id: string) => void;
|
|
23
|
-
updateThread: (id: string, updates: Partial<Pick<Thread, "title" | "messages">>) => void;
|
|
24
|
-
renameThread: (id: string, title: string) => void;
|
|
25
|
-
}
|
|
26
|
-
/** React hook for threads. */
|
|
27
|
-
export declare function useThreads(options?: UseThreadsOptions): UseThreadsResult;
|
|
28
|
-
//# sourceMappingURL=use-threads.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-threads.d.ts","sourceRoot":"","sources":["../../../../../../../src/src/react/components/chat/chat/hooks/use-threads.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAGvE,sCAAsC;AACtC,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC,KAAK,IAAI,CAAC;IACzF,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAiDD,8BAA8B;AAC9B,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,CA+HxE"}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import * as React from "../../../../../../react/react.js";
|
|
2
|
-
import { isBrowserEnvironment } from "../../../../../platform/compat/runtime.js";
|
|
3
|
-
function generateId() {
|
|
4
|
-
return `t_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5
|
-
}
|
|
6
|
-
function createEmptyThread() {
|
|
7
|
-
return {
|
|
8
|
-
id: generateId(),
|
|
9
|
-
title: "New Chat",
|
|
10
|
-
messages: [],
|
|
11
|
-
createdAt: Date.now(),
|
|
12
|
-
updatedAt: Date.now(),
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function loadIndex(key) {
|
|
16
|
-
if (!isBrowserEnvironment())
|
|
17
|
-
return { ids: [] };
|
|
18
|
-
try {
|
|
19
|
-
const raw = localStorage.getItem(`${key}-index`);
|
|
20
|
-
if (raw)
|
|
21
|
-
return JSON.parse(raw);
|
|
22
|
-
}
|
|
23
|
-
catch (_) { /* expected: corrupted localStorage data */ }
|
|
24
|
-
return { ids: [] };
|
|
25
|
-
}
|
|
26
|
-
function saveIndex(key, index) {
|
|
27
|
-
if (!isBrowserEnvironment())
|
|
28
|
-
return;
|
|
29
|
-
localStorage.setItem(`${key}-index`, JSON.stringify(index));
|
|
30
|
-
}
|
|
31
|
-
function loadThread(key, id) {
|
|
32
|
-
if (!isBrowserEnvironment())
|
|
33
|
-
return null;
|
|
34
|
-
try {
|
|
35
|
-
const raw = localStorage.getItem(`${key}-${id}`);
|
|
36
|
-
if (raw)
|
|
37
|
-
return JSON.parse(raw);
|
|
38
|
-
}
|
|
39
|
-
catch (_) { /* expected: corrupted localStorage data */ }
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
function saveThread(key, thread) {
|
|
43
|
-
if (!isBrowserEnvironment())
|
|
44
|
-
return;
|
|
45
|
-
localStorage.setItem(`${key}-${thread.id}`, JSON.stringify(thread));
|
|
46
|
-
}
|
|
47
|
-
function removeThread(key, id) {
|
|
48
|
-
if (!isBrowserEnvironment())
|
|
49
|
-
return;
|
|
50
|
-
localStorage.removeItem(`${key}-${id}`);
|
|
51
|
-
}
|
|
52
|
-
/** React hook for threads. */
|
|
53
|
-
export function useThreads(options) {
|
|
54
|
-
const storageKey = options?.storageKey ?? "vf-threads";
|
|
55
|
-
const [threads, setThreads] = React.useState(() => {
|
|
56
|
-
const index = loadIndex(storageKey);
|
|
57
|
-
const loaded = [];
|
|
58
|
-
for (const id of index.ids) {
|
|
59
|
-
const thread = loadThread(storageKey, id);
|
|
60
|
-
if (thread)
|
|
61
|
-
loaded.push(thread);
|
|
62
|
-
}
|
|
63
|
-
if (loaded.length === 0) {
|
|
64
|
-
// Return empty array during SSR/first render; create thread in useEffect
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
// Sort by updatedAt desc
|
|
68
|
-
loaded.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
69
|
-
return loaded;
|
|
70
|
-
});
|
|
71
|
-
const [activeThreadId, setActiveThreadId] = React.useState(() => threads[0]?.id ?? null);
|
|
72
|
-
// Create initial thread on client only to avoid SSR hydration mismatch
|
|
73
|
-
React.useEffect(() => {
|
|
74
|
-
if (threads.length > 0)
|
|
75
|
-
return;
|
|
76
|
-
const first = createEmptyThread();
|
|
77
|
-
saveThread(storageKey, first);
|
|
78
|
-
saveIndex(storageKey, { ids: [first.id] });
|
|
79
|
-
setThreads([first]);
|
|
80
|
-
setActiveThreadId(first.id);
|
|
81
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
82
|
-
}, []);
|
|
83
|
-
// Debounced persist — clear on unmount to avoid stale writes
|
|
84
|
-
const saveTimerRef = React.useRef(undefined);
|
|
85
|
-
React.useEffect(() => () => clearTimeout(saveTimerRef.current), []);
|
|
86
|
-
const persistThreads = React.useCallback((updated) => {
|
|
87
|
-
clearTimeout(saveTimerRef.current);
|
|
88
|
-
saveTimerRef.current = globalThis.setTimeout(() => {
|
|
89
|
-
saveIndex(storageKey, { ids: updated.map((t) => t.id) });
|
|
90
|
-
for (const thread of updated) {
|
|
91
|
-
saveThread(storageKey, thread);
|
|
92
|
-
}
|
|
93
|
-
}, 300);
|
|
94
|
-
}, [storageKey]);
|
|
95
|
-
const activeThread = React.useMemo(() => threads.find((t) => t.id === activeThreadId) ?? null, [threads, activeThreadId]);
|
|
96
|
-
const createThread = React.useCallback(() => {
|
|
97
|
-
const thread = createEmptyThread();
|
|
98
|
-
setThreads((prev) => {
|
|
99
|
-
const next = [thread, ...prev];
|
|
100
|
-
persistThreads(next);
|
|
101
|
-
return next;
|
|
102
|
-
});
|
|
103
|
-
setActiveThreadId(thread.id);
|
|
104
|
-
return thread;
|
|
105
|
-
}, [persistThreads]);
|
|
106
|
-
const selectThread = React.useCallback((id) => {
|
|
107
|
-
setActiveThreadId(id);
|
|
108
|
-
}, []);
|
|
109
|
-
const deleteThread = React.useCallback((id) => {
|
|
110
|
-
setThreads((prev) => {
|
|
111
|
-
const next = prev.filter((t) => t.id !== id);
|
|
112
|
-
removeThread(storageKey, id);
|
|
113
|
-
if (next.length === 0) {
|
|
114
|
-
const fresh = createEmptyThread();
|
|
115
|
-
saveThread(storageKey, fresh);
|
|
116
|
-
const result = [fresh];
|
|
117
|
-
persistThreads(result);
|
|
118
|
-
setActiveThreadId(fresh.id);
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
persistThreads(next);
|
|
122
|
-
// If we deleted the active thread, switch to the first one
|
|
123
|
-
setActiveThreadId((current) => current === id ? (next[0]?.id ?? null) : current);
|
|
124
|
-
return next;
|
|
125
|
-
});
|
|
126
|
-
}, [storageKey, persistThreads]);
|
|
127
|
-
const updateThread = React.useCallback((id, updates) => {
|
|
128
|
-
setThreads((prev) => {
|
|
129
|
-
const next = prev.map((t) => {
|
|
130
|
-
if (t.id !== id)
|
|
131
|
-
return t;
|
|
132
|
-
return { ...t, ...updates, updatedAt: Date.now() };
|
|
133
|
-
});
|
|
134
|
-
persistThreads(next);
|
|
135
|
-
return next;
|
|
136
|
-
});
|
|
137
|
-
}, [persistThreads]);
|
|
138
|
-
const renameThread = React.useCallback((id, title) => {
|
|
139
|
-
updateThread(id, { title });
|
|
140
|
-
}, [updateThread]);
|
|
141
|
-
return {
|
|
142
|
-
threads,
|
|
143
|
-
activeThreadId,
|
|
144
|
-
activeThread,
|
|
145
|
-
createThread,
|
|
146
|
-
selectThread,
|
|
147
|
-
deleteThread,
|
|
148
|
-
updateThread,
|
|
149
|
-
renameThread,
|
|
150
|
-
};
|
|
151
|
-
}
|