agim-cli 1.2.1 → 1.2.17

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 (199) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +18 -5
  3. package/README.zh-CN.md +20 -7
  4. package/dist/cli-ui/entry-menu.d.ts +2 -0
  5. package/dist/cli-ui/entry-menu.d.ts.map +1 -1
  6. package/dist/cli-ui/entry-menu.js +1 -0
  7. package/dist/cli-ui/entry-menu.js.map +1 -1
  8. package/dist/cli-ui/i18n.d.ts +5 -0
  9. package/dist/cli-ui/i18n.d.ts.map +1 -1
  10. package/dist/cli-ui/i18n.js +10 -0
  11. package/dist/cli-ui/i18n.js.map +1 -1
  12. package/dist/cli.js +117 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/core/admin-bootstrap.d.ts +14 -0
  15. package/dist/core/admin-bootstrap.d.ts.map +1 -1
  16. package/dist/core/admin-bootstrap.js +21 -0
  17. package/dist/core/admin-bootstrap.js.map +1 -1
  18. package/dist/core/commands/builtin.d.ts.map +1 -1
  19. package/dist/core/commands/builtin.js +2 -0
  20. package/dist/core/commands/builtin.js.map +1 -1
  21. package/dist/core/commands/router.js +1 -1
  22. package/dist/core/commands/router.js.map +1 -1
  23. package/dist/core/commands/web.d.ts +3 -0
  24. package/dist/core/commands/web.d.ts.map +1 -0
  25. package/dist/core/commands/web.js +28 -0
  26. package/dist/core/commands/web.js.map +1 -0
  27. package/dist/core/intent.d.ts +11 -2
  28. package/dist/core/intent.d.ts.map +1 -1
  29. package/dist/core/intent.js +26 -4
  30. package/dist/core/intent.js.map +1 -1
  31. package/dist/core/memory.js.map +1 -1
  32. package/dist/core/render-router.d.ts.map +1 -1
  33. package/dist/core/render-router.js +3 -2
  34. package/dist/core/render-router.js.map +1 -1
  35. package/dist/core/router.d.ts.map +1 -1
  36. package/dist/core/router.js +8 -1
  37. package/dist/core/router.js.map +1 -1
  38. package/dist/core/types.d.ts +3 -0
  39. package/dist/core/types.d.ts.map +1 -1
  40. package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -1
  41. package/dist/plugins/agents/acp/acp-client.js +7 -0
  42. package/dist/plugins/agents/acp/acp-client.js.map +1 -1
  43. package/dist/plugins/agents/acp/discovery.d.ts.map +1 -1
  44. package/dist/plugins/agents/acp/discovery.js +4 -0
  45. package/dist/plugins/agents/acp/discovery.js.map +1 -1
  46. package/dist/plugins/agents/acp/url-guard.d.ts +44 -0
  47. package/dist/plugins/agents/acp/url-guard.d.ts.map +1 -0
  48. package/dist/plugins/agents/acp/url-guard.js +109 -0
  49. package/dist/plugins/agents/acp/url-guard.js.map +1 -0
  50. package/dist/web/env-mask.d.ts +21 -0
  51. package/dist/web/env-mask.d.ts.map +1 -0
  52. package/dist/web/env-mask.js +44 -0
  53. package/dist/web/env-mask.js.map +1 -0
  54. package/dist/web/public/assets/a2a-Dk2fSs33.js +7 -0
  55. package/dist/web/public/assets/a2a-Dk2fSs33.js.map +1 -0
  56. package/dist/web/public/assets/activity-eiIPshcV.js +7 -0
  57. package/dist/web/public/assets/activity-eiIPshcV.js.map +1 -0
  58. package/dist/web/public/assets/admins-DlbQYdW_.js +12 -0
  59. package/dist/web/public/assets/admins-DlbQYdW_.js.map +1 -0
  60. package/dist/web/public/assets/agents-BMI1WbZj.js +12 -0
  61. package/dist/web/public/assets/agents-BMI1WbZj.js.map +1 -0
  62. package/dist/web/public/assets/approvals-DlXS_sKD.js +10 -0
  63. package/dist/web/public/assets/approvals-DlXS_sKD.js.map +1 -0
  64. package/dist/web/public/assets/audit-C8I8xC_6.js +2 -0
  65. package/dist/web/public/assets/audit-C8I8xC_6.js.map +1 -0
  66. package/dist/web/public/assets/bgjobs-PFYinH7D.js +7 -0
  67. package/dist/web/public/assets/bgjobs-PFYinH7D.js.map +1 -0
  68. package/dist/web/public/assets/brain-DEEJttEL.js +7 -0
  69. package/dist/web/public/assets/brain-DEEJttEL.js.map +1 -0
  70. package/dist/web/public/assets/briefcase-BlMy8gI6.js +7 -0
  71. package/dist/web/public/assets/briefcase-BlMy8gI6.js.map +1 -0
  72. package/dist/web/public/assets/browser-ponyfill-BOcGq8h9.js +3 -0
  73. package/dist/web/public/assets/browser-ponyfill-BOcGq8h9.js.map +1 -0
  74. package/dist/web/public/assets/chevron-right-DmABPvoA.js +7 -0
  75. package/dist/web/public/assets/chevron-right-DmABPvoA.js.map +1 -0
  76. package/dist/web/public/assets/circle-check-C0Qpg1vL.js +7 -0
  77. package/dist/web/public/assets/circle-check-C0Qpg1vL.js.map +1 -0
  78. package/dist/web/public/assets/circle-check-big-C8LG3beV.js +7 -0
  79. package/dist/web/public/assets/circle-check-big-C8LG3beV.js.map +1 -0
  80. package/dist/web/public/assets/circle-x-D_cRHcHK.js +7 -0
  81. package/dist/web/public/assets/circle-x-D_cRHcHK.js.map +1 -0
  82. package/dist/web/public/assets/confirm-dialog-Baz_xFle.js +2 -0
  83. package/dist/web/public/assets/confirm-dialog-Baz_xFle.js.map +1 -0
  84. package/dist/web/public/assets/data-table--I_ktDF4.js +17 -0
  85. package/dist/web/public/assets/data-table--I_ktDF4.js.map +1 -0
  86. package/dist/web/public/assets/dialog-DZpoEskO.js +6 -0
  87. package/dist/web/public/assets/dialog-DZpoEskO.js.map +1 -0
  88. package/dist/web/public/assets/download-DbFGHwZ5.js +7 -0
  89. package/dist/web/public/assets/download-DbFGHwZ5.js.map +1 -0
  90. package/dist/web/public/assets/email-BB1Hq8eE.js +7 -0
  91. package/dist/web/public/assets/email-BB1Hq8eE.js.map +1 -0
  92. package/dist/web/public/assets/empty-state-DXNa90pP.js +2 -0
  93. package/dist/web/public/assets/empty-state-DXNa90pP.js.map +1 -0
  94. package/dist/web/public/assets/env-Bqrb9XkC.js +2 -0
  95. package/dist/web/public/assets/env-Bqrb9XkC.js.map +1 -0
  96. package/dist/web/public/assets/external-link-nhnJN0qg.js +7 -0
  97. package/dist/web/public/assets/external-link-nhnJN0qg.js.map +1 -0
  98. package/dist/web/public/assets/eye-IKkn_oUo.js +12 -0
  99. package/dist/web/public/assets/eye-IKkn_oUo.js.map +1 -0
  100. package/dist/web/public/assets/facts-C7Qy9vTw.js +2 -0
  101. package/dist/web/public/assets/facts-C7Qy9vTw.js.map +1 -0
  102. package/dist/web/public/assets/health-CMRdeNEW.js +2 -0
  103. package/dist/web/public/assets/health-CMRdeNEW.js.map +1 -0
  104. package/dist/web/public/assets/hot-Bh5Nrc7i.js +17 -0
  105. package/dist/web/public/assets/hot-Bh5Nrc7i.js.map +1 -0
  106. package/dist/web/public/assets/index-CpGWCLE5.js +166 -0
  107. package/dist/web/public/assets/index-CpGWCLE5.js.map +1 -0
  108. package/dist/web/public/assets/index-GpceOxum.css +1 -0
  109. package/dist/web/public/assets/installed-FYLkPij2.js +7 -0
  110. package/dist/web/public/assets/installed-FYLkPij2.js.map +1 -0
  111. package/dist/web/public/assets/jobs-BmqLUzHp.js +2 -0
  112. package/dist/web/public/assets/jobs-BmqLUzHp.js.map +1 -0
  113. package/dist/web/public/assets/layout-9Gp_myEd.js +2 -0
  114. package/dist/web/public/assets/layout-9Gp_myEd.js.map +1 -0
  115. package/dist/web/public/assets/layout-BZaHqf69.js +2 -0
  116. package/dist/web/public/assets/layout-BZaHqf69.js.map +1 -0
  117. package/dist/web/public/assets/layout-CXsUyEpG.js +2 -0
  118. package/dist/web/public/assets/layout-CXsUyEpG.js.map +1 -0
  119. package/dist/web/public/assets/layout-DFxtpNut.js +2 -0
  120. package/dist/web/public/assets/layout-DFxtpNut.js.map +1 -0
  121. package/dist/web/public/assets/layout-d8qxPKQk.js +2 -0
  122. package/dist/web/public/assets/layout-d8qxPKQk.js.map +1 -0
  123. package/dist/web/public/assets/loader-circle-JaKY-xMt.js +7 -0
  124. package/dist/web/public/assets/loader-circle-JaKY-xMt.js.map +1 -0
  125. package/dist/web/public/assets/map-pin-hFFSWZ3B.js +7 -0
  126. package/dist/web/public/assets/map-pin-hFFSWZ3B.js.map +1 -0
  127. package/dist/web/public/assets/memos-EhjMUvVZ.js +12 -0
  128. package/dist/web/public/assets/memos-EhjMUvVZ.js.map +1 -0
  129. package/dist/web/public/assets/messengers-BRV1IVGX.js +7 -0
  130. package/dist/web/public/assets/messengers-BRV1IVGX.js.map +1 -0
  131. package/dist/web/public/assets/network-DtCI2ZUU.js +7 -0
  132. package/dist/web/public/assets/network-DtCI2ZUU.js.map +1 -0
  133. package/dist/web/public/assets/outbox-CxUbMp6o.js +7 -0
  134. package/dist/web/public/assets/outbox-CxUbMp6o.js.map +1 -0
  135. package/dist/web/public/assets/pagination-CkZY8YNa.js +17 -0
  136. package/dist/web/public/assets/pagination-CkZY8YNa.js.map +1 -0
  137. package/dist/web/public/assets/persona-B6TFMSnI.js +2 -0
  138. package/dist/web/public/assets/persona-B6TFMSnI.js.map +1 -0
  139. package/dist/web/public/assets/play-BxRcWaH5.js +7 -0
  140. package/dist/web/public/assets/play-BxRcWaH5.js.map +1 -0
  141. package/dist/web/public/assets/policy-ndE1Y8zD.js +2 -0
  142. package/dist/web/public/assets/policy-ndE1Y8zD.js.map +1 -0
  143. package/dist/web/public/assets/react-C9F3QeMB.js +33 -0
  144. package/dist/web/public/assets/react-C9F3QeMB.js.map +1 -0
  145. package/dist/web/public/assets/refresh-ccw-Bx817_KW.js +7 -0
  146. package/dist/web/public/assets/refresh-ccw-Bx817_KW.js.map +1 -0
  147. package/dist/web/public/assets/reminders-XynkGQc5.js +17 -0
  148. package/dist/web/public/assets/reminders-XynkGQc5.js.map +1 -0
  149. package/dist/web/public/assets/save-CqMcATrh.js +7 -0
  150. package/dist/web/public/assets/save-CqMcATrh.js.map +1 -0
  151. package/dist/web/public/assets/schedules-VM02w_Om.js +7 -0
  152. package/dist/web/public/assets/schedules-VM02w_Om.js.map +1 -0
  153. package/dist/web/public/assets/search-Ba-e1t1P.js +7 -0
  154. package/dist/web/public/assets/search-Ba-e1t1P.js.map +1 -0
  155. package/dist/web/public/assets/service-C-wnwJ-b.js +7 -0
  156. package/dist/web/public/assets/service-C-wnwJ-b.js.map +1 -0
  157. package/dist/web/public/assets/status-badge-CsdJ6k8Q.js +2 -0
  158. package/dist/web/public/assets/status-badge-CsdJ6k8Q.js.map +1 -0
  159. package/dist/web/public/assets/subtasks-mGRKpF0G.js +7 -0
  160. package/dist/web/public/assets/subtasks-mGRKpF0G.js.map +1 -0
  161. package/dist/web/public/assets/table-vmLMgj6_.js +2 -0
  162. package/dist/web/public/assets/table-vmLMgj6_.js.map +1 -0
  163. package/dist/web/public/assets/topn-nu66Fotx.js +7 -0
  164. package/dist/web/public/assets/topn-nu66Fotx.js.map +1 -0
  165. package/dist/web/public/assets/trash-2-ZIitN_U3.js +7 -0
  166. package/dist/web/public/assets/trash-2-ZIitN_U3.js.map +1 -0
  167. package/dist/web/public/assets/use-event-stream-BGeFcayX.js +2 -0
  168. package/dist/web/public/assets/use-event-stream-BGeFcayX.js.map +1 -0
  169. package/dist/web/public/assets/use-memory-DgEqHEca.js +2 -0
  170. package/dist/web/public/assets/use-memory-DgEqHEca.js.map +1 -0
  171. package/dist/web/public/assets/use-observability-CQev_A8e.js +2 -0
  172. package/dist/web/public/assets/use-observability-CQev_A8e.js.map +1 -0
  173. package/dist/web/public/assets/use-settings-CU-UcrVD.js +2 -0
  174. package/dist/web/public/assets/use-settings-CU-UcrVD.js.map +1 -0
  175. package/dist/web/public/assets/use-skills-Dr77CXLA.js +2 -0
  176. package/dist/web/public/assets/use-skills-Dr77CXLA.js.map +1 -0
  177. package/dist/web/public/assets/use-workspace-PNv9Z4de.js +2 -0
  178. package/dist/web/public/assets/use-workspace-PNv9Z4de.js.map +1 -0
  179. package/dist/web/public/assets/useQuery-BTyugXYV.js +2 -0
  180. package/dist/web/public/assets/useQuery-BTyugXYV.js.map +1 -0
  181. package/dist/web/public/assets/vector-w-Ea3pg6.js +2 -0
  182. package/dist/web/public/assets/vector-w-Ea3pg6.js.map +1 -0
  183. package/dist/web/public/assets/viewer-DKA7QP9U.js +12 -0
  184. package/dist/web/public/assets/viewer-DKA7QP9U.js.map +1 -0
  185. package/dist/web/public/assets/workspace-DVLZca7t.js +17 -0
  186. package/dist/web/public/assets/workspace-DVLZca7t.js.map +1 -0
  187. package/dist/web/public/assets/workspaces-DYZsMmY-.js +7 -0
  188. package/dist/web/public/assets/workspaces-DYZsMmY-.js.map +1 -0
  189. package/dist/web/public/assets/x-Ru3rHT82.js +7 -0
  190. package/dist/web/public/assets/x-Ru3rHT82.js.map +1 -0
  191. package/dist/web/public/favicon.svg +4 -0
  192. package/dist/web/public/index.html +37 -928
  193. package/dist/web/public/manifest.webmanifest +19 -0
  194. package/dist/web/public/tasks.html +362 -6
  195. package/dist/web/public/vendor/chart.umd.min.js +20 -0
  196. package/dist/web/server.d.ts.map +1 -1
  197. package/dist/web/server.js +694 -60
  198. package/dist/web/server.js.map +1 -1
  199. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-BB1Hq8eE.js","sources":["../../node_modules/lucide-react/dist/esm/icons/mail.js","../../src/routes/settings/email.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Mail = createLucideIcon(\"Mail\", [\n [\"rect\", { width: \"20\", height: \"16\", x: \"2\", y: \"4\", rx: \"2\", key: \"18n3k1\" }],\n [\"path\", { d: \"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\", key: \"1ocrg3\" }]\n]);\n\nexport { Mail as default };\n//# sourceMappingURL=mail.js.map\n","/**\n * /settings/email — SMTP credentials editor (matches v1 settings.html's\n * \"📨 SMTP\" card). Drives email delivery for `/remind email me@x.com …`.\n *\n * Six env keys, persisted to `~/.agim/.env`:\n * * IMHUB_SMTP_HOST / PORT / USER / PASS / FROM / SECURE\n *\n * IMHUB_SMTP_PASS is in the backend's SECRET_KEYS set — masked unless\n * the Reveal toggle issues a `?reveal=1` fetch. Submitting a masked\n * string back is a no-op on the backend (it preserves the real value).\n *\n * Test button calls POST /api/messengers/email/test which runs\n * `nodemailer.verify()` against the live env (i.e. the operator must\n * Save first, then Test — same UX as v1).\n *\n * Disable button clears all six keys via a single PUT /api/env with\n * null values; the email adapter then logs `email.disabled` on the\n * next service restart.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n CheckCircle2, Eye, EyeOff, Loader2, Mail, RefreshCcw,\n Save, Send, X, XCircle,\n} from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv, useTestEmail } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\nconst SMTP_KEYS = [\n 'IMHUB_SMTP_HOST',\n 'IMHUB_SMTP_PORT',\n 'IMHUB_SMTP_USER',\n 'IMHUB_SMTP_PASS',\n 'IMHUB_SMTP_FROM',\n 'IMHUB_SMTP_SECURE',\n] as const\n\ntype SmtpKey = typeof SMTP_KEYS[number]\ntype SecureMode = 'auto' | 'true' | 'false'\n\ninterface Draft {\n IMHUB_SMTP_HOST: string\n IMHUB_SMTP_PORT: string\n IMHUB_SMTP_USER: string\n IMHUB_SMTP_PASS: string\n IMHUB_SMTP_FROM: string\n IMHUB_SMTP_SECURE: SecureMode\n}\n\nfunction emptyDraft(): Draft {\n return {\n IMHUB_SMTP_HOST: '',\n IMHUB_SMTP_PORT: '',\n IMHUB_SMTP_USER: '',\n IMHUB_SMTP_PASS: '',\n IMHUB_SMTP_FROM: '',\n IMHUB_SMTP_SECURE: 'auto',\n }\n}\n\n// R13: exported so the unit suite can lock the env-string → SecureMode\n// mapping without spinning up Radix Select (whose trigger label isn't\n// readable in jsdom). Mirrors the operator's mental model: anything\n// that reads as \"yes/true/on\" maps to true; \"no/false/off\" to false;\n// blank / unknown to auto (defer to port-based heuristic at send time).\nexport function normalizeSecure(raw: string | undefined): SecureMode {\n const v = (raw ?? '').trim().toLowerCase()\n if (v === '1' || v === 'true' || v === 'yes') return 'true'\n if (v === '0' || v === 'false' || v === 'no') return 'false'\n return 'auto'\n}\n\nfunction draftFromEnv(env: Record<string, string>): Draft {\n return {\n IMHUB_SMTP_HOST: env.IMHUB_SMTP_HOST ?? '',\n IMHUB_SMTP_PORT: env.IMHUB_SMTP_PORT ?? '',\n IMHUB_SMTP_USER: env.IMHUB_SMTP_USER ?? '',\n IMHUB_SMTP_PASS: env.IMHUB_SMTP_PASS ?? '',\n IMHUB_SMTP_FROM: env.IMHUB_SMTP_FROM ?? '',\n IMHUB_SMTP_SECURE: normalizeSecure(env.IMHUB_SMTP_SECURE),\n }\n}\n\n/** Detect the backend's secret-mask format: `ab****yz` (3+ `*` between\n * surrounding chars). PUT /api/env preserves the real value when it\n * sees this pattern, so client edits that just echo the mask are a\n * no-op — we skip them in the dirty check + the submit body. */\nfunction isMasked(s: string): boolean {\n return /\\*{3,}/.test(s)\n}\n\nexport default function SettingsEmailRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n\n const [reveal, setReveal] = useState(false)\n const envQuery = useEnv({ reveal })\n const updateEnv = useUpdateEnv()\n const testMut = useTestEmail()\n\n const env = envQuery.data?.env ?? {}\n\n const [draft, setDraft] = useState<Draft>(emptyDraft)\n // Sync draft from server whenever a fresh env arrives (initial load\n // + reveal toggle + post-save refetch). Local edits that diverge stay\n // until the user Saves or Discards.\n const [syncedHash, setSyncedHash] = useState<string>('')\n useEffect(() => {\n if (!envQuery.data) return\n const next = draftFromEnv(env)\n const hash = JSON.stringify(next)\n if (hash !== syncedHash) {\n setDraft(next)\n setSyncedHash(hash)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [envQuery.dataUpdatedAt])\n\n const isConfigured = !!(env.IMHUB_SMTP_HOST && env.IMHUB_SMTP_USER && env.IMHUB_SMTP_PASS)\n\n const isDirty = useMemo(() => {\n if (!envQuery.data) return false\n const current = draftFromEnv(env)\n for (const k of SMTP_KEYS) {\n // PASS: ignore \"user just echoed the mask back\" as no-change.\n if (k === 'IMHUB_SMTP_PASS' && draft[k] && isMasked(draft[k]) && draft[k] === current[k]) continue\n if (draft[k] !== current[k]) return true\n }\n return false\n }, [draft, env, envQuery.data])\n\n function setField<K extends SmtpKey>(key: K, value: Draft[K]): void {\n setDraft((d) => ({ ...d, [key]: value }))\n }\n\n function onDiscard(): void {\n if (!envQuery.data) return\n setDraft(draftFromEnv(env))\n }\n\n async function onSave(): Promise<void> {\n if (!envQuery.data) return\n const current = draftFromEnv(env)\n const updates: Record<string, string | null> = {}\n for (const k of SMTP_KEYS) {\n const next = draft[k]\n const cur = current[k]\n if (next === cur) continue\n // Don't ship a masked PASS as the new value — backend's\n // isMasked-detect already skips it server-side, but keep the\n // wire honest.\n if (k === 'IMHUB_SMTP_PASS' && isMasked(next)) continue\n // Empty string → null (explicit unset) for non-PASS fields.\n // For PASS, an empty string also means \"clear\" (Disable button uses\n // the same path).\n updates[k] = next === '' ? null : next\n }\n if (Object.keys(updates).length === 0) {\n toast.info(t('email.toast.noChanges'))\n return\n }\n try {\n await updateEnv.mutateAsync({ updates })\n toast.success(t('email.toast.saved'))\n // refetch handled by hook's onSuccess; draft will re-sync via\n // the dataUpdatedAt effect on the next paint.\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n async function onDisable(): Promise<void> {\n if (!envQuery.data) return\n const updates: Record<string, string | null> = {}\n for (const k of SMTP_KEYS) updates[k] = null\n try {\n await updateEnv.mutateAsync({ updates })\n setDraft(emptyDraft())\n toast.success(t('email.toast.disabled'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n async function onTest(): Promise<void> {\n try {\n const res = await testMut.mutateAsync()\n toast.success(res.message)\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const testIcon =\n testMut.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : testMut.isError ? <XCircle className=\"h-4 w-4 text-danger\" />\n : testMut.isSuccess ? <CheckCircle2 className=\"h-4 w-4 text-success\" />\n : <Send className=\"h-4 w-4\" />\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('email.title')}</h1>\n {isConfigured\n ? <Badge variant=\"success\">{t('email.statusOn')}</Badge>\n : <Badge variant=\"outline\">{t('email.statusOff')}</Badge>}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => void envQuery.refetch()}\n disabled={envQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('email.subtitle')}</p>\n </header>\n\n {envQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <section className=\"rounded-md border border-border bg-surface\">\n <header className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <Mail className=\"h-4 w-4 text-text-dim\" />\n <h2 className=\"text-sm font-semibold\">{t('email.cardTitle')}</h2>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => setReveal((v) => !v)}\n disabled={envQuery.isFetching}\n aria-label={reveal ? t('email.hide') : t('email.reveal')}\n >\n {reveal ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{reveal ? t('email.hide') : t('email.reveal')}</span>\n </Button>\n </header>\n\n <div className=\"grid grid-cols-1 gap-3 px-4 py-4 sm:grid-cols-2\">\n <Field label={t('email.host')} hint={t('email.hostHint')}>\n <Input\n value={draft.IMHUB_SMTP_HOST}\n onChange={(e) => setField('IMHUB_SMTP_HOST', e.target.value)}\n placeholder=\"smtp.gmail.com\"\n autoComplete=\"off\"\n />\n </Field>\n <Field label={t('email.port')} hint={t('email.portHint')}>\n <Input\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n max={65535}\n value={draft.IMHUB_SMTP_PORT}\n onChange={(e) => setField('IMHUB_SMTP_PORT', e.target.value)}\n placeholder=\"465\"\n autoComplete=\"off\"\n />\n </Field>\n <Field label={t('email.user')} hint={t('email.userHint')}>\n <Input\n value={draft.IMHUB_SMTP_USER}\n onChange={(e) => setField('IMHUB_SMTP_USER', e.target.value)}\n placeholder=\"you@example.com\"\n autoComplete=\"off\"\n />\n </Field>\n <Field label={t('email.pass')} hint={t('email.passHint')}>\n <Input\n type={reveal ? 'text' : 'password'}\n value={draft.IMHUB_SMTP_PASS}\n onChange={(e) => setField('IMHUB_SMTP_PASS', e.target.value)}\n placeholder=\"••••••••\"\n autoComplete=\"new-password\"\n className={cn(isMasked(draft.IMHUB_SMTP_PASS) && 'font-mono text-text-muted')}\n />\n </Field>\n <Field label={t('email.from')} hint={t('email.fromHint')}>\n <Input\n value={draft.IMHUB_SMTP_FROM}\n onChange={(e) => setField('IMHUB_SMTP_FROM', e.target.value)}\n placeholder=\"\"\n autoComplete=\"off\"\n />\n </Field>\n <Field label={t('email.secure')} hint={t('email.secureHint')}>\n <Select\n value={draft.IMHUB_SMTP_SECURE}\n onValueChange={(v) => setField('IMHUB_SMTP_SECURE', v as SecureMode)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"auto\">{t('email.secureAuto')}</SelectItem>\n <SelectItem value=\"true\">{t('email.secureTrue')}</SelectItem>\n <SelectItem value=\"false\">{t('email.secureFalse')}</SelectItem>\n </SelectContent>\n </Select>\n </Field>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2 border-t border-border px-4 py-3\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onTest()}\n disabled={!isConfigured || testMut.isPending}\n title={!isConfigured ? t('email.testNeedsConfigured') : undefined}\n >\n {testIcon}\n {t('email.testBtn')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void onDisable()}\n disabled={!isConfigured || updateEnv.isPending}\n className=\"text-danger hover:text-danger\"\n >\n <X className=\"h-4 w-4\" />\n {t('email.disableBtn')}\n </Button>\n <span className=\"ml-auto\" />\n {isDirty && (\n <>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n {t('email.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {updateEnv.isPending ? t('email.saving') : t('email.saveBtn')}\n </Button>\n </>\n )}\n </div>\n </section>\n )}\n </div>\n )\n}\n\ninterface FieldProps {\n label: string\n hint?: string\n children: React.ReactNode\n}\n\nfunction Field({ label, hint, children }: FieldProps): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">{label}</Label>\n {children}\n {hint && <p className=\"text-[11px] text-text-dim\">{hint}</p>}\n </div>\n )\n}\n"],"names":["Mail","createLucideIcon","SMTP_KEYS","emptyDraft","normalizeSecure","raw","v","draftFromEnv","env","isMasked","SettingsEmailRoute","t","useTranslation","reveal","setReveal","useState","envQuery","useEnv","updateEnv","useUpdateEnv","testMut","useTestEmail","draft","setDraft","syncedHash","setSyncedHash","useEffect","next","hash","isConfigured","isDirty","useMemo","current","k","setField","key","value","d","onDiscard","onSave","updates","cur","toast","err","describeError","onDisable","onTest","res","testIcon","jsx","Loader2","XCircle","CheckCircle2","Send","jsxs","Badge","Button","RefreshCcw","EyeOff","Eye","Field","Input","e","cn","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","X","Fragment","Save","label","hint","children","Label"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1,2 @@
1
+ import{Q as e,r as x}from"./index-CpGWCLE5.js";function l({icon:t,title:m,description:s,children:a,className:r,...i}){return e.jsxs("div",{className:x("flex flex-col items-center justify-center text-center","py-12 px-4 gap-3",r),...i,children:[t&&e.jsx("div",{className:x("flex items-center justify-center","h-12 w-12 rounded-full bg-surface-2 text-text-dim","[&_svg]:h-6 [&_svg]:w-6"),"aria-hidden":"true",children:t}),e.jsx("h3",{className:"text-base font-semibold text-text",children:m}),s&&e.jsx("p",{className:"text-sm text-text-dim max-w-sm",children:s}),a&&e.jsx("div",{className:"mt-2 flex flex-wrap items-center justify-center gap-2",children:a})]})}l.displayName="EmptyState";export{l as E};
2
+ //# sourceMappingURL=empty-state-DXNa90pP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-state-DXNa90pP.js","sources":["../../src/components/common/empty-state.tsx"],"sourcesContent":["/**\n * EmptyState — placeholder shown when a list / table / panel has no\n * rows yet. Used by DataTable's empty branch, by the chat welcome\n * screen (when no messages), and by route bodies that point at an\n * unconfigured / unused subsystem.\n *\n * Slots:\n * icon — optional lucide-react icon node. Sits above the\n * title. Renders inside a soft surface-2 circle so\n * the icon doesn't look stranded on a blank page.\n * title — required short headline (\"No jobs yet\").\n * description — optional sub-line; can wrap.\n * children — optional action slot for one or two Buttons / Links\n * (\"Create your first job\", \"Read the docs\").\n *\n * Visual: centered column, generous vertical padding, max-width on\n * description so wrapping looks intentional on wide screens.\n */\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n icon?: React.ReactNode\n title: React.ReactNode\n description?: React.ReactNode\n}\n\nfunction EmptyState({\n icon,\n title,\n description,\n children,\n className,\n ...props\n}: EmptyStateProps): JSX.Element {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center text-center',\n 'py-12 px-4 gap-3',\n className,\n )}\n {...props}\n >\n {icon && (\n <div\n className={cn(\n 'flex items-center justify-center',\n 'h-12 w-12 rounded-full bg-surface-2 text-text-dim',\n '[&_svg]:h-6 [&_svg]:w-6',\n )}\n aria-hidden=\"true\"\n >\n {icon}\n </div>\n )}\n <h3 className=\"text-base font-semibold text-text\">{title}</h3>\n {description && (\n <p className=\"text-sm text-text-dim max-w-sm\">{description}</p>\n )}\n {children && <div className=\"mt-2 flex flex-wrap items-center justify-center gap-2\">{children}</div>}\n </div>\n )\n}\nEmptyState.displayName = 'EmptyState'\n\nexport { EmptyState }\n"],"names":["EmptyState","icon","title","description","children","className","props","jsxs","cn","jsx"],"mappings":"+CA4BA,SAASA,EAAW,CAClB,KAAAC,EACA,MAAAC,EACA,YAAAC,EACA,SAAAC,EACA,UAAAC,EACA,GAAGC,CACL,EAAiC,CAC/B,OACEC,EAAAA,KAAC,MAAA,CACC,UAAWC,EACT,wDACA,mBACAH,CAAA,EAED,GAAGC,EAEH,SAAA,CAAAL,GACCQ,EAAAA,IAAC,MAAA,CACC,UAAWD,EACT,mCACA,oDACA,yBAAA,EAEF,cAAY,OAEX,SAAAP,CAAA,CAAA,EAGLQ,EAAAA,IAAC,KAAA,CAAG,UAAU,oCAAqC,SAAAP,EAAM,EACxDC,GACCM,EAAAA,IAAC,IAAA,CAAE,UAAU,iCAAkC,SAAAN,EAAY,EAE5DC,GAAYK,EAAAA,IAAC,MAAA,CAAI,UAAU,wDAAyD,SAAAL,CAAA,CAAS,CAAA,CAAA,CAAA,CAGpG,CACAJ,EAAW,YAAc"}
@@ -0,0 +1,2 @@
1
+ import{ac as b,ab as B,Q as e,c as x,I as y,B as E,a2 as N,z as L,r as F}from"./index-CpGWCLE5.js";import{e as c}from"./react-C9F3QeMB.js";import{E as P}from"./empty-state-DXNa90pP.js";import{d as D,n as O}from"./use-settings-CU-UcrVD.js";import{L as w}from"./loader-circle-JaKY-xMt.js";import{R as T}from"./refresh-ccw-Bx817_KW.js";import{a as I,E as Q}from"./eye-IKkn_oUo.js";import{X as C}from"./x-Ru3rHT82.js";import{S as U}from"./save-CqMcATrh.js";import"./useQuery-BTyugXYV.js";function $(){const{t:a}=b(["settings","common"]),[o,m]=c.useState(!1),t=D({reveal:o}),l=t.data?.env??{},h=c.useMemo(()=>new Set(t.data?.secretKeys??[]),[t.data?.secretKeys]),[n,i]=c.useState(()=>new Map),[v]=B(),[f,S]=c.useState(v.get("q")??""),u=O(),p=c.useMemo(()=>Object.keys(l).sort(),[l]),z=c.useMemo(()=>p.filter(s=>s.toLowerCase().includes(f.toLowerCase())),[p,f]);function j(s,r){i(d=>{const g=new Map(d),R=l[s]??"";return r!=null&&r===R?g.delete(s):g.set(s,r),g})}async function M(){if(n.size===0)return;const s={};for(const[r,d]of n)s[r]=d;try{await u.mutateAsync({updates:s}),i(new Map),N.success(a("env.savedToast",{count:Object.keys(s).length}))}catch(r){const{message:d}=L(r,a);N.error(d)}}function K(){i(new Map)}return e.jsxs("div",{className:"mx-auto flex max-w-5xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:a("env.title")}),e.jsxs(x,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>t.refetch(),disabled:t.isFetching,"aria-label":a("actions.refresh",{ns:"common"}),children:[t.isFetching?e.jsx(w,{className:"h-4 w-4 animate-spin"}):e.jsx(T,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:a("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:a("env.subtitle")})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(y,{value:f,onChange:s=>S(s.target.value),placeholder:a("env.search"),className:"w-64"}),e.jsxs(x,{type:"button",variant:o?"default":"outline",size:"sm",onClick:()=>m(s=>!s),children:[o?e.jsx(I,{className:"h-4 w-4"}):e.jsx(Q,{className:"h-4 w-4"}),a("env.reveal")]}),e.jsx("div",{className:"ml-auto flex items-center gap-2",children:n.size>0?e.jsxs(e.Fragment,{children:[e.jsx(E,{variant:"info",children:a("env.dirtyHint",{count:n.size})}),e.jsxs(x,{type:"button",variant:"secondary",size:"sm",onClick:K,disabled:u.isPending,children:[e.jsx(C,{className:"h-4 w-4"}),a("env.discard")]}),e.jsxs(x,{type:"button",size:"sm",onClick:()=>void M(),disabled:u.isPending,children:[u.isPending?e.jsx(w,{className:"h-4 w-4 animate-spin"}):e.jsx(U,{className:"h-4 w-4"}),a("env.save")]})]}):e.jsx("span",{className:"text-xs text-text-muted",children:a("env.noChanges")})})]}),t.isLoading?e.jsx("div",{className:"h-64 w-full rounded-md bg-surface-2 animate-pulse"}):p.length===0?e.jsx(P,{title:a("env.empty")}):e.jsx("div",{className:"flex flex-col divide-y divide-border rounded-md border border-border bg-surface",children:z.map(s=>e.jsx(V,{envKey:s,serverValue:l[s]??"",draft:n.has(s)?n.get(s)??null:l[s]??"",isDirty:n.has(s),isSecret:h.has(s),onChange:r=>j(s,r),onUnset:()=>j(s,null)},s))})]})}function V({envKey:a,serverValue:o,draft:m,isDirty:t,isSecret:l,onChange:h,onUnset:n}){const{t:i}=b("settings");return e.jsxs("div",{className:F("grid grid-cols-1 gap-2 px-3 py-2 sm:grid-cols-[280px_1fr_auto] sm:items-center",t&&"bg-accent-bg/40"),children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 min-w-0",children:[e.jsx("code",{className:"font-mono text-xs break-all",children:a}),l&&e.jsx(E,{variant:"warning",children:i("env.secretTag")})]}),m===null?e.jsx("div",{className:"text-sm text-text-muted italic",children:i("env.unset")}):e.jsx(y,{value:m,onChange:v=>h(v.target.value),className:"font-mono text-xs",placeholder:o}),e.jsx("div",{className:"flex justify-end gap-1",children:m!==null&&e.jsxs(x,{type:"button",variant:"ghost",size:"sm",onClick:n,"aria-label":i("env.unsetButton"),children:[e.jsx(C,{className:"h-3 w-3"}),e.jsx("span",{className:"sr-only sm:not-sr-only sm:ml-1",children:i("env.unsetButton")})]})})]})}export{$ as default};
2
+ //# sourceMappingURL=env-Bqrb9XkC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-Bqrb9XkC.js","sources":["../../src/routes/settings/env.tsx"],"sourcesContent":["/**\n * /settings/env — bulk editor for the `~/.agim/.env` file.\n *\n * Tracks per-key dirty state in a Map<string, string|null>. The\n * `null` sentinel maps the backend's \"unset this key\" semantic.\n * Save submits only the dirty subset so we don't churn untouched\n * lines.\n *\n * Secrets (per the backend's `secretKeys` list) are masked unless\n * the Reveal toggle is on. The toggle issues a separate\n * `?reveal=1` fetch — it doesn't show locally-cached unmasked\n * values for free since the masked fetch never received them.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Eye, EyeOff, Loader2, RefreshCcw, Save, X } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\nexport default function SettingsEnvRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n\n const [reveal, setReveal] = useState(false)\n const envQuery = useEnv({ reveal })\n const env = envQuery.data?.env ?? {}\n const secretKeys = useMemo(\n () => new Set(envQuery.data?.secretKeys ?? []),\n [envQuery.data?.secretKeys],\n )\n\n /** Edits keyed by env name. null sentinel = explicit unset. */\n const [edits, setEdits] = useState<Map<string, string | null>>(() => new Map())\n // Pre-seed the filter from `?q=` so deep-links like\n // /settings/env?q=IMHUB_MEMORY_VECTOR open straight to the\n // matching subset.\n const [params] = useSearchParams()\n const [filter, setFilter] = useState(params.get('q') ?? '')\n\n const updateEnv = useUpdateEnv()\n\n const orderedKeys = useMemo(() => Object.keys(env).sort(), [env])\n const filteredKeys = useMemo(\n () => orderedKeys.filter((k) => k.toLowerCase().includes(filter.toLowerCase())),\n [orderedKeys, filter],\n )\n\n function setDraft(key: string, next: string | null): void {\n setEdits((prev) => {\n const m = new Map(prev)\n // If the new value matches the server value, drop the entry —\n // keeps the dirty count accurate after a round-trip.\n const serverVal = env[key] ?? ''\n if (next != null && next === serverVal) {\n m.delete(key)\n } else {\n m.set(key, next)\n }\n return m\n })\n }\n\n async function onSave(): Promise<void> {\n if (edits.size === 0) return\n const updates: Record<string, string | null> = {}\n for (const [k, v] of edits) updates[k] = v\n try {\n await updateEnv.mutateAsync({ updates })\n setEdits(new Map())\n toast.success(t('env.savedToast', { count: Object.keys(updates).length }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n function onDiscard(): void {\n setEdits(new Map())\n }\n\n return (\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('env.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => envQuery.refetch()}\n disabled={envQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('env.subtitle')}</p>\n </header>\n\n {/* Controls row */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Input\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n placeholder={t('env.search')}\n className=\"w-64\"\n />\n <Button\n type=\"button\"\n variant={reveal ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setReveal((v) => !v)}\n >\n {reveal ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n {t('env.reveal')}\n </Button>\n <div className=\"ml-auto flex items-center gap-2\">\n {edits.size > 0 ? (\n <>\n <Badge variant=\"info\">{t('env.dirtyHint', { count: edits.size })}</Badge>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" />\n {t('env.discard')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('env.save')}\n </Button>\n </>\n ) : (\n <span className=\"text-xs text-text-muted\">{t('env.noChanges')}</span>\n )}\n </div>\n </div>\n\n {envQuery.isLoading ? (\n <div className=\"h-64 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : orderedKeys.length === 0 ? (\n <EmptyState title={t('env.empty')} />\n ) : (\n <div className=\"flex flex-col divide-y divide-border rounded-md border border-border bg-surface\">\n {filteredKeys.map((key) => (\n <EnvRow\n key={key}\n envKey={key}\n serverValue={env[key] ?? ''}\n draft={edits.has(key) ? edits.get(key) ?? null : env[key] ?? ''}\n isDirty={edits.has(key)}\n isSecret={secretKeys.has(key)}\n onChange={(v) => setDraft(key, v)}\n onUnset={() => setDraft(key, null)}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n\ninterface EnvRowProps {\n envKey: string\n serverValue: string\n /** Current displayed value — string when edited or server-side\n * value; null when the user staged an \"unset\". */\n draft: string | null\n isDirty: boolean\n isSecret: boolean\n onChange: (v: string) => void\n onUnset: () => void\n}\n\nfunction EnvRow({\n envKey, serverValue, draft, isDirty, isSecret, onChange, onUnset,\n}: EnvRowProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <div className={cn(\n 'grid grid-cols-1 gap-2 px-3 py-2 sm:grid-cols-[280px_1fr_auto] sm:items-center',\n isDirty && 'bg-accent-bg/40',\n )}>\n <div className=\"flex flex-wrap items-center gap-2 min-w-0\">\n <code className=\"font-mono text-xs break-all\">{envKey}</code>\n {isSecret && <Badge variant=\"warning\">{t('env.secretTag')}</Badge>}\n </div>\n {draft === null ? (\n <div className=\"text-sm text-text-muted italic\">{t('env.unset')}</div>\n ) : (\n <Input\n value={draft}\n onChange={(e) => onChange(e.target.value)}\n className=\"font-mono text-xs\"\n placeholder={serverValue}\n />\n )}\n <div className=\"flex justify-end gap-1\">\n {draft !== null && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={onUnset}\n aria-label={t('env.unsetButton')}\n >\n <X className=\"h-3 w-3\" />\n <span className=\"sr-only sm:not-sr-only sm:ml-1\">{t('env.unsetButton')}</span>\n </Button>\n )}\n </div>\n </div>\n )\n}\n"],"names":["SettingsEnvRoute","t","useTranslation","reveal","setReveal","useState","envQuery","useEnv","env","secretKeys","useMemo","edits","setEdits","params","useSearchParams","filter","setFilter","updateEnv","useUpdateEnv","orderedKeys","filteredKeys","k","setDraft","key","next","prev","m","serverVal","onSave","updates","v","toast","err","message","describeError","onDiscard","jsxs","jsx","Button","Loader2","RefreshCcw","Input","e","EyeOff","Eye","Fragment","Badge","X","Save","EmptyState","EnvRow","envKey","serverValue","draft","isDirty","isSecret","onChange","onUnset","cn"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import{x as a}from"./index-CpGWCLE5.js";/**
2
+ * @license lucide-react v0.469.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const e=a("ExternalLink",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);export{e as E};
7
+ //# sourceMappingURL=external-link-nhnJN0qg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-link-nhnJN0qg.js","sources":["../../node_modules/lucide-react/dist/esm/icons/external-link.js"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst ExternalLink = createLucideIcon(\"ExternalLink\", [\n [\"path\", { d: \"M15 3h6v6\", key: \"1q9fwt\" }],\n [\"path\", { d: \"M10 14 21 3\", key: \"gplh6r\" }],\n [\"path\", { d: \"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\", key: \"a6xqqp\" }]\n]);\n\nexport { ExternalLink as default };\n//# sourceMappingURL=external-link.js.map\n"],"names":["ExternalLink","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAeC,EAAiB,eAAgB,CACpD,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,QAAQ,CAAE,EAC1C,CAAC,OAAQ,CAAE,EAAG,cAAe,IAAK,QAAQ,CAAE,EAC5C,CAAC,OAAQ,CAAE,EAAG,2DAA4D,IAAK,QAAQ,CAAE,CAC3F,CAAC","x_google_ignoreList":[0]}
@@ -0,0 +1,12 @@
1
+ import{x as e}from"./index-CpGWCLE5.js";/**
2
+ * @license lucide-react v0.469.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const c=e("EyeOff",[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]]);/**
7
+ * @license lucide-react v0.469.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const y=e("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);export{y as E,c as a};
12
+ //# sourceMappingURL=eye-IKkn_oUo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eye-IKkn_oUo.js","sources":["../../node_modules/lucide-react/dist/esm/icons/eye-off.js","../../node_modules/lucide-react/dist/esm/icons/eye.js"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst EyeOff = createLucideIcon(\"EyeOff\", [\n [\n \"path\",\n {\n d: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n]);\n\nexport { EyeOff as default };\n//# sourceMappingURL=eye-off.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Eye = createLucideIcon(\"Eye\", [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n]);\n\nexport { Eye as default };\n//# sourceMappingURL=eye.js.map\n"],"names":["EyeOff","createLucideIcon","Eye"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CACE,OACA,CACE,EAAG,iGACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,uCAAwC,IAAK,QAAQ,CAAE,EACrE,CACE,OACA,CACE,EAAG,+FACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,CAC7C,CAAC,EC1BD;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACC,EAAMD,EAAiB,MAAO,CAClC,CACE,OACA,CACE,EAAG,wGACH,IAAK,QACX,CACA,EACE,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAQ,CAAE,CAC1D,CAAC","x_google_ignoreList":[0,1]}
@@ -0,0 +1,2 @@
1
+ import{ac as K,ab as Y,Q as e,B as D,c as b,L as E,I as J,S as W,l as X,m as Z,j as ee,k,a2 as p,z as T}from"./index-CpGWCLE5.js";import{e as r}from"./react-C9F3QeMB.js";import{D as te}from"./data-table--I_ktDF4.js";import{E as B}from"./empty-state-DXNa90pP.js";import{P as se}from"./pagination-CkZY8YNa.js";import{C as M}from"./confirm-dialog-Baz_xFle.js";import{d as ae,b as ne,u as ce}from"./use-memory-DgEqHEca.js";import{T as A}from"./trash-2-ZIitN_U3.js";import{B as R}from"./brain-DEEJttEL.js";import{L as re}from"./loader-circle-JaKY-xMt.js";import{R as ie}from"./refresh-ccw-Bx817_KW.js";import{S as le}from"./search-Ba-e1t1P.js";import"./table-vmLMgj6_.js";import"./chevron-right-DmABPvoA.js";import"./dialog-DZpoEskO.js";import"./x-Ru3rHT82.js";import"./useQuery-BTyugXYV.js";const oe=["fact","preference","goal","history","profile"],me=[25,50,100],F=50,de=300;function Ee(){const{t:s}=K(["memory","common"]),[n,L]=Y(),c=n.get("user")??"",i=n.get("q")??"",m=n.get("cat")??null,d=Math.max(1,Number(n.get("page"))||1),l=Number(n.get("per_page"))||F,[f,I]=r.useState(i);r.useEffect(()=>{if(f===i)return;const t=window.setTimeout(()=>h({q:f||null}),de);return()=>window.clearTimeout(t)},[f]);const q=r.useMemo(()=>({user_key:c,...i?{query:i}:{},...m?{category:m}:{},limit:l,offset:(d-1)*l}),[c,i,m,d,l]),{data:S,isLoading:z,isFetching:C,refetch:U}=ae(q,{enabled:!!c}),_=S?.facts??[],P=S?.total??0,G=Math.max(1,Math.ceil(P/l)),[u,g]=r.useState(new Set);r.useEffect(()=>{g(new Set)},[c,i,m,d,l]);const j=ne(),x=ce(),[y,v]=r.useState(null),[V,O]=r.useState(!1);function h(t){const a=new URLSearchParams(n);for(const[o,w]of Object.entries(t))w==null||w===""?a.delete(o):a.set(o,w);Object.keys(t).some(o=>o!=="page")&&a.delete("page"),L(a,{replace:!1})}async function Q(){if(!(y==null||!c))try{await j.mutateAsync({id:y,user_key:c}),p.success(s("facts.toast.deletedOne"))}catch(t){const{message:a}=T(t,s);throw p.error(a),t}}async function $(){if(!c||u.size===0)return;const t=Array.from(u).map(Number).filter(Number.isFinite);try{const a=await x.mutateAsync({user_key:c,body:{ids:t}});p.success(s("facts.toast.deletedMany",{count:a.deleted})),g(new Set)}catch(a){const{message:o}=T(a,s);throw p.error(o),a}}const H=r.useMemo(()=>[{id:"id",header:s("facts.col.id"),cell:t=>e.jsxs("span",{className:"tabular-nums text-text-dim",children:["#",t.id]}),headClassName:"w-16"},{id:"what",header:s("facts.col.what"),cell:t=>e.jsx("span",{className:"line-clamp-2 text-text",children:t.what}),asCardTitle:!0},{id:"category",header:s("facts.col.category"),cell:t=>e.jsx(D,{variant:"outline",children:s(`facts.category.${t.category}`,{defaultValue:t.category})}),headClassName:"w-28"},{id:"confidence",header:s("facts.col.confidence"),cell:t=>e.jsx("span",{className:"tabular-nums text-text-dim",children:t.confidence.toFixed(2)}),headClassName:"w-24",hideOnMobile:!0},{id:"source",header:s("facts.col.source"),cell:t=>e.jsx("span",{className:"text-text-dim text-xs",children:t.source}),headClassName:"w-28",hideOnMobile:!0},{id:"createdAt",header:s("facts.col.createdAt"),cell:t=>e.jsx("span",{className:"text-text-dim",children:fe(t.created_at)}),headClassName:"w-32",hideOnMobile:!0},{id:"actions",header:"",cell:t=>e.jsx("div",{className:"flex justify-end",children:e.jsxs(b,{variant:"ghost",size:"sm",onClick:a=>{a.stopPropagation(),v(t.id)},disabled:j.isPending||x.isPending,"aria-label":s("facts.action.delete"),children:[e.jsx(A,{className:"h-3 w-3"}),e.jsx("span",{className:"sr-only sm:not-sr-only sm:ml-1",children:s("facts.action.delete")})]})}),headClassName:"w-24"}],[s,j.isPending,x.isPending]);if(!c)return e.jsx("div",{className:"mx-auto max-w-3xl",children:e.jsx(B,{icon:e.jsx(R,{}),title:s("noUserSelected.title"),description:s("noUserSelected.description")})});const N=u.size;return e.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:s("facts.title")}),e.jsxs(b,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>U(),disabled:C,"aria-label":s("actions.refresh",{ns:"common"}),children:[C?e.jsx(re,{className:"h-4 w-4 animate-spin"}):e.jsx(ie,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:s("subtitle")})]}),e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{className:"relative flex-1 min-w-[200px] max-w-md",children:[e.jsx(E,{htmlFor:"search",className:"text-xs text-text-dim",children:s("facts.filter.search")}),e.jsx(le,{className:"pointer-events-none absolute left-2 bottom-2 h-4 w-4 text-text-muted"}),e.jsx(J,{id:"search",value:f,onChange:t=>I(t.target.value),className:"pl-7 mt-1"})]}),e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(E,{htmlFor:"category",className:"text-xs text-text-dim",children:s("facts.filter.category")}),e.jsxs(W,{value:m??"__any__",onValueChange:t=>h({cat:t==="__any__"?null:t}),children:[e.jsx(X,{id:"category",className:"w-[140px]",children:e.jsx(Z,{})}),e.jsxs(ee,{children:[e.jsx(k,{value:"__any__",children:s("facts.filter.categoryAny")}),oe.map(t=>e.jsx(k,{value:t,children:s(`facts.category.${t}`)},t))]})]})]}),N>0&&e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[e.jsx(D,{variant:"secondary",children:s("facts.batch.selected",{count:N})}),e.jsxs(b,{variant:"destructive",size:"sm",onClick:()=>O(!0),disabled:x.isPending,children:[e.jsx(A,{className:"h-4 w-4"}),s("facts.batch.deleteSelected")]})]})]}),e.jsx(te,{columns:H,rows:_,getRowId:t=>String(t.id),loading:z,selection:u,onSelectionChange:g,emptyState:e.jsx(B,{icon:e.jsx(R,{}),title:s("facts.empty.title"),description:s("facts.empty.description")})}),(_.length>0||d>1)&&e.jsx(se,{page:d,totalPages:G,totalRows:P,perPage:l,perPageOptions:me,onPageChange:t=>h({page:t===1?null:String(t)}),onPerPageChange:t=>h({per_page:t===F?null:String(t)})}),e.jsx(M,{open:y!=null,onOpenChange:t=>{t||v(null)},title:s("facts.action.confirmDelete"),description:s("facts.action.confirmDeleteDesc"),intent:"danger",confirmLabel:s("facts.action.delete"),onConfirm:Q}),e.jsx(M,{open:V,onOpenChange:O,title:s("facts.batch.confirmDelete",{count:N}),description:s("facts.batch.confirmDeleteDesc"),intent:"danger",confirmLabel:s("facts.batch.deleteSelected"),onConfirm:$})]})}function fe(s){try{const n=new Date(s);return Number.isNaN(n.getTime())?String(s):n.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return String(s)}}export{Ee as default};
2
+ //# sourceMappingURL=facts-C7Qy9vTw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts-C7Qy9vTw.js","sources":["../../src/routes/memory/facts.tsx"],"sourcesContent":["/**\n * /memory/facts — list / search / delete the user's facts.\n *\n * Reads `?user=` from the URL (the layout writes it). Without a\n * user_key the route shows a friendly empty state pointing at the\n * picker — no query fires until a user is chosen.\n *\n * Search + category filter live in URL params so deep-link sharing\n * works (\"show me john's goals matching tax\"). The free-text input\n * is local-only with a 300ms debounce, same pattern as /memos, so\n * the FTS5 layer isn't hit on every keystroke.\n *\n * Single-row delete via the per-row Delete button; batch delete via\n * checkbox selection + the bulk-delete action bar. Both route\n * through ConfirmDialog.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Brain, Loader2, RefreshCcw, Search, Trash2 } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Pagination } from '@/components/common/pagination'\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useMemoryFacts,\n useDeleteFactById,\n useBulkDeleteFacts,\n} from '@/hooks/use-memory'\nimport { describeError } from '@/lib/api/errors'\nimport type { Fact, FactCategory, ListMemoryFactsQuery } from '@/types/api'\n\nconst CATEGORY_OPTIONS: FactCategory[] = ['fact', 'preference', 'goal', 'history', 'profile']\nconst PER_PAGE_OPTIONS = [25, 50, 100]\nconst DEFAULT_PER_PAGE = 50\nconst SEARCH_DEBOUNCE_MS = 300\n\nexport default function MemoryFactsRoute(): JSX.Element {\n const { t } = useTranslation(['memory', 'common'])\n const [params, setParams] = useSearchParams()\n\n const userKey = params.get('user') ?? ''\n const q = params.get('q') ?? ''\n const cat = (params.get('cat') as FactCategory | null) ?? null\n const page = Math.max(1, Number(params.get('page')) || 1)\n const perPage = Number(params.get('per_page')) || DEFAULT_PER_PAGE\n\n const [qDraft, setQDraft] = useState(q)\n useEffect(() => {\n if (qDraft === q) return\n const timer = window.setTimeout(() => patchParams({ q: qDraft || null }), SEARCH_DEBOUNCE_MS)\n return () => window.clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [qDraft])\n\n const query: ListMemoryFactsQuery = useMemo(\n () => ({\n user_key: userKey,\n ...(q ? { query: q } : {}),\n ...(cat ? { category: cat } : {}),\n limit: perPage,\n offset: (page - 1) * perPage,\n }),\n [userKey, q, cat, page, perPage],\n )\n\n const { data, isLoading, isFetching, refetch } = useMemoryFacts(query, { enabled: Boolean(userKey) })\n const facts = data?.facts ?? []\n const total = data?.total ?? 0\n const totalPages = Math.max(1, Math.ceil(total / perPage))\n\n // Selection state for the bulk delete bar.\n const [selection, setSelection] = useState<Set<string>>(new Set())\n useEffect(() => { setSelection(new Set()) }, [userKey, q, cat, page, perPage])\n\n const deleteOne = useDeleteFactById()\n const bulkDelete = useBulkDeleteFacts()\n\n const [confirmOneId, setConfirmOneId] = useState<number | null>(null)\n const [confirmBulk, setConfirmBulk] = useState(false)\n\n function patchParams(patch: Record<string, string | null>): void {\n const next = new URLSearchParams(params)\n for (const [k, v] of Object.entries(patch)) {\n if (v == null || v === '') next.delete(k)\n else next.set(k, v)\n }\n // Reset to page 1 on any filter mutation.\n if (Object.keys(patch).some((k) => k !== 'page')) next.delete('page')\n setParams(next, { replace: false })\n }\n\n async function onConfirmDeleteOne(): Promise<void> {\n if (confirmOneId == null || !userKey) return\n try {\n await deleteOne.mutateAsync({ id: confirmOneId, user_key: userKey })\n toast.success(t('facts.toast.deletedOne'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n async function onConfirmBulk(): Promise<void> {\n if (!userKey || selection.size === 0) return\n const ids = Array.from(selection).map(Number).filter(Number.isFinite)\n try {\n const r = await bulkDelete.mutateAsync({ user_key: userKey, body: { ids } })\n toast.success(t('facts.toast.deletedMany', { count: r.deleted }))\n setSelection(new Set())\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n const columns: DataTableColumn<Fact>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('facts.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'what',\n header: t('facts.col.what'),\n cell: (r) => <span className=\"line-clamp-2 text-text\">{r.what}</span>,\n asCardTitle: true,\n },\n {\n id: 'category',\n header: t('facts.col.category'),\n cell: (r) => (\n <Badge variant=\"outline\">\n {t(`facts.category.${r.category}`, { defaultValue: r.category })}\n </Badge>\n ),\n headClassName: 'w-28',\n },\n {\n id: 'confidence',\n header: t('facts.col.confidence'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">{r.confidence.toFixed(2)}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'source',\n header: t('facts.col.source'),\n cell: (r) => <span className=\"text-text-dim text-xs\">{r.source}</span>,\n headClassName: 'w-28',\n hideOnMobile: true,\n },\n {\n id: 'createdAt',\n header: t('facts.col.createdAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatEpoch(r.created_at)}</span>,\n headClassName: 'w-32',\n hideOnMobile: true,\n },\n {\n id: 'actions',\n header: '',\n cell: (r) => (\n <div className=\"flex justify-end\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n setConfirmOneId(r.id)\n }}\n disabled={deleteOne.isPending || bulkDelete.isPending}\n aria-label={t('facts.action.delete')}\n >\n <Trash2 className=\"h-3 w-3\" />\n <span className=\"sr-only sm:not-sr-only sm:ml-1\">{t('facts.action.delete')}</span>\n </Button>\n </div>\n ),\n headClassName: 'w-24',\n },\n ],\n [t, deleteOne.isPending, bulkDelete.isPending],\n )\n\n if (!userKey) {\n return (\n <div className=\"mx-auto max-w-3xl\">\n <EmptyState\n icon={<Brain />}\n title={t('noUserSelected.title')}\n description={t('noUserSelected.description')}\n />\n </div>\n )\n }\n\n const selectedCount = selection.size\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('facts.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('subtitle')}</p>\n </header>\n\n {/* Filter row */}\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"relative flex-1 min-w-[200px] max-w-md\">\n <Label htmlFor=\"search\" className=\"text-xs text-text-dim\">\n {t('facts.filter.search')}\n </Label>\n <Search className=\"pointer-events-none absolute left-2 bottom-2 h-4 w-4 text-text-muted\" />\n <Input\n id=\"search\"\n value={qDraft}\n onChange={(e) => setQDraft(e.target.value)}\n className=\"pl-7 mt-1\"\n />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"category\" className=\"text-xs text-text-dim\">\n {t('facts.filter.category')}\n </Label>\n <Select\n value={cat ?? '__any__'}\n onValueChange={(v) => patchParams({ cat: v === '__any__' ? null : v })}\n >\n <SelectTrigger id=\"category\" className=\"w-[140px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('facts.filter.categoryAny')}</SelectItem>\n {CATEGORY_OPTIONS.map((c) => (\n <SelectItem key={c} value={c}>\n {t(`facts.category.${c}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {selectedCount > 0 && (\n <div className=\"ml-auto flex items-center gap-2\">\n <Badge variant=\"secondary\">\n {t('facts.batch.selected', { count: selectedCount })}\n </Badge>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setConfirmBulk(true)}\n disabled={bulkDelete.isPending}\n >\n <Trash2 className=\"h-4 w-4\" />\n {t('facts.batch.deleteSelected')}\n </Button>\n </div>\n )}\n </div>\n\n <DataTable\n columns={columns}\n rows={facts}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n selection={selection}\n onSelectionChange={setSelection}\n emptyState={\n <EmptyState\n icon={<Brain />}\n title={t('facts.empty.title')}\n description={t('facts.empty.description')}\n />\n }\n />\n\n {(facts.length > 0 || page > 1) && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalRows={total}\n perPage={perPage}\n perPageOptions={PER_PAGE_OPTIONS}\n onPageChange={(p) => patchParams({ page: p === 1 ? null : String(p) })}\n onPerPageChange={(pp) => patchParams({ per_page: pp === DEFAULT_PER_PAGE ? null : String(pp) })}\n />\n )}\n\n <ConfirmDialog\n open={confirmOneId != null}\n onOpenChange={(open) => { if (!open) setConfirmOneId(null) }}\n title={t('facts.action.confirmDelete')}\n description={t('facts.action.confirmDeleteDesc')}\n intent=\"danger\"\n confirmLabel={t('facts.action.delete')}\n onConfirm={onConfirmDeleteOne}\n />\n\n <ConfirmDialog\n open={confirmBulk}\n onOpenChange={setConfirmBulk}\n title={t('facts.batch.confirmDelete', { count: selectedCount })}\n description={t('facts.batch.confirmDeleteDesc')}\n intent=\"danger\"\n confirmLabel={t('facts.batch.deleteSelected')}\n onConfirm={onConfirmBulk}\n />\n </div>\n )\n}\n\n/** Format an epoch-ms number as a short locale string. */\nfunction formatEpoch(ms: number): string {\n try {\n const d = new Date(ms)\n if (Number.isNaN(d.getTime())) return String(ms)\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return String(ms)\n }\n}\n"],"names":["CATEGORY_OPTIONS","PER_PAGE_OPTIONS","DEFAULT_PER_PAGE","SEARCH_DEBOUNCE_MS","MemoryFactsRoute","t","useTranslation","params","setParams","useSearchParams","userKey","q","cat","page","perPage","qDraft","setQDraft","useState","useEffect","timer","patchParams","query","useMemo","data","isLoading","isFetching","refetch","useMemoryFacts","facts","total","totalPages","selection","setSelection","deleteOne","useDeleteFactById","bulkDelete","useBulkDeleteFacts","confirmOneId","setConfirmOneId","confirmBulk","setConfirmBulk","patch","next","k","v","onConfirmDeleteOne","toast","err","message","describeError","onConfirmBulk","ids","r","columns","jsxs","jsx","Badge","formatEpoch","Button","e","Trash2","EmptyState","Brain","selectedCount","Loader2","RefreshCcw","Label","Search","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","c","DataTable","Pagination","p","pp","ConfirmDialog","open","ms","d"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{ac as p,ab as f,Q as e,c as g,r as y}from"./index-CpGWCLE5.js";import{e as N}from"./react-C9F3QeMB.js";import{E as v}from"./empty-state-DXNa90pP.js";import{T as w,d as C,e as h,c as i,a as M,b as o}from"./table-vmLMgj6_.js";import{a as L}from"./use-observability-CQev_A8e.js";import{L as T}from"./loader-circle-JaKY-xMt.js";import{R as k}from"./refresh-ccw-Bx817_KW.js";import{A as D}from"./activity-eiIPshcV.js";import"./useQuery-BTyugXYV.js";function P(){const{t:s}=p(["observability","common"]),[l]=f(),n=Math.max(1,Number(l.get("days"))||7),{data:c,isLoading:u,isFetching:m,refetch:b}=L(n),a=c?.totals,d=c?.byDay??[],j=N.useMemo(()=>d.reduce((t,x)=>x.calls>t?x.calls:t,0),[d]);return e.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:s("health.title")}),e.jsxs(g,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>b(),disabled:m,"aria-label":s("actions.refresh",{ns:"common"}),children:[m?e.jsx(T,{className:"h-4 w-4 animate-spin"}):e.jsx(k,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("actions.refresh",{ns:"common"})})]})]}),c&&e.jsxs("p",{className:"text-xs text-text-dim tabular-nums",children:[c.since," → ",c.until]})]}),u?e.jsx("div",{className:"h-48 w-full rounded-md bg-surface-2 animate-pulse"}):!a||a.calls===0&&d.length===0?e.jsx(v,{icon:e.jsx(D,{}),title:s("health.empty.title"),description:s("health.empty.description")}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6",children:[e.jsx(r,{label:s("health.kpi.calls"),value:String(a.calls),tone:"info"}),e.jsx(r,{label:s("health.kpi.cost"),value:`$${a.cost.toFixed(4)}`,tone:"info"}),e.jsx(r,{label:s("health.kpi.errors"),value:String(a.errors),tone:a.errors>0?"danger":"success"}),e.jsx(r,{label:s("health.kpi.errorRate"),value:`${(a.errorRate*100).toFixed(1)}%`,tone:a.errorRate>.05?"warning":"success"}),e.jsx(r,{label:s("health.kpi.avgLatencyMs"),value:`${Math.round(a.avgLatencyMs)}ms`,tone:"info"}),e.jsx(r,{label:s("health.kpi.p95LatencyMs"),value:`${Math.round(a.p95LatencyMs)}ms`,tone:"info"})]}),e.jsxs("div",{children:[e.jsx("h2",{className:"mb-2 text-sm font-medium uppercase tracking-wide text-text-dim",children:s("health.byDay")}),e.jsxs(w,{children:[e.jsx(C,{children:e.jsxs(h,{children:[e.jsx(i,{className:"w-28",children:s("health.byDayCol.date")}),e.jsx(i,{children:s("health.byDayCol.calls")}),e.jsx(i,{className:"w-24",children:s("health.byDayCol.cost")}),e.jsx(i,{className:"w-24",children:s("health.byDayCol.errors")}),e.jsx(i,{className:"w-32",children:s("health.byDayCol.avgLatencyMs")})]})}),e.jsx(M,{children:d.map(t=>e.jsxs(h,{children:[e.jsx(o,{className:"font-mono text-xs",children:t.date}),e.jsx(o,{children:e.jsx(S,{value:t.calls,max:j})}),e.jsxs(o,{className:"tabular-nums",children:["$",t.cost.toFixed(4)]}),e.jsx(o,{className:"tabular-nums",children:e.jsx("span",{className:t.errors>0?"text-danger":"text-text-dim",children:t.errors})}),e.jsxs(o,{className:"tabular-nums",children:[Math.round(t.avgLatencyMs),"ms"]})]},t.date))})]})]})]})]})}const R={info:"border-info/30 text-info",success:"border-success/30 text-success",warning:"border-warning/30 text-warning",danger:"border-danger/30 text-danger"};function r({label:s,value:l,tone:n}){return e.jsxs("div",{className:y("rounded-md border bg-surface px-3 py-2",R[n]),children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-text-dim",children:s}),e.jsx("div",{className:"text-xl font-semibold tabular-nums",children:l})]})}function S({value:s,max:l}){const n=l>0?s/l*100:0;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"relative h-3 w-full max-w-xs overflow-hidden rounded bg-surface-2",children:e.jsx("div",{className:"absolute inset-y-0 left-0 bg-accent/60",style:{width:`${n}%`}})}),e.jsx("span",{className:"tabular-nums text-sm text-text-dim",children:s})]})}export{P as default};
2
+ //# sourceMappingURL=health-CMRdeNEW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-CMRdeNEW.js","sources":["../../src/routes/observability/health.tsx"],"sourcesContent":["/**\n * /observability/health — invocation totals + per-day breakdown.\n *\n * No chart library yet (would push bundle past budget). Instead we\n * render a CSS bar chart inline: each byDay row gets a horizontal\n * bar proportional to the max calls across the window. Cheap,\n * readable, no JS dependency.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Activity, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table'\nimport { Button } from '@/components/ui/button'\nimport { useHealthSummary } from '@/hooks/use-observability'\nimport { cn } from '@/lib/utils'\n\nexport default function ObservabilityHealthRoute(): JSX.Element {\n const { t } = useTranslation(['observability', 'common'])\n const [params] = useSearchParams()\n const days = Math.max(1, Number(params.get('days')) || 7)\n\n const { data, isLoading, isFetching, refetch } = useHealthSummary(days)\n const totals = data?.totals\n const byDay = data?.byDay ?? []\n\n const maxCalls = useMemo(\n () => byDay.reduce((m, r) => (r.calls > m ? r.calls : m), 0),\n [byDay],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('health.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n {data && (\n <p className=\"text-xs text-text-dim tabular-nums\">{data.since} → {data.until}</p>\n )}\n </header>\n\n {isLoading ? (\n <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : !totals || (totals.calls === 0 && byDay.length === 0) ? (\n <EmptyState\n icon={<Activity />}\n title={t('health.empty.title')}\n description={t('health.empty.description')}\n />\n ) : (\n <>\n {/* KPI strip */}\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n <Kpi label={t('health.kpi.calls')} value={String(totals.calls)} tone=\"info\" />\n <Kpi label={t('health.kpi.cost')} value={`$${totals.cost.toFixed(4)}`} tone=\"info\" />\n <Kpi label={t('health.kpi.errors')} value={String(totals.errors)} tone={totals.errors > 0 ? 'danger' : 'success'} />\n <Kpi label={t('health.kpi.errorRate')} value={`${(totals.errorRate * 100).toFixed(1)}%`} tone={totals.errorRate > 0.05 ? 'warning' : 'success'} />\n <Kpi label={t('health.kpi.avgLatencyMs')} value={`${Math.round(totals.avgLatencyMs)}ms`} tone=\"info\" />\n <Kpi label={t('health.kpi.p95LatencyMs')} value={`${Math.round(totals.p95LatencyMs)}ms`} tone=\"info\" />\n </div>\n\n {/* By-day table with inline CSS bars */}\n <div>\n <h2 className=\"mb-2 text-sm font-medium uppercase tracking-wide text-text-dim\">\n {t('health.byDay')}\n </h2>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-28\">{t('health.byDayCol.date')}</TableHead>\n <TableHead>{t('health.byDayCol.calls')}</TableHead>\n <TableHead className=\"w-24\">{t('health.byDayCol.cost')}</TableHead>\n <TableHead className=\"w-24\">{t('health.byDayCol.errors')}</TableHead>\n <TableHead className=\"w-32\">{t('health.byDayCol.avgLatencyMs')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {byDay.map((r) => (\n <TableRow key={r.date}>\n <TableCell className=\"font-mono text-xs\">{r.date}</TableCell>\n <TableCell>\n <CallsBar value={r.calls} max={maxCalls} />\n </TableCell>\n <TableCell className=\"tabular-nums\">${r.cost.toFixed(4)}</TableCell>\n <TableCell className=\"tabular-nums\">\n <span className={r.errors > 0 ? 'text-danger' : 'text-text-dim'}>{r.errors}</span>\n </TableCell>\n <TableCell className=\"tabular-nums\">{Math.round(r.avgLatencyMs)}ms</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </>\n )}\n </div>\n )\n}\n\ninterface KpiProps {\n label: string\n value: string\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<KpiProps['tone'], string> = {\n info: 'border-info/30 text-info',\n success: 'border-success/30 text-success',\n warning: 'border-warning/30 text-warning',\n danger: 'border-danger/30 text-danger',\n}\n\nfunction Kpi({ label, value, tone }: KpiProps): JSX.Element {\n return (\n <div className={cn('rounded-md border bg-surface px-3 py-2', TONE_STYLES[tone])}>\n <div className=\"text-xs uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-xl font-semibold tabular-nums\">{value}</div>\n </div>\n )\n}\n\ninterface CallsBarProps {\n value: number\n max: number\n}\n\n/** Inline CSS bar — width proportional to max across the window. */\nfunction CallsBar({ value, max }: CallsBarProps): JSX.Element {\n const pct = max > 0 ? (value / max) * 100 : 0\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"relative h-3 w-full max-w-xs overflow-hidden rounded bg-surface-2\">\n <div\n className=\"absolute inset-y-0 left-0 bg-accent/60\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"tabular-nums text-sm text-text-dim\">{value}</span>\n </div>\n )\n}\n"],"names":["ObservabilityHealthRoute","t","useTranslation","params","useSearchParams","days","data","isLoading","isFetching","refetch","useHealthSummary","totals","byDay","maxCalls","useMemo","m","r","jsxs","jsx","Button","Loader2","RefreshCcw","EmptyState","Activity","Fragment","Kpi","Table","TableHeader","TableRow","TableHead","TableBody","TableCell","CallsBar","TONE_STYLES","label","value","tone","cn","max","pct"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import{x as p,ac as x,Q as e,c as u,I as v,B as m,r as C,a2 as N}from"./index-CpGWCLE5.js";import{e as c}from"./react-C9F3QeMB.js";import{E as S}from"./empty-state-DXNa90pP.js";import{D as k,a as L,d as D,e as F,b as M}from"./dialog-DZpoEskO.js";import{b as z}from"./use-skills-Dr77CXLA.js";import{E}from"./external-link-nhnJN0qg.js";import{L as R}from"./loader-circle-JaKY-xMt.js";import{R as I}from"./refresh-ccw-Bx817_KW.js";import{S as T}from"./search-Ba-e1t1P.js";import{D as A}from"./download-DbFGHwZ5.js";import"./x-Ru3rHT82.js";import"./useQuery-BTyugXYV.js";/**
2
+ * @license lucide-react v0.469.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const B=p("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);/**
7
+ * @license lucide-react v0.469.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const w=p("Flame",[["path",{d:"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z",key:"96xj49"}]]);/**
12
+ * @license lucide-react v0.469.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const H=p("Star",[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]]);function ee(){const{t:s}=x(["skills","common"]),{data:l,isLoading:a,isFetching:r,refetch:i}=z(),n=l?.skills??[],[h,b]=c.useState(""),[f,g]=c.useState(null),j=c.useMemo(()=>{const t=h.trim().toLowerCase();return t?n.filter(d=>d.slug.toLowerCase().includes(t)||(d.name||"").toLowerCase().includes(t)||(d.description||"").toLowerCase().includes(t)||(d.category||"").toLowerCase().includes(t)):n},[n,h]),y=f?n.find(t=>t.slug===f)??null:null;return e.jsxs("div",{className:"mx-auto flex max-w-5xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsxs("h1",{className:"text-xl font-semibold inline-flex items-center gap-2",children:[e.jsx(w,{className:"h-5 w-5 text-warning"}),s("hot.title")]}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[e.jsx(u,{asChild:!0,variant:"ghost",size:"sm",children:e.jsxs("a",{href:"https://skillhub.cn",target:"_blank",rel:"noopener noreferrer",children:[e.jsx(E,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("hot.openHub")})]})}),e.jsxs(u,{variant:"ghost",size:"sm",onClick:()=>i(),disabled:r,"aria-label":s("actions.refresh",{ns:"common"}),children:[r?e.jsx(R,{className:"h-4 w-4 animate-spin"}):e.jsx(I,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("actions.refresh",{ns:"common"})})]})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:s("hot.subtitle")}),l&&e.jsx($,{cached:!!l.cached,stale:!!l.stale,fetchedAt:l.fetchedAt})]}),n.length>0&&e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{className:"relative flex-1 min-w-[180px] max-w-md",children:[e.jsx(T,{className:"pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-text-muted"}),e.jsx(v,{value:h,onChange:t=>b(t.target.value),placeholder:s("actions.search",{ns:"common"}),className:"pl-7"})]}),e.jsxs(m,{variant:"secondary",className:"ml-auto",children:[j.length," / ",n.length]})]}),a?e.jsx("div",{className:"h-48 w-full rounded-md bg-surface-2 animate-pulse"}):n.length===0?e.jsx(S,{icon:e.jsx(w,{}),title:s("hot.empty")}):e.jsx("ul",{className:"flex flex-col gap-2",children:j.map(t=>e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>g(t.slug),className:C("flex w-full flex-col gap-1 rounded-md border border-border bg-surface px-3 py-2 text-left","transition-colors hover:border-border-strong hover:bg-surface-hover"),children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-medium text-text",children:t.name||t.slug}),t.version&&e.jsxs("span",{className:"text-text-muted text-xs font-mono",children:["v",t.version]}),t.category&&e.jsx(m,{variant:"outline",children:t.category}),e.jsxs("div",{className:"ml-auto flex items-center gap-3 text-xs text-text-dim tabular-nums",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(H,{className:"h-3 w-3"}),o(t.stars)]}),e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(A,{className:"h-3 w-3"}),o(t.installs)]})]})]}),e.jsx("code",{className:"text-text-muted text-xs font-mono",children:t.slug}),t.description&&e.jsx("p",{className:"line-clamp-2 text-sm text-text-dim",children:t.description})]})},t.slug))}),e.jsx(O,{skill:y,onOpenChange:t=>{t||g(null)}})]})}function $({cached:s,stale:l,fetchedAt:a}){const{t:r}=x("skills"),i=c.useMemo(()=>{try{const n=new Date(a);return Number.isNaN(n.getTime())?"":n.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return""}},[a]);return e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-xs text-text-muted",children:[e.jsx("span",{children:r("hot.updatedAt",{at:i})}),s&&e.jsx(m,{variant:"outline",children:r("hot.cached")}),l&&e.jsx(m,{variant:"warning",children:r("hot.stale")})]})}function O({skill:s,onOpenChange:l}){const{t:a}=x("skills"),r=c.useMemo(()=>s?[{key:"cliInstall",cmd:`skillhub install ${s.slug}`},{key:"cliSearch",cmd:`skillhub search ${s.slug}`},{key:"imAsk",cmd:`请用 skillhub 安装 ${s.slug} 技能`}]:[],[s]);return e.jsx(k,{open:s!=null,onOpenChange:l,children:e.jsxs(L,{className:"sm:max-w-2xl",children:[e.jsxs(D,{children:[e.jsx(F,{children:s?.name||s?.slug||""}),s?.description&&e.jsx(M,{children:s.description})]}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("dl",{className:"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs",children:[e.jsx("dt",{className:"text-text-dim",children:"slug"}),e.jsx("dd",{className:"font-mono",children:s.slug}),s.version&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:a("hot.detail.version")}),e.jsxs("dd",{className:"font-mono",children:["v",s.version]})]}),s.category&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:a("hot.detail.category")}),e.jsx("dd",{children:s.category})]}),e.jsx("dt",{className:"text-text-dim",children:a("hot.detail.stars")}),e.jsx("dd",{className:"tabular-nums",children:o(s.stars)}),e.jsx("dt",{className:"text-text-dim",children:a("hot.detail.installs")}),e.jsx("dd",{className:"tabular-nums",children:o(s.installs)}),s.downloads!=null&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:a("hot.detail.downloads")}),e.jsx("dd",{className:"tabular-nums",children:o(s.downloads)})]})]}),e.jsxs("div",{className:"mt-4 border-t border-border pt-3",children:[e.jsx("h3",{className:"text-sm font-medium",children:a("hot.detail.howInstall")}),e.jsx("ul",{className:"mt-2 flex flex-col gap-2",children:r.map(i=>e.jsxs("li",{className:"flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-2",children:[e.jsx("span",{className:"text-xs text-text-dim sm:w-40 sm:shrink-0",children:a(`hot.detail.label.${i.key}`)}),e.jsx("code",{className:"flex-1 rounded bg-surface-2 px-2 py-1 font-mono text-xs break-all",children:i.cmd}),e.jsx(Q,{text:i.cmd})]},i.key))}),e.jsx("p",{className:"mt-3 text-[11px] text-text-muted",children:a("hot.detail.hint")})]})]})]})})}function Q({text:s}){const{t:l}=x("skills"),[a,r]=c.useState(!1);async function i(){try{await navigator.clipboard.writeText(s),r(!0),N.success(l("hot.detail.copied")),setTimeout(()=>r(!1),1500)}catch(n){N.error(String(n?.message??n))}}return e.jsxs(u,{type:"button",variant:"secondary",size:"sm",onClick:()=>void i(),className:"shrink-0",children:[e.jsx(B,{className:"h-3 w-3"}),l(a?"hot.detail.copied":"hot.detail.copy")]})}function o(s){return s==null||!Number.isFinite(s)?"—":s.toLocaleString()}export{ee as default};
17
+ //# sourceMappingURL=hot-Bh5Nrc7i.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-Bh5Nrc7i.js","sources":["../../node_modules/lucide-react/dist/esm/icons/copy.js","../../node_modules/lucide-react/dist/esm/icons/flame.js","../../node_modules/lucide-react/dist/esm/icons/star.js","../../src/routes/skills/hot.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Copy = createLucideIcon(\"Copy\", [\n [\"rect\", { width: \"14\", height: \"14\", x: \"8\", y: \"8\", rx: \"2\", ry: \"2\", key: \"17jyea\" }],\n [\"path\", { d: \"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\", key: \"zix9uf\" }]\n]);\n\nexport { Copy as default };\n//# sourceMappingURL=copy.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Flame = createLucideIcon(\"Flame\", [\n [\n \"path\",\n {\n d: \"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\",\n key: \"96xj49\"\n }\n ]\n]);\n\nexport { Flame as default };\n//# sourceMappingURL=flame.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Star = createLucideIcon(\"Star\", [\n [\n \"path\",\n {\n d: \"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z\",\n key: \"r04s7s\"\n }\n ]\n]);\n\nexport { Star as default };\n//# sourceMappingURL=star.js.map\n","/**\n * /skills/hot — read-only browse of skillhub.cn's hot list.\n *\n * Backed by /api/skills/remote/hot (server proxies upstream with a\n * 5-min cache + stale-while-error). agim never auto-runs an install:\n * the detail dialog surfaces three copy-pasteable commands and the\n * operator decides where to execute.\n *\n * Restored from v1's tasks.html in 2026-05; the v2 SPA had the\n * installed-skills view but had dropped the hot list during the\n * memory-tab port.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n Copy,\n Download,\n ExternalLink,\n Flame,\n Loader2,\n RefreshCcw,\n Search,\n Star,\n} from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport { Input } from '@/components/ui/input'\nimport { useSkillsRemoteHot } from '@/hooks/use-skills'\nimport type { RemoteSkill } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function SkillsHotRoute(): JSX.Element {\n const { t } = useTranslation(['skills', 'common'])\n const { data, isLoading, isFetching, refetch } = useSkillsRemoteHot()\n\n const skills: RemoteSkill[] = data?.skills ?? []\n const [q, setQ] = useState('')\n const [openSlug, setOpenSlug] = useState<string | null>(null)\n\n const filtered = useMemo(() => {\n const needle = q.trim().toLowerCase()\n if (!needle) return skills\n return skills.filter((s) =>\n s.slug.toLowerCase().includes(needle)\n || (s.name || '').toLowerCase().includes(needle)\n || (s.description || '').toLowerCase().includes(needle)\n || (s.category || '').toLowerCase().includes(needle)\n )\n }, [skills, q])\n\n const openSkill = openSlug ? skills.find((s) => s.slug === openSlug) ?? null : null\n\n return (\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold inline-flex items-center gap-2\">\n <Flame className=\"h-5 w-5 text-warning\" />\n {t('hot.title')}\n </h1>\n <div className=\"ml-auto flex items-center gap-2\">\n <Button\n asChild\n variant=\"ghost\"\n size=\"sm\"\n >\n <a\n href=\"https://skillhub.cn\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <ExternalLink className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">{t('hot.openHub')}</span>\n </a>\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n </div>\n <p className=\"text-sm text-text-dim\">{t('hot.subtitle')}</p>\n {data && (\n <CacheMeta\n cached={!!data.cached}\n stale={!!data.stale}\n fetchedAt={data.fetchedAt}\n />\n )}\n </header>\n\n {skills.length > 0 && (\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"relative flex-1 min-w-[180px] max-w-md\">\n <Search className=\"pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-text-muted\" />\n <Input\n value={q}\n onChange={(e) => setQ(e.target.value)}\n placeholder={t('actions.search', { ns: 'common' })}\n className=\"pl-7\"\n />\n </div>\n <Badge variant=\"secondary\" className=\"ml-auto\">\n {filtered.length} / {skills.length}\n </Badge>\n </div>\n )}\n\n {isLoading ? (\n <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : skills.length === 0 ? (\n <EmptyState icon={<Flame />} title={t('hot.empty')} />\n ) : (\n <ul className=\"flex flex-col gap-2\">\n {filtered.map((s) => (\n <li key={s.slug}>\n <button\n type=\"button\"\n onClick={() => setOpenSlug(s.slug)}\n className={cn(\n 'flex w-full flex-col gap-1 rounded-md border border-border bg-surface px-3 py-2 text-left',\n 'transition-colors hover:border-border-strong hover:bg-surface-hover',\n )}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{s.name || s.slug}</span>\n {s.version && (\n <span className=\"text-text-muted text-xs font-mono\">v{s.version}</span>\n )}\n {s.category && (\n <Badge variant=\"outline\">{s.category}</Badge>\n )}\n <div className=\"ml-auto flex items-center gap-3 text-xs text-text-dim tabular-nums\">\n <span className=\"inline-flex items-center gap-1\">\n <Star className=\"h-3 w-3\" />\n {formatNum(s.stars)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Download className=\"h-3 w-3\" />\n {formatNum(s.installs)}\n </span>\n </div>\n </div>\n <code className=\"text-text-muted text-xs font-mono\">{s.slug}</code>\n {s.description && (\n <p className=\"line-clamp-2 text-sm text-text-dim\">{s.description}</p>\n )}\n </button>\n </li>\n ))}\n </ul>\n )}\n\n <RemoteSkillDialog\n skill={openSkill}\n onOpenChange={(open) => { if (!open) setOpenSlug(null) }}\n />\n </div>\n )\n}\n\nfunction CacheMeta({\n cached,\n stale,\n fetchedAt,\n}: {\n cached: boolean\n stale: boolean\n fetchedAt: number\n}): JSX.Element | null {\n const { t } = useTranslation('skills')\n const when = useMemo(() => {\n try {\n const d = new Date(fetchedAt)\n if (Number.isNaN(d.getTime())) return ''\n return d.toLocaleString(undefined, {\n month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit',\n })\n } catch {\n return ''\n }\n }, [fetchedAt])\n return (\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span>{t('hot.updatedAt', { at: when })}</span>\n {cached && <Badge variant=\"outline\">{t('hot.cached')}</Badge>}\n {stale && <Badge variant=\"warning\">{t('hot.stale')}</Badge>}\n </div>\n )\n}\n\ninterface RemoteSkillDialogProps {\n skill: RemoteSkill | null\n onOpenChange: (open: boolean) => void\n}\n\nfunction RemoteSkillDialog({ skill, onOpenChange }: RemoteSkillDialogProps): JSX.Element {\n const { t } = useTranslation('skills')\n\n // Three copy-pasteable install paths. agim never executes them itself\n // — the operator chooses CLI or IM.\n const commands = useMemo(() => {\n if (!skill) return []\n return [\n { key: 'cliInstall', cmd: `skillhub install ${skill.slug}` },\n { key: 'cliSearch', cmd: `skillhub search ${skill.slug}` },\n { key: 'imAsk', cmd: `请用 skillhub 安装 ${skill.slug} 技能` },\n ]\n }, [skill])\n\n return (\n <Dialog open={skill != null} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>{skill?.name || skill?.slug || ''}</DialogTitle>\n {skill?.description && (\n <DialogDescription>{skill.description}</DialogDescription>\n )}\n </DialogHeader>\n\n {skill && (\n <>\n <dl className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs\">\n <dt className=\"text-text-dim\">slug</dt>\n <dd className=\"font-mono\">{skill.slug}</dd>\n {skill.version && (\n <>\n <dt className=\"text-text-dim\">{t('hot.detail.version')}</dt>\n <dd className=\"font-mono\">v{skill.version}</dd>\n </>\n )}\n {skill.category && (\n <>\n <dt className=\"text-text-dim\">{t('hot.detail.category')}</dt>\n <dd>{skill.category}</dd>\n </>\n )}\n <dt className=\"text-text-dim\">{t('hot.detail.stars')}</dt>\n <dd className=\"tabular-nums\">{formatNum(skill.stars)}</dd>\n <dt className=\"text-text-dim\">{t('hot.detail.installs')}</dt>\n <dd className=\"tabular-nums\">{formatNum(skill.installs)}</dd>\n {skill.downloads != null && (\n <>\n <dt className=\"text-text-dim\">{t('hot.detail.downloads')}</dt>\n <dd className=\"tabular-nums\">{formatNum(skill.downloads)}</dd>\n </>\n )}\n </dl>\n\n <div className=\"mt-4 border-t border-border pt-3\">\n <h3 className=\"text-sm font-medium\">{t('hot.detail.howInstall')}</h3>\n <ul className=\"mt-2 flex flex-col gap-2\">\n {commands.map((c) => (\n <li key={c.key} className=\"flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-2\">\n <span className=\"text-xs text-text-dim sm:w-40 sm:shrink-0\">\n {t(`hot.detail.label.${c.key}`)}\n </span>\n <code className=\"flex-1 rounded bg-surface-2 px-2 py-1 font-mono text-xs break-all\">\n {c.cmd}\n </code>\n <CopyButton text={c.cmd} />\n </li>\n ))}\n </ul>\n <p className=\"mt-3 text-[11px] text-text-muted\">{t('hot.detail.hint')}</p>\n </div>\n </>\n )}\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction CopyButton({ text }: { text: string }): JSX.Element {\n const { t } = useTranslation('skills')\n const [copied, setCopied] = useState(false)\n async function onClick(): Promise<void> {\n try {\n await navigator.clipboard.writeText(text)\n setCopied(true)\n toast.success(t('hot.detail.copied'))\n setTimeout(() => setCopied(false), 1500)\n } catch (err) {\n toast.error(String((err as Error)?.message ?? err))\n }\n }\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onClick()}\n className=\"shrink-0\"\n >\n <Copy className=\"h-3 w-3\" />\n {copied ? t('hot.detail.copied') : t('hot.detail.copy')}\n </Button>\n )\n}\n\nfunction formatNum(n: number | undefined): string {\n if (n == null || !Number.isFinite(n)) return '—'\n return n.toLocaleString()\n}\n"],"names":["Copy","createLucideIcon","Flame","Star","SkillsHotRoute","t","useTranslation","data","isLoading","isFetching","refetch","useSkillsRemoteHot","skills","q","setQ","useState","openSlug","setOpenSlug","filtered","useMemo","needle","s","openSkill","jsxs","jsx","Button","ExternalLink","Loader2","RefreshCcw","CacheMeta","Search","Input","e","Badge","EmptyState","cn","formatNum","Download","RemoteSkillDialog","open","cached","stale","fetchedAt","when","d","skill","onOpenChange","commands","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","Fragment","c","CopyButton","text","copied","setCopied","onClick","toast","err","n"],"mappings":"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","x_google_ignoreList":[0,1,2]}