@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,121 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Field, Select, Input, PasswordInput } from './Primitives';
|
|
4
|
+
import { PROVIDER_PRESETS, ALL_PROVIDER_IDS, type ProviderId } from '@/lib/agent/providers';
|
|
5
|
+
import ModelInput from '@/components/shared/ModelInput';
|
|
6
|
+
import type { CustomProviderFormState } from './useCustomProviderForm';
|
|
7
|
+
import type { AiTabProps } from './types';
|
|
8
|
+
|
|
9
|
+
interface CustomProviderFieldsProps {
|
|
10
|
+
form: CustomProviderFormState;
|
|
11
|
+
t: AiTabProps['t'];
|
|
12
|
+
locale: string;
|
|
13
|
+
/** "compact" = inline in AiTab (name+protocol side by side), "full" = modal layout */
|
|
14
|
+
layout?: 'compact' | 'full';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Shared form fields for provider editing.
|
|
19
|
+
* Renders: Name, Protocol, Base URL, API Key, Model.
|
|
20
|
+
*/
|
|
21
|
+
export default function CustomProviderFields({
|
|
22
|
+
form, t, locale, layout = 'full',
|
|
23
|
+
}: CustomProviderFieldsProps) {
|
|
24
|
+
const basePreset = PROVIDER_PRESETS[form.protocol];
|
|
25
|
+
|
|
26
|
+
const nameLabel = locale === 'zh' ? '名称' : 'Name';
|
|
27
|
+
const protocolLabel = locale === 'zh' ? '协议' : 'Protocol';
|
|
28
|
+
const namePlaceholder = locale === 'zh' ? '输入名称' : 'Enter name';
|
|
29
|
+
|
|
30
|
+
const nameHint = form.isDuplicateName
|
|
31
|
+
? (locale === 'zh' ? '名称已存在' : 'Name already exists')
|
|
32
|
+
: undefined;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="space-y-3">
|
|
36
|
+
{/* Name + Protocol */}
|
|
37
|
+
{layout === 'compact' ? (
|
|
38
|
+
<div className="grid grid-cols-2 gap-3">
|
|
39
|
+
<Field label={nameLabel} hint={nameHint} hintError={form.isDuplicateName}>
|
|
40
|
+
<Input
|
|
41
|
+
value={form.name}
|
|
42
|
+
onChange={e => form.setName(e.target.value)}
|
|
43
|
+
placeholder={namePlaceholder}
|
|
44
|
+
autoFocus
|
|
45
|
+
/>
|
|
46
|
+
</Field>
|
|
47
|
+
<Field label={protocolLabel}>
|
|
48
|
+
<Select
|
|
49
|
+
value={form.protocol}
|
|
50
|
+
onChange={e => form.setProtocol(e.target.value as ProviderId)}
|
|
51
|
+
>
|
|
52
|
+
{ALL_PROVIDER_IDS.map(id => (
|
|
53
|
+
<option key={id} value={id}>
|
|
54
|
+
{locale === 'zh' ? PROVIDER_PRESETS[id].nameZh : PROVIDER_PRESETS[id].name}
|
|
55
|
+
</option>
|
|
56
|
+
))}
|
|
57
|
+
</Select>
|
|
58
|
+
</Field>
|
|
59
|
+
</div>
|
|
60
|
+
) : (
|
|
61
|
+
<>
|
|
62
|
+
<Field label={nameLabel} hint={nameHint} hintError={form.isDuplicateName}>
|
|
63
|
+
<Input
|
|
64
|
+
value={form.name}
|
|
65
|
+
onChange={e => form.setName(e.target.value)}
|
|
66
|
+
placeholder={namePlaceholder}
|
|
67
|
+
/>
|
|
68
|
+
</Field>
|
|
69
|
+
<Field label={protocolLabel}>
|
|
70
|
+
<Select
|
|
71
|
+
value={form.protocol}
|
|
72
|
+
onChange={e => form.setProtocol(e.target.value as ProviderId)}
|
|
73
|
+
>
|
|
74
|
+
{ALL_PROVIDER_IDS.map(id => (
|
|
75
|
+
<option key={id} value={id}>
|
|
76
|
+
{locale === 'zh' ? PROVIDER_PRESETS[id].nameZh : PROVIDER_PRESETS[id].name}
|
|
77
|
+
</option>
|
|
78
|
+
))}
|
|
79
|
+
</Select>
|
|
80
|
+
</Field>
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{/* Base URL */}
|
|
85
|
+
<Field label="Base URL">
|
|
86
|
+
<Input
|
|
87
|
+
value={form.baseUrl}
|
|
88
|
+
onChange={e => form.setBaseUrl(e.target.value)}
|
|
89
|
+
placeholder={basePreset.fixedBaseUrl || 'https://api.example.com/v1'}
|
|
90
|
+
/>
|
|
91
|
+
</Field>
|
|
92
|
+
|
|
93
|
+
{/* API Key */}
|
|
94
|
+
<Field
|
|
95
|
+
label={<>API Key <span className="text-muted-foreground/50 font-normal">{locale === 'zh' ? '(可选)' : '(optional)'}</span></>}
|
|
96
|
+
>
|
|
97
|
+
<PasswordInput
|
|
98
|
+
value={form.apiKey}
|
|
99
|
+
onChange={form.setApiKey}
|
|
100
|
+
placeholder="sk-..."
|
|
101
|
+
/>
|
|
102
|
+
</Field>
|
|
103
|
+
|
|
104
|
+
{/* Model */}
|
|
105
|
+
<Field label={locale === 'zh' ? '模型' : 'Model'}>
|
|
106
|
+
<ModelInput
|
|
107
|
+
value={form.model}
|
|
108
|
+
onChange={form.setModel}
|
|
109
|
+
placeholder={basePreset.defaultModel}
|
|
110
|
+
provider={form.protocol}
|
|
111
|
+
apiKey={form.apiKey}
|
|
112
|
+
baseUrl={form.baseUrl}
|
|
113
|
+
supportsListModels={!!form.baseUrl.trim()}
|
|
114
|
+
allowNoKey
|
|
115
|
+
browseLabel={t.settings.ai.listModels}
|
|
116
|
+
noModelsLabel={t.settings.ai.noModelsFound}
|
|
117
|
+
/>
|
|
118
|
+
</Field>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useMemo, useRef } from 'react';
|
|
4
|
+
import { Trash2, Edit2, Layers } from 'lucide-react';
|
|
5
|
+
import { useLocale } from '@/lib/stores/locale-store';
|
|
6
|
+
import { type CustomProvider, generateCustomProviderId, isValidProvider } from '@/lib/custom-endpoints';
|
|
7
|
+
import { SettingCard } from './Primitives';
|
|
8
|
+
import ProviderModal from './ProviderModal';
|
|
9
|
+
|
|
10
|
+
interface CustomProvidersCardProps {
|
|
11
|
+
providers: CustomProvider[];
|
|
12
|
+
onProvidersChange: (providers: CustomProvider[]) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function CustomProvidersCard({
|
|
16
|
+
providers,
|
|
17
|
+
onProvidersChange,
|
|
18
|
+
}: CustomProvidersCardProps) {
|
|
19
|
+
const { t } = useLocale();
|
|
20
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
21
|
+
const [editingId, setEditingId] = useState<string | null>(null);
|
|
22
|
+
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
|
|
23
|
+
|
|
24
|
+
const editingProvider = useMemo(
|
|
25
|
+
() => editingId ? providers.find(p => p.id === editingId) : null,
|
|
26
|
+
[editingId, providers],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const handleAddClick = useCallback(() => {
|
|
30
|
+
setEditingId(null);
|
|
31
|
+
setIsModalOpen(true);
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
const handleEditClick = useCallback((id: string) => {
|
|
35
|
+
setEditingId(id);
|
|
36
|
+
setIsModalOpen(true);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
const handleSaveProvider = useCallback((provider: CustomProvider) => {
|
|
40
|
+
const updated = editingId
|
|
41
|
+
? providers.map(p => (p.id === editingId ? provider : p))
|
|
42
|
+
: [...providers, provider];
|
|
43
|
+
onProvidersChange(updated);
|
|
44
|
+
setIsModalOpen(false);
|
|
45
|
+
}, [editingId, providers, onProvidersChange]);
|
|
46
|
+
|
|
47
|
+
const handleDeleteClick = useCallback((id: string) => {
|
|
48
|
+
setDeleteConfirmId(id);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const handleConfirmDelete = useCallback(() => {
|
|
52
|
+
if (deleteConfirmId) {
|
|
53
|
+
const updated = providers.filter(p => p.id !== deleteConfirmId);
|
|
54
|
+
onProvidersChange(updated);
|
|
55
|
+
setDeleteConfirmId(null);
|
|
56
|
+
}
|
|
57
|
+
}, [deleteConfirmId, providers, onProvidersChange]);
|
|
58
|
+
|
|
59
|
+
const label = t.settings?.customProviders?.title ?? 'Model Providers';
|
|
60
|
+
const subtitle = t.settings?.customProviders?.subtitle ?? 'Add custom API endpoints with your own names';
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<SettingCard icon={<Layers size={15} />} title={label} description={subtitle}>
|
|
65
|
+
<div className="space-y-2">
|
|
66
|
+
{providers.length === 0 ? (
|
|
67
|
+
<div className="text-xs text-muted-foreground py-4 px-3 rounded-lg bg-muted/30 text-center">
|
|
68
|
+
{t.settings?.customProviders?.emptyState ?? 'No custom providers yet.'}
|
|
69
|
+
</div>
|
|
70
|
+
) : (
|
|
71
|
+
providers.map(provider => (
|
|
72
|
+
<div
|
|
73
|
+
key={provider.id}
|
|
74
|
+
className="flex items-start gap-3 p-3 rounded-lg border border-border/50 hover:border-border transition-colors"
|
|
75
|
+
>
|
|
76
|
+
<div className="flex-1 min-w-0">
|
|
77
|
+
<div className="font-medium text-sm">{provider.name}</div>
|
|
78
|
+
<div className="text-xs text-muted-foreground mt-1">
|
|
79
|
+
{provider.protocol} · {provider.model}
|
|
80
|
+
</div>
|
|
81
|
+
<div className="text-2xs text-muted-foreground/60 mt-1 truncate font-mono">
|
|
82
|
+
{provider.baseUrl}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="flex gap-1 shrink-0">
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
onClick={() => handleEditClick(provider.id)}
|
|
89
|
+
className="inline-flex items-center gap-1 px-2 py-1 text-xs rounded border border-border/50 text-muted-foreground hover:text-foreground hover:border-border transition-colors"
|
|
90
|
+
title={t.settings?.customProviders?.editButton}
|
|
91
|
+
>
|
|
92
|
+
<Edit2 size={12} />
|
|
93
|
+
</button>
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
onClick={() => handleDeleteClick(provider.id)}
|
|
97
|
+
className="inline-flex items-center gap-1 px-2 py-1 text-xs rounded border border-border/50 text-muted-foreground hover:text-destructive hover:border-destructive/20 transition-colors"
|
|
98
|
+
title={t.settings?.customProviders?.deleteButton}
|
|
99
|
+
>
|
|
100
|
+
<Trash2 size={12} />
|
|
101
|
+
</button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
))
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
onClick={handleAddClick}
|
|
111
|
+
className="mt-3 text-xs font-medium text-[var(--amber)] hover:text-[var(--amber)]/80 transition-colors"
|
|
112
|
+
>
|
|
113
|
+
{t.settings?.customProviders?.addButton ?? '+ Add Provider'}
|
|
114
|
+
</button>
|
|
115
|
+
</SettingCard>
|
|
116
|
+
|
|
117
|
+
<ProviderModal
|
|
118
|
+
isOpen={isModalOpen}
|
|
119
|
+
onClose={() => setIsModalOpen(false)}
|
|
120
|
+
onSave={handleSaveProvider}
|
|
121
|
+
initialProvider={editingProvider ?? undefined}
|
|
122
|
+
t={t}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
{deleteConfirmId && (
|
|
126
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
|
|
127
|
+
<div className="bg-card border border-border rounded-lg shadow-lg p-4 max-w-sm mx-4">
|
|
128
|
+
<p className="text-sm font-medium">
|
|
129
|
+
{t.settings?.customProviders?.deleteConfirm?.(
|
|
130
|
+
providers.find(p => p.id === deleteConfirmId)?.name ?? '',
|
|
131
|
+
) ?? 'Delete this provider?'}
|
|
132
|
+
</p>
|
|
133
|
+
<div className="flex gap-2 mt-4">
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
onClick={() => setDeleteConfirmId(null)}
|
|
137
|
+
className="flex-1 px-3 py-1.5 text-sm rounded border border-border text-muted-foreground hover:text-foreground transition-colors"
|
|
138
|
+
>
|
|
139
|
+
Cancel
|
|
140
|
+
</button>
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={handleConfirmDelete}
|
|
144
|
+
className="flex-1 px-3 py-1.5 text-sm rounded bg-destructive/10 border border-destructive/20 text-destructive hover:bg-destructive/20 transition-colors"
|
|
145
|
+
>
|
|
146
|
+
{t.settings?.customProviders?.deleteConfirmButton ?? 'Delete'}
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -4,7 +4,7 @@ import { useState, useEffect, useCallback, useSyncExternalStore, useRef } from '
|
|
|
4
4
|
import { Copy, Check, RefreshCw, Trash2, Sparkles, ChevronDown, ChevronRight, Loader2, Cpu, Zap, Database as DatabaseIcon, HardDrive, RotateCcw, FlaskConical } from 'lucide-react';
|
|
5
5
|
import { toast } from '@/lib/toast';
|
|
6
6
|
import type { KnowledgeTabProps } from './types';
|
|
7
|
-
import { Field, Input, EnvBadge, SectionLabel, Toggle, SettingCard, SettingRow } from './Primitives';
|
|
7
|
+
import { Field, Input, EnvBadge, SectionLabel, Toggle, SettingCard, SettingRow, PasswordInput } from './Primitives';
|
|
8
8
|
import { ConfirmDialog } from '@/components/agents/AgentsPrimitives';
|
|
9
9
|
import { apiFetch } from '@/lib/api';
|
|
10
10
|
import { copyToClipboard } from '@/lib/clipboard';
|
|
@@ -103,9 +103,6 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
103
103
|
() => 'http://localhost',
|
|
104
104
|
);
|
|
105
105
|
|
|
106
|
-
const [showPassword, setShowPassword] = useState(false);
|
|
107
|
-
const isPasswordMasked = data.webPassword === '***set***';
|
|
108
|
-
|
|
109
106
|
const [resetting, setResetting] = useState(false);
|
|
110
107
|
// revealed holds the plaintext token after regenerate, until user navigates away
|
|
111
108
|
const [revealedToken, setRevealedToken] = useState<string | null>(null);
|
|
@@ -198,22 +195,11 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
198
195
|
title={k.securityTitle ?? 'Security'}
|
|
199
196
|
>
|
|
200
197
|
<Field label={k.webPassword} hint={k.webPasswordHint}>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
onFocus={() => { if (isPasswordMasked) setData(d => d ? { ...d, webPassword: '' } : d); }}
|
|
207
|
-
placeholder={k.passwordPlaceholder ?? 'Leave empty to disable'}
|
|
208
|
-
/>
|
|
209
|
-
<button
|
|
210
|
-
type="button"
|
|
211
|
-
onClick={() => setShowPassword(v => !v)}
|
|
212
|
-
className="px-3 py-2 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors shrink-0"
|
|
213
|
-
>
|
|
214
|
-
{showPassword ? (k.passwordHide ?? 'Hide') : (k.passwordShow ?? 'Show')}
|
|
215
|
-
</button>
|
|
216
|
-
</div>
|
|
198
|
+
<PasswordInput
|
|
199
|
+
value={data.webPassword ?? ''}
|
|
200
|
+
onChange={v => setData(d => d ? { ...d, webPassword: v } : d)}
|
|
201
|
+
placeholder={k.passwordPlaceholder ?? 'Leave empty to disable'}
|
|
202
|
+
/>
|
|
217
203
|
</Field>
|
|
218
204
|
|
|
219
205
|
<Field
|
|
@@ -6,6 +6,7 @@ import CustomSelect from '@/components/CustomSelect';
|
|
|
6
6
|
import { apiFetch } from '@/lib/api';
|
|
7
7
|
import { copyToClipboard } from '@/lib/clipboard';
|
|
8
8
|
import { toast } from '@/lib/toast';
|
|
9
|
+
import { PasswordInput } from './Primitives';
|
|
9
10
|
import type { AgentInfo, McpAgentInstallProps } from './types';
|
|
10
11
|
|
|
11
12
|
/* ── Agent Install ─────────────────────────────────────────────── */
|
|
@@ -177,8 +178,12 @@ export default function AgentInstall({ agents, t, onRefresh, mode = 'mcp', activ
|
|
|
177
178
|
</div>
|
|
178
179
|
<div className="space-y-1">
|
|
179
180
|
<label className="text-muted-foreground">{m?.httpToken ?? 'Auth Token'}</label>
|
|
180
|
-
<
|
|
181
|
-
|
|
181
|
+
<PasswordInput
|
|
182
|
+
value={httpToken}
|
|
183
|
+
onChange={setHttpToken}
|
|
184
|
+
placeholder="Bearer token"
|
|
185
|
+
size="sm"
|
|
186
|
+
/>
|
|
182
187
|
</div>
|
|
183
188
|
</div>
|
|
184
189
|
)}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState, useRef, useEffect, useCallback, useMemo, useId } from 'react';
|
|
4
|
-
import { ChevronDown, Check } from 'lucide-react';
|
|
4
|
+
import { ChevronDown, Check, Eye, EyeOff } from 'lucide-react';
|
|
5
5
|
|
|
6
6
|
export function SectionLabel({ children }: { children: React.ReactNode }) {
|
|
7
7
|
return <p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">{children}</p>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function Field({ label, hint, children }: { label: React.ReactNode; hint?: string; children: React.ReactNode }) {
|
|
10
|
+
export function Field({ label, hint, hintError, children }: { label: React.ReactNode; hint?: string; hintError?: boolean; children: React.ReactNode }) {
|
|
11
11
|
return (
|
|
12
12
|
<div className="space-y-1.5">
|
|
13
13
|
<label className="text-sm text-foreground font-medium">{label}</label>
|
|
14
14
|
{children}
|
|
15
|
-
{hint && <p className=
|
|
15
|
+
{hint && <p className={`text-xs ${hintError ? 'text-destructive' : 'text-muted-foreground'}`}>{hint}</p>}
|
|
16
16
|
</div>
|
|
17
17
|
);
|
|
18
18
|
}
|
|
@@ -26,6 +26,51 @@ export function Input({ className = '', ...props }: React.InputHTMLAttributes<HT
|
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Password/secret input with inline eye toggle.
|
|
31
|
+
* Eye only shows when input has content. Icon sits inside the border.
|
|
32
|
+
*/
|
|
33
|
+
export function PasswordInput({ value, onChange, placeholder, disabled, className = '', size = 'md' }: {
|
|
34
|
+
value: string;
|
|
35
|
+
onChange: (v: string) => void;
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
className?: string;
|
|
39
|
+
size?: 'sm' | 'md';
|
|
40
|
+
}) {
|
|
41
|
+
const [show, setShow] = useState(false);
|
|
42
|
+
const sm = size === 'sm';
|
|
43
|
+
return (
|
|
44
|
+
<div className={`flex items-center border border-border rounded-lg bg-background focus-within:ring-1 focus-within:ring-ring overflow-hidden ${className}`}>
|
|
45
|
+
<input
|
|
46
|
+
type={show ? 'text' : 'password'}
|
|
47
|
+
value={value}
|
|
48
|
+
onChange={e => onChange(e.target.value)}
|
|
49
|
+
placeholder={placeholder ?? '••••••••'}
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
className={`flex-1 bg-transparent text-foreground placeholder:text-muted-foreground outline-none disabled:opacity-50 ${
|
|
52
|
+
sm ? 'px-2.5 py-1.5 text-xs font-mono' : 'px-3 py-2 text-sm'
|
|
53
|
+
}`}
|
|
54
|
+
/>
|
|
55
|
+
{!!value && (
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
tabIndex={-1}
|
|
59
|
+
onMouseDown={e => e.preventDefault()}
|
|
60
|
+
onClick={() => setShow(v => !v)}
|
|
61
|
+
disabled={disabled}
|
|
62
|
+
className={`shrink-0 text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50 ${
|
|
63
|
+
sm ? 'p-1.5' : 'p-2'
|
|
64
|
+
}`}
|
|
65
|
+
title={show ? 'Hide' : 'Show'}
|
|
66
|
+
>
|
|
67
|
+
{show ? <EyeOff size={sm ? 14 : 16} /> : <Eye size={sm ? 14 : 16} />}
|
|
68
|
+
</button>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
29
74
|
interface SelectOption { value: string; label: string }
|
|
30
75
|
|
|
31
76
|
export function Select({ value, onChange, children, className = '', disabled }: {
|
|
@@ -195,107 +240,6 @@ export function Toggle({ checked, onChange, size = 'md', disabled, title, onClic
|
|
|
195
240
|
);
|
|
196
241
|
}
|
|
197
242
|
|
|
198
|
-
export function ApiKeyInput({ value, onChange, placeholder, disabled, labels }: {
|
|
199
|
-
value: string;
|
|
200
|
-
onChange: (v: string) => void;
|
|
201
|
-
placeholder?: string;
|
|
202
|
-
disabled?: boolean;
|
|
203
|
-
labels?: { change?: string; cancel?: string };
|
|
204
|
-
}) {
|
|
205
|
-
const isMasked = value === '***set***';
|
|
206
|
-
const [editing, setEditing] = useState(false);
|
|
207
|
-
const [draft, setDraft] = useState('');
|
|
208
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
209
|
-
const prevValueRef = useRef(value);
|
|
210
|
-
|
|
211
|
-
// Reset editing when the masked value identity changes (e.g. provider switch)
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
if (prevValueRef.current !== value) {
|
|
214
|
-
prevValueRef.current = value;
|
|
215
|
-
setEditing(false);
|
|
216
|
-
setDraft('');
|
|
217
|
-
}
|
|
218
|
-
}, [value]);
|
|
219
|
-
|
|
220
|
-
const commitDraft = useCallback(() => {
|
|
221
|
-
if (draft.trim()) {
|
|
222
|
-
onChange(draft);
|
|
223
|
-
}
|
|
224
|
-
setEditing(false);
|
|
225
|
-
setDraft('');
|
|
226
|
-
}, [draft, onChange]);
|
|
227
|
-
|
|
228
|
-
const cancelEdit = useCallback(() => {
|
|
229
|
-
setEditing(false);
|
|
230
|
-
setDraft('');
|
|
231
|
-
}, []);
|
|
232
|
-
|
|
233
|
-
const changeLabel = labels?.change ?? 'Change';
|
|
234
|
-
const cancelLabel = labels?.cancel ?? 'Cancel';
|
|
235
|
-
|
|
236
|
-
// Masked state: show dots + "Change" button. No clearing on click.
|
|
237
|
-
if (isMasked && !editing) {
|
|
238
|
-
return (
|
|
239
|
-
<div className="flex items-center gap-2">
|
|
240
|
-
<div className="flex-1 px-3 py-2 text-sm bg-muted/50 border border-border rounded-lg text-muted-foreground select-none tracking-widest">
|
|
241
|
-
••••••••••••
|
|
242
|
-
</div>
|
|
243
|
-
<button
|
|
244
|
-
type="button"
|
|
245
|
-
onClick={() => {
|
|
246
|
-
setDraft('');
|
|
247
|
-
setEditing(true);
|
|
248
|
-
requestAnimationFrame(() => inputRef.current?.focus());
|
|
249
|
-
}}
|
|
250
|
-
disabled={disabled}
|
|
251
|
-
className="shrink-0 px-3 py-2 text-xs font-medium rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors disabled:opacity-50"
|
|
252
|
-
>
|
|
253
|
-
{changeLabel}
|
|
254
|
-
</button>
|
|
255
|
-
</div>
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Edit mode (replacing masked key) — uses local draft, commits on Enter/blur
|
|
260
|
-
if (editing) {
|
|
261
|
-
return (
|
|
262
|
-
<div className="flex items-center gap-2">
|
|
263
|
-
<input
|
|
264
|
-
ref={inputRef}
|
|
265
|
-
type="password"
|
|
266
|
-
value={draft}
|
|
267
|
-
onChange={e => setDraft(e.target.value)}
|
|
268
|
-
onKeyDown={e => { if (e.key === 'Enter') commitDraft(); }}
|
|
269
|
-
onBlur={commitDraft}
|
|
270
|
-
placeholder={placeholder ?? 'sk-...'}
|
|
271
|
-
disabled={disabled}
|
|
272
|
-
className="flex-1 px-3 py-2 text-sm bg-background border border-border rounded-lg text-foreground placeholder:text-muted-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-50"
|
|
273
|
-
/>
|
|
274
|
-
<button
|
|
275
|
-
type="button"
|
|
276
|
-
onMouseDown={e => e.preventDefault()}
|
|
277
|
-
onClick={cancelEdit}
|
|
278
|
-
className="shrink-0 px-3 py-2 text-xs font-medium rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
279
|
-
>
|
|
280
|
-
{cancelLabel}
|
|
281
|
-
</button>
|
|
282
|
-
</div>
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Normal (no masked key) — direct editing
|
|
287
|
-
return (
|
|
288
|
-
<input
|
|
289
|
-
type="password"
|
|
290
|
-
value={value}
|
|
291
|
-
onChange={e => onChange(e.target.value)}
|
|
292
|
-
placeholder={placeholder ?? 'sk-...'}
|
|
293
|
-
disabled={disabled}
|
|
294
|
-
className="w-full px-3 py-2 text-sm bg-background border border-border rounded-lg text-foreground placeholder:text-muted-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-50"
|
|
295
|
-
/>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
243
|
/**
|
|
300
244
|
* 💡 SUGGESTION #10: Unified primary button primitive for amber actions
|
|
301
245
|
* Replaces inline `style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}` pattern
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import { useLocale } from '@/lib/stores/locale-store';
|
|
5
|
+
import { type Messages } from '@/lib/i18n';
|
|
6
|
+
import { type CustomProvider } from '@/lib/custom-endpoints';
|
|
7
|
+
import { useCustomProviderForm } from './useCustomProviderForm';
|
|
8
|
+
import CustomProviderFields from './CustomProviderFields';
|
|
9
|
+
import { TestButton } from './TestButton';
|
|
10
|
+
|
|
11
|
+
interface ProviderModalProps {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
onSave: (provider: CustomProvider) => void;
|
|
15
|
+
initialProvider?: CustomProvider;
|
|
16
|
+
existingNames?: string[];
|
|
17
|
+
t: Messages;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Modal wrapper — renders nothing when closed, remounts inner form
|
|
22
|
+
* on open so hook state resets cleanly.
|
|
23
|
+
*/
|
|
24
|
+
export default function ProviderModal({
|
|
25
|
+
isOpen, onClose, onSave, initialProvider, existingNames, t,
|
|
26
|
+
}: ProviderModalProps) {
|
|
27
|
+
if (!isOpen) return null;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ProviderModalInner
|
|
31
|
+
key={initialProvider?.id ?? '__new__'}
|
|
32
|
+
onClose={onClose}
|
|
33
|
+
onSave={onSave}
|
|
34
|
+
initialProvider={initialProvider}
|
|
35
|
+
existingNames={existingNames}
|
|
36
|
+
t={t}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ProviderModalInner({
|
|
42
|
+
onClose, onSave, initialProvider, existingNames, t,
|
|
43
|
+
}: Omit<ProviderModalProps, 'isOpen'>) {
|
|
44
|
+
const { locale } = useLocale();
|
|
45
|
+
const form = useCustomProviderForm({ initial: initialProvider, onSave, locale, existingNames });
|
|
46
|
+
|
|
47
|
+
const title = initialProvider
|
|
48
|
+
? (locale === 'zh' ? '编辑 Provider' : 'Edit Provider')
|
|
49
|
+
: (locale === 'zh' ? '添加 Provider' : 'Add Provider');
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40" onClick={onClose}>
|
|
53
|
+
<div
|
|
54
|
+
className="bg-card border border-border rounded-lg shadow-lg p-6 max-w-md w-full mx-4 max-h-[90vh] overflow-y-auto"
|
|
55
|
+
onClick={e => e.stopPropagation()}
|
|
56
|
+
>
|
|
57
|
+
<div className="flex items-center justify-between mb-4">
|
|
58
|
+
<h2 className="font-semibold text-foreground">{title}</h2>
|
|
59
|
+
<button type="button" onClick={onClose} className="p-1 hover:bg-muted rounded transition-colors">
|
|
60
|
+
<X size={16} />
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<CustomProviderFields form={form} t={t} locale={locale} layout="full" />
|
|
65
|
+
|
|
66
|
+
<div className="flex gap-2 mt-6">
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
onClick={onClose}
|
|
70
|
+
className="flex-1 px-3 py-2 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:border-foreground/20 transition-colors"
|
|
71
|
+
>
|
|
72
|
+
{t.settings?.customProviders?.modal?.buttonCancel ?? 'Cancel'}
|
|
73
|
+
</button>
|
|
74
|
+
<TestButton result={form.testResult} disabled={!form.canSave} onTest={form.handleTest} t={t} />
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
onClick={form.handleSave}
|
|
78
|
+
disabled={!form.canSave}
|
|
79
|
+
className="flex-1 px-4 py-2 text-sm font-medium rounded-lg bg-[var(--amber)] text-[var(--amber-foreground)] hover:bg-[var(--amber)]/90 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
80
|
+
>
|
|
81
|
+
{locale === 'zh' ? '保存' : 'Save'}
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -186,11 +186,8 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
186
186
|
const restoreFromEnv = useCallback(async () => {
|
|
187
187
|
if (!data) return;
|
|
188
188
|
const defaults: AiSettings = {
|
|
189
|
-
|
|
190
|
-
providers:
|
|
191
|
-
anthropic: { apiKey: '', model: 'claude-sonnet-4-6' },
|
|
192
|
-
openai: { apiKey: '', model: 'gpt-5.4', baseUrl: '' },
|
|
193
|
-
},
|
|
189
|
+
activeProvider: '',
|
|
190
|
+
providers: [],
|
|
194
191
|
};
|
|
195
192
|
setData(d => d ? { ...d, ai: defaults } : d);
|
|
196
193
|
const DEBOUNCE_DELAY = 800;
|