@larkup/workspace-app 0.0.0-stage → 0.1.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,907 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { workspaceUrl, workspaceFetch as fetch } from '../../../host';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import {
|
|
6
|
+
Cable,
|
|
7
|
+
CheckIcon,
|
|
8
|
+
Globe,
|
|
9
|
+
Loader2,
|
|
10
|
+
Pencil,
|
|
11
|
+
Plus,
|
|
12
|
+
RefreshCw,
|
|
13
|
+
Search,
|
|
14
|
+
Terminal,
|
|
15
|
+
Trash2,
|
|
16
|
+
XCircle,
|
|
17
|
+
AlertCircle,
|
|
18
|
+
ChevronLeft,
|
|
19
|
+
ChevronRight,
|
|
20
|
+
} from 'lucide-react';
|
|
21
|
+
import { toast } from 'sonner';
|
|
22
|
+
import { Button } from '@larkup/workspace-ui/button';
|
|
23
|
+
import {
|
|
24
|
+
Dialog,
|
|
25
|
+
DialogContent,
|
|
26
|
+
DialogDescription,
|
|
27
|
+
DialogFooter,
|
|
28
|
+
DialogHeader,
|
|
29
|
+
DialogTitle,
|
|
30
|
+
} from '@larkup/workspace-app/components/ui/dialog';
|
|
31
|
+
import { Input } from '@larkup/workspace-app/components/ui/input';
|
|
32
|
+
import { Label } from '@larkup/workspace-ui/label';
|
|
33
|
+
import { Switch } from '@larkup/workspace-app/components/ui/switch';
|
|
34
|
+
import { Textarea } from '@larkup/workspace-app/components/ui/textarea';
|
|
35
|
+
import { Select, SelectContent, SelectItem, SelectTrigger } from '@larkup/workspace-ui/select';
|
|
36
|
+
import { GenericAlert } from '@larkup/workspace-app/components/alerts/generic-alert';
|
|
37
|
+
import { Badge } from '@larkup/workspace-app/components/ui/badge';
|
|
38
|
+
import {
|
|
39
|
+
Tooltip,
|
|
40
|
+
TooltipContent,
|
|
41
|
+
TooltipProvider,
|
|
42
|
+
TooltipTrigger,
|
|
43
|
+
} from '@larkup/workspace-app/components/ui/tooltip';
|
|
44
|
+
import {
|
|
45
|
+
Card,
|
|
46
|
+
CardContent,
|
|
47
|
+
CardDescription,
|
|
48
|
+
CardHeader,
|
|
49
|
+
CardTitle,
|
|
50
|
+
} from '@larkup/workspace-app/components/ui/card';
|
|
51
|
+
|
|
52
|
+
type McpConnection = {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
url: string;
|
|
56
|
+
transport: 'http' | 'sse';
|
|
57
|
+
connectionType: 'direct' | 'proxy';
|
|
58
|
+
proxyUrl?: string;
|
|
59
|
+
proxyAuthToken?: string;
|
|
60
|
+
proxyAuthHeader?: string;
|
|
61
|
+
headers: Record<string, string>;
|
|
62
|
+
enabled: boolean;
|
|
63
|
+
enabledForLocalChat: boolean;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type Draft = Omit<McpConnection, 'id' | 'enabled'>;
|
|
67
|
+
|
|
68
|
+
const emptyDraft = (): Draft => ({
|
|
69
|
+
name: '',
|
|
70
|
+
url: '',
|
|
71
|
+
transport: 'http',
|
|
72
|
+
connectionType: 'direct',
|
|
73
|
+
proxyUrl: '',
|
|
74
|
+
proxyAuthToken: '',
|
|
75
|
+
proxyAuthHeader: 'Authorization',
|
|
76
|
+
headers: {},
|
|
77
|
+
enabledForLocalChat: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function parseHeaders(value: string): Record<string, string> {
|
|
81
|
+
if (!value.trim()) return {};
|
|
82
|
+
const headers: Record<string, string> = {};
|
|
83
|
+
for (const line of value.split('\n')) {
|
|
84
|
+
const separator = line.indexOf(':');
|
|
85
|
+
if (separator < 1) throw new Error('Use one header per line: Name: value');
|
|
86
|
+
headers[line.slice(0, separator).trim()] = line.slice(separator + 1).trim();
|
|
87
|
+
}
|
|
88
|
+
return headers;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Status pill. */
|
|
92
|
+
function StatusIndicator({
|
|
93
|
+
status,
|
|
94
|
+
toolCount,
|
|
95
|
+
}: {
|
|
96
|
+
status?: 'testing' | 'connected' | 'disconnected';
|
|
97
|
+
toolCount: number;
|
|
98
|
+
}) {
|
|
99
|
+
if (!status) return null;
|
|
100
|
+
|
|
101
|
+
if (status === 'testing') {
|
|
102
|
+
return (
|
|
103
|
+
<span className="inline-flex items-center gap-1.5">
|
|
104
|
+
<Loader2 className="size-3.5 animate-spin text-amber-500 dark:text-amber-400" />
|
|
105
|
+
<span className="text-xs font-medium text-amber-600 dark:text-amber-400">Testing…</span>
|
|
106
|
+
</span>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (status === 'connected') {
|
|
111
|
+
return (
|
|
112
|
+
<span className="inline-flex items-center gap-1.5">
|
|
113
|
+
<CheckIcon className="size-3.5 rounded-full bg-emerald-600 p-0.5 text-white dark:bg-emerald-500" />
|
|
114
|
+
<span className="text-xs font-medium text-emerald-700 dark:text-emerald-400">
|
|
115
|
+
Connected
|
|
116
|
+
</span>
|
|
117
|
+
<Badge
|
|
118
|
+
variant="secondary"
|
|
119
|
+
className="ml-0.5 px-1.5 py-0 text-[10px] font-semibold leading-4"
|
|
120
|
+
>
|
|
121
|
+
{toolCount} tool{toolCount !== 1 ? 's' : ''}
|
|
122
|
+
</Badge>
|
|
123
|
+
</span>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<span className="inline-flex items-center gap-1.5">
|
|
129
|
+
<XCircle className="size-3.5 text-destructive" />
|
|
130
|
+
<span className="text-xs font-medium text-destructive">Disconnected</span>
|
|
131
|
+
</span>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Connection card. */
|
|
136
|
+
function ConnectionCard({
|
|
137
|
+
connection,
|
|
138
|
+
testResult,
|
|
139
|
+
isTesting,
|
|
140
|
+
onToggle,
|
|
141
|
+
onTest,
|
|
142
|
+
onDelete,
|
|
143
|
+
onEdit,
|
|
144
|
+
}: {
|
|
145
|
+
connection: McpConnection;
|
|
146
|
+
testResult?: { status: 'testing' | 'connected' | 'disconnected'; toolCount: number };
|
|
147
|
+
isTesting: boolean;
|
|
148
|
+
onToggle: (enabled: boolean) => void;
|
|
149
|
+
onTest: () => void;
|
|
150
|
+
onDelete: () => void;
|
|
151
|
+
onEdit: () => void;
|
|
152
|
+
}) {
|
|
153
|
+
return (
|
|
154
|
+
<div
|
|
155
|
+
className="group flex flex-wrap items-center gap-4 rounded-xl border border-border bg-white/70 px-4 py-3.5 transition-colors hover:border-primary/20 hover:bg-white/90 dark:bg-card/60 dark:hover:border-primary/30 dark:hover:bg-card/80 cursor-pointer"
|
|
156
|
+
onClick={onEdit}
|
|
157
|
+
role="button"
|
|
158
|
+
tabIndex={0}
|
|
159
|
+
onKeyDown={(e) => e.key === 'Enter' && onEdit()}
|
|
160
|
+
>
|
|
161
|
+
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-border bg-white dark:border-border/60 dark:bg-secondary/70">
|
|
162
|
+
<img src="/icons/mcp.svg" alt="" className="size-5 object-contain" />
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="min-w-0 flex-1">
|
|
166
|
+
<div className="flex items-center gap-2">
|
|
167
|
+
<p className="truncate text-sm font-medium">{connection.name}</p>
|
|
168
|
+
<Badge variant="outline" className="shrink-0 text-[10px] uppercase tracking-wide">
|
|
169
|
+
{connection.transport === 'sse' ? 'SSE' : 'HTTP'}
|
|
170
|
+
</Badge>
|
|
171
|
+
{connection.connectionType === 'proxy' && (
|
|
172
|
+
<Badge variant="outline" className="shrink-0 text-[10px] uppercase tracking-wide">
|
|
173
|
+
Proxy
|
|
174
|
+
</Badge>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
<p className="mt-0.5 truncate font-mono text-xs text-muted-foreground">{connection.url}</p>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<StatusIndicator status={testResult?.status} toolCount={testResult?.toolCount ?? 0} />
|
|
181
|
+
|
|
182
|
+
<div className="flex items-center gap-1.5" onClick={(e) => e.stopPropagation()}>
|
|
183
|
+
<Switch
|
|
184
|
+
checked={connection.enabled}
|
|
185
|
+
onCheckedChange={onToggle}
|
|
186
|
+
aria-label={`Enable ${connection.name}`}
|
|
187
|
+
/>
|
|
188
|
+
|
|
189
|
+
<TooltipProvider delay={300}>
|
|
190
|
+
<Tooltip>
|
|
191
|
+
<TooltipTrigger
|
|
192
|
+
render={
|
|
193
|
+
<Button
|
|
194
|
+
variant="ghost"
|
|
195
|
+
size="icon"
|
|
196
|
+
className="size-8 text-muted-foreground hover:text-foreground"
|
|
197
|
+
onClick={onTest}
|
|
198
|
+
disabled={isTesting}
|
|
199
|
+
>
|
|
200
|
+
{isTesting ? (
|
|
201
|
+
<Loader2 className="size-3.5 animate-spin" />
|
|
202
|
+
) : (
|
|
203
|
+
<RefreshCw className="size-3.5" />
|
|
204
|
+
)}
|
|
205
|
+
</Button>
|
|
206
|
+
}
|
|
207
|
+
/>
|
|
208
|
+
<TooltipContent side="bottom">Test connection</TooltipContent>
|
|
209
|
+
</Tooltip>
|
|
210
|
+
</TooltipProvider>
|
|
211
|
+
|
|
212
|
+
<TooltipProvider delay={300}>
|
|
213
|
+
<Tooltip>
|
|
214
|
+
<TooltipTrigger
|
|
215
|
+
render={
|
|
216
|
+
<Button
|
|
217
|
+
variant="ghost"
|
|
218
|
+
size="icon"
|
|
219
|
+
className="size-8 text-muted-foreground hover:text-destructive"
|
|
220
|
+
onClick={onDelete}
|
|
221
|
+
aria-label={`Remove ${connection.name}`}
|
|
222
|
+
>
|
|
223
|
+
<Trash2 className="size-3.5" />
|
|
224
|
+
</Button>
|
|
225
|
+
}
|
|
226
|
+
/>
|
|
227
|
+
<TooltipContent side="bottom">Remove connection</TooltipContent>
|
|
228
|
+
</Tooltip>
|
|
229
|
+
</TooltipProvider>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function McpConnectionsSection() {
|
|
236
|
+
const [connections, setConnections] = useState<McpConnection[]>([]);
|
|
237
|
+
const [loading, setLoading] = useState(true);
|
|
238
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
239
|
+
const [draft, setDraft] = useState<Draft>(emptyDraft);
|
|
240
|
+
const [headersText, setHeadersText] = useState('');
|
|
241
|
+
const [saving, setSaving] = useState(false);
|
|
242
|
+
const [testingId, setTestingId] = useState<string | null>(null);
|
|
243
|
+
const [toDelete, setToDelete] = useState<McpConnection | null>(null);
|
|
244
|
+
const [search, setSearch] = useState('');
|
|
245
|
+
const [page, setPage] = useState(1);
|
|
246
|
+
const pageSize = 5;
|
|
247
|
+
const [testResults, setTestResults] = useState<
|
|
248
|
+
Record<string, { status: 'testing' | 'connected' | 'disconnected'; toolCount: number }>
|
|
249
|
+
>({});
|
|
250
|
+
|
|
251
|
+
// ── Edit dialog state ──
|
|
252
|
+
const [editTarget, setEditTarget] = useState<McpConnection | null>(null);
|
|
253
|
+
const [editDraft, setEditDraft] = useState<Draft>(emptyDraft);
|
|
254
|
+
const [editHeadersText, setEditHeadersText] = useState('');
|
|
255
|
+
const [editSaving, setEditSaving] = useState(false);
|
|
256
|
+
|
|
257
|
+
const silentTestConnection = async (id: string) => {
|
|
258
|
+
setTestResults((prev) => ({ ...prev, [id]: { status: 'testing', toolCount: 0 } }));
|
|
259
|
+
try {
|
|
260
|
+
const response = await fetch(workspaceUrl(`/api/mcp/${id}/test`), { method: 'POST' });
|
|
261
|
+
const data = await response.json();
|
|
262
|
+
if (!response.ok) throw new Error();
|
|
263
|
+
setTestResults((prev) => ({
|
|
264
|
+
...prev,
|
|
265
|
+
[id]: { status: 'connected', toolCount: data.toolCount || 0 },
|
|
266
|
+
}));
|
|
267
|
+
} catch (e) {
|
|
268
|
+
setTestResults((prev) => ({ ...prev, [id]: { status: 'disconnected', toolCount: 0 } }));
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const load = async () => {
|
|
273
|
+
try {
|
|
274
|
+
const response = await fetch(workspaceUrl('/api/mcp'));
|
|
275
|
+
if (!response.ok) throw new Error('Could not load MCP connections');
|
|
276
|
+
const data = (await response.json()) as { connections: McpConnection[] };
|
|
277
|
+
setConnections(data.connections);
|
|
278
|
+
data.connections.forEach((c) => void silentTestConnection(c.id));
|
|
279
|
+
} catch (error) {
|
|
280
|
+
toast.error(error instanceof Error ? error.message : 'Could not load MCP connections');
|
|
281
|
+
} finally {
|
|
282
|
+
setLoading(false);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
useEffect(() => {
|
|
287
|
+
void load();
|
|
288
|
+
}, []);
|
|
289
|
+
|
|
290
|
+
const openCreate = () => {
|
|
291
|
+
setDraft(emptyDraft());
|
|
292
|
+
setHeadersText('');
|
|
293
|
+
setDialogOpen(true);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const save = async () => {
|
|
297
|
+
setSaving(true);
|
|
298
|
+
try {
|
|
299
|
+
const { proxyUrl, proxyAuthToken, proxyAuthHeader, ...connection } = draft;
|
|
300
|
+
const response = await fetch(workspaceUrl('/api/mcp'), {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
headers: { 'Content-Type': 'application/json' },
|
|
303
|
+
body: JSON.stringify({
|
|
304
|
+
...connection,
|
|
305
|
+
...(draft.connectionType === 'proxy'
|
|
306
|
+
? {
|
|
307
|
+
proxyUrl,
|
|
308
|
+
proxyAuthToken,
|
|
309
|
+
proxyAuthHeader,
|
|
310
|
+
}
|
|
311
|
+
: {}),
|
|
312
|
+
headers: parseHeaders(headersText),
|
|
313
|
+
}),
|
|
314
|
+
});
|
|
315
|
+
const data = await response.json();
|
|
316
|
+
if (!response.ok) throw new Error(data.error || 'Could not add MCP connection');
|
|
317
|
+
setConnections((current) => [...current, data]);
|
|
318
|
+
setDialogOpen(false);
|
|
319
|
+
void silentTestConnection(data.id);
|
|
320
|
+
toast.success('MCP connected');
|
|
321
|
+
} catch (error) {
|
|
322
|
+
toast.error(error instanceof Error ? error.message : 'Could not add MCP connection');
|
|
323
|
+
} finally {
|
|
324
|
+
setSaving(false);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const update = async (connection: McpConnection, patch: Partial<McpConnection>) => {
|
|
329
|
+
const response = await fetch(workspaceUrl(`/api/mcp/${connection.id}`), {
|
|
330
|
+
method: 'PUT',
|
|
331
|
+
headers: { 'Content-Type': 'application/json' },
|
|
332
|
+
body: JSON.stringify(patch),
|
|
333
|
+
});
|
|
334
|
+
const data = await response.json();
|
|
335
|
+
if (!response.ok) throw new Error(data.error || 'Could not update MCP connection');
|
|
336
|
+
setConnections((current) => current.map((item) => (item.id === connection.id ? data : item)));
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const testConnection = async (connection: McpConnection) => {
|
|
340
|
+
setTestingId(connection.id);
|
|
341
|
+
setTestResults((prev) => ({ ...prev, [connection.id]: { status: 'testing', toolCount: 0 } }));
|
|
342
|
+
try {
|
|
343
|
+
const response = await fetch(workspaceUrl(`/api/mcp/${connection.id}/test`), {
|
|
344
|
+
method: 'POST',
|
|
345
|
+
});
|
|
346
|
+
const data = await response.json();
|
|
347
|
+
if (!response.ok) throw new Error(data.error || 'Connection test failed');
|
|
348
|
+
setTestResults((prev) => ({
|
|
349
|
+
...prev,
|
|
350
|
+
[connection.id]: { status: 'connected', toolCount: data.toolCount || 0 },
|
|
351
|
+
}));
|
|
352
|
+
toast.success(`Connected — ${data.toolCount} tool${data.toolCount === 1 ? '' : 's'} found`);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
setTestResults((prev) => ({
|
|
355
|
+
...prev,
|
|
356
|
+
[connection.id]: { status: 'disconnected', toolCount: 0 },
|
|
357
|
+
}));
|
|
358
|
+
toast.error(error instanceof Error ? error.message : 'Connection test failed');
|
|
359
|
+
} finally {
|
|
360
|
+
setTestingId(null);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const remove = async () => {
|
|
365
|
+
if (!toDelete) return;
|
|
366
|
+
try {
|
|
367
|
+
const response = await fetch(workspaceUrl(`/api/mcp/${toDelete.id}`), { method: 'DELETE' });
|
|
368
|
+
if (!response.ok) throw new Error((await response.json()).error || 'Could not remove MCP');
|
|
369
|
+
setConnections((current) => current.filter((item) => item.id !== toDelete.id));
|
|
370
|
+
toast.success('MCP connection removed');
|
|
371
|
+
} catch (error) {
|
|
372
|
+
toast.error(error instanceof Error ? error.message : 'Could not remove MCP');
|
|
373
|
+
} finally {
|
|
374
|
+
setToDelete(null);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// ── Edit helpers ──
|
|
379
|
+
const openEdit = (connection: McpConnection) => {
|
|
380
|
+
setEditTarget(connection);
|
|
381
|
+
setEditDraft({
|
|
382
|
+
name: connection.name,
|
|
383
|
+
url: connection.url,
|
|
384
|
+
transport: connection.transport,
|
|
385
|
+
connectionType: connection.connectionType,
|
|
386
|
+
proxyUrl: connection.proxyUrl ?? '',
|
|
387
|
+
proxyAuthToken: connection.proxyAuthToken ?? '',
|
|
388
|
+
proxyAuthHeader: connection.proxyAuthHeader ?? 'Authorization',
|
|
389
|
+
headers: connection.headers ?? {},
|
|
390
|
+
enabledForLocalChat: connection.enabledForLocalChat,
|
|
391
|
+
});
|
|
392
|
+
setEditHeadersText(
|
|
393
|
+
Object.entries(connection.headers ?? {})
|
|
394
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
395
|
+
.join('\n'),
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const saveEdit = async () => {
|
|
400
|
+
if (!editTarget) return;
|
|
401
|
+
setEditSaving(true);
|
|
402
|
+
try {
|
|
403
|
+
const { proxyUrl, proxyAuthToken, proxyAuthHeader, ...rest } = editDraft;
|
|
404
|
+
const payload: Partial<McpConnection> = {
|
|
405
|
+
...rest,
|
|
406
|
+
...(editDraft.connectionType === 'proxy'
|
|
407
|
+
? { proxyUrl, proxyAuthToken, proxyAuthHeader }
|
|
408
|
+
: {}),
|
|
409
|
+
headers: parseHeaders(editHeadersText),
|
|
410
|
+
};
|
|
411
|
+
await update(editTarget, payload);
|
|
412
|
+
setEditTarget(null);
|
|
413
|
+
void silentTestConnection(editTarget.id);
|
|
414
|
+
toast.success('Connection updated');
|
|
415
|
+
} catch (error) {
|
|
416
|
+
toast.error(error instanceof Error ? error.message : 'Could not update connection');
|
|
417
|
+
} finally {
|
|
418
|
+
setEditSaving(false);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const filtered = connections.filter(
|
|
423
|
+
(c) =>
|
|
424
|
+
!search.trim() ||
|
|
425
|
+
c.name.toLowerCase().includes(search.trim().toLowerCase()) ||
|
|
426
|
+
c.url.toLowerCase().includes(search.trim().toLowerCase()),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
setPage(1);
|
|
431
|
+
}, [search]);
|
|
432
|
+
|
|
433
|
+
const totalPages = Math.ceil(filtered.length / pageSize);
|
|
434
|
+
const paginatedConnections = filtered.slice((page - 1) * pageSize, page * pageSize);
|
|
435
|
+
|
|
436
|
+
return (
|
|
437
|
+
<Card className="bg-transparent border-none">
|
|
438
|
+
<CardContent className="space-y-4">
|
|
439
|
+
<div className="w-full gap-3 flex items-center justify-between">
|
|
440
|
+
{!loading && connections.length > 0 && (
|
|
441
|
+
<div className="relative w-full">
|
|
442
|
+
<Search className="absolute left-3 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
443
|
+
<Input
|
|
444
|
+
placeholder="Search connections…"
|
|
445
|
+
className="h-9 pl-9 w-full bg-white/70"
|
|
446
|
+
value={search}
|
|
447
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
448
|
+
/>
|
|
449
|
+
</div>
|
|
450
|
+
)}
|
|
451
|
+
|
|
452
|
+
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
453
|
+
{connections.length > 0 && (
|
|
454
|
+
<Button onClick={openCreate} size="default" className="gap-1.5 px-3">
|
|
455
|
+
<Plus className="size-3.5" /> Add MCP
|
|
456
|
+
</Button>
|
|
457
|
+
)}
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
{loading ? (
|
|
462
|
+
<div className="flex items-center justify-center py-14 text-sm text-muted-foreground">
|
|
463
|
+
<Loader2 className="mr-2 size-4 animate-spin" /> Loading connections…
|
|
464
|
+
</div>
|
|
465
|
+
) : connections.length === 0 ? (
|
|
466
|
+
<div className="rounded-xl px-4 py-20 dark:bg-card/40">
|
|
467
|
+
<div className="flex flex-col items-center gap-3">
|
|
468
|
+
<div className="flex size-12 items-center justify-center rounded-xl border border-border dark:bg-secondary/70">
|
|
469
|
+
<img src="/icons/mcp.svg" alt="MCP" className="size-7 object-contain" />
|
|
470
|
+
</div>
|
|
471
|
+
<div className="text-center">
|
|
472
|
+
<h2 className="text-base font-semibold tracking-tight">No MCP connections yet</h2>
|
|
473
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
474
|
+
Add a remote MCP endpoint to discover its tools.
|
|
475
|
+
</p>
|
|
476
|
+
</div>
|
|
477
|
+
<Button onClick={openCreate} className="mt-2 gap-1.5">
|
|
478
|
+
<Plus className="size-4" /> Add MCP
|
|
479
|
+
</Button>
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
) : filtered.length === 0 ? (
|
|
483
|
+
<div className="flex flex-col items-center gap-2 rounded-xl border border-dashed border-border bg-white/60 px-4 py-14 dark:bg-card/40">
|
|
484
|
+
<AlertCircle className="size-5 text-muted-foreground" />
|
|
485
|
+
<p className="text-sm text-muted-foreground">
|
|
486
|
+
No connections match “{search}”
|
|
487
|
+
</p>
|
|
488
|
+
</div>
|
|
489
|
+
) : (
|
|
490
|
+
<div className="space-y-2">
|
|
491
|
+
{paginatedConnections.map((connection) => (
|
|
492
|
+
<ConnectionCard
|
|
493
|
+
key={connection.id}
|
|
494
|
+
connection={connection}
|
|
495
|
+
testResult={testResults[connection.id]}
|
|
496
|
+
isTesting={testingId === connection.id}
|
|
497
|
+
onToggle={(enabled) =>
|
|
498
|
+
void update(connection, { enabled }).catch((error) => toast.error(error.message))
|
|
499
|
+
}
|
|
500
|
+
onTest={() => void testConnection(connection)}
|
|
501
|
+
onDelete={() => setToDelete(connection)}
|
|
502
|
+
onEdit={() => openEdit(connection)}
|
|
503
|
+
/>
|
|
504
|
+
))}
|
|
505
|
+
{totalPages > 1 && (
|
|
506
|
+
<div className="flex items-center justify-between pt-2">
|
|
507
|
+
<span className="text-xs text-muted-foreground">
|
|
508
|
+
Showing {(page - 1) * pageSize + 1} to{' '}
|
|
509
|
+
{Math.min(page * pageSize, filtered.length)} of {filtered.length} connections
|
|
510
|
+
</span>
|
|
511
|
+
<div className="flex items-center gap-1">
|
|
512
|
+
<Button
|
|
513
|
+
variant="outline"
|
|
514
|
+
size="sm"
|
|
515
|
+
className="h-7 w-7 p-0"
|
|
516
|
+
disabled={page === 1}
|
|
517
|
+
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
|
518
|
+
>
|
|
519
|
+
<ChevronLeft className="size-3.5" />
|
|
520
|
+
</Button>
|
|
521
|
+
<span className="text-xs text-muted-foreground mx-2">
|
|
522
|
+
Page {page} of {totalPages}
|
|
523
|
+
</span>
|
|
524
|
+
<Button
|
|
525
|
+
variant="outline"
|
|
526
|
+
size="sm"
|
|
527
|
+
className="h-7 w-7 p-0"
|
|
528
|
+
disabled={page === totalPages}
|
|
529
|
+
onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
|
|
530
|
+
>
|
|
531
|
+
<ChevronRight className="size-3.5" />
|
|
532
|
+
</Button>
|
|
533
|
+
</div>
|
|
534
|
+
</div>
|
|
535
|
+
)}
|
|
536
|
+
</div>
|
|
537
|
+
)}
|
|
538
|
+
</CardContent>
|
|
539
|
+
|
|
540
|
+
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
541
|
+
<DialogContent className="max-h-[calc(100dvh-2rem)] w-full max-w-3xl! overflow-hidden flex flex-col">
|
|
542
|
+
<DialogHeader>
|
|
543
|
+
<DialogTitle>Add MCP connection</DialogTitle>
|
|
544
|
+
<DialogDescription>
|
|
545
|
+
Supports remote Streamable HTTP and SSE. Store MCP credentials in headers, never in a
|
|
546
|
+
URL.
|
|
547
|
+
</DialogDescription>
|
|
548
|
+
</DialogHeader>
|
|
549
|
+
|
|
550
|
+
<div className="grid gap-4 py-2 max-h-[60vh] overflow-y-auto pr-1">
|
|
551
|
+
{/* Name */}
|
|
552
|
+
<div className="grid gap-1.5">
|
|
553
|
+
<Label htmlFor="mcp-name">
|
|
554
|
+
Name <span className="text-destructive">*</span>
|
|
555
|
+
</Label>
|
|
556
|
+
<Input
|
|
557
|
+
id="mcp-name"
|
|
558
|
+
placeholder="Customer support"
|
|
559
|
+
value={draft.name}
|
|
560
|
+
onChange={(event) => setDraft({ ...draft, name: event.target.value })}
|
|
561
|
+
disabled={saving}
|
|
562
|
+
/>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
{/* Transport + Connection type */}
|
|
566
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
567
|
+
<div className="grid gap-1.5">
|
|
568
|
+
<Label>
|
|
569
|
+
Transport <span className="text-destructive">*</span>
|
|
570
|
+
</Label>
|
|
571
|
+
<Select
|
|
572
|
+
value={draft.transport}
|
|
573
|
+
onValueChange={(transport) =>
|
|
574
|
+
setDraft({ ...draft, transport: transport as Draft['transport'] })
|
|
575
|
+
}
|
|
576
|
+
disabled={saving}
|
|
577
|
+
>
|
|
578
|
+
<SelectTrigger className="w-full">
|
|
579
|
+
{draft.transport === 'sse' ? 'SSE' : 'Streamable HTTP'}
|
|
580
|
+
</SelectTrigger>
|
|
581
|
+
<SelectContent>
|
|
582
|
+
<SelectItem value="http">Streamable HTTP</SelectItem>
|
|
583
|
+
<SelectItem value="sse">SSE (legacy)</SelectItem>
|
|
584
|
+
</SelectContent>
|
|
585
|
+
</Select>
|
|
586
|
+
</div>
|
|
587
|
+
<div className="grid gap-1.5">
|
|
588
|
+
<Label>
|
|
589
|
+
Connection type <span className="text-destructive">*</span>
|
|
590
|
+
</Label>
|
|
591
|
+
<Select
|
|
592
|
+
value={draft.connectionType}
|
|
593
|
+
onValueChange={(connectionType) =>
|
|
594
|
+
setDraft({
|
|
595
|
+
...draft,
|
|
596
|
+
connectionType: connectionType as Draft['connectionType'],
|
|
597
|
+
})
|
|
598
|
+
}
|
|
599
|
+
disabled={saving}
|
|
600
|
+
>
|
|
601
|
+
<SelectTrigger className="w-full" aria-label="Connection type">
|
|
602
|
+
{draft.connectionType === 'proxy' ? 'Proxy' : 'Direct'}
|
|
603
|
+
</SelectTrigger>
|
|
604
|
+
<SelectContent>
|
|
605
|
+
<SelectItem value="direct">Direct</SelectItem>
|
|
606
|
+
<SelectItem value="proxy">Proxy</SelectItem>
|
|
607
|
+
</SelectContent>
|
|
608
|
+
</Select>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
|
|
612
|
+
{/* Server URL */}
|
|
613
|
+
<div className="grid gap-1.5">
|
|
614
|
+
<Label htmlFor="mcp-url">
|
|
615
|
+
Server URL <span className="text-destructive">*</span>
|
|
616
|
+
</Label>
|
|
617
|
+
<Input
|
|
618
|
+
id="mcp-url"
|
|
619
|
+
inputMode="url"
|
|
620
|
+
placeholder="https://mcp.example.com/mcp"
|
|
621
|
+
value={draft.url}
|
|
622
|
+
onChange={(event) => setDraft({ ...draft, url: event.target.value })}
|
|
623
|
+
disabled={saving}
|
|
624
|
+
/>
|
|
625
|
+
</div>
|
|
626
|
+
|
|
627
|
+
{/* Proxy config */}
|
|
628
|
+
{draft.connectionType === 'proxy' && (
|
|
629
|
+
<div className="grid gap-4 rounded-lg border border-dashed bg-muted/20 p-4">
|
|
630
|
+
<div>
|
|
631
|
+
<p className="text-sm font-medium">Proxy configuration</p>
|
|
632
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
633
|
+
MCP traffic is sent to this proxy instead of the MCP server URL above.
|
|
634
|
+
</p>
|
|
635
|
+
</div>
|
|
636
|
+
<div className="grid gap-1.5">
|
|
637
|
+
<Label htmlFor="mcp-proxy-url">
|
|
638
|
+
Proxy URL <span className="text-destructive">*</span>
|
|
639
|
+
</Label>
|
|
640
|
+
<Input
|
|
641
|
+
id="mcp-proxy-url"
|
|
642
|
+
inputMode="url"
|
|
643
|
+
placeholder="https://proxy.example.com/mcp"
|
|
644
|
+
value={draft.proxyUrl ?? ''}
|
|
645
|
+
onChange={(event) => setDraft({ ...draft, proxyUrl: event.target.value })}
|
|
646
|
+
disabled={saving}
|
|
647
|
+
/>
|
|
648
|
+
</div>
|
|
649
|
+
<div className="grid gap-1.5">
|
|
650
|
+
<Label htmlFor="mcp-proxy-token">
|
|
651
|
+
Proxy auth token{' '}
|
|
652
|
+
<span className="font-normal text-muted-foreground">(optional)</span>
|
|
653
|
+
</Label>
|
|
654
|
+
<Input
|
|
655
|
+
id="mcp-proxy-token"
|
|
656
|
+
type="password"
|
|
657
|
+
autoComplete="new-password"
|
|
658
|
+
placeholder="Proxy access token"
|
|
659
|
+
value={draft.proxyAuthToken ?? ''}
|
|
660
|
+
onChange={(event) => setDraft({ ...draft, proxyAuthToken: event.target.value })}
|
|
661
|
+
disabled={saving}
|
|
662
|
+
/>
|
|
663
|
+
</div>
|
|
664
|
+
<div className="grid gap-1.5">
|
|
665
|
+
<Label htmlFor="mcp-proxy-auth-header">Proxy auth header</Label>
|
|
666
|
+
<Input
|
|
667
|
+
id="mcp-proxy-auth-header"
|
|
668
|
+
placeholder="Authorization"
|
|
669
|
+
value={draft.proxyAuthHeader ?? 'Authorization'}
|
|
670
|
+
onChange={(event) =>
|
|
671
|
+
setDraft({ ...draft, proxyAuthHeader: event.target.value })
|
|
672
|
+
}
|
|
673
|
+
disabled={saving}
|
|
674
|
+
/>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
)}
|
|
678
|
+
|
|
679
|
+
{/* Headers */}
|
|
680
|
+
<div className="grid gap-1.5">
|
|
681
|
+
<Label htmlFor="mcp-headers">
|
|
682
|
+
Headers <span className="font-normal text-muted-foreground">(optional)</span>
|
|
683
|
+
</Label>
|
|
684
|
+
<Textarea
|
|
685
|
+
id="mcp-headers"
|
|
686
|
+
className="min-h-24 font-mono text-xs"
|
|
687
|
+
placeholder={'Authorization: Bearer your-token\nX-Workspace: acme'}
|
|
688
|
+
value={headersText}
|
|
689
|
+
onChange={(event) => setHeadersText(event.target.value)}
|
|
690
|
+
disabled={saving}
|
|
691
|
+
/>
|
|
692
|
+
</div>
|
|
693
|
+
</div>
|
|
694
|
+
|
|
695
|
+
<DialogFooter>
|
|
696
|
+
<Button variant="outline" onClick={() => setDialogOpen(false)} disabled={saving}>
|
|
697
|
+
Cancel
|
|
698
|
+
</Button>
|
|
699
|
+
<Button
|
|
700
|
+
onClick={() => void save()}
|
|
701
|
+
disabled={saving || !draft.name.trim() || !draft.url.trim()}
|
|
702
|
+
>
|
|
703
|
+
{saving ? (
|
|
704
|
+
<>
|
|
705
|
+
<Loader2 className="mr-2 size-4 animate-spin" />
|
|
706
|
+
Connecting…
|
|
707
|
+
</>
|
|
708
|
+
) : (
|
|
709
|
+
'Add connection'
|
|
710
|
+
)}
|
|
711
|
+
</Button>
|
|
712
|
+
</DialogFooter>
|
|
713
|
+
</DialogContent>
|
|
714
|
+
</Dialog>
|
|
715
|
+
|
|
716
|
+
{/* Edit connection dialog. */}
|
|
717
|
+
<Dialog open={editTarget !== null} onOpenChange={(open) => !open && setEditTarget(null)}>
|
|
718
|
+
<DialogContent className="max-h-[calc(100dvh-2rem)] w-full max-w-3xl! overflow-hidden flex flex-col">
|
|
719
|
+
<DialogHeader>
|
|
720
|
+
<DialogTitle className="flex items-center gap-2">
|
|
721
|
+
<Pencil className="size-4" />
|
|
722
|
+
Edit connection
|
|
723
|
+
</DialogTitle>
|
|
724
|
+
<DialogDescription>
|
|
725
|
+
Update the configuration for{' '}
|
|
726
|
+
<span className="font-medium text-foreground">{editTarget?.name}</span>.
|
|
727
|
+
</DialogDescription>
|
|
728
|
+
</DialogHeader>
|
|
729
|
+
|
|
730
|
+
<div className="grid gap-4 py-2 max-h-[60vh] overflow-y-auto pr-1">
|
|
731
|
+
{/* Name */}
|
|
732
|
+
<div className="grid gap-1.5">
|
|
733
|
+
<Label htmlFor="edit-mcp-name">
|
|
734
|
+
Name <span className="text-destructive">*</span>
|
|
735
|
+
</Label>
|
|
736
|
+
<Input
|
|
737
|
+
id="edit-mcp-name"
|
|
738
|
+
placeholder="Customer support"
|
|
739
|
+
value={editDraft.name}
|
|
740
|
+
onChange={(e) => setEditDraft({ ...editDraft, name: e.target.value })}
|
|
741
|
+
disabled={editSaving}
|
|
742
|
+
/>
|
|
743
|
+
</div>
|
|
744
|
+
|
|
745
|
+
{/* Transport + Connection type */}
|
|
746
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
747
|
+
<div className="grid gap-1.5">
|
|
748
|
+
<Label>
|
|
749
|
+
Transport <span className="text-destructive">*</span>
|
|
750
|
+
</Label>
|
|
751
|
+
<Select
|
|
752
|
+
value={editDraft.transport}
|
|
753
|
+
onValueChange={(v) =>
|
|
754
|
+
setEditDraft({ ...editDraft, transport: v as Draft['transport'] })
|
|
755
|
+
}
|
|
756
|
+
disabled={editSaving}
|
|
757
|
+
>
|
|
758
|
+
<SelectTrigger className="w-full">
|
|
759
|
+
{editDraft.transport === 'sse' ? 'SSE' : 'Streamable HTTP'}
|
|
760
|
+
</SelectTrigger>
|
|
761
|
+
<SelectContent>
|
|
762
|
+
<SelectItem value="http">Streamable HTTP</SelectItem>
|
|
763
|
+
<SelectItem value="sse">SSE (legacy)</SelectItem>
|
|
764
|
+
</SelectContent>
|
|
765
|
+
</Select>
|
|
766
|
+
</div>
|
|
767
|
+
<div className="grid gap-1.5">
|
|
768
|
+
<Label>
|
|
769
|
+
Connection type <span className="text-destructive">*</span>
|
|
770
|
+
</Label>
|
|
771
|
+
<Select
|
|
772
|
+
value={editDraft.connectionType}
|
|
773
|
+
onValueChange={(v) =>
|
|
774
|
+
setEditDraft({
|
|
775
|
+
...editDraft,
|
|
776
|
+
connectionType: v as Draft['connectionType'],
|
|
777
|
+
})
|
|
778
|
+
}
|
|
779
|
+
disabled={editSaving}
|
|
780
|
+
>
|
|
781
|
+
<SelectTrigger className="w-full" aria-label="Connection type">
|
|
782
|
+
{editDraft.connectionType === 'proxy' ? 'Proxy' : 'Direct'}
|
|
783
|
+
</SelectTrigger>
|
|
784
|
+
<SelectContent>
|
|
785
|
+
<SelectItem value="direct">Direct</SelectItem>
|
|
786
|
+
<SelectItem value="proxy">Proxy</SelectItem>
|
|
787
|
+
</SelectContent>
|
|
788
|
+
</Select>
|
|
789
|
+
</div>
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
{/* Server URL */}
|
|
793
|
+
<div className="grid gap-1.5">
|
|
794
|
+
<Label htmlFor="edit-mcp-url">
|
|
795
|
+
Server URL <span className="text-destructive">*</span>
|
|
796
|
+
</Label>
|
|
797
|
+
<Input
|
|
798
|
+
id="edit-mcp-url"
|
|
799
|
+
inputMode="url"
|
|
800
|
+
placeholder="https://mcp.example.com/mcp"
|
|
801
|
+
value={editDraft.url}
|
|
802
|
+
onChange={(e) => setEditDraft({ ...editDraft, url: e.target.value })}
|
|
803
|
+
disabled={editSaving}
|
|
804
|
+
/>
|
|
805
|
+
</div>
|
|
806
|
+
|
|
807
|
+
{/* Proxy config */}
|
|
808
|
+
{editDraft.connectionType === 'proxy' && (
|
|
809
|
+
<div className="grid gap-4 rounded-lg border border-dashed bg-muted/20 p-4">
|
|
810
|
+
<div>
|
|
811
|
+
<p className="text-sm font-medium">Proxy configuration</p>
|
|
812
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
813
|
+
MCP traffic is sent to this proxy instead of the MCP server URL above.
|
|
814
|
+
</p>
|
|
815
|
+
</div>
|
|
816
|
+
<div className="grid gap-1.5">
|
|
817
|
+
<Label htmlFor="edit-mcp-proxy-url">
|
|
818
|
+
Proxy URL <span className="text-destructive">*</span>
|
|
819
|
+
</Label>
|
|
820
|
+
<Input
|
|
821
|
+
id="edit-mcp-proxy-url"
|
|
822
|
+
inputMode="url"
|
|
823
|
+
placeholder="https://proxy.example.com/mcp"
|
|
824
|
+
value={editDraft.proxyUrl ?? ''}
|
|
825
|
+
onChange={(e) => setEditDraft({ ...editDraft, proxyUrl: e.target.value })}
|
|
826
|
+
disabled={editSaving}
|
|
827
|
+
/>
|
|
828
|
+
</div>
|
|
829
|
+
<div className="grid gap-1.5">
|
|
830
|
+
<Label htmlFor="edit-mcp-proxy-token">
|
|
831
|
+
Proxy auth token{' '}
|
|
832
|
+
<span className="font-normal text-muted-foreground">(optional)</span>
|
|
833
|
+
</Label>
|
|
834
|
+
<Input
|
|
835
|
+
id="edit-mcp-proxy-token"
|
|
836
|
+
type="password"
|
|
837
|
+
autoComplete="new-password"
|
|
838
|
+
placeholder="Proxy access token"
|
|
839
|
+
value={editDraft.proxyAuthToken ?? ''}
|
|
840
|
+
onChange={(e) => setEditDraft({ ...editDraft, proxyAuthToken: e.target.value })}
|
|
841
|
+
disabled={editSaving}
|
|
842
|
+
/>
|
|
843
|
+
</div>
|
|
844
|
+
<div className="grid gap-1.5">
|
|
845
|
+
<Label htmlFor="edit-mcp-proxy-auth-header">Proxy auth header</Label>
|
|
846
|
+
<Input
|
|
847
|
+
id="edit-mcp-proxy-auth-header"
|
|
848
|
+
placeholder="Authorization"
|
|
849
|
+
value={editDraft.proxyAuthHeader ?? 'Authorization'}
|
|
850
|
+
onChange={(e) =>
|
|
851
|
+
setEditDraft({ ...editDraft, proxyAuthHeader: e.target.value })
|
|
852
|
+
}
|
|
853
|
+
disabled={editSaving}
|
|
854
|
+
/>
|
|
855
|
+
</div>
|
|
856
|
+
</div>
|
|
857
|
+
)}
|
|
858
|
+
|
|
859
|
+
{/* Headers */}
|
|
860
|
+
<div className="grid gap-1.5">
|
|
861
|
+
<Label htmlFor="edit-mcp-headers">
|
|
862
|
+
Headers <span className="font-normal text-muted-foreground">(optional)</span>
|
|
863
|
+
</Label>
|
|
864
|
+
<Textarea
|
|
865
|
+
id="edit-mcp-headers"
|
|
866
|
+
className="min-h-24 font-mono text-xs"
|
|
867
|
+
placeholder={'Authorization: Bearer your-token\nX-Workspace: acme'}
|
|
868
|
+
value={editHeadersText}
|
|
869
|
+
onChange={(e) => setEditHeadersText(e.target.value)}
|
|
870
|
+
disabled={editSaving}
|
|
871
|
+
/>
|
|
872
|
+
</div>
|
|
873
|
+
</div>
|
|
874
|
+
|
|
875
|
+
<DialogFooter>
|
|
876
|
+
<Button variant="outline" onClick={() => setEditTarget(null)} disabled={editSaving}>
|
|
877
|
+
Cancel
|
|
878
|
+
</Button>
|
|
879
|
+
<Button
|
|
880
|
+
onClick={() => void saveEdit()}
|
|
881
|
+
disabled={editSaving || !editDraft.name.trim() || !editDraft.url.trim()}
|
|
882
|
+
>
|
|
883
|
+
{editSaving ? (
|
|
884
|
+
<>
|
|
885
|
+
<Loader2 className="mr-2 size-4 animate-spin" />
|
|
886
|
+
Saving…
|
|
887
|
+
</>
|
|
888
|
+
) : (
|
|
889
|
+
'Save changes'
|
|
890
|
+
)}
|
|
891
|
+
</Button>
|
|
892
|
+
</DialogFooter>
|
|
893
|
+
</DialogContent>
|
|
894
|
+
</Dialog>
|
|
895
|
+
|
|
896
|
+
<GenericAlert
|
|
897
|
+
open={toDelete !== null}
|
|
898
|
+
onOpenChange={(open) => !open && setToDelete(null)}
|
|
899
|
+
title="Remove MCP connection"
|
|
900
|
+
description={`Remove "${toDelete?.name}"? It will no longer be available to Local Chat or Agents.`}
|
|
901
|
+
actionText="Remove"
|
|
902
|
+
variant="destructive"
|
|
903
|
+
onAction={() => void remove()}
|
|
904
|
+
/>
|
|
905
|
+
</Card>
|
|
906
|
+
);
|
|
907
|
+
}
|