@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.
Files changed (313) hide show
  1. package/_standalone/.antigravity/mcp_config.json +14 -0
  2. package/_standalone/.mindos-build-version +1 -1
  3. package/_standalone/.next/BUILD_ID +1 -1
  4. package/_standalone/.next/app-path-routes-manifest.json +23 -23
  5. package/_standalone/.next/build-manifest.json +2 -2
  6. package/_standalone/.next/cache/.previewinfo +1 -1
  7. package/_standalone/.next/cache/.rscinfo +1 -1
  8. package/_standalone/.next/cache/config.json +3 -3
  9. package/_standalone/.next/prerender-manifest.json +3 -3
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/registry/route.js +1 -1
  38. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
  42. package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/ask/route.js +53 -47
  46. package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
  47. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
  51. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
  53. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
  55. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
  57. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/file/import/route.js +1 -1
  60. package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
  61. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
  63. package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
  65. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  67. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  69. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
  71. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
  74. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
  76. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/mcp/agents/route.js +1 -1
  78. package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
  79. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
  86. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  87. package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
  88. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  90. package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  91. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/api/settings/list-models/route.js +1 -1
  93. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/settings/route.js +1 -1
  96. package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  97. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/api/settings/test-key/route.js +1 -1
  99. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  102. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/api/setup/route.js +1 -1
  105. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/api/skills/route.js +1 -1
  107. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  108. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  109. package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
  110. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  111. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  113. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  114. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
  116. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  117. package/_standalone/.next/server/app/changes/page.js +1 -1
  118. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  119. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  120. package/_standalone/.next/server/app/echo/[segment]/page.js +2 -2
  121. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  122. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  123. package/_standalone/.next/server/app/echo/page.js +1 -1
  124. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  125. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  126. package/_standalone/.next/server/app/explore/page.js +1 -1
  127. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  128. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  129. package/_standalone/.next/server/app/help/page.js +1 -1
  130. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  131. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  132. package/_standalone/.next/server/app/inbox/history/page.js +1 -1
  133. package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
  134. package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
  135. package/_standalone/.next/server/app/login/page.js +1 -1
  136. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  137. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  138. package/_standalone/.next/server/app/page.js +1 -1
  139. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  140. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  141. package/_standalone/.next/server/app/setup/page.js +2 -2
  142. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  143. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  144. package/_standalone/.next/server/app/trash/page.js +3 -3
  145. package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
  146. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  147. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  148. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  149. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  150. package/_standalone/.next/server/app/wiki/page.js +1 -1
  151. package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
  152. package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
  153. package/_standalone/.next/server/app-paths-manifest.json +23 -23
  154. package/_standalone/.next/server/chunks/122.js +222 -0
  155. package/_standalone/.next/server/chunks/1550.js +1 -1
  156. package/_standalone/.next/server/chunks/1750.js +1 -1
  157. package/_standalone/.next/server/chunks/3113.js +52 -0
  158. package/_standalone/.next/server/chunks/6539.js +1 -1
  159. package/_standalone/.next/server/chunks/8388.js +3 -3
  160. package/_standalone/.next/server/chunks/953.js +3 -3
  161. package/_standalone/.next/server/pages/500.html +2 -2
  162. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  163. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  164. package/_standalone/.next/static/chunks/1001-99da82ec8d8c136f.js +1 -0
  165. package/_standalone/.next/static/chunks/1088-77544af0a50cb7a4.js +1 -0
  166. package/_standalone/.next/static/chunks/1467-87dde7eed498806f.js +1 -0
  167. package/_standalone/.next/static/chunks/5149-4d828886dda479fa.js +1 -0
  168. package/_standalone/.next/static/chunks/5581-c671163a2fe1b312.js +29 -0
  169. package/_standalone/.next/static/chunks/{7266-bb7be1128eccd48e.js → 5718-3837c3210a0e175f.js} +2 -2
  170. package/_standalone/.next/static/chunks/6636-53238eff89503f03.js +6 -0
  171. package/_standalone/.next/static/chunks/6757-1c1a89720fdda8f0.js +1 -0
  172. package/_standalone/.next/static/chunks/7129-20e9d2463a9da646.js +1 -0
  173. package/_standalone/.next/static/chunks/7294-cac25d97869afadc.js +1 -0
  174. package/_standalone/.next/static/chunks/8225-21e5cebc3731ddf0.js +1 -0
  175. package/_standalone/.next/static/chunks/8520-b51810e66293ceb8.js +22 -0
  176. package/_standalone/.next/static/chunks/9207-dc9c31b351a2ed78.js +1 -0
  177. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-2f5cf97e03dc1cc9.js +1 -0
  178. package/_standalone/.next/static/chunks/app/agents/page-50eac58d511dcc6e.js +1 -0
  179. package/_standalone/.next/static/chunks/app/echo/[segment]/page-2a00f4686adf3885.js +11 -0
  180. package/_standalone/.next/static/chunks/app/layout-2cb7a6602d2e5d5f.js +168 -0
  181. package/_standalone/.next/static/chunks/app/{page-6a1f8d21c12b829e.js → page-5ab911b2226f6ff7.js} +1 -1
  182. package/_standalone/.next/static/chunks/app/setup/page-907b7c57fad2292b.js +1 -0
  183. package/_standalone/.next/static/chunks/app/trash/page-11a511b065ea84c2.js +1 -0
  184. package/_standalone/.next/static/chunks/app/view/[...path]/page-26e47dd4c533a58c.js +12 -0
  185. package/_standalone/.next/static/chunks/app/wiki/page-dce495b9048022fb.js +1 -0
  186. package/_standalone/.next/static/css/67e7918f5ed7d147.css +1 -0
  187. package/_standalone/.next/trace +65 -65
  188. package/_standalone/__tests__/acp/registry.test.ts +30 -20
  189. package/_standalone/__tests__/api/ask-attachments.test.ts +194 -0
  190. package/_standalone/__tests__/api/mcp-install.test.ts +49 -2
  191. package/_standalone/__tests__/api/settings.test.ts +16 -12
  192. package/_standalone/__tests__/api/setup.test.ts +11 -9
  193. package/_standalone/__tests__/api/test-key.test.ts +0 -10
  194. package/_standalone/__tests__/components/UpdateToast.test.ts +344 -0
  195. package/_standalone/__tests__/core/context.test.ts +48 -426
  196. package/_standalone/__tests__/lib/pi-skills.test.ts +4 -4
  197. package/_standalone/__tests__/lib/settings-ai-client.test.ts +32 -12
  198. package/_standalone/__tests__/setup.ts +5 -5
  199. package/_standalone/app/globals.css +4 -4
  200. package/_standalone/components/ActivityBar.tsx +17 -6
  201. package/_standalone/components/Panel.tsx +24 -6
  202. package/_standalone/components/SidebarLayout.tsx +36 -8
  203. package/_standalone/components/agents/AgentsMcpSection.tsx +2 -2
  204. package/_standalone/components/agents/AgentsOverviewSection.tsx +5 -1
  205. package/_standalone/components/agents/AgentsPanelA2aTab.tsx +173 -113
  206. package/_standalone/components/agents/AgentsSkillsSection.tsx +2 -2
  207. package/_standalone/components/ask/AskContent.tsx +83 -44
  208. package/_standalone/components/ask/AskHeader.tsx +8 -1
  209. package/_standalone/components/ask/MessageList.tsx +37 -3
  210. package/_standalone/components/ask/ProviderModelCapsule.tsx +444 -174
  211. package/_standalone/components/home/InboxSection.tsx +25 -25
  212. package/_standalone/components/settings/AiTab.tsx +353 -298
  213. package/_standalone/components/settings/CustomProviderFields.tsx +121 -0
  214. package/_standalone/components/settings/CustomProvidersCard.tsx +154 -0
  215. package/_standalone/components/settings/KnowledgeTab.tsx +6 -20
  216. package/_standalone/components/settings/McpAgentInstall.tsx +7 -2
  217. package/_standalone/components/settings/Primitives.tsx +48 -104
  218. package/_standalone/components/settings/ProviderModal.tsx +87 -0
  219. package/_standalone/components/settings/SettingsContent.tsx +2 -5
  220. package/_standalone/components/settings/TestButton.tsx +64 -0
  221. package/_standalone/components/settings/types.ts +3 -9
  222. package/_standalone/components/settings/useCustomProviderForm.ts +132 -0
  223. package/_standalone/components/setup/StepAI.tsx +12 -5
  224. package/_standalone/components/shared/ModelInput.tsx +220 -0
  225. package/_standalone/components/shared/ProviderSelect.tsx +126 -36
  226. package/_standalone/hooks/useAskChat.ts +100 -13
  227. package/_standalone/hooks/useAskPanel.ts +17 -1
  228. package/_standalone/lib/settings-ai-client.ts +17 -8
  229. package/_standalone/tsconfig.tsbuildinfo +1 -1
  230. package/app/.antigravity/mcp_config.json +14 -0
  231. package/app/app/api/ask/route.ts +154 -45
  232. package/app/app/api/mcp/agents/route.ts +3 -3
  233. package/app/app/api/settings/list-models/route.ts +36 -9
  234. package/app/app/api/settings/route.ts +14 -42
  235. package/app/app/api/settings/test-key/route.ts +78 -2
  236. package/app/app/api/setup/route.ts +36 -18
  237. package/app/app/api/skills/route.ts +1 -1
  238. package/app/app/globals.css +4 -4
  239. package/app/app/layout.tsx +5 -3
  240. package/app/app/view/[...path]/page.tsx +5 -0
  241. package/app/components/ActivityBar.tsx +17 -6
  242. package/app/components/HomeContent.tsx +11 -0
  243. package/app/components/InboxView.tsx +656 -0
  244. package/app/components/Panel.tsx +24 -6
  245. package/app/components/SidebarLayout.tsx +36 -8
  246. package/app/components/UpdateToast.tsx +255 -0
  247. package/app/components/agents/AgentDetailContent.tsx +8 -8
  248. package/app/components/agents/AgentsMcpSection.tsx +2 -2
  249. package/app/components/agents/AgentsOverviewSection.tsx +5 -1
  250. package/app/components/agents/AgentsPanelA2aTab.tsx +173 -113
  251. package/app/components/agents/AgentsSkillsSection.tsx +2 -2
  252. package/app/components/ask/AskContent.tsx +83 -44
  253. package/app/components/ask/AskHeader.tsx +8 -1
  254. package/app/components/ask/MessageList.tsx +37 -3
  255. package/app/components/ask/ProviderModelCapsule.tsx +444 -174
  256. package/app/components/home/InboxSection.tsx +25 -25
  257. package/app/components/settings/AiTab.tsx +353 -298
  258. package/app/components/settings/CustomProviderFields.tsx +121 -0
  259. package/app/components/settings/CustomProvidersCard.tsx +154 -0
  260. package/app/components/settings/KnowledgeTab.tsx +6 -20
  261. package/app/components/settings/McpAgentInstall.tsx +7 -2
  262. package/app/components/settings/Primitives.tsx +48 -104
  263. package/app/components/settings/ProviderModal.tsx +87 -0
  264. package/app/components/settings/SettingsContent.tsx +2 -5
  265. package/app/components/settings/TestButton.tsx +64 -0
  266. package/app/components/settings/types.ts +3 -9
  267. package/app/components/settings/useCustomProviderForm.ts +132 -0
  268. package/app/components/setup/StepAI.tsx +12 -5
  269. package/app/components/shared/ModelInput.tsx +220 -0
  270. package/app/components/shared/ProviderSelect.tsx +126 -36
  271. package/app/hooks/useAskChat.ts +100 -13
  272. package/app/hooks/useAskPanel.ts +17 -1
  273. package/app/lib/acp/registry.ts +92 -10
  274. package/app/lib/agent/context.ts +65 -0
  275. package/app/lib/agent/providers.ts +25 -0
  276. package/app/lib/agent/tools.ts +1 -1
  277. package/app/lib/custom-endpoints.ts +160 -0
  278. package/app/lib/fs.ts +8 -1
  279. package/app/lib/i18n/modules/ai-chat.ts +6 -0
  280. package/app/lib/i18n/modules/knowledge.ts +16 -0
  281. package/app/lib/i18n/modules/onboarding.ts +4 -0
  282. package/app/lib/i18n/modules/settings.ts +88 -2
  283. package/app/lib/mcp-agents.ts +11 -0
  284. package/app/lib/pi-integration/skills.ts +16 -4
  285. package/app/lib/settings-ai-client.ts +17 -8
  286. package/app/lib/settings.ts +68 -72
  287. package/app/lib/types.ts +4 -0
  288. package/bin/lib/mcp-agents.js +11 -0
  289. package/bin/lib/mcp-install.js +71 -7
  290. package/package.json +1 -1
  291. package/_standalone/.next/server/chunks/530.js +0 -218
  292. package/_standalone/.next/server/chunks/8955.js +0 -52
  293. package/_standalone/.next/static/chunks/1369-7d0ac5d1564eed1e.js +0 -1
  294. package/_standalone/.next/static/chunks/3427-2e61a5df1f5e55fb.js +0 -1
  295. package/_standalone/.next/static/chunks/5581-0c700c20718bd916.js +0 -29
  296. package/_standalone/.next/static/chunks/6297-085daa21037d5f81.js +0 -1
  297. package/_standalone/.next/static/chunks/6636-9bbc90fb3b8731fe.js +0 -6
  298. package/_standalone/.next/static/chunks/7637-904b0a381dc3ec02.js +0 -1
  299. package/_standalone/.next/static/chunks/8520-76d1b05072178b43.js +0 -22
  300. package/_standalone/.next/static/chunks/8658-16ff58b75ae37fbb.js +0 -1
  301. package/_standalone/.next/static/chunks/9905-a19d379cb225246e.js +0 -1
  302. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-0ea3571c8fbae823.js +0 -1
  303. package/_standalone/.next/static/chunks/app/agents/page-66858acbcd1d4bf8.js +0 -1
  304. package/_standalone/.next/static/chunks/app/echo/[segment]/page-bf5c290fa3ccff09.js +0 -11
  305. package/_standalone/.next/static/chunks/app/layout-a5d5925b47e87cc3.js +0 -164
  306. package/_standalone/.next/static/chunks/app/setup/page-821714e7477be46c.js +0 -1
  307. package/_standalone/.next/static/chunks/app/trash/page-40bc7316806acd62.js +0 -1
  308. package/_standalone/.next/static/chunks/app/view/[...path]/page-6fbb14b8f322d0f0.js +0 -12
  309. package/_standalone/.next/static/chunks/app/wiki/page-ba36eccf4fe62cfe.js +0 -1
  310. package/_standalone/.next/static/css/b57c4eb3cc88308b.css +0 -1
  311. package/_standalone/lib/agent/context.ts +0 -403
  312. /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_buildManifest.js +0 -0
  313. /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
- <div className="flex gap-2">
202
- <Input
203
- type={showPassword ? 'text' : 'password'}
204
- value={isPasswordMasked ? '••••••••' : (data.webPassword ?? '')}
205
- onChange={e => setData(d => d ? { ...d, webPassword: e.target.value } : d)}
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
- <input type="password" value={httpToken} onChange={e => setHttpToken(e.target.value)} placeholder="Bearer token"
181
- className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background font-mono text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring" />
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="text-xs text-muted-foreground">{hint}</p>}
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
- provider: 'anthropic',
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;