@larkup/workspace-app 0.0.0-stage → 0.2.0
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/LICENSE +176 -0
- package/README.md +4 -2
- package/assets/github.svg +3 -0
- package/assets/icons/Confluence.png +0 -0
- package/assets/icons/Instagram.png +0 -0
- package/assets/icons/Northflank.png +0 -0
- package/assets/icons/Salesforce.png +0 -0
- package/assets/icons/agentskills.png +0 -0
- package/assets/icons/airtable.png +0 -0
- package/assets/icons/asana.png +0 -0
- package/assets/icons/ashby.png +0 -0
- package/assets/icons/audio.png +0 -0
- package/assets/icons/aws-kendra.png +0 -0
- package/assets/icons/aws.svg +38 -0
- package/assets/icons/azure-ai-search.png +0 -0
- package/assets/icons/azure.svg +1 -0
- package/assets/icons/bigquery.png +0 -0
- package/assets/icons/bing.png +0 -0
- package/assets/icons/bitbucket.png +0 -0
- package/assets/icons/brave.png +0 -0
- package/assets/icons/browserbase.png +0 -0
- package/assets/icons/calendly.png +0 -0
- package/assets/icons/chroma.png +0 -0
- package/assets/icons/cloudflare.png +0 -0
- package/assets/icons/cohere.svg +30 -0
- package/assets/icons/databricks.png +0 -0
- package/assets/icons/daytonaio.png +0 -0
- package/assets/icons/daytonaio2.png +0 -0
- package/assets/icons/deepgram.png +0 -0
- package/assets/icons/deepl.png +0 -0
- package/assets/icons/digital-ocean.webp +0 -0
- package/assets/icons/discord.png +0 -0
- package/assets/icons/e2b-2.png +0 -0
- package/assets/icons/e2b.png +0 -0
- package/assets/icons/elevenlabs.png +0 -0
- package/assets/icons/email.png +0 -0
- package/assets/icons/exa.png +0 -0
- package/assets/icons/excel.png +0 -0
- package/assets/icons/fal.png +0 -0
- package/assets/icons/firecrawl.png +0 -0
- package/assets/icons/firecrawl2.png +0 -0
- package/assets/icons/flyio.jpeg +0 -0
- package/assets/icons/gcp.svg +1 -0
- package/assets/icons/gemini.svg +1 -0
- package/assets/icons/gmail.png +0 -0
- package/assets/icons/google-analytics.png +0 -0
- package/assets/icons/google-calender.png +0 -0
- package/assets/icons/google-docs.png +0 -0
- package/assets/icons/google-drive.png +0 -0
- package/assets/icons/google-maps.png +0 -0
- package/assets/icons/google-meet.png +0 -0
- package/assets/icons/google-sheets.png +0 -0
- package/assets/icons/google-slides.png +0 -0
- package/assets/icons/google-tasks.png +0 -0
- package/assets/icons/google.png +0 -0
- package/assets/icons/grok.png +0 -0
- package/assets/icons/hubspot.png +0 -0
- package/assets/icons/image.png +0 -0
- package/assets/icons/jina.svg +1 -0
- package/assets/icons/jira.png +0 -0
- package/assets/icons/lancedb2.png +0 -0
- package/assets/icons/linear.png +0 -0
- package/assets/icons/linux.png +0 -0
- package/assets/icons/looker.png +0 -0
- package/assets/icons/luma.png +0 -0
- package/assets/icons/mac.png +0 -0
- package/assets/icons/markdown.png +0 -0
- package/assets/icons/mcp.png +0 -0
- package/assets/icons/mcp.svg +1 -0
- package/assets/icons/messenger.svg +1 -0
- package/assets/icons/microsoft-intra.png +0 -0
- package/assets/icons/microsoft-onenote.png +0 -0
- package/assets/icons/microsoft-planner.png +0 -0
- package/assets/icons/microsoft-teams.png +0 -0
- package/assets/icons/miro.png +0 -0
- package/assets/icons/mistral.svg +469 -0
- package/assets/icons/modal.png +0 -0
- package/assets/icons/ngrok.png +0 -0
- package/assets/icons/nomic.png +0 -0
- package/assets/icons/onedrive.png +0 -0
- package/assets/icons/openai.svg +1 -0
- package/assets/icons/outlook-calendar.png +0 -0
- package/assets/icons/outlook-email.png +0 -0
- package/assets/icons/outlook.png +0 -0
- package/assets/icons/pdf.png +0 -0
- package/assets/icons/personio.png +0 -0
- package/assets/icons/pgvector2.png +0 -0
- package/assets/icons/phone.png +0 -0
- package/assets/icons/pinecone.png +0 -0
- package/assets/icons/pipedrive.png +0 -0
- package/assets/icons/power-pi.png +0 -0
- package/assets/icons/pylon.png +0 -0
- package/assets/icons/python.png +0 -0
- package/assets/icons/qdrant.svg +35 -0
- package/assets/icons/revai.png +0 -0
- package/assets/icons/revai.svg +1 -0
- package/assets/icons/sap.png +0 -0
- package/assets/icons/serpapi.svg +34 -0
- package/assets/icons/serper.png +0 -0
- package/assets/icons/servicenow.png +0 -0
- package/assets/icons/sharepoint.png +0 -0
- package/assets/icons/slack.png +0 -0
- package/assets/icons/snowflake.png +0 -0
- package/assets/icons/statistia.png +0 -0
- package/assets/icons/stripe.png +0 -0
- package/assets/icons/supabase.png +0 -0
- package/assets/icons/tableau.png +0 -0
- package/assets/icons/tavily.png +0 -0
- package/assets/icons/telegram.png +0 -0
- package/assets/icons/typescript.png +0 -0
- package/assets/icons/vercel copy.svg +1 -0
- package/assets/icons/vercel.svg +1 -0
- package/assets/icons/vertex-ai.png +0 -0
- package/assets/icons/video.png +0 -0
- package/assets/icons/voyage-light.png +0 -0
- package/assets/icons/weaviate.webp +0 -0
- package/assets/icons/webcontainers.svg +11 -0
- package/assets/icons/webhook.png +0 -0
- package/assets/icons/whatsapp copy.png +0 -0
- package/assets/icons/whatsapp.png +0 -0
- package/assets/icons/windows.png +0 -0
- package/assets/icons/word.png +0 -0
- package/assets/icons/zendesk.png +0 -0
- package/assets/icons/zoom.png +0 -0
- package/assets/notion-white.png +0 -0
- package/assets/notion.png +0 -0
- package/dist-types/asset-server.d.ts +3 -0
- package/dist-types/asset-server.d.ts.map +1 -0
- package/dist-types/components/alerts/generic-alert.d.ts +15 -0
- package/dist-types/components/alerts/generic-alert.d.ts.map +1 -0
- package/dist-types/components/chat/canvas/doc-editor-provider.d.ts +62 -0
- package/dist-types/components/chat/canvas/doc-editor-provider.d.ts.map +1 -0
- package/dist-types/components/chat/canvas/document-canvas.d.ts +7 -0
- package/dist-types/components/chat/canvas/document-canvas.d.ts.map +1 -0
- package/dist-types/components/chat/canvas/document-field.d.ts +13 -0
- package/dist-types/components/chat/canvas/document-field.d.ts.map +1 -0
- package/dist-types/components/chat/canvas/document-viewer.d.ts +2 -0
- package/dist-types/components/chat/canvas/document-viewer.d.ts.map +1 -0
- package/dist-types/components/chat/canvas/file-attachment-button.d.ts +9 -0
- package/dist-types/components/chat/canvas/file-attachment-button.d.ts.map +1 -0
- package/dist-types/components/chat/chat-workspace.d.ts +4 -0
- package/dist-types/components/chat/chat-workspace.d.ts.map +1 -0
- package/dist-types/components/chat/message-actions.d.ts +11 -0
- package/dist-types/components/chat/message-actions.d.ts.map +1 -0
- package/dist-types/components/chat/message-item.d.ts +20 -0
- package/dist-types/components/chat/message-item.d.ts.map +1 -0
- package/dist-types/components/chat/message-queue.d.ts +15 -0
- package/dist-types/components/chat/message-queue.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-chart.d.ts +6 -0
- package/dist-types/components/chat/tools/chat-chart.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-citations.d.ts +32 -0
- package/dist-types/components/chat/tools/chat-citations.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-data-table.d.ts +14 -0
- package/dist-types/components/chat/tools/chat-data-table.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-file-export.d.ts +5 -0
- package/dist-types/components/chat/tools/chat-file-export.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-media-preview.d.ts +35 -0
- package/dist-types/components/chat/tools/chat-media-preview.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-sandbox-result.d.ts +17 -0
- package/dist-types/components/chat/tools/chat-sandbox-result.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-signature-request.d.ts +13 -0
- package/dist-types/components/chat/tools/chat-signature-request.d.ts.map +1 -0
- package/dist-types/components/chat/tools/chat-tabs.d.ts +17 -0
- package/dist-types/components/chat/tools/chat-tabs.d.ts.map +1 -0
- package/dist-types/components/chat/tools/corpus-data-result.d.ts +36 -0
- package/dist-types/components/chat/tools/corpus-data-result.d.ts.map +1 -0
- package/dist-types/components/chat/tools/knowledge-base-result.d.ts +14 -0
- package/dist-types/components/chat/tools/knowledge-base-result.d.ts.map +1 -0
- package/dist-types/components/chat/tools/visual-placement-modal.d.ts +23 -0
- package/dist-types/components/chat/tools/visual-placement-modal.d.ts.map +1 -0
- package/dist-types/components/configure/custom-model-modal.d.ts +13 -0
- package/dist-types/components/configure/custom-model-modal.d.ts.map +1 -0
- package/dist-types/components/configure/store-fields.d.ts +11 -0
- package/dist-types/components/configure/store-fields.d.ts.map +1 -0
- package/dist-types/components/data/corpus-panel.d.ts +12 -0
- package/dist-types/components/data/corpus-panel.d.ts.map +1 -0
- package/dist-types/components/data/data-entry-mode-switch.d.ts +13 -0
- package/dist-types/components/data/data-entry-mode-switch.d.ts.map +1 -0
- package/dist-types/components/data/data-primary-action.d.ts +7 -0
- package/dist-types/components/data/data-primary-action.d.ts.map +1 -0
- package/dist-types/components/data/data-workspace.d.ts +6 -0
- package/dist-types/components/data/data-workspace.d.ts.map +1 -0
- package/dist-types/components/data/groups-panel.d.ts +5 -0
- package/dist-types/components/data/groups-panel.d.ts.map +1 -0
- package/dist-types/components/data/integration-resources-panel.d.ts +9 -0
- package/dist-types/components/data/integration-resources-panel.d.ts.map +1 -0
- package/dist-types/components/data/integrations-panel.d.ts +5 -0
- package/dist-types/components/data/integrations-panel.d.ts.map +1 -0
- package/dist-types/components/data/jobs-panel.d.ts +6 -0
- package/dist-types/components/data/jobs-panel.d.ts.map +1 -0
- package/dist-types/components/data/jobs-tracking-button.d.ts +2 -0
- package/dist-types/components/data/jobs-tracking-button.d.ts.map +1 -0
- package/dist-types/components/data/live-media-progress.d.ts +9 -0
- package/dist-types/components/data/live-media-progress.d.ts.map +1 -0
- package/dist-types/components/data/media-indexing-jobs-panel.d.ts +34 -0
- package/dist-types/components/data/media-indexing-jobs-panel.d.ts.map +1 -0
- package/dist-types/components/data/media-panel.d.ts +58 -0
- package/dist-types/components/data/media-panel.d.ts.map +1 -0
- package/dist-types/components/data/notion-panel.d.ts +6 -0
- package/dist-types/components/data/notion-panel.d.ts.map +1 -0
- package/dist-types/components/data/paste-panel.d.ts +7 -0
- package/dist-types/components/data/paste-panel.d.ts.map +1 -0
- package/dist-types/components/data/project-data-page.d.ts +3 -0
- package/dist-types/components/data/project-data-page.d.ts.map +1 -0
- package/dist-types/components/data/scrape-panel.d.ts +9 -0
- package/dist-types/components/data/scrape-panel.d.ts.map +1 -0
- package/dist-types/components/data/tabular-preview.d.ts +10 -0
- package/dist-types/components/data/tabular-preview.d.ts.map +1 -0
- package/dist-types/components/data/tool-indexing-dialog.d.ts +54 -0
- package/dist-types/components/data/tool-indexing-dialog.d.ts.map +1 -0
- package/dist-types/components/data/upload-panel.d.ts +7 -0
- package/dist-types/components/data/upload-panel.d.ts.map +1 -0
- package/dist-types/components/data/web-crawler-status.d.ts +3 -0
- package/dist-types/components/data/web-crawler-status.d.ts.map +1 -0
- package/dist-types/components/projects/project-form-dialog.d.ts +10 -0
- package/dist-types/components/projects/project-form-dialog.d.ts.map +1 -0
- package/dist-types/components/projects/project-provider.d.ts +34 -0
- package/dist-types/components/projects/project-provider.d.ts.map +1 -0
- package/dist-types/components/settings/general/agent-customization-section.d.ts +2 -0
- package/dist-types/components/settings/general/agent-customization-section.d.ts.map +1 -0
- package/dist-types/components/settings/general/mcp-connections-section.d.ts +2 -0
- package/dist-types/components/settings/general/mcp-connections-section.d.ts.map +1 -0
- package/dist-types/components/settings/general/model-combobox.d.ts +24 -0
- package/dist-types/components/settings/general/model-combobox.d.ts.map +1 -0
- package/dist-types/components/settings/general/models-section.d.ts +2 -0
- package/dist-types/components/settings/general/models-section.d.ts.map +1 -0
- package/dist-types/components/settings/general/sandbox-section.d.ts +7 -0
- package/dist-types/components/settings/general/sandbox-section.d.ts.map +1 -0
- package/dist-types/components/settings/general/search-scraping-section.d.ts +2 -0
- package/dist-types/components/settings/general/search-scraping-section.d.ts.map +1 -0
- package/dist-types/components/settings/general/storage-indexing-section.d.ts +2 -0
- package/dist-types/components/settings/general/storage-indexing-section.d.ts.map +1 -0
- package/dist-types/components/ui/alert-dialog.d.ts +19 -0
- package/dist-types/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist-types/components/ui/alert.d.ts +11 -0
- package/dist-types/components/ui/alert.d.ts.map +1 -0
- package/dist-types/components/ui/badge.d.ts +8 -0
- package/dist-types/components/ui/badge.d.ts.map +1 -0
- package/dist-types/components/ui/card.d.ts +2 -0
- package/dist-types/components/ui/card.d.ts.map +1 -0
- package/dist-types/components/ui/checkbox.d.ts +4 -0
- package/dist-types/components/ui/checkbox.d.ts.map +1 -0
- package/dist-types/components/ui/collapsible.d.ts +6 -0
- package/dist-types/components/ui/collapsible.d.ts.map +1 -0
- package/dist-types/components/ui/command.d.ts +20 -0
- package/dist-types/components/ui/command.d.ts.map +1 -0
- package/dist-types/components/ui/dialog.d.ts +18 -0
- package/dist-types/components/ui/dialog.d.ts.map +1 -0
- package/dist-types/components/ui/dropdown-menu.d.ts +30 -0
- package/dist-types/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist-types/components/ui/input-group.d.ts +19 -0
- package/dist-types/components/ui/input-group.d.ts.map +1 -0
- package/dist-types/components/ui/input.d.ts +4 -0
- package/dist-types/components/ui/input.d.ts.map +1 -0
- package/dist-types/components/ui/line-tabs.d.ts +15 -0
- package/dist-types/components/ui/line-tabs.d.ts.map +1 -0
- package/dist-types/components/ui/popover.d.ts +10 -0
- package/dist-types/components/ui/popover.d.ts.map +1 -0
- package/dist-types/components/ui/progress.d.ts +8 -0
- package/dist-types/components/ui/progress.d.ts.map +1 -0
- package/dist-types/components/ui/scroll-area.d.ts +6 -0
- package/dist-types/components/ui/scroll-area.d.ts.map +1 -0
- package/dist-types/components/ui/sheet.d.ts +15 -0
- package/dist-types/components/ui/sheet.d.ts.map +1 -0
- package/dist-types/components/ui/slider.d.ts +4 -0
- package/dist-types/components/ui/slider.d.ts.map +1 -0
- package/dist-types/components/ui/switch.d.ts +6 -0
- package/dist-types/components/ui/switch.d.ts.map +1 -0
- package/dist-types/components/ui/table.d.ts +2 -0
- package/dist-types/components/ui/table.d.ts.map +1 -0
- package/dist-types/components/ui/tabs.d.ts +11 -0
- package/dist-types/components/ui/tabs.d.ts.map +1 -0
- package/dist-types/components/ui/textarea.d.ts +2 -0
- package/dist-types/components/ui/textarea.d.ts.map +1 -0
- package/dist-types/components/ui/tooltip.d.ts +7 -0
- package/dist-types/components/ui/tooltip.d.ts.map +1 -0
- package/dist-types/hooks/use-copy-to-clipboard.d.ts +5 -0
- package/dist-types/hooks/use-copy-to-clipboard.d.ts.map +1 -0
- package/dist-types/hooks/use-integration-auth.d.ts +10 -0
- package/dist-types/hooks/use-integration-auth.d.ts.map +1 -0
- package/dist-types/hooks/use-live-media-progress.d.ts +14 -0
- package/dist-types/hooks/use-live-media-progress.d.ts.map +1 -0
- package/dist-types/hooks/use-notion-auth.d.ts +9 -0
- package/dist-types/hooks/use-notion-auth.d.ts.map +1 -0
- package/dist-types/host.d.ts +20 -0
- package/dist-types/host.d.ts.map +1 -0
- package/dist-types/lib/browser-cache.d.ts +7 -0
- package/dist-types/lib/browser-cache.d.ts.map +1 -0
- package/dist-types/lib/chat/chart-config.d.ts +2 -0
- package/dist-types/lib/chat/chart-config.d.ts.map +1 -0
- package/dist-types/lib/chat/data-export.d.ts +2 -0
- package/dist-types/lib/chat/data-export.d.ts.map +1 -0
- package/dist-types/lib/chat/live-tool-progress.d.ts +44 -0
- package/dist-types/lib/chat/live-tool-progress.d.ts.map +1 -0
- package/dist-types/lib/chat/message-feedback.d.ts +2 -0
- package/dist-types/lib/chat/message-feedback.d.ts.map +1 -0
- package/dist-types/lib/chat/table-presentation.d.ts +2 -0
- package/dist-types/lib/chat/table-presentation.d.ts.map +1 -0
- package/dist-types/lib/chat/tool-output.d.ts +3 -0
- package/dist-types/lib/chat/tool-output.d.ts.map +1 -0
- package/dist-types/lib/chat-supporting-clip.d.ts +11 -0
- package/dist-types/lib/chat-supporting-clip.d.ts.map +1 -0
- package/dist-types/lib/constants/stores.d.ts +6 -0
- package/dist-types/lib/constants/stores.d.ts.map +1 -0
- package/dist-types/lib/constants/tools.d.ts +27 -0
- package/dist-types/lib/constants/tools.d.ts.map +1 -0
- package/dist-types/lib/error-formatter.d.ts +2 -0
- package/dist-types/lib/error-formatter.d.ts.map +1 -0
- package/dist-types/lib/jobs-client.d.ts +7 -0
- package/dist-types/lib/jobs-client.d.ts.map +1 -0
- package/dist-types/lib/media/pdf.d.ts +6 -0
- package/dist-types/lib/media/pdf.d.ts.map +1 -0
- package/dist-types/lib/media/progress.d.ts +61 -0
- package/dist-types/lib/media/progress.d.ts.map +1 -0
- package/dist-types/lib/media/source-selection.d.ts +10 -0
- package/dist-types/lib/media/source-selection.d.ts.map +1 -0
- package/dist-types/lib/media/tool-indexing-estimate.d.ts +13 -0
- package/dist-types/lib/media/tool-indexing-estimate.d.ts.map +1 -0
- package/dist-types/lib/media/video-intelligence-capacity.d.ts +2 -0
- package/dist-types/lib/media/video-intelligence-capacity.d.ts.map +1 -0
- package/dist-types/lib/media/video-runtime-scope.d.ts +2 -0
- package/dist-types/lib/media/video-runtime-scope.d.ts.map +1 -0
- package/dist-types/lib/shared/error-formatter.d.ts +2 -0
- package/dist-types/lib/shared/error-formatter.d.ts.map +1 -0
- package/dist-types/lib/utils.d.ts +3 -0
- package/dist-types/lib/utils.d.ts.map +1 -0
- package/dist-types/storage.d.ts +5 -0
- package/dist-types/storage.d.ts.map +1 -0
- package/dist-types/store/chat-store.d.ts +19 -0
- package/dist-types/store/chat-store.d.ts.map +1 -0
- package/dist-types/store/scrape-store.d.ts +42 -0
- package/dist-types/store/scrape-store.d.ts.map +1 -0
- package/package.json +579 -3
- package/scripts/build-assets.mjs +12 -0
- package/src/asset-data.json +1 -0
- package/src/asset-server.ts +19 -0
- package/src/components/alerts/generic-alert.tsx +76 -0
- package/src/components/chat/canvas/doc-editor-provider.tsx +338 -0
- package/src/components/chat/canvas/document-canvas.tsx +98 -0
- package/src/components/chat/canvas/document-field.tsx +167 -0
- package/src/components/chat/canvas/document-viewer.tsx +446 -0
- package/src/components/chat/canvas/file-attachment-button.tsx +88 -0
- package/src/components/chat/chat-workspace.tsx +1552 -0
- package/src/components/chat/message-actions.tsx +96 -0
- package/src/components/chat/message-item.tsx +1398 -0
- package/src/components/chat/message-queue.tsx +86 -0
- package/src/components/chat/tools/chat-chart.tsx +558 -0
- package/src/components/chat/tools/chat-citations.tsx +152 -0
- package/src/components/chat/tools/chat-data-table.tsx +373 -0
- package/src/components/chat/tools/chat-file-export.tsx +49 -0
- package/src/components/chat/tools/chat-media-preview.tsx +466 -0
- package/src/components/chat/tools/chat-sandbox-result.tsx +130 -0
- package/src/components/chat/tools/chat-signature-request.tsx +810 -0
- package/src/components/chat/tools/chat-tabs.tsx +59 -0
- package/src/components/chat/tools/corpus-data-result.tsx +194 -0
- package/src/components/chat/tools/knowledge-base-result.tsx +345 -0
- package/src/components/chat/tools/visual-placement-modal.tsx +308 -0
- package/src/components/configure/custom-model-modal.tsx +209 -0
- package/src/components/configure/store-fields.tsx +129 -0
- package/src/components/data/corpus-panel.tsx +1223 -0
- package/src/components/data/data-entry-mode-switch.tsx +53 -0
- package/src/components/data/data-primary-action.ts +6 -0
- package/src/components/data/data-workspace.tsx +623 -0
- package/src/components/data/groups-panel.tsx +530 -0
- package/src/components/data/integration-resources-panel.tsx +282 -0
- package/src/components/data/integrations-panel.tsx +392 -0
- package/src/components/data/jobs-panel.tsx +490 -0
- package/src/components/data/jobs-tracking-button.tsx +224 -0
- package/src/components/data/live-media-progress.tsx +79 -0
- package/src/components/data/media-indexing-jobs-panel.tsx +190 -0
- package/src/components/data/media-panel.tsx +2905 -0
- package/src/components/data/notion-panel.tsx +408 -0
- package/src/components/data/paste-panel.tsx +82 -0
- package/src/components/data/project-data-page.tsx +55 -0
- package/src/components/data/scrape-panel.tsx +1290 -0
- package/src/components/data/tabular-preview.tsx +205 -0
- package/src/components/data/tool-indexing-dialog.tsx +210 -0
- package/src/components/data/upload-panel.tsx +1313 -0
- package/src/components/data/web-crawler-status.tsx +132 -0
- package/src/components/projects/project-form-dialog.tsx +97 -0
- package/src/components/projects/project-provider.tsx +143 -0
- package/src/components/settings/general/agent-customization-section.tsx +620 -0
- package/src/components/settings/general/mcp-connections-section.tsx +907 -0
- package/src/components/settings/general/model-combobox.tsx +235 -0
- package/src/components/settings/general/models-section.tsx +909 -0
- package/src/components/settings/general/sandbox-section.tsx +394 -0
- package/src/components/settings/general/search-scraping-section.tsx +757 -0
- package/src/components/settings/general/storage-indexing-section.tsx +394 -0
- package/src/components/ui/alert-dialog.tsx +162 -0
- package/src/components/ui/alert.tsx +69 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/card.tsx +9 -0
- package/src/components/ui/checkbox.tsx +28 -0
- package/src/components/ui/collapsible.tsx +17 -0
- package/src/components/ui/command.tsx +175 -0
- package/src/components/ui/dialog.tsx +138 -0
- package/src/components/ui/dropdown-menu.tsx +267 -0
- package/src/components/ui/input-group.tsx +146 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/line-tabs.tsx +49 -0
- package/src/components/ui/popover.tsx +77 -0
- package/src/components/ui/progress.tsx +66 -0
- package/src/components/ui/scroll-area.tsx +51 -0
- package/src/components/ui/sheet.tsx +125 -0
- package/src/components/ui/slider.tsx +52 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +10 -0
- package/src/components/ui/tabs.tsx +77 -0
- package/src/components/ui/textarea.tsx +1 -0
- package/src/components/ui/tooltip.tsx +54 -0
- package/src/hooks/use-copy-to-clipboard.ts +25 -0
- package/src/hooks/use-integration-auth.ts +39 -0
- package/src/hooks/use-live-media-progress.ts +71 -0
- package/src/hooks/use-notion-auth.ts +66 -0
- package/src/host.ts +55 -0
- package/src/lib/browser-cache.ts +17 -0
- package/src/lib/chat/chart-config.ts +1 -0
- package/src/lib/chat/data-export.ts +1 -0
- package/src/lib/chat/live-tool-progress.ts +69 -0
- package/src/lib/chat/message-feedback.ts +1 -0
- package/src/lib/chat/table-presentation.ts +1 -0
- package/src/lib/chat/tool-output.ts +14 -0
- package/src/lib/chat-supporting-clip.ts +16 -0
- package/src/lib/constants/stores.ts +12 -0
- package/src/lib/constants/tools.ts +178 -0
- package/src/lib/error-formatter.ts +30 -0
- package/src/lib/jobs-client.ts +27 -0
- package/src/lib/media/pdf.ts +57 -0
- package/src/lib/media/progress.ts +209 -0
- package/src/lib/media/source-selection.ts +47 -0
- package/src/lib/media/tool-indexing-estimate.ts +39 -0
- package/src/lib/media/video-intelligence-capacity.ts +1 -0
- package/src/lib/media/video-runtime-scope.ts +1 -0
- package/src/lib/shared/error-formatter.ts +1 -0
- package/src/lib/utils.ts +6 -0
- package/src/source.css +1 -0
- package/src/storage.ts +16 -0
- package/src/store/chat-store.ts +19 -0
- package/src/store/scrape-store.ts +76 -0
- package/src/styles.d.ts +1 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Image as ImageIcon, Film, AudioLines, Maximize2, X } from 'lucide-react';
|
|
5
|
+
import { workspaceUrl } from '../../../host';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Inline media preview for chat messages.
|
|
9
|
+
*
|
|
10
|
+
* Renders a compact preview of media referenced in assistant responses:
|
|
11
|
+
* - Images: thumbnail with lightbox expand
|
|
12
|
+
* - Video: compact player with timestamp range
|
|
13
|
+
* - Audio: mini player bar
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export function ChatMediaPreview({
|
|
17
|
+
assetId,
|
|
18
|
+
mediaType,
|
|
19
|
+
fileName,
|
|
20
|
+
mediaUrl,
|
|
21
|
+
sourceUrl,
|
|
22
|
+
startSecs,
|
|
23
|
+
endSecs,
|
|
24
|
+
}: {
|
|
25
|
+
assetId: string;
|
|
26
|
+
mediaType: 'image' | 'video' | 'audio' | 'frame-preview';
|
|
27
|
+
fileName?: string;
|
|
28
|
+
mediaUrl?: string;
|
|
29
|
+
sourceUrl?: string;
|
|
30
|
+
startSecs?: number;
|
|
31
|
+
endSecs?: number;
|
|
32
|
+
}) {
|
|
33
|
+
const [expanded, setExpanded] = useState(false);
|
|
34
|
+
const [imageUnavailable, setImageUnavailable] = useState(false);
|
|
35
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
36
|
+
// Media is rendered only from a server-validated presentMedia result. Do
|
|
37
|
+
// not invent a URL from an identifier emitted in model text: it could point
|
|
38
|
+
// to a missing or unrelated asset and creates the misleading dummy preview
|
|
39
|
+
// shown in the previous chat experience.
|
|
40
|
+
const assetUrl = mediaUrl ? workspaceUrl(mediaUrl) : undefined;
|
|
41
|
+
const playbackUrl =
|
|
42
|
+
assetUrl && startSecs !== undefined
|
|
43
|
+
? `${assetUrl.split('#')[0]}#t=${startSecs}${endSecs !== undefined ? `,${endSecs}` : ''}`
|
|
44
|
+
: assetUrl;
|
|
45
|
+
const providerEmbedUrl = useMemo(
|
|
46
|
+
() => (mediaType === 'video' ? getProviderEmbedUrl(sourceUrl, startSecs, endSecs) : undefined),
|
|
47
|
+
[mediaType, sourceUrl, startSecs, endSecs],
|
|
48
|
+
);
|
|
49
|
+
const hasClipEnd =
|
|
50
|
+
mediaType === 'video' &&
|
|
51
|
+
Number.isFinite(startSecs) &&
|
|
52
|
+
Number.isFinite(endSecs) &&
|
|
53
|
+
(endSecs as number) > (startSecs as number);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const video = videoRef.current;
|
|
56
|
+
if (!video || !Number.isFinite(startSecs)) return;
|
|
57
|
+
const seek = () => {
|
|
58
|
+
// Media fragments are not applied consistently after client-side
|
|
59
|
+
// navigation. Setting currentTime after metadata makes the cited moment
|
|
60
|
+
// deterministic for local uploads and downloaded provider videos.
|
|
61
|
+
video.currentTime = Math.max(0, startSecs as number);
|
|
62
|
+
};
|
|
63
|
+
if (video.readyState >= HTMLMediaElement.HAVE_METADATA) seek();
|
|
64
|
+
else video.addEventListener('loadedmetadata', seek, { once: true });
|
|
65
|
+
return () => video.removeEventListener('loadedmetadata', seek);
|
|
66
|
+
}, [playbackUrl, startSecs]);
|
|
67
|
+
|
|
68
|
+
if (!assetUrl) return null;
|
|
69
|
+
|
|
70
|
+
if (mediaType === 'image') {
|
|
71
|
+
if (imageUnavailable) {
|
|
72
|
+
return (
|
|
73
|
+
<div className="my-2 w-full max-w-2xl rounded-lg border border-border bg-card p-3">
|
|
74
|
+
<p className="text-xs text-muted-foreground">This source image is no longer available.</p>
|
|
75
|
+
<MediaCitationFooter
|
|
76
|
+
icon={<ImageIcon className="size-3 text-muted-foreground" />}
|
|
77
|
+
fileName={fileName}
|
|
78
|
+
sourceUrl={sourceUrl}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return (
|
|
84
|
+
<>
|
|
85
|
+
<div className="my-2 w-full max-w-2xl overflow-hidden rounded-lg border border-border bg-card">
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
onClick={() => setExpanded(true)}
|
|
89
|
+
className="group relative block w-full overflow-hidden"
|
|
90
|
+
>
|
|
91
|
+
<img
|
|
92
|
+
src={assetUrl}
|
|
93
|
+
alt={fileName ?? 'Image'}
|
|
94
|
+
className="max-h-105 w-full object-contain"
|
|
95
|
+
loading="lazy"
|
|
96
|
+
onError={() => setImageUnavailable(true)}
|
|
97
|
+
/>
|
|
98
|
+
<div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover:bg-black/20 group-hover:opacity-100">
|
|
99
|
+
<Maximize2 className="size-5 text-white" />
|
|
100
|
+
</div>
|
|
101
|
+
</button>
|
|
102
|
+
<MediaCitationFooter
|
|
103
|
+
icon={<ImageIcon className="size-3 text-muted-foreground" />}
|
|
104
|
+
fileName={fileName}
|
|
105
|
+
sourceUrl={sourceUrl}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{expanded && (
|
|
110
|
+
<div
|
|
111
|
+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-8"
|
|
112
|
+
onClick={() => setExpanded(false)}
|
|
113
|
+
>
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
aria-label="Close image preview"
|
|
117
|
+
className="absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
|
|
118
|
+
onClick={() => setExpanded(false)}
|
|
119
|
+
>
|
|
120
|
+
<X className="size-5" />
|
|
121
|
+
</button>
|
|
122
|
+
<img
|
|
123
|
+
src={assetUrl}
|
|
124
|
+
alt={fileName ?? 'Image'}
|
|
125
|
+
className="max-h-[80vh] max-w-[90vw] rounded-lg object-contain"
|
|
126
|
+
onClick={(event) => event.stopPropagation()}
|
|
127
|
+
onError={() => setImageUnavailable(true)}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (mediaType === 'video') {
|
|
136
|
+
return (
|
|
137
|
+
<div className="my-2 w-full max-w-2xl overflow-hidden rounded-lg border border-border bg-card">
|
|
138
|
+
{providerEmbedUrl ? (
|
|
139
|
+
<iframe
|
|
140
|
+
src={providerEmbedUrl}
|
|
141
|
+
title={fileName ? `Watch ${fileName}` : 'Video preview'}
|
|
142
|
+
className="aspect-video w-full bg-black"
|
|
143
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
144
|
+
allowFullScreen
|
|
145
|
+
/>
|
|
146
|
+
) : (
|
|
147
|
+
<video
|
|
148
|
+
ref={videoRef}
|
|
149
|
+
src={playbackUrl}
|
|
150
|
+
controls
|
|
151
|
+
playsInline
|
|
152
|
+
preload="metadata"
|
|
153
|
+
className="max-h-90 w-full bg-black object-contain"
|
|
154
|
+
onTimeUpdate={(event) => {
|
|
155
|
+
// Media-fragment end times are not enforced consistently by
|
|
156
|
+
// browsers. Stop local playback at the cited evidence boundary
|
|
157
|
+
// so a supporting clip never continues into unrelated footage.
|
|
158
|
+
if (hasClipEnd && event.currentTarget.currentTime >= (endSecs as number)) {
|
|
159
|
+
event.currentTarget.pause();
|
|
160
|
+
event.currentTarget.currentTime = endSecs as number;
|
|
161
|
+
}
|
|
162
|
+
}}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
<MediaCitationFooter
|
|
166
|
+
icon={<Film className="size-3 text-muted-foreground" />}
|
|
167
|
+
fileName={fileName}
|
|
168
|
+
sourceUrl={timestampedMediaUrl(sourceUrl, startSecs, endSecs)}
|
|
169
|
+
timestamp={
|
|
170
|
+
startSecs !== undefined
|
|
171
|
+
? `${formatTimestamp(startSecs)}${
|
|
172
|
+
endSecs !== undefined ? ` – ${formatTimestamp(endSecs)}` : ''
|
|
173
|
+
}`
|
|
174
|
+
: undefined
|
|
175
|
+
}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (mediaType === 'audio') {
|
|
182
|
+
return (
|
|
183
|
+
<div className="my-2 w-full max-w-xl overflow-hidden rounded-lg border border-border bg-card">
|
|
184
|
+
<div className="flex items-center gap-2.5 border-b border-border px-3 py-2">
|
|
185
|
+
<div className="flex size-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
|
|
186
|
+
<AudioLines className="size-3.5" />
|
|
187
|
+
</div>
|
|
188
|
+
<div className="min-w-0 flex-1">
|
|
189
|
+
<p className="truncate text-[11px] font-medium text-foreground">
|
|
190
|
+
{fileName ?? 'Audio recording'}
|
|
191
|
+
</p>
|
|
192
|
+
{startSecs !== undefined ? (
|
|
193
|
+
<p className="mt-0.5 text-[10px] tabular-nums text-muted-foreground">
|
|
194
|
+
{formatTimestamp(startSecs)}
|
|
195
|
+
{endSecs !== undefined ? ` – ${formatTimestamp(endSecs)}` : ''}
|
|
196
|
+
</p>
|
|
197
|
+
) : (
|
|
198
|
+
<p className="mt-0.5 text-[10px] text-muted-foreground">Audio reference</p>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="px-3 py-2">
|
|
203
|
+
<audio src={playbackUrl} controls preload="metadata" className="h-8 w-full" />
|
|
204
|
+
</div>
|
|
205
|
+
{sourceUrl ? (
|
|
206
|
+
<div className="flex justify-end border-t border-border px-3 py-1.5">
|
|
207
|
+
<a
|
|
208
|
+
href={timestampedMediaUrl(sourceUrl, startSecs, endSecs)}
|
|
209
|
+
target="_blank"
|
|
210
|
+
rel="noreferrer"
|
|
211
|
+
className="text-[11px] font-medium text-primary hover:text-primary/80"
|
|
212
|
+
>
|
|
213
|
+
Open source
|
|
214
|
+
</a>
|
|
215
|
+
</div>
|
|
216
|
+
) : null}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (mediaType === 'frame-preview') {
|
|
222
|
+
if (imageUnavailable) {
|
|
223
|
+
return (
|
|
224
|
+
<div className="my-2 w-full max-w-xl rounded-lg border border-border bg-card p-3">
|
|
225
|
+
<p className="text-xs text-muted-foreground">This frame could not be extracted.</p>
|
|
226
|
+
<MediaCitationFooter
|
|
227
|
+
icon={<Film className="size-3 text-muted-foreground" />}
|
|
228
|
+
fileName={fileName}
|
|
229
|
+
sourceUrl={timestampedMediaUrl(sourceUrl, startSecs)}
|
|
230
|
+
timestamp={startSecs !== undefined ? formatTimestamp(startSecs) : undefined}
|
|
231
|
+
/>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return (
|
|
236
|
+
<>
|
|
237
|
+
<div className="my-2 w-full max-w-xl overflow-hidden rounded-lg border border-border bg-card">
|
|
238
|
+
<button
|
|
239
|
+
type="button"
|
|
240
|
+
onClick={() => setExpanded(true)}
|
|
241
|
+
className="group relative block w-full overflow-hidden"
|
|
242
|
+
>
|
|
243
|
+
<img
|
|
244
|
+
src={assetUrl}
|
|
245
|
+
alt={`Frame at ${startSecs !== undefined ? formatTimestamp(startSecs) : 'unknown'}`}
|
|
246
|
+
className="max-h-105 w-full object-contain"
|
|
247
|
+
loading="lazy"
|
|
248
|
+
onError={() => setImageUnavailable(true)}
|
|
249
|
+
/>
|
|
250
|
+
{startSecs !== undefined && (
|
|
251
|
+
<div className="absolute bottom-2 left-2 rounded-md bg-black/70 px-2 py-0.5">
|
|
252
|
+
<span className="text-[11px] font-medium tabular-nums text-white">
|
|
253
|
+
{formatTimestamp(startSecs)}
|
|
254
|
+
</span>
|
|
255
|
+
</div>
|
|
256
|
+
)}
|
|
257
|
+
<div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover:bg-black/20 group-hover:opacity-100">
|
|
258
|
+
<Maximize2 className="size-5 text-white" />
|
|
259
|
+
</div>
|
|
260
|
+
</button>
|
|
261
|
+
<MediaCitationFooter
|
|
262
|
+
icon={<Film className="size-3 text-muted-foreground" />}
|
|
263
|
+
fileName={fileName ? `${fileName} — Frame` : 'Video Frame'}
|
|
264
|
+
sourceUrl={timestampedMediaUrl(sourceUrl, startSecs)}
|
|
265
|
+
timestamp={startSecs !== undefined ? formatTimestamp(startSecs) : undefined}
|
|
266
|
+
/>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
{expanded && (
|
|
270
|
+
<div
|
|
271
|
+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-8"
|
|
272
|
+
onClick={() => setExpanded(false)}
|
|
273
|
+
>
|
|
274
|
+
<button
|
|
275
|
+
type="button"
|
|
276
|
+
aria-label="Close frame preview"
|
|
277
|
+
className="absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
|
|
278
|
+
onClick={() => setExpanded(false)}
|
|
279
|
+
>
|
|
280
|
+
<X className="size-5" />
|
|
281
|
+
</button>
|
|
282
|
+
<img
|
|
283
|
+
src={assetUrl}
|
|
284
|
+
alt={`Frame at ${startSecs !== undefined ? formatTimestamp(startSecs) : 'unknown'}`}
|
|
285
|
+
className="max-h-[80vh] max-w-[90vw] rounded-lg object-contain"
|
|
286
|
+
onClick={(event) => event.stopPropagation()}
|
|
287
|
+
onError={() => setImageUnavailable(true)}
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
</>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Use a first-party embed only for providers with a known safe player. All
|
|
300
|
+
* other URLs fall back to the locally indexed asset, which also keeps private
|
|
301
|
+
* uploads and direct video links reliable in chat.
|
|
302
|
+
*/
|
|
303
|
+
export function getProviderEmbedUrl(
|
|
304
|
+
sourceUrl?: string,
|
|
305
|
+
startSecs?: number,
|
|
306
|
+
endSecs?: number,
|
|
307
|
+
): string | undefined {
|
|
308
|
+
if (!sourceUrl) return undefined;
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const url = new URL(sourceUrl);
|
|
312
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, '');
|
|
313
|
+
const start = Math.max(0, Math.floor(startSecs ?? 0));
|
|
314
|
+
const end =
|
|
315
|
+
Number.isFinite(endSecs) && (endSecs as number) > start ? Math.floor(endSecs!) : undefined;
|
|
316
|
+
|
|
317
|
+
const youtubeEmbedUrl = (videoId: string) => {
|
|
318
|
+
const embed = new URL(`https://www.youtube-nocookie.com/embed/${videoId}`);
|
|
319
|
+
embed.searchParams.set('start', String(start));
|
|
320
|
+
embed.searchParams.set('rel', '0');
|
|
321
|
+
if (end !== undefined) embed.searchParams.set('end', String(end));
|
|
322
|
+
return embed.toString();
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
if (host === 'youtu.be' || host.endsWith('.youtu.be')) {
|
|
326
|
+
const videoId = url.pathname.split('/').filter(Boolean)[0];
|
|
327
|
+
return videoId ? youtubeEmbedUrl(videoId) : undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (host === 'youtube.com' || host.endsWith('.youtube.com')) {
|
|
331
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
332
|
+
const videoId =
|
|
333
|
+
url.searchParams.get('v') ||
|
|
334
|
+
(['embed', 'shorts', 'live'].includes(pathParts[0]) ? pathParts[1] : undefined);
|
|
335
|
+
return videoId ? youtubeEmbedUrl(videoId) : undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (host === 'vimeo.com' || host.endsWith('.vimeo.com')) {
|
|
339
|
+
const videoId = url.pathname.split('/').find((part) => /^\d+$/.test(part));
|
|
340
|
+
return videoId ? `https://player.vimeo.com/video/${videoId}#t=${start}s` : undefined;
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
// The local media URL remains the reliable fallback for malformed or
|
|
344
|
+
// unsupported provider URLs.
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Build a provider-aware source link that opens at the cited evidence. */
|
|
351
|
+
export function timestampedMediaUrl(
|
|
352
|
+
sourceUrl?: string,
|
|
353
|
+
startSecs?: number,
|
|
354
|
+
endSecs?: number,
|
|
355
|
+
): string | undefined {
|
|
356
|
+
if (!sourceUrl || !Number.isFinite(startSecs)) return sourceUrl;
|
|
357
|
+
const start = Math.max(0, Math.floor(startSecs as number));
|
|
358
|
+
const end =
|
|
359
|
+
Number.isFinite(endSecs) && (endSecs as number) > start ? Math.floor(endSecs as number) : null;
|
|
360
|
+
try {
|
|
361
|
+
const url = new URL(sourceUrl, 'http://larkup.local');
|
|
362
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, '');
|
|
363
|
+
if (host === 'youtube.com' || host.endsWith('.youtube.com') || host === 'youtu.be') {
|
|
364
|
+
url.searchParams.set('t', `${start}s`);
|
|
365
|
+
return url.origin === 'http://larkup.local'
|
|
366
|
+
? `${url.pathname}${url.search}${url.hash}`
|
|
367
|
+
: url.toString();
|
|
368
|
+
}
|
|
369
|
+
const fragment = `t=${start}${end !== null ? `,${end}` : ''}`;
|
|
370
|
+
url.hash = fragment;
|
|
371
|
+
return url.origin === 'http://larkup.local'
|
|
372
|
+
? `${url.pathname}${url.search}${url.hash}`
|
|
373
|
+
: url.toString();
|
|
374
|
+
} catch {
|
|
375
|
+
return `${sourceUrl.split('#')[0]}#t=${start}${end !== null ? `,${end}` : ''}`;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function MediaCitationFooter({
|
|
380
|
+
icon,
|
|
381
|
+
fileName,
|
|
382
|
+
sourceUrl,
|
|
383
|
+
timestamp,
|
|
384
|
+
}: {
|
|
385
|
+
icon: React.ReactNode;
|
|
386
|
+
fileName?: string;
|
|
387
|
+
sourceUrl?: string;
|
|
388
|
+
timestamp?: string;
|
|
389
|
+
}) {
|
|
390
|
+
if (!fileName && !sourceUrl && !timestamp) return null;
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<div className="flex items-center justify-between gap-3 border-t border-border px-3 py-1.5">
|
|
394
|
+
<div className="flex min-w-0 items-center gap-1.5">
|
|
395
|
+
{icon}
|
|
396
|
+
<span className="truncate text-[11px] text-muted-foreground">
|
|
397
|
+
{fileName ?? 'Media citation'}
|
|
398
|
+
{timestamp ? ` · ${timestamp}` : ''}
|
|
399
|
+
</span>
|
|
400
|
+
</div>
|
|
401
|
+
{sourceUrl ? (
|
|
402
|
+
<a
|
|
403
|
+
href={sourceUrl}
|
|
404
|
+
target="_blank"
|
|
405
|
+
rel="noreferrer"
|
|
406
|
+
className="shrink-0 text-[11px] font-medium text-primary hover:text-primary/80"
|
|
407
|
+
>
|
|
408
|
+
Open source
|
|
409
|
+
</a>
|
|
410
|
+
) : null}
|
|
411
|
+
</div>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* Media preview helpers. */
|
|
416
|
+
/* Markdown media reference parser */
|
|
417
|
+
/* End media preview helpers. */
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Parse media references from assistant text.
|
|
421
|
+
*
|
|
422
|
+
* Supported formats:
|
|
423
|
+
* - [IMAGE_REF:assetId]
|
|
424
|
+
* - [IMAGE_REF:assetId:fileName]
|
|
425
|
+
* - [VIDEO_REF:assetId:startSecs-endSecs]
|
|
426
|
+
* - [AUDIO_REF:assetId]
|
|
427
|
+
*/
|
|
428
|
+
const MEDIA_REF_REGEX = /\[(IMAGE_REF|VIDEO_REF|AUDIO_REF):([a-f0-9-]+)(?::([^\]]*))?\]/g;
|
|
429
|
+
|
|
430
|
+
export interface ParsedMediaRef {
|
|
431
|
+
fullMatch: string;
|
|
432
|
+
type: 'image' | 'video' | 'audio';
|
|
433
|
+
assetId: string;
|
|
434
|
+
extra?: string;
|
|
435
|
+
startSecs?: number;
|
|
436
|
+
endSecs?: number;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function parseMediaRefs(text: string): ParsedMediaRef[] {
|
|
440
|
+
const refs: ParsedMediaRef[] = [];
|
|
441
|
+
let match: RegExpExecArray | null;
|
|
442
|
+
|
|
443
|
+
while ((match = MEDIA_REF_REGEX.exec(text)) !== null) {
|
|
444
|
+
const [fullMatch, refType, assetId, extra] = match;
|
|
445
|
+
const type = refType === 'IMAGE_REF' ? 'image' : refType === 'VIDEO_REF' ? 'video' : 'audio';
|
|
446
|
+
|
|
447
|
+
let startSecs: number | undefined;
|
|
448
|
+
let endSecs: number | undefined;
|
|
449
|
+
|
|
450
|
+
if ((type === 'video' || type === 'audio') && extra?.includes('-')) {
|
|
451
|
+
const [s, e] = extra.split('-').map(Number);
|
|
452
|
+
if (!isNaN(s)) startSecs = s;
|
|
453
|
+
if (!isNaN(e)) endSecs = e;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
refs.push({ fullMatch, type, assetId, extra, startSecs, endSecs });
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return refs;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function formatTimestamp(secs: number): string {
|
|
463
|
+
const m = Math.floor(secs / 60);
|
|
464
|
+
const s = Math.floor(secs % 60);
|
|
465
|
+
return `${m}:${String(s).padStart(2, '0')}`;
|
|
466
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { ChevronDown, Play, Clock, Image as ImageIcon, Table2 } from 'lucide-react';
|
|
5
|
+
import { ChatDataTable, type DataTableConfig } from './chat-data-table';
|
|
6
|
+
import { Card, CardContent } from '../../ui/card';
|
|
7
|
+
|
|
8
|
+
interface SandboxArtifact {
|
|
9
|
+
name: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
data: string; // base64
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SandboxResultConfig {
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
exitCode: number;
|
|
18
|
+
artifacts: SandboxArtifact[];
|
|
19
|
+
executionTimeMs: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function tryParseStdoutAsTable(stdout: string): DataTableConfig | null {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function ChatSandboxResult({ config }: { config: SandboxResultConfig }) {
|
|
27
|
+
const { stdout, stderr, exitCode, artifacts, executionTimeMs } = config;
|
|
28
|
+
|
|
29
|
+
const images = useMemo(
|
|
30
|
+
() => artifacts.filter((a) => a.mimeType.startsWith('image/')),
|
|
31
|
+
[artifacts],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Try to parse stdout as structured table data
|
|
35
|
+
const stdoutTable = useMemo(() => tryParseStdoutAsTable(stdout), [stdout]);
|
|
36
|
+
|
|
37
|
+
const isSuccess = exitCode === 0;
|
|
38
|
+
const isRuntimeUnavailable =
|
|
39
|
+
/docker\.sock|docker (?:isn't|is not) running|code execution is unavailable|needs python 3/i.test(
|
|
40
|
+
stderr,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Card className="overflow-hidden animate-in fade-in slide-in-from-bottom-2 duration-500">
|
|
45
|
+
{/* A successful result speaks for itself; retain a clear header only for failures. */}
|
|
46
|
+
{!isSuccess && (
|
|
47
|
+
<div className="flex items-center justify-between border-b border-border/40 px-4 py-2.5">
|
|
48
|
+
<div className="flex items-center gap-2">
|
|
49
|
+
<div className="flex h-6 w-6 items-center justify-center rounded-md bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400">
|
|
50
|
+
<Play className="size-3" />
|
|
51
|
+
</div>
|
|
52
|
+
<span className="text-xs font-medium text-foreground">Analysis failed</span>
|
|
53
|
+
</div>
|
|
54
|
+
<span className="flex items-center gap-1 text-[10px] tabular-nums text-muted-foreground">
|
|
55
|
+
<Clock className="size-3" />
|
|
56
|
+
{executionTimeMs < 1000
|
|
57
|
+
? `${executionTimeMs}ms`
|
|
58
|
+
: `${(executionTimeMs / 1000).toFixed(1)}s`}
|
|
59
|
+
</span>
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
{/* Generated images (matplotlib charts, etc.) */}
|
|
64
|
+
{images.length > 0 && (
|
|
65
|
+
<div className="space-y-3 p-4">
|
|
66
|
+
{images.map((img, i) => (
|
|
67
|
+
<div key={i} className="overflow-hidden rounded-lg border border-border/40">
|
|
68
|
+
<img
|
|
69
|
+
src={`data:${img.mimeType};base64,${img.data}`}
|
|
70
|
+
alt={img.name}
|
|
71
|
+
className="w-full"
|
|
72
|
+
/>
|
|
73
|
+
<div className="flex items-center gap-2 border-t border-border/40 px-3 py-1.5">
|
|
74
|
+
<ImageIcon className="size-3 text-muted-foreground" />
|
|
75
|
+
<span className="text-[10px] text-muted-foreground">{img.name}</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
{/* Structured stdout (rendered as table) */}
|
|
83
|
+
{stdoutTable && (
|
|
84
|
+
<CardContent className="p-3 pt-3">
|
|
85
|
+
<div className="flex items-center gap-1.5 mb-2 text-xs text-muted-foreground">
|
|
86
|
+
<Table2 className="size-3" />
|
|
87
|
+
<span className="font-medium">Analysis Results</span>
|
|
88
|
+
</div>
|
|
89
|
+
<ChatDataTable config={stdoutTable} />
|
|
90
|
+
</CardContent>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
{/* Plain stdout (only if not parsed as table) */}
|
|
94
|
+
{stdout && !stdoutTable && (
|
|
95
|
+
<div className="border-b border-border/40 px-4 py-3">
|
|
96
|
+
<pre className="max-h-48 overflow-auto whitespace-pre-wrap text-xs leading-relaxed text-foreground [&::-webkit-scrollbar]:hidden">
|
|
97
|
+
{stdout}
|
|
98
|
+
</pre>
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{/* Error display */}
|
|
103
|
+
{!isSuccess && (
|
|
104
|
+
<div className="border-t border-border/40 bg-red-50/50 p-4 dark:bg-red-900/10">
|
|
105
|
+
<div className="flex flex-col gap-2">
|
|
106
|
+
<span className="text-sm font-medium text-red-800 dark:text-red-400">
|
|
107
|
+
{isRuntimeUnavailable ? 'Analysis is unavailable' : 'Analysis encountered an issue'}
|
|
108
|
+
</span>
|
|
109
|
+
<span className="text-xs text-red-600/80 dark:text-red-400/80">
|
|
110
|
+
{isRuntimeUnavailable
|
|
111
|
+
? 'This workspace does not have a code-analysis runtime configured.'
|
|
112
|
+
: 'This calculation could not be completed.'}
|
|
113
|
+
</span>
|
|
114
|
+
{stderr && !isRuntimeUnavailable && (
|
|
115
|
+
<details className="mt-2 group">
|
|
116
|
+
<summary className="cursor-pointer text-xs font-medium text-red-600/70 hover:text-red-600 dark:text-red-400/70 dark:hover:text-red-400 transition-colors list-none flex items-center gap-1">
|
|
117
|
+
<ChevronDown className="size-3 transition-transform group-open:rotate-180" />
|
|
118
|
+
Technical Details
|
|
119
|
+
</summary>
|
|
120
|
+
<pre className="mt-2 max-h-40 overflow-auto whitespace-pre-wrap rounded bg-red-100/50 p-3 text-[11px] leading-relaxed text-red-900/70 dark:bg-red-900/20 dark:text-red-200/70 [&::-webkit-scrollbar]:hidden">
|
|
121
|
+
{stderr}
|
|
122
|
+
</pre>
|
|
123
|
+
</details>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
</Card>
|
|
129
|
+
);
|
|
130
|
+
}
|