@geminilight/mindos 0.6.61 → 0.6.65
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/_standalone/.antigravity/mcp_config.json +14 -0
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +23 -23
- package/_standalone/.next/build-manifest.json +2 -2
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +53 -47
- package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +2 -2
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +23 -23
- package/_standalone/.next/server/chunks/122.js +222 -0
- package/_standalone/.next/server/chunks/1550.js +1 -1
- package/_standalone/.next/server/chunks/1750.js +1 -1
- package/_standalone/.next/server/chunks/3113.js +52 -0
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/8388.js +3 -3
- package/_standalone/.next/server/chunks/953.js +3 -3
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/1001-99da82ec8d8c136f.js +1 -0
- package/_standalone/.next/static/chunks/1088-77544af0a50cb7a4.js +1 -0
- package/_standalone/.next/static/chunks/1467-87dde7eed498806f.js +1 -0
- package/_standalone/.next/static/chunks/5149-4d828886dda479fa.js +1 -0
- package/_standalone/.next/static/chunks/5581-c671163a2fe1b312.js +29 -0
- package/_standalone/.next/static/chunks/{7266-bb7be1128eccd48e.js → 5718-3837c3210a0e175f.js} +2 -2
- package/_standalone/.next/static/chunks/6636-53238eff89503f03.js +6 -0
- package/_standalone/.next/static/chunks/6757-1c1a89720fdda8f0.js +1 -0
- package/_standalone/.next/static/chunks/7129-20e9d2463a9da646.js +1 -0
- package/_standalone/.next/static/chunks/7294-cac25d97869afadc.js +1 -0
- package/_standalone/.next/static/chunks/8225-21e5cebc3731ddf0.js +1 -0
- package/_standalone/.next/static/chunks/8520-b51810e66293ceb8.js +22 -0
- package/_standalone/.next/static/chunks/9207-dc9c31b351a2ed78.js +1 -0
- package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-2f5cf97e03dc1cc9.js +1 -0
- package/_standalone/.next/static/chunks/app/agents/page-50eac58d511dcc6e.js +1 -0
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-2a00f4686adf3885.js +11 -0
- package/_standalone/.next/static/chunks/app/layout-2cb7a6602d2e5d5f.js +168 -0
- package/_standalone/.next/static/chunks/app/{page-6a1f8d21c12b829e.js → page-5ab911b2226f6ff7.js} +1 -1
- package/_standalone/.next/static/chunks/app/setup/page-907b7c57fad2292b.js +1 -0
- package/_standalone/.next/static/chunks/app/trash/page-11a511b065ea84c2.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/page-26e47dd4c533a58c.js +12 -0
- package/_standalone/.next/static/chunks/app/wiki/page-dce495b9048022fb.js +1 -0
- package/_standalone/.next/static/css/67e7918f5ed7d147.css +1 -0
- package/_standalone/.next/trace +65 -65
- package/_standalone/__tests__/acp/registry.test.ts +30 -20
- package/_standalone/__tests__/api/ask-attachments.test.ts +194 -0
- package/_standalone/__tests__/api/mcp-install.test.ts +49 -2
- package/_standalone/__tests__/api/settings.test.ts +16 -12
- package/_standalone/__tests__/api/setup.test.ts +11 -9
- package/_standalone/__tests__/api/test-key.test.ts +0 -10
- package/_standalone/__tests__/components/UpdateToast.test.ts +344 -0
- package/_standalone/__tests__/core/context.test.ts +48 -426
- package/_standalone/__tests__/lib/pi-skills.test.ts +4 -4
- package/_standalone/__tests__/lib/settings-ai-client.test.ts +32 -12
- package/_standalone/__tests__/setup.ts +5 -5
- package/_standalone/app/globals.css +4 -4
- package/_standalone/components/ActivityBar.tsx +17 -6
- package/_standalone/components/Panel.tsx +24 -6
- package/_standalone/components/SidebarLayout.tsx +36 -8
- package/_standalone/components/agents/AgentsMcpSection.tsx +2 -2
- package/_standalone/components/agents/AgentsOverviewSection.tsx +5 -1
- package/_standalone/components/agents/AgentsPanelA2aTab.tsx +173 -113
- package/_standalone/components/agents/AgentsSkillsSection.tsx +2 -2
- package/_standalone/components/ask/AskContent.tsx +83 -44
- package/_standalone/components/ask/AskHeader.tsx +8 -1
- package/_standalone/components/ask/MessageList.tsx +37 -3
- package/_standalone/components/ask/ProviderModelCapsule.tsx +444 -174
- package/_standalone/components/home/InboxSection.tsx +25 -25
- package/_standalone/components/settings/AiTab.tsx +353 -298
- package/_standalone/components/settings/CustomProviderFields.tsx +121 -0
- package/_standalone/components/settings/CustomProvidersCard.tsx +154 -0
- package/_standalone/components/settings/KnowledgeTab.tsx +6 -20
- package/_standalone/components/settings/McpAgentInstall.tsx +7 -2
- package/_standalone/components/settings/Primitives.tsx +48 -104
- package/_standalone/components/settings/ProviderModal.tsx +87 -0
- package/_standalone/components/settings/SettingsContent.tsx +2 -5
- package/_standalone/components/settings/TestButton.tsx +64 -0
- package/_standalone/components/settings/types.ts +3 -9
- package/_standalone/components/settings/useCustomProviderForm.ts +132 -0
- package/_standalone/components/setup/StepAI.tsx +12 -5
- package/_standalone/components/shared/ModelInput.tsx +220 -0
- package/_standalone/components/shared/ProviderSelect.tsx +126 -36
- package/_standalone/hooks/useAskChat.ts +100 -13
- package/_standalone/hooks/useAskPanel.ts +17 -1
- package/_standalone/lib/settings-ai-client.ts +17 -8
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/.antigravity/mcp_config.json +14 -0
- package/app/app/api/ask/route.ts +154 -45
- package/app/app/api/mcp/agents/route.ts +3 -3
- package/app/app/api/settings/list-models/route.ts +36 -9
- package/app/app/api/settings/route.ts +14 -42
- package/app/app/api/settings/test-key/route.ts +78 -2
- package/app/app/api/setup/route.ts +36 -18
- package/app/app/api/skills/route.ts +1 -1
- package/app/app/globals.css +4 -4
- package/app/app/layout.tsx +5 -3
- package/app/app/view/[...path]/page.tsx +5 -0
- package/app/components/ActivityBar.tsx +17 -6
- package/app/components/HomeContent.tsx +11 -0
- package/app/components/InboxView.tsx +656 -0
- package/app/components/Panel.tsx +24 -6
- package/app/components/SidebarLayout.tsx +36 -8
- package/app/components/UpdateToast.tsx +255 -0
- package/app/components/agents/AgentDetailContent.tsx +8 -8
- package/app/components/agents/AgentsMcpSection.tsx +2 -2
- package/app/components/agents/AgentsOverviewSection.tsx +5 -1
- package/app/components/agents/AgentsPanelA2aTab.tsx +173 -113
- package/app/components/agents/AgentsSkillsSection.tsx +2 -2
- package/app/components/ask/AskContent.tsx +83 -44
- package/app/components/ask/AskHeader.tsx +8 -1
- package/app/components/ask/MessageList.tsx +37 -3
- package/app/components/ask/ProviderModelCapsule.tsx +444 -174
- package/app/components/home/InboxSection.tsx +25 -25
- package/app/components/settings/AiTab.tsx +353 -298
- package/app/components/settings/CustomProviderFields.tsx +121 -0
- package/app/components/settings/CustomProvidersCard.tsx +154 -0
- package/app/components/settings/KnowledgeTab.tsx +6 -20
- package/app/components/settings/McpAgentInstall.tsx +7 -2
- package/app/components/settings/Primitives.tsx +48 -104
- package/app/components/settings/ProviderModal.tsx +87 -0
- package/app/components/settings/SettingsContent.tsx +2 -5
- package/app/components/settings/TestButton.tsx +64 -0
- package/app/components/settings/types.ts +3 -9
- package/app/components/settings/useCustomProviderForm.ts +132 -0
- package/app/components/setup/StepAI.tsx +12 -5
- package/app/components/shared/ModelInput.tsx +220 -0
- package/app/components/shared/ProviderSelect.tsx +126 -36
- package/app/hooks/useAskChat.ts +100 -13
- package/app/hooks/useAskPanel.ts +17 -1
- package/app/lib/acp/registry.ts +92 -10
- package/app/lib/agent/context.ts +65 -0
- package/app/lib/agent/providers.ts +25 -0
- package/app/lib/agent/tools.ts +1 -1
- package/app/lib/custom-endpoints.ts +160 -0
- package/app/lib/fs.ts +8 -1
- package/app/lib/i18n/modules/ai-chat.ts +6 -0
- package/app/lib/i18n/modules/knowledge.ts +16 -0
- package/app/lib/i18n/modules/onboarding.ts +4 -0
- package/app/lib/i18n/modules/settings.ts +88 -2
- package/app/lib/mcp-agents.ts +11 -0
- package/app/lib/pi-integration/skills.ts +16 -4
- package/app/lib/settings-ai-client.ts +17 -8
- package/app/lib/settings.ts +68 -72
- package/app/lib/types.ts +4 -0
- package/bin/lib/mcp-agents.js +11 -0
- package/bin/lib/mcp-install.js +71 -7
- package/package.json +1 -1
- package/_standalone/.next/server/chunks/530.js +0 -218
- package/_standalone/.next/server/chunks/8955.js +0 -52
- package/_standalone/.next/static/chunks/1369-7d0ac5d1564eed1e.js +0 -1
- package/_standalone/.next/static/chunks/3427-2e61a5df1f5e55fb.js +0 -1
- package/_standalone/.next/static/chunks/5581-0c700c20718bd916.js +0 -29
- package/_standalone/.next/static/chunks/6297-085daa21037d5f81.js +0 -1
- package/_standalone/.next/static/chunks/6636-9bbc90fb3b8731fe.js +0 -6
- package/_standalone/.next/static/chunks/7637-904b0a381dc3ec02.js +0 -1
- package/_standalone/.next/static/chunks/8520-76d1b05072178b43.js +0 -22
- package/_standalone/.next/static/chunks/8658-16ff58b75ae37fbb.js +0 -1
- package/_standalone/.next/static/chunks/9905-a19d379cb225246e.js +0 -1
- package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-0ea3571c8fbae823.js +0 -1
- package/_standalone/.next/static/chunks/app/agents/page-66858acbcd1d4bf8.js +0 -1
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-bf5c290fa3ccff09.js +0 -11
- package/_standalone/.next/static/chunks/app/layout-a5d5925b47e87cc3.js +0 -164
- package/_standalone/.next/static/chunks/app/setup/page-821714e7477be46c.js +0 -1
- package/_standalone/.next/static/chunks/app/trash/page-40bc7316806acd62.js +0 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-6fbb14b8f322d0f0.js +0 -12
- package/_standalone/.next/static/chunks/app/wiki/page-ba36eccf4fe62cfe.js +0 -1
- package/_standalone/.next/static/css/b57c4eb3cc88308b.css +0 -1
- package/_standalone/lib/agent/context.ts +0 -403
- /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AlertCircle, Loader2, Check, Zap } from 'lucide-react';
|
|
4
|
+
import type { TestResult, ErrorCode } from './useCustomProviderForm';
|
|
5
|
+
import type { AiTabProps } from './types';
|
|
6
|
+
|
|
7
|
+
function errorMessage(t: AiTabProps['t'], code?: ErrorCode): string {
|
|
8
|
+
switch (code) {
|
|
9
|
+
case 'auth_error': return t.settings.ai.testKeyAuthError;
|
|
10
|
+
case 'model_not_found': return t.settings.ai.testKeyModelNotFound;
|
|
11
|
+
case 'rate_limited': return t.settings.ai.testKeyRateLimited;
|
|
12
|
+
case 'network_error': return t.settings.ai.testKeyNetworkError;
|
|
13
|
+
default: return t.settings.ai.testKeyUnknown;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Shared test-connection button used by both built-in and custom provider forms.
|
|
19
|
+
* Shows contextual icon + text based on test result state (idle/testing/ok/error).
|
|
20
|
+
*/
|
|
21
|
+
export function TestButton({
|
|
22
|
+
result, disabled, onTest, t,
|
|
23
|
+
}: {
|
|
24
|
+
result: TestResult;
|
|
25
|
+
disabled: boolean;
|
|
26
|
+
onTest: () => void;
|
|
27
|
+
t: AiTabProps['t'];
|
|
28
|
+
}) {
|
|
29
|
+
const isTesting = result.state === 'testing';
|
|
30
|
+
const isOk = result.state === 'ok';
|
|
31
|
+
const isError = result.state === 'error';
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
disabled={disabled || isTesting}
|
|
37
|
+
onClick={onTest}
|
|
38
|
+
className={`inline-flex items-center gap-1.5 px-3.5 py-1.5 text-sm font-medium rounded-lg transition-all duration-200 disabled:cursor-not-allowed ${
|
|
39
|
+
isOk
|
|
40
|
+
? 'bg-success/10 text-success border border-success/20'
|
|
41
|
+
: isError
|
|
42
|
+
? 'bg-destructive/8 text-destructive border border-destructive/20 hover:bg-destructive/12'
|
|
43
|
+
: 'border border-border text-muted-foreground hover:text-foreground hover:border-foreground/20 disabled:opacity-40'
|
|
44
|
+
}`}
|
|
45
|
+
>
|
|
46
|
+
{isTesting ? (
|
|
47
|
+
<Loader2 size={13} className="animate-spin" />
|
|
48
|
+
) : isOk ? (
|
|
49
|
+
<Check size={13} />
|
|
50
|
+
) : isError ? (
|
|
51
|
+
<AlertCircle size={13} />
|
|
52
|
+
) : (
|
|
53
|
+
<Zap size={13} />
|
|
54
|
+
)}
|
|
55
|
+
{isTesting
|
|
56
|
+
? t.settings.ai.testKeyTesting
|
|
57
|
+
: isOk && result.latency != null
|
|
58
|
+
? t.settings.ai.testKeyOk(result.latency)
|
|
59
|
+
: isError
|
|
60
|
+
? errorMessage(t, result.code)
|
|
61
|
+
: t.settings.ai.testKey}
|
|
62
|
+
</button>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import type { Locale, Messages } from '@/lib/i18n';
|
|
2
2
|
import type React from 'react';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
export interface ProviderConfig {
|
|
6
|
-
apiKey: string;
|
|
7
|
-
model: string;
|
|
8
|
-
baseUrl?: string;
|
|
9
|
-
}
|
|
3
|
+
import type { Provider } from '@/lib/custom-endpoints';
|
|
10
4
|
|
|
11
5
|
export interface AiSettings {
|
|
12
|
-
|
|
13
|
-
providers:
|
|
6
|
+
activeProvider: string;
|
|
7
|
+
providers: Provider[];
|
|
14
8
|
}
|
|
15
9
|
|
|
16
10
|
export interface AgentSettings {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
import { type ProviderId } from '@/lib/agent/providers';
|
|
5
|
+
import { type Provider, generateProviderId } from '@/lib/custom-endpoints';
|
|
6
|
+
|
|
7
|
+
export type TestState = 'idle' | 'testing' | 'ok' | 'error';
|
|
8
|
+
export type ErrorCode = 'auth_error' | 'model_not_found' | 'rate_limited' | 'network_error' | 'unknown';
|
|
9
|
+
|
|
10
|
+
export interface TestResult {
|
|
11
|
+
state: TestState;
|
|
12
|
+
latency?: number;
|
|
13
|
+
error?: string;
|
|
14
|
+
code?: ErrorCode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CustomProviderFormState {
|
|
18
|
+
name: string;
|
|
19
|
+
setName: (v: string) => void;
|
|
20
|
+
protocol: ProviderId;
|
|
21
|
+
setProtocol: (v: ProviderId) => void;
|
|
22
|
+
apiKey: string;
|
|
23
|
+
setApiKey: (v: string) => void;
|
|
24
|
+
model: string;
|
|
25
|
+
setModel: (v: string) => void;
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
setBaseUrl: (v: string) => void;
|
|
28
|
+
testResult: TestResult;
|
|
29
|
+
canSave: boolean;
|
|
30
|
+
isDuplicateName: boolean;
|
|
31
|
+
handleTest: () => Promise<void>;
|
|
32
|
+
handleSave: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Shared form state + test/save logic for provider forms.
|
|
37
|
+
* Used by both the inline form (AiTab) and the modal (ProviderModal).
|
|
38
|
+
*/
|
|
39
|
+
export function useCustomProviderForm({
|
|
40
|
+
initial,
|
|
41
|
+
onSave,
|
|
42
|
+
locale,
|
|
43
|
+
existingNames,
|
|
44
|
+
}: {
|
|
45
|
+
initial?: Provider;
|
|
46
|
+
onSave: (provider: Provider) => void;
|
|
47
|
+
locale: string;
|
|
48
|
+
existingNames?: string[];
|
|
49
|
+
}): CustomProviderFormState {
|
|
50
|
+
const [name, setName] = useState(initial?.name ?? '');
|
|
51
|
+
const [protocol, setProtocol] = useState<ProviderId>(initial?.protocol ?? 'openai');
|
|
52
|
+
const [apiKey, setApiKey] = useState(initial?.apiKey ?? '');
|
|
53
|
+
const [model, setModel] = useState(initial?.model ?? '');
|
|
54
|
+
const [baseUrl, setBaseUrl] = useState(initial?.baseUrl ?? '');
|
|
55
|
+
const [testResult, setTestResult] = useState<TestResult>({ state: 'idle' });
|
|
56
|
+
|
|
57
|
+
// Check for duplicate name (exclude the provider being edited)
|
|
58
|
+
const trimmedName = name.trim();
|
|
59
|
+
const isDuplicateName = !!(trimmedName && existingNames?.some(
|
|
60
|
+
n => n.toLowerCase() === trimmedName.toLowerCase(),
|
|
61
|
+
));
|
|
62
|
+
|
|
63
|
+
const canSave = !!(trimmedName && baseUrl.trim() && model.trim() && !isDuplicateName);
|
|
64
|
+
|
|
65
|
+
const handleTest = useCallback(async () => {
|
|
66
|
+
if (!canSave) {
|
|
67
|
+
setTestResult({
|
|
68
|
+
state: 'error',
|
|
69
|
+
error: locale === 'zh' ? '名称、接口地址和模型为必填' : 'Name, base URL, and model are required',
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
setTestResult({ state: 'testing' });
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch('/api/settings/test-key', {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
body: JSON.stringify(
|
|
79
|
+
initial?.id
|
|
80
|
+
? { provider: initial.id, apiKey, model, baseUrl }
|
|
81
|
+
: { protocol, apiKey, model, baseUrl },
|
|
82
|
+
),
|
|
83
|
+
});
|
|
84
|
+
const json = await res.json();
|
|
85
|
+
if (json.ok) {
|
|
86
|
+
setTestResult({ state: 'ok', latency: json.latency });
|
|
87
|
+
} else {
|
|
88
|
+
setTestResult({ state: 'error', error: json.error || 'Test failed', code: json.code });
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
setTestResult({ state: 'error', code: 'network_error', error: 'Network error' });
|
|
92
|
+
}
|
|
93
|
+
}, [canSave, apiKey, model, baseUrl, protocol, locale, initial?.id]);
|
|
94
|
+
|
|
95
|
+
const handleSave = useCallback(() => {
|
|
96
|
+
if (isDuplicateName) {
|
|
97
|
+
setTestResult({
|
|
98
|
+
state: 'error',
|
|
99
|
+
error: locale === 'zh' ? '名称已存在,请使用其他名称' : 'Name already exists, please use a different name',
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!canSave) {
|
|
104
|
+
setTestResult({
|
|
105
|
+
state: 'error',
|
|
106
|
+
error: locale === 'zh' ? '名称、接口地址和模型为必填' : 'Name, base URL, and model are required',
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
onSave({
|
|
111
|
+
id: initial?.id || generateProviderId(),
|
|
112
|
+
name: name.trim(),
|
|
113
|
+
protocol,
|
|
114
|
+
apiKey,
|
|
115
|
+
model: model.trim(),
|
|
116
|
+
baseUrl: baseUrl.trim(),
|
|
117
|
+
});
|
|
118
|
+
}, [canSave, isDuplicateName, name, protocol, apiKey, model, baseUrl, initial?.id, onSave, locale]);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
name, setName,
|
|
122
|
+
protocol, setProtocol,
|
|
123
|
+
apiKey, setApiKey,
|
|
124
|
+
model, setModel,
|
|
125
|
+
baseUrl, setBaseUrl,
|
|
126
|
+
testResult,
|
|
127
|
+
canSave,
|
|
128
|
+
isDuplicateName,
|
|
129
|
+
handleTest,
|
|
130
|
+
handleSave,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import { ChevronDown, ChevronRight, Copy, ExternalLink } from 'lucide-react';
|
|
5
|
-
import { Field, Input,
|
|
5
|
+
import { Field, Input, PasswordInput } from '@/components/settings/Primitives';
|
|
6
6
|
import type { SetupState, SetupMessages, PortStatus, ProviderSetupConfig } from './types';
|
|
7
7
|
import type { ProviderId } from '@/lib/agent/providers';
|
|
8
8
|
import { PROVIDER_PRESETS, isProviderId, getApiKeyEnvVar, getDefaultBaseUrl } from '@/lib/agent/providers';
|
|
9
9
|
import ProviderSelect from '@/components/shared/ProviderSelect';
|
|
10
|
+
import ModelInput from '@/components/shared/ModelInput';
|
|
10
11
|
import StepPorts from './StepPorts';
|
|
11
12
|
import { useLocale } from '@/lib/stores/locale-store';
|
|
12
13
|
|
|
@@ -57,7 +58,7 @@ export default function StepAI({ state, update, s, onCopyToken, webPortStatus, m
|
|
|
57
58
|
<div className="space-y-5">
|
|
58
59
|
<ProviderSelect
|
|
59
60
|
value={state.provider}
|
|
60
|
-
onChange={id => update('provider', id)}
|
|
61
|
+
onChange={id => update('provider', id as ProviderId | 'skip')}
|
|
61
62
|
showSkip
|
|
62
63
|
compact
|
|
63
64
|
configuredProviders={configuredProviders}
|
|
@@ -67,7 +68,7 @@ export default function StepAI({ state, update, s, onCopyToken, webPortStatus, m
|
|
|
67
68
|
<div className="space-y-4 pt-2">
|
|
68
69
|
{/* API Key */}
|
|
69
70
|
<Field label={s.apiKey}>
|
|
70
|
-
<
|
|
71
|
+
<PasswordInput
|
|
71
72
|
value={currentConfig.apiKey}
|
|
72
73
|
onChange={v => patchConfig({ apiKey: v })}
|
|
73
74
|
placeholder={currentConfig.apiKeyMask || `${getApiKeyEnvVar(currentProvider as ProviderId) ?? 'API Key'}...`}
|
|
@@ -106,10 +107,16 @@ export default function StepAI({ state, update, s, onCopyToken, webPortStatus, m
|
|
|
106
107
|
|
|
107
108
|
{/* Model */}
|
|
108
109
|
<Field label={s.model}>
|
|
109
|
-
<
|
|
110
|
+
<ModelInput
|
|
110
111
|
value={currentConfig.model}
|
|
111
|
-
onChange={
|
|
112
|
+
onChange={v => patchConfig({ model: v })}
|
|
112
113
|
placeholder={currentPreset.defaultModel}
|
|
114
|
+
provider={currentProvider}
|
|
115
|
+
apiKey={currentConfig.apiKey}
|
|
116
|
+
baseUrl={currentConfig.baseUrl}
|
|
117
|
+
supportsListModels={currentPreset.supportsListModels}
|
|
118
|
+
browseLabel={s.listModels}
|
|
119
|
+
noModelsLabel={s.noModelsFound}
|
|
113
120
|
/>
|
|
114
121
|
</Field>
|
|
115
122
|
</div>
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useRef, useCallback, useEffect, useMemo, type CSSProperties } from 'react';
|
|
4
|
+
import { ChevronDown, Loader2 } from 'lucide-react';
|
|
5
|
+
import { Input } from '@/components/settings/Primitives';
|
|
6
|
+
import { type ProviderId, PROVIDER_PRESETS } from '@/lib/agent/providers';
|
|
7
|
+
|
|
8
|
+
export interface ModelInputProps {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (v: string) => void;
|
|
11
|
+
placeholder: string;
|
|
12
|
+
provider: ProviderId;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
envKey?: boolean;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
supportsListModels: boolean;
|
|
17
|
+
allowNoKey?: boolean;
|
|
18
|
+
browseLabel?: string;
|
|
19
|
+
noModelsLabel?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function ModelInput({
|
|
23
|
+
value, onChange, placeholder, provider, apiKey, envKey, baseUrl,
|
|
24
|
+
supportsListModels, allowNoKey,
|
|
25
|
+
browseLabel = 'Browse',
|
|
26
|
+
noModelsLabel = 'No models found',
|
|
27
|
+
}: ModelInputProps) {
|
|
28
|
+
const [models, setModels] = useState<string[] | null>(null);
|
|
29
|
+
const [loading, setLoading] = useState(false);
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const [error, setError] = useState('');
|
|
32
|
+
const [focused, setFocused] = useState(false);
|
|
33
|
+
const [highlightIdx, setHighlightIdx] = useState(-1);
|
|
34
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
35
|
+
const inputRowRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
const listRef = useRef<HTMLDivElement>(null);
|
|
37
|
+
const [dropdownStyle, setDropdownStyle] = useState<CSSProperties>({});
|
|
38
|
+
const fetchedRef = useRef(false);
|
|
39
|
+
const fetchVersionRef = useRef(0);
|
|
40
|
+
const loadingRef = useRef(false);
|
|
41
|
+
|
|
42
|
+
const hasKey = allowNoKey || !!apiKey || !!envKey || !!PROVIDER_PRESETS[provider]?.apiKeyFallback;
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
fetchedRef.current = false;
|
|
46
|
+
fetchVersionRef.current++;
|
|
47
|
+
setModels(null);
|
|
48
|
+
}, [provider, apiKey, baseUrl]);
|
|
49
|
+
|
|
50
|
+
const fetchModels = useCallback(async (silent = false) => {
|
|
51
|
+
if (loadingRef.current) return;
|
|
52
|
+
loadingRef.current = true;
|
|
53
|
+
setLoading(true);
|
|
54
|
+
if (!silent) setError('');
|
|
55
|
+
const version = fetchVersionRef.current;
|
|
56
|
+
try {
|
|
57
|
+
const body: Record<string, string> = { provider };
|
|
58
|
+
if (apiKey) body.apiKey = apiKey;
|
|
59
|
+
if (baseUrl) body.baseUrl = baseUrl;
|
|
60
|
+
|
|
61
|
+
const res = await fetch('/api/settings/list-models', {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { 'Content-Type': 'application/json' },
|
|
64
|
+
body: JSON.stringify(body),
|
|
65
|
+
});
|
|
66
|
+
if (version !== fetchVersionRef.current) return;
|
|
67
|
+
const json = await res.json();
|
|
68
|
+
if (version !== fetchVersionRef.current) return;
|
|
69
|
+
if (json.ok && Array.isArray(json.models)) {
|
|
70
|
+
setModels(json.models);
|
|
71
|
+
fetchedRef.current = true;
|
|
72
|
+
if (!silent) setOpen(true);
|
|
73
|
+
} else if (!silent) {
|
|
74
|
+
setError(json.error || 'Failed to fetch models');
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
if (version !== fetchVersionRef.current) return;
|
|
78
|
+
if (!silent) setError('Network error');
|
|
79
|
+
} finally {
|
|
80
|
+
loadingRef.current = false;
|
|
81
|
+
setLoading(false);
|
|
82
|
+
}
|
|
83
|
+
}, [provider, apiKey, baseUrl]);
|
|
84
|
+
|
|
85
|
+
const handleFocus = useCallback(() => {
|
|
86
|
+
setFocused(true);
|
|
87
|
+
if (!fetchedRef.current && supportsListModels && hasKey && !loadingRef.current) {
|
|
88
|
+
fetchModels(true);
|
|
89
|
+
}
|
|
90
|
+
}, [supportsListModels, hasKey, fetchModels]);
|
|
91
|
+
|
|
92
|
+
const filtered = useMemo(() => {
|
|
93
|
+
if (!models) return [];
|
|
94
|
+
if (!value.trim()) return models;
|
|
95
|
+
const q = value.toLowerCase();
|
|
96
|
+
return [...models].sort((a, b) => {
|
|
97
|
+
const al = a.toLowerCase(), bl = b.toLowerCase();
|
|
98
|
+
const aMatch = al.includes(q), bMatch = bl.includes(q);
|
|
99
|
+
if (aMatch !== bMatch) return aMatch ? -1 : 1;
|
|
100
|
+
if (aMatch && bMatch) {
|
|
101
|
+
const aPrefix = al.startsWith(q), bPrefix = bl.startsWith(q);
|
|
102
|
+
if (aPrefix !== bPrefix) return aPrefix ? -1 : 1;
|
|
103
|
+
const aExact = al === q, bExact = bl === q;
|
|
104
|
+
if (aExact !== bExact) return aExact ? -1 : 1;
|
|
105
|
+
}
|
|
106
|
+
return a.localeCompare(b);
|
|
107
|
+
});
|
|
108
|
+
}, [models, value]);
|
|
109
|
+
|
|
110
|
+
const showDropdown = open || (focused && models !== null && value.trim().length > 0 && filtered.length > 0);
|
|
111
|
+
|
|
112
|
+
// Compute fixed position for dropdown so it escapes overflow:auto ancestors
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (!showDropdown || !inputRowRef.current) return;
|
|
115
|
+
const rect = inputRowRef.current.getBoundingClientRect();
|
|
116
|
+
setDropdownStyle({
|
|
117
|
+
position: 'fixed',
|
|
118
|
+
top: rect.bottom + 4,
|
|
119
|
+
left: rect.left,
|
|
120
|
+
width: rect.width,
|
|
121
|
+
});
|
|
122
|
+
}, [showDropdown]);
|
|
123
|
+
|
|
124
|
+
useEffect(() => { setHighlightIdx(-1); }, [filtered]);
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (highlightIdx < 0 || !listRef.current) return;
|
|
128
|
+
const items = listRef.current.querySelectorAll('[data-model-item]');
|
|
129
|
+
items[highlightIdx]?.scrollIntoView({ block: 'nearest' });
|
|
130
|
+
}, [highlightIdx]);
|
|
131
|
+
|
|
132
|
+
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
133
|
+
const items = showDropdown ? filtered : [];
|
|
134
|
+
if (!items.length) return;
|
|
135
|
+
|
|
136
|
+
if (e.key === 'ArrowDown') {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
setHighlightIdx(i => (i + 1) % items.length);
|
|
139
|
+
} else if (e.key === 'ArrowUp') {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
setHighlightIdx(i => (i - 1 + items.length) % items.length);
|
|
142
|
+
} else if (e.key === 'Enter' && highlightIdx >= 0 && highlightIdx < items.length) {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
onChange(items[highlightIdx]);
|
|
145
|
+
setOpen(false);
|
|
146
|
+
setFocused(false);
|
|
147
|
+
} else if (e.key === 'Escape') {
|
|
148
|
+
setOpen(false);
|
|
149
|
+
setFocused(false);
|
|
150
|
+
}
|
|
151
|
+
}, [showDropdown, filtered, highlightIdx, onChange]);
|
|
152
|
+
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (!showDropdown) return;
|
|
155
|
+
function handleClickOutside(e: MouseEvent) {
|
|
156
|
+
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
|
157
|
+
setOpen(false);
|
|
158
|
+
setFocused(false);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
162
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
163
|
+
}, [showDropdown]);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div ref={containerRef} className="relative">
|
|
167
|
+
<div ref={inputRowRef} className="flex gap-1.5">
|
|
168
|
+
<Input
|
|
169
|
+
value={value}
|
|
170
|
+
onChange={e => { onChange(e.target.value); if (!open) setFocused(true); }}
|
|
171
|
+
onFocus={handleFocus}
|
|
172
|
+
onKeyDown={handleKeyDown}
|
|
173
|
+
placeholder={placeholder}
|
|
174
|
+
className="flex-1"
|
|
175
|
+
autoComplete="off"
|
|
176
|
+
/>
|
|
177
|
+
{supportsListModels && (
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
disabled={!hasKey || loading}
|
|
181
|
+
onClick={() => fetchModels(false)}
|
|
182
|
+
title={browseLabel}
|
|
183
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:border-foreground/20 transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0"
|
|
184
|
+
>
|
|
185
|
+
{loading ? <Loader2 size={12} className="animate-spin" /> : <ChevronDown size={12} />}
|
|
186
|
+
{browseLabel}
|
|
187
|
+
</button>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
{error && <p className="text-xs text-error mt-1">{error}</p>}
|
|
191
|
+
{showDropdown && filtered.length > 0 && (
|
|
192
|
+
<div ref={listRef} style={dropdownStyle} className="fixed z-[9999] max-h-48 overflow-y-auto rounded-lg border border-border bg-popover shadow-lg">
|
|
193
|
+
{filtered.map((m, i) => {
|
|
194
|
+
const isMatch = !value.trim() || m.toLowerCase().includes(value.toLowerCase());
|
|
195
|
+
return (
|
|
196
|
+
<button
|
|
197
|
+
key={m}
|
|
198
|
+
type="button"
|
|
199
|
+
data-model-item
|
|
200
|
+
className={`w-full text-left px-3 py-1.5 text-sm transition-colors ${
|
|
201
|
+
m === value ? 'bg-accent/60 font-medium'
|
|
202
|
+
: i === highlightIdx ? 'bg-accent'
|
|
203
|
+
: 'hover:bg-accent'
|
|
204
|
+
} ${!isMatch ? 'opacity-50' : ''}`}
|
|
205
|
+
onClick={() => { onChange(m); setOpen(false); setFocused(false); }}
|
|
206
|
+
>
|
|
207
|
+
{m}
|
|
208
|
+
</button>
|
|
209
|
+
);
|
|
210
|
+
})}
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
{open && filtered.length === 0 && (
|
|
214
|
+
<div style={dropdownStyle} className="fixed z-[9999] rounded-lg border border-border bg-popover shadow-lg px-3 py-2 text-xs text-muted-foreground">
|
|
215
|
+
{noModelsLabel}
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|