agim-cli 1.4.8 → 1.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/memory-distiller.d.ts +10 -4
- package/dist/core/memory-distiller.d.ts.map +1 -1
- package/dist/core/memory-distiller.js +23 -9
- package/dist/core/memory-distiller.js.map +1 -1
- package/dist/web/public/assets/{a2a-Dnc-MHI-.js → a2a-CZxwvgvh.js} +2 -2
- package/dist/web/public/assets/{a2a-Dnc-MHI-.js.map → a2a-CZxwvgvh.js.map} +1 -1
- package/dist/web/public/assets/{activity-CKQwfSVC.js → activity-BnlFGipw.js} +2 -2
- package/dist/web/public/assets/{activity-CKQwfSVC.js.map → activity-BnlFGipw.js.map} +1 -1
- package/dist/web/public/assets/{admins-C7MdDEF0.js → admins-CH84Rw1d.js} +2 -2
- package/dist/web/public/assets/{admins-C7MdDEF0.js.map → admins-CH84Rw1d.js.map} +1 -1
- package/dist/web/public/assets/{agents-Be4IUCYz.js → agents-CFB4Uj6b.js} +2 -2
- package/dist/web/public/assets/{agents-Be4IUCYz.js.map → agents-CFB4Uj6b.js.map} +1 -1
- package/dist/web/public/assets/{approvals-KCd1BSvN.js → approvals-DnzjbDR6.js} +2 -2
- package/dist/web/public/assets/{approvals-KCd1BSvN.js.map → approvals-DnzjbDR6.js.map} +1 -1
- package/dist/web/public/assets/{arrow-down-NRUr_gzg.js → arrow-down-DxlRHrs8.js} +2 -2
- package/dist/web/public/assets/{arrow-down-NRUr_gzg.js.map → arrow-down-DxlRHrs8.js.map} +1 -1
- package/dist/web/public/assets/{arrow-up-4KyqPaSd.js → arrow-up-CAiQ2jy-.js} +2 -2
- package/dist/web/public/assets/{arrow-up-4KyqPaSd.js.map → arrow-up-CAiQ2jy-.js.map} +1 -1
- package/dist/web/public/assets/{asks-nKwuDJpD.js → asks-DyUQopay.js} +2 -2
- package/dist/web/public/assets/{asks-nKwuDJpD.js.map → asks-DyUQopay.js.map} +1 -1
- package/dist/web/public/assets/{audit-Bf4Yv1or.js → audit-BVHTMS82.js} +2 -2
- package/dist/web/public/assets/{audit-Bf4Yv1or.js.map → audit-BVHTMS82.js.map} +1 -1
- package/dist/web/public/assets/{bell--lSOhH-X.js → bell-D7iS7ctN.js} +2 -2
- package/dist/web/public/assets/{bell--lSOhH-X.js.map → bell-D7iS7ctN.js.map} +1 -1
- package/dist/web/public/assets/{bgjobs-Daya2BMh.js → bgjobs-BYCQhd_1.js} +2 -2
- package/dist/web/public/assets/{bgjobs-Daya2BMh.js.map → bgjobs-BYCQhd_1.js.map} +1 -1
- package/dist/web/public/assets/{brain-DF90t0yo.js → brain-CCF25Egu.js} +2 -2
- package/dist/web/public/assets/{brain-DF90t0yo.js.map → brain-CCF25Egu.js.map} +1 -1
- package/dist/web/public/assets/{briefcase-aRM_fCrr.js → briefcase-BMLMD8nM.js} +2 -2
- package/dist/web/public/assets/{briefcase-aRM_fCrr.js.map → briefcase-BMLMD8nM.js.map} +1 -1
- package/dist/web/public/assets/{chat-BhnNXkIJ.js → chat-DPkKy5y_.js} +2 -2
- package/dist/web/public/assets/{chat-BhnNXkIJ.js.map → chat-DPkKy5y_.js.map} +1 -1
- package/dist/web/public/assets/{chevron-left-DK-fhgiL.js → chevron-left-BDO3vw8G.js} +2 -2
- package/dist/web/public/assets/{chevron-left-DK-fhgiL.js.map → chevron-left-BDO3vw8G.js.map} +1 -1
- package/dist/web/public/assets/{chevron-right-BGhMfCcE.js → chevron-right-5ZUC2opg.js} +2 -2
- package/dist/web/public/assets/{chevron-right-BGhMfCcE.js.map → chevron-right-5ZUC2opg.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-BWBI4Qhk.js → circle-check-COhM5JsK.js} +2 -2
- package/dist/web/public/assets/{circle-check-BWBI4Qhk.js.map → circle-check-COhM5JsK.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-big-BppjBT7j.js → circle-check-big-DHAHqUlS.js} +2 -2
- package/dist/web/public/assets/{circle-check-big-BppjBT7j.js.map → circle-check-big-DHAHqUlS.js.map} +1 -1
- package/dist/web/public/assets/{circle-x-BZ3UUhcm.js → circle-x-7VaoZ7dY.js} +2 -2
- package/dist/web/public/assets/{circle-x-BZ3UUhcm.js.map → circle-x-7VaoZ7dY.js.map} +1 -1
- package/dist/web/public/assets/{clock-ChP0IxI4.js → clock-ZPVXnd6V.js} +2 -2
- package/dist/web/public/assets/{clock-ChP0IxI4.js.map → clock-ZPVXnd6V.js.map} +1 -1
- package/dist/web/public/assets/{confirm-dialog-DrP5Lfwx.js → confirm-dialog-CgpJL9oN.js} +2 -2
- package/dist/web/public/assets/{confirm-dialog-DrP5Lfwx.js.map → confirm-dialog-CgpJL9oN.js.map} +1 -1
- package/dist/web/public/assets/{copy-CR2c0pyG.js → copy-BaZl52tH.js} +2 -2
- package/dist/web/public/assets/{copy-CR2c0pyG.js.map → copy-BaZl52tH.js.map} +1 -1
- package/dist/web/public/assets/{data-table-DElP7TJm.js → data-table-BLYG79TX.js} +2 -2
- package/dist/web/public/assets/{data-table-DElP7TJm.js.map → data-table-BLYG79TX.js.map} +1 -1
- package/dist/web/public/assets/distill-D9p8O8Jf.js +7 -0
- package/dist/web/public/assets/distill-D9p8O8Jf.js.map +1 -0
- package/dist/web/public/assets/{download-DO4Kw3Fi.js → download-bzCepctg.js} +2 -2
- package/dist/web/public/assets/{download-DO4Kw3Fi.js.map → download-bzCepctg.js.map} +1 -1
- package/dist/web/public/assets/{email-Dkqwudqq.js → email-Douv-rZ5.js} +2 -2
- package/dist/web/public/assets/{email-Dkqwudqq.js.map → email-Douv-rZ5.js.map} +1 -1
- package/dist/web/public/assets/{empty-state-DGH3i5i2.js → empty-state-96qpPTNg.js} +2 -2
- package/dist/web/public/assets/{empty-state-DGH3i5i2.js.map → empty-state-96qpPTNg.js.map} +1 -1
- package/dist/web/public/assets/{external-link-B6aOBfUG.js → external-link-J4nIlCem.js} +2 -2
- package/dist/web/public/assets/{external-link-B6aOBfUG.js.map → external-link-J4nIlCem.js.map} +1 -1
- package/dist/web/public/assets/{eye-BMxHZFil.js → eye-BvsBLK42.js} +2 -2
- package/dist/web/public/assets/{eye-BMxHZFil.js.map → eye-BvsBLK42.js.map} +1 -1
- package/dist/web/public/assets/{facts-B9PlO3xo.js → facts-DMucDXYd.js} +2 -2
- package/dist/web/public/assets/{facts-B9PlO3xo.js.map → facts-DMucDXYd.js.map} +1 -1
- package/dist/web/public/assets/{goals-DX_RjDAM.js → goals-B9ETHgL0.js} +2 -2
- package/dist/web/public/assets/{goals-DX_RjDAM.js.map → goals-B9ETHgL0.js.map} +1 -1
- package/dist/web/public/assets/{health-BAcwcy02.js → health-DLzZ7KHc.js} +2 -2
- package/dist/web/public/assets/{health-BAcwcy02.js.map → health-DLzZ7KHc.js.map} +1 -1
- package/dist/web/public/assets/{heart-pulse-Cl-tJTJY.js → heart-pulse-CVEeD2sz.js} +2 -2
- package/dist/web/public/assets/{heart-pulse-Cl-tJTJY.js.map → heart-pulse-CVEeD2sz.js.map} +1 -1
- package/dist/web/public/assets/{heartbeat-BF_rPcKb.js → heartbeat-B1BEBHge.js} +2 -2
- package/dist/web/public/assets/{heartbeat-BF_rPcKb.js.map → heartbeat-B1BEBHge.js.map} +1 -1
- package/dist/web/public/assets/{hot-XOCA1xAY.js → hot-Yc7iad0D.js} +2 -2
- package/dist/web/public/assets/{hot-XOCA1xAY.js.map → hot-Yc7iad0D.js.map} +1 -1
- package/dist/web/public/assets/{index-CzlhMSb2.js → index-DY2Zglku.js} +3 -3
- package/dist/web/public/assets/{index-CzlhMSb2.js.map → index-DY2Zglku.js.map} +1 -1
- package/dist/web/public/assets/{injection-ecvQ-6Ya.js → injection-Ca-9gbo0.js} +2 -2
- package/dist/web/public/assets/{injection-ecvQ-6Ya.js.map → injection-Ca-9gbo0.js.map} +1 -1
- package/dist/web/public/assets/{installed-D8VzYRBe.js → installed-Zz34apdi.js} +2 -2
- package/dist/web/public/assets/{installed-D8VzYRBe.js.map → installed-Zz34apdi.js.map} +1 -1
- package/dist/web/public/assets/{jobs-CpuY6G8D.js → jobs-CtVRpl0r.js} +2 -2
- package/dist/web/public/assets/{jobs-CpuY6G8D.js.map → jobs-CtVRpl0r.js.map} +1 -1
- package/dist/web/public/assets/{layout-DdbuRh8P.js → layout-BTCLgkgN.js} +2 -2
- package/dist/web/public/assets/{layout-DdbuRh8P.js.map → layout-BTCLgkgN.js.map} +1 -1
- package/dist/web/public/assets/{layout-Pxfmfh_2.js → layout-CtDc2i7w.js} +2 -2
- package/dist/web/public/assets/{layout-Pxfmfh_2.js.map → layout-CtDc2i7w.js.map} +1 -1
- package/dist/web/public/assets/{layout--XsfZzpI.js → layout-Cxch59sI.js} +2 -2
- package/dist/web/public/assets/{layout--XsfZzpI.js.map → layout-Cxch59sI.js.map} +1 -1
- package/dist/web/public/assets/{layout-BROGs5wv.js → layout-DnANw2Q2.js} +2 -2
- package/dist/web/public/assets/{layout-BROGs5wv.js.map → layout-DnANw2Q2.js.map} +1 -1
- package/dist/web/public/assets/{layout-BezPqRLV.js → layout-yIZG87fq.js} +2 -2
- package/dist/web/public/assets/{layout-BezPqRLV.js.map → layout-yIZG87fq.js.map} +1 -1
- package/dist/web/public/assets/{llm-IXFv_MMM.js → llm-CMMa85Ig.js} +2 -2
- package/dist/web/public/assets/{llm-IXFv_MMM.js.map → llm-CMMa85Ig.js.map} +1 -1
- package/dist/web/public/assets/{loader-circle-AgQ2XO06.js → loader-circle-D8L1w9c1.js} +2 -2
- package/dist/web/public/assets/{loader-circle-AgQ2XO06.js.map → loader-circle-D8L1w9c1.js.map} +1 -1
- package/dist/web/public/assets/{map-pin-C6sGB9SZ.js → map-pin--GMunU9n.js} +2 -2
- package/dist/web/public/assets/{map-pin-C6sGB9SZ.js.map → map-pin--GMunU9n.js.map} +1 -1
- package/dist/web/public/assets/{mcp-jqsjSumH.js → mcp-DhnXhO9B.js} +2 -2
- package/dist/web/public/assets/{mcp-jqsjSumH.js.map → mcp-DhnXhO9B.js.map} +1 -1
- package/dist/web/public/assets/{memos-C38YN2Rf.js → memos-DQZc7llR.js} +2 -2
- package/dist/web/public/assets/{memos-C38YN2Rf.js.map → memos-DQZc7llR.js.map} +1 -1
- package/dist/web/public/assets/{messengers-l74nqYZF.js → messengers-DwSmauLs.js} +2 -2
- package/dist/web/public/assets/{messengers-l74nqYZF.js.map → messengers-DwSmauLs.js.map} +1 -1
- package/dist/web/public/assets/{mobile-CHhYS0Tw.js → mobile-CUZ359rQ.js} +2 -2
- package/dist/web/public/assets/{mobile-CHhYS0Tw.js.map → mobile-CUZ359rQ.js.map} +1 -1
- package/dist/web/public/assets/{network-CIrcKaZn.js → network-BBdRdrH_.js} +2 -2
- package/dist/web/public/assets/{network-CIrcKaZn.js.map → network-BBdRdrH_.js.map} +1 -1
- package/dist/web/public/assets/{outbox-O1lzMZPk.js → outbox-CmN0U35l.js} +2 -2
- package/dist/web/public/assets/{outbox-O1lzMZPk.js.map → outbox-CmN0U35l.js.map} +1 -1
- package/dist/web/public/assets/{pagination-CXBUSf6r.js → pagination-DcsgDXXM.js} +2 -2
- package/dist/web/public/assets/{pagination-CXBUSf6r.js.map → pagination-DcsgDXXM.js.map} +1 -1
- package/dist/web/public/assets/{persona-CI4tkm_n.js → persona-ig2ac4mY.js} +2 -2
- package/dist/web/public/assets/{persona-CI4tkm_n.js.map → persona-ig2ac4mY.js.map} +1 -1
- package/dist/web/public/assets/{plans-D8RZ9aXq.js → plans-Bzbk8eUr.js} +2 -2
- package/dist/web/public/assets/{plans-D8RZ9aXq.js.map → plans-Bzbk8eUr.js.map} +1 -1
- package/dist/web/public/assets/{play-DQ3P_XWl.js → play-Di1jqulh.js} +2 -2
- package/dist/web/public/assets/{play-DQ3P_XWl.js.map → play-Di1jqulh.js.map} +1 -1
- package/dist/web/public/assets/{plus-Zai0UXKL.js → plus-C2zyJ1mF.js} +2 -2
- package/dist/web/public/assets/{plus-Zai0UXKL.js.map → plus-C2zyJ1mF.js.map} +1 -1
- package/dist/web/public/assets/{policy-BodrOHl5.js → policy-yD1c2Pcn.js} +2 -2
- package/dist/web/public/assets/{policy-BodrOHl5.js.map → policy-yD1c2Pcn.js.map} +1 -1
- package/dist/web/public/assets/{qr-code-zcjicWRK.js → qr-code-C-ACsKv_.js} +2 -2
- package/dist/web/public/assets/{qr-code-zcjicWRK.js.map → qr-code-C-ACsKv_.js.map} +1 -1
- package/dist/web/public/assets/{refresh-ccw-CM-Cjwud.js → refresh-ccw-7HdadG5V.js} +2 -2
- package/dist/web/public/assets/{refresh-ccw-CM-Cjwud.js.map → refresh-ccw-7HdadG5V.js.map} +1 -1
- package/dist/web/public/assets/{reminders-BR6noe6I.js → reminders-BDeiVYTC.js} +2 -2
- package/dist/web/public/assets/{reminders-BR6noe6I.js.map → reminders-BDeiVYTC.js.map} +1 -1
- package/dist/web/public/assets/{save-xBMgs6Bd.js → save-BYFmz9gD.js} +2 -2
- package/dist/web/public/assets/{save-xBMgs6Bd.js.map → save-BYFmz9gD.js.map} +1 -1
- package/dist/web/public/assets/{schedules-Brp1Fomd.js → schedules-BddzBrxI.js} +2 -2
- package/dist/web/public/assets/{schedules-Brp1Fomd.js.map → schedules-BddzBrxI.js.map} +1 -1
- package/dist/web/public/assets/{search-nk8nyLFI.js → search-B8PlbtLg.js} +2 -2
- package/dist/web/public/assets/{search-nk8nyLFI.js.map → search-B8PlbtLg.js.map} +1 -1
- package/dist/web/public/assets/{search-CIc8bWW1.js → search-DxkvV-8p.js} +2 -2
- package/dist/web/public/assets/{search-CIc8bWW1.js.map → search-DxkvV-8p.js.map} +1 -1
- package/dist/web/public/assets/{security-DWxWfpuX.js → security-DpOGp3nS.js} +2 -2
- package/dist/web/public/assets/{security-DWxWfpuX.js.map → security-DpOGp3nS.js.map} +1 -1
- package/dist/web/public/assets/{service-Bo9rkfQX.js → service-BsY8CaNG.js} +2 -2
- package/dist/web/public/assets/{service-Bo9rkfQX.js.map → service-BsY8CaNG.js.map} +1 -1
- package/dist/web/public/assets/{shield-alert-bg0_ILjD.js → shield-alert-DXGk9Rak.js} +2 -2
- package/dist/web/public/assets/{shield-alert-bg0_ILjD.js.map → shield-alert-DXGk9Rak.js.map} +1 -1
- package/dist/web/public/assets/{status-badge-Bw4TDDCb.js → status-badge-D0nbFSom.js} +2 -2
- package/dist/web/public/assets/{status-badge-Bw4TDDCb.js.map → status-badge-D0nbFSom.js.map} +1 -1
- package/dist/web/public/assets/{subtasks-D58R9C_Z.js → subtasks-DFiAXvDp.js} +2 -2
- package/dist/web/public/assets/{subtasks-D58R9C_Z.js.map → subtasks-DFiAXvDp.js.map} +1 -1
- package/dist/web/public/assets/{table-D4S5FCk_.js → table-_WsrnZ_T.js} +2 -2
- package/dist/web/public/assets/{table-D4S5FCk_.js.map → table-_WsrnZ_T.js.map} +1 -1
- package/dist/web/public/assets/{topn-DDxDNIZf.js → topn-C2PHE4tX.js} +2 -2
- package/dist/web/public/assets/{topn-DDxDNIZf.js.map → topn-C2PHE4tX.js.map} +1 -1
- package/dist/web/public/assets/{trash-2-CB7X8tay.js → trash-2-1Km439fW.js} +2 -2
- package/dist/web/public/assets/{trash-2-CB7X8tay.js.map → trash-2-1Km439fW.js.map} +1 -1
- package/dist/web/public/assets/{use-agim-skills-BV2IJPlc.js → use-agim-skills-COSqZrdH.js} +2 -2
- package/dist/web/public/assets/{use-agim-skills-BV2IJPlc.js.map → use-agim-skills-COSqZrdH.js.map} +1 -1
- package/dist/web/public/assets/{use-background-tasks-Dk9S55ip.js → use-background-tasks-wCL7jn68.js} +2 -2
- package/dist/web/public/assets/{use-background-tasks-Dk9S55ip.js.map → use-background-tasks-wCL7jn68.js.map} +1 -1
- package/dist/web/public/assets/{use-memory-DncmZXGJ.js → use-memory-B9oRyQC8.js} +2 -2
- package/dist/web/public/assets/{use-memory-DncmZXGJ.js.map → use-memory-B9oRyQC8.js.map} +1 -1
- package/dist/web/public/assets/{use-observability-CcDO7PdN.js → use-observability-DGkN80qa.js} +2 -2
- package/dist/web/public/assets/{use-observability-CcDO7PdN.js.map → use-observability-DGkN80qa.js.map} +1 -1
- package/dist/web/public/assets/{use-settings-DZhfMpNR.js → use-settings-Co-iafXD.js} +2 -2
- package/dist/web/public/assets/{use-settings-DZhfMpNR.js.map → use-settings-Co-iafXD.js.map} +1 -1
- package/dist/web/public/assets/{use-workspace-ywkrT7Aj.js → use-workspace-BDXP5XO1.js} +2 -2
- package/dist/web/public/assets/{use-workspace-ywkrT7Aj.js.map → use-workspace-BDXP5XO1.js.map} +1 -1
- package/dist/web/public/assets/{vector-B3h4iyfn.js → vector-BfIEx8xX.js} +2 -2
- package/dist/web/public/assets/{vector-B3h4iyfn.js.map → vector-BfIEx8xX.js.map} +1 -1
- package/dist/web/public/assets/{viewer-Bz3wL-99.js → viewer-BPMZkT05.js} +2 -2
- package/dist/web/public/assets/{viewer-Bz3wL-99.js.map → viewer-BPMZkT05.js.map} +1 -1
- package/dist/web/public/assets/{workspace-BvrXvliN.js → workspace-BbCchiHE.js} +2 -2
- package/dist/web/public/assets/{workspace-BvrXvliN.js.map → workspace-BbCchiHE.js.map} +1 -1
- package/dist/web/public/assets/{workspaces-0H6Dzz6N.js → workspaces-5HE7pjdA.js} +2 -2
- package/dist/web/public/assets/{workspaces-0H6Dzz6N.js.map → workspaces-5HE7pjdA.js.map} +1 -1
- package/dist/web/public/index.html +1 -1
- package/dist/web/server.js +3 -1
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/assets/distill-DxSq7g_8.js +0 -7
- package/dist/web/public/assets/distill-DxSq7g_8.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-BodrOHl5.js","sources":["../../src/routes/settings/policy.tsx"],"sourcesContent":["/**\n * /settings/policy — dedicated UI for the highest-frequency env\n * toggles. Each is a one-click switch (or 2-3 option select) that\n * persists to `~/.agim/.env` via PUT /api/env immediately.\n *\n * Surfaced here:\n * * IMHUB_DANGEROUSLY_SKIP_PERMISSIONS (safety)\n * * IMHUB_TIMEOUT_DEFAULT (approval bus default)\n * * IMHUB_MEMORY_ENABLED (memory subsystem)\n * * IMHUB_VIEWER_ENABLED (long-message viewer)\n * * IMHUB_A2A_NOTIFY_MODE (cross-agent notify verbosity)\n *\n * All five are still editable in /settings/env. This page exists\n * because:\n * 1. They're the ones operators flip most often\n * 2. The env editor's table doesn't communicate \"this is a\n * boolean / 2-state / 3-state value\" — operators have to\n * know the literal strings \"1\"/\"0\"/\"allow\"/\"deny\"/etc.\n * 3. Safety toggle deserves a visible warning + danger color\n */\n\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { AlertTriangle, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\n/** Treat the env value as a boolean — '1' / 'true' / 'yes' / 'on'\n * → true; everything else → false. Matches the backend's casual\n * truthy parsing in core/* modules. */\nfunction envBool(v: string | undefined): boolean {\n if (v == null) return false\n const t = v.toLowerCase()\n return t === '1' || t === 'true' || t === 'yes' || t === 'on'\n}\n\nexport default function SettingsPolicyRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv()\n const updateEnv = useUpdateEnv()\n\n async function setValue(key: string, value: string | null): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { [key]: value } })\n toast.success(t('policy.savingToast'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const env = envQuery.data?.env ?? {}\n const skipPerms = envBool(env.IMHUB_DANGEROUSLY_SKIP_PERMISSIONS)\n const memEnabled = envBool(env.IMHUB_MEMORY_ENABLED)\n const viewerEnabled = envBool(env.IMHUB_VIEWER_ENABLED)\n const timeoutAllow = (env.IMHUB_TIMEOUT_DEFAULT ?? '').toLowerCase() === 'allow'\n const a2aMode = (env.IMHUB_A2A_NOTIFY_MODE ?? 'essential').toLowerCase()\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('policy.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('policy.subtitle')}</p>\n </header>\n\n {envQuery.isLoading ? (\n <div className=\"h-96 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <div className=\"flex flex-col gap-3\">\n <SafetyCard\n value={skipPerms}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_DANGEROUSLY_SKIP_PERMISSIONS', v ? '1' : '0')}\n />\n <ApprovalCard\n allowOnTimeout={timeoutAllow}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_TIMEOUT_DEFAULT', v)}\n />\n <ToggleCard\n sectionKey=\"memory\"\n value={memEnabled}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_MEMORY_ENABLED', v ? '1' : '0')}\n />\n <ToggleCard\n sectionKey=\"viewer\"\n value={viewerEnabled}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_VIEWER_ENABLED', v ? '1' : '0')}\n />\n <A2ANotifyCard\n value={a2aMode}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_A2A_NOTIFY_MODE', v)}\n />\n </div>\n )}\n </div>\n )\n}\n\n/* ─────────────── Cards ─────────────── */\n\ninterface SafetyCardProps {\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\nfunction SafetyCard({ value, disabled, onChange }: SafetyCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section\n className={cn(\n 'rounded-md border p-4',\n value ? 'border-danger/60 bg-danger-bg/30' : 'border-border bg-surface',\n )}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{t('policy.safety.title')}</span>\n {value && (\n <Badge variant=\"danger\">\n <AlertTriangle className=\"h-3 w-3\" />\n ON\n </Badge>\n )}\n </div>\n <p className=\"mt-1 text-sm text-text-dim\">{t('policy.safety.description')}</p>\n {!value && (\n <p className=\"mt-1 text-xs text-text-muted\">{t('policy.safety.tagWarn')}</p>\n )}\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.safety.envKey')}\n </code>\n </div>\n <Toggle value={value} disabled={disabled} onChange={onChange} />\n </div>\n </section>\n )\n}\n\ninterface ApprovalCardProps {\n allowOnTimeout: boolean\n disabled: boolean\n onChange: (value: 'allow' | 'deny') => void\n}\n\nfunction ApprovalCard({ allowOnTimeout, disabled, onChange }: ApprovalCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-medium text-text\">{t('policy.approval.title')}</span>\n <p className=\"text-sm text-text-dim\">{t('policy.approval.description')}</p>\n <code className=\"self-start rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.approval.envKey')}\n </code>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant={!allowOnTimeout ? 'default' : 'outline'}\n size=\"sm\"\n disabled={disabled}\n onClick={() => onChange('deny')}\n >\n {t('policy.approval.valDeny')}\n </Button>\n <Button\n type=\"button\"\n variant={allowOnTimeout ? 'default' : 'outline'}\n size=\"sm\"\n disabled={disabled}\n onClick={() => onChange('allow')}\n >\n {t('policy.approval.valAllow')}\n </Button>\n </div>\n </div>\n </section>\n )\n}\n\ninterface ToggleCardProps {\n sectionKey: 'memory' | 'viewer'\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\nfunction ToggleCard({ sectionKey, value, disabled, onChange }: ToggleCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{t(`policy.${sectionKey}.title`)}</span>\n <Badge variant={value ? 'success' : 'outline'}>{value ? 'ON' : 'OFF'}</Badge>\n </div>\n <p className=\"mt-1 text-sm text-text-dim\">{t(`policy.${sectionKey}.description`)}</p>\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t(`policy.${sectionKey}.envKey`)}\n </code>\n </div>\n <Toggle value={value} disabled={disabled} onChange={onChange} />\n </div>\n </section>\n )\n}\n\ninterface A2ANotifyCardProps {\n value: string\n disabled: boolean\n onChange: (v: string) => void\n}\n\nfunction A2ANotifyCard({ value, disabled, onChange }: A2ANotifyCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <span className=\"font-medium text-text\">{t('policy.a2aNotify.title')}</span>\n <p className=\"mt-1 text-sm text-text-dim\">{t('policy.a2aNotify.description')}</p>\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.a2aNotify.envKey')}\n </code>\n </div>\n <Select value={value} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"essential\">{t('policy.a2aNotify.valEssential')}</SelectItem>\n <SelectItem value=\"verbose\">{t('policy.a2aNotify.valVerbose')}</SelectItem>\n <SelectItem value=\"off\">{t('policy.a2aNotify.valOff')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </section>\n )\n}\n\ninterface ToggleProps {\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\n/** Thumb-style switch styled to the agim accent. Tailwind-only,\n * no Radix dep — keeps the click feel snappy. */\nfunction Toggle({ value, disabled, onChange }: ToggleProps): JSX.Element {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={value}\n disabled={disabled}\n onClick={() => onChange(!value)}\n className={cn(\n 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n value ? 'bg-accent' : 'bg-surface-2 border border-border',\n )}\n >\n <span\n className={cn(\n 'inline-block h-5 w-5 rounded-full bg-white shadow transition-transform',\n value ? 'translate-x-5' : 'translate-x-0.5',\n )}\n />\n </button>\n )\n}\n"],"names":["envBool","v","t","SettingsPolicyRoute","useTranslation","envQuery","useEnv","updateEnv","useUpdateEnv","setValue","key","value","toast","err","describeError","env","skipPerms","memEnabled","viewerEnabled","timeoutAllow","a2aMode","jsxs","jsx","Button","Loader2","RefreshCcw","SafetyCard","ApprovalCard","ToggleCard","A2ANotifyCard","disabled","onChange","cn","Badge","AlertTriangle","Toggle","allowOnTimeout","sectionKey","Select","SelectTrigger","SelectValue","SelectContent","SelectItem"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policy-yD1c2Pcn.js","sources":["../../src/routes/settings/policy.tsx"],"sourcesContent":["/**\n * /settings/policy — dedicated UI for the highest-frequency env\n * toggles. Each is a one-click switch (or 2-3 option select) that\n * persists to `~/.agim/.env` via PUT /api/env immediately.\n *\n * Surfaced here:\n * * IMHUB_DANGEROUSLY_SKIP_PERMISSIONS (safety)\n * * IMHUB_TIMEOUT_DEFAULT (approval bus default)\n * * IMHUB_MEMORY_ENABLED (memory subsystem)\n * * IMHUB_VIEWER_ENABLED (long-message viewer)\n * * IMHUB_A2A_NOTIFY_MODE (cross-agent notify verbosity)\n *\n * All five are still editable in /settings/env. This page exists\n * because:\n * 1. They're the ones operators flip most often\n * 2. The env editor's table doesn't communicate \"this is a\n * boolean / 2-state / 3-state value\" — operators have to\n * know the literal strings \"1\"/\"0\"/\"allow\"/\"deny\"/etc.\n * 3. Safety toggle deserves a visible warning + danger color\n */\n\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { AlertTriangle, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\n/** Treat the env value as a boolean — '1' / 'true' / 'yes' / 'on'\n * → true; everything else → false. Matches the backend's casual\n * truthy parsing in core/* modules. */\nfunction envBool(v: string | undefined): boolean {\n if (v == null) return false\n const t = v.toLowerCase()\n return t === '1' || t === 'true' || t === 'yes' || t === 'on'\n}\n\nexport default function SettingsPolicyRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv()\n const updateEnv = useUpdateEnv()\n\n async function setValue(key: string, value: string | null): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { [key]: value } })\n toast.success(t('policy.savingToast'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const env = envQuery.data?.env ?? {}\n const skipPerms = envBool(env.IMHUB_DANGEROUSLY_SKIP_PERMISSIONS)\n const memEnabled = envBool(env.IMHUB_MEMORY_ENABLED)\n const viewerEnabled = envBool(env.IMHUB_VIEWER_ENABLED)\n const timeoutAllow = (env.IMHUB_TIMEOUT_DEFAULT ?? '').toLowerCase() === 'allow'\n const a2aMode = (env.IMHUB_A2A_NOTIFY_MODE ?? 'essential').toLowerCase()\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('policy.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('policy.subtitle')}</p>\n </header>\n\n {envQuery.isLoading ? (\n <div className=\"h-96 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <div className=\"flex flex-col gap-3\">\n <SafetyCard\n value={skipPerms}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_DANGEROUSLY_SKIP_PERMISSIONS', v ? '1' : '0')}\n />\n <ApprovalCard\n allowOnTimeout={timeoutAllow}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_TIMEOUT_DEFAULT', v)}\n />\n <ToggleCard\n sectionKey=\"memory\"\n value={memEnabled}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_MEMORY_ENABLED', v ? '1' : '0')}\n />\n <ToggleCard\n sectionKey=\"viewer\"\n value={viewerEnabled}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_VIEWER_ENABLED', v ? '1' : '0')}\n />\n <A2ANotifyCard\n value={a2aMode}\n disabled={updateEnv.isPending}\n onChange={(v) => void setValue('IMHUB_A2A_NOTIFY_MODE', v)}\n />\n </div>\n )}\n </div>\n )\n}\n\n/* ─────────────── Cards ─────────────── */\n\ninterface SafetyCardProps {\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\nfunction SafetyCard({ value, disabled, onChange }: SafetyCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section\n className={cn(\n 'rounded-md border p-4',\n value ? 'border-danger/60 bg-danger-bg/30' : 'border-border bg-surface',\n )}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{t('policy.safety.title')}</span>\n {value && (\n <Badge variant=\"danger\">\n <AlertTriangle className=\"h-3 w-3\" />\n ON\n </Badge>\n )}\n </div>\n <p className=\"mt-1 text-sm text-text-dim\">{t('policy.safety.description')}</p>\n {!value && (\n <p className=\"mt-1 text-xs text-text-muted\">{t('policy.safety.tagWarn')}</p>\n )}\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.safety.envKey')}\n </code>\n </div>\n <Toggle value={value} disabled={disabled} onChange={onChange} />\n </div>\n </section>\n )\n}\n\ninterface ApprovalCardProps {\n allowOnTimeout: boolean\n disabled: boolean\n onChange: (value: 'allow' | 'deny') => void\n}\n\nfunction ApprovalCard({ allowOnTimeout, disabled, onChange }: ApprovalCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-medium text-text\">{t('policy.approval.title')}</span>\n <p className=\"text-sm text-text-dim\">{t('policy.approval.description')}</p>\n <code className=\"self-start rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.approval.envKey')}\n </code>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant={!allowOnTimeout ? 'default' : 'outline'}\n size=\"sm\"\n disabled={disabled}\n onClick={() => onChange('deny')}\n >\n {t('policy.approval.valDeny')}\n </Button>\n <Button\n type=\"button\"\n variant={allowOnTimeout ? 'default' : 'outline'}\n size=\"sm\"\n disabled={disabled}\n onClick={() => onChange('allow')}\n >\n {t('policy.approval.valAllow')}\n </Button>\n </div>\n </div>\n </section>\n )\n}\n\ninterface ToggleCardProps {\n sectionKey: 'memory' | 'viewer'\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\nfunction ToggleCard({ sectionKey, value, disabled, onChange }: ToggleCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{t(`policy.${sectionKey}.title`)}</span>\n <Badge variant={value ? 'success' : 'outline'}>{value ? 'ON' : 'OFF'}</Badge>\n </div>\n <p className=\"mt-1 text-sm text-text-dim\">{t(`policy.${sectionKey}.description`)}</p>\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t(`policy.${sectionKey}.envKey`)}\n </code>\n </div>\n <Toggle value={value} disabled={disabled} onChange={onChange} />\n </div>\n </section>\n )\n}\n\ninterface A2ANotifyCardProps {\n value: string\n disabled: boolean\n onChange: (v: string) => void\n}\n\nfunction A2ANotifyCard({ value, disabled, onChange }: A2ANotifyCardProps): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1\">\n <span className=\"font-medium text-text\">{t('policy.a2aNotify.title')}</span>\n <p className=\"mt-1 text-sm text-text-dim\">{t('policy.a2aNotify.description')}</p>\n <code className=\"mt-2 inline-block rounded bg-surface-2 px-1.5 py-0.5 text-xs text-text-muted\">\n {t('policy.a2aNotify.envKey')}\n </code>\n </div>\n <Select value={value} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"essential\">{t('policy.a2aNotify.valEssential')}</SelectItem>\n <SelectItem value=\"verbose\">{t('policy.a2aNotify.valVerbose')}</SelectItem>\n <SelectItem value=\"off\">{t('policy.a2aNotify.valOff')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </section>\n )\n}\n\ninterface ToggleProps {\n value: boolean\n disabled: boolean\n onChange: (v: boolean) => void\n}\n\n/** Thumb-style switch styled to the agim accent. Tailwind-only,\n * no Radix dep — keeps the click feel snappy. */\nfunction Toggle({ value, disabled, onChange }: ToggleProps): JSX.Element {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={value}\n disabled={disabled}\n onClick={() => onChange(!value)}\n className={cn(\n 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n value ? 'bg-accent' : 'bg-surface-2 border border-border',\n )}\n >\n <span\n className={cn(\n 'inline-block h-5 w-5 rounded-full bg-white shadow transition-transform',\n value ? 'translate-x-5' : 'translate-x-0.5',\n )}\n />\n </button>\n )\n}\n"],"names":["envBool","v","t","SettingsPolicyRoute","useTranslation","envQuery","useEnv","updateEnv","useUpdateEnv","setValue","key","value","toast","err","describeError","env","skipPerms","memEnabled","viewerEnabled","timeoutAllow","a2aMode","jsxs","jsx","Button","Loader2","RefreshCcw","SafetyCard","ApprovalCard","ToggleCard","A2ANotifyCard","disabled","onChange","cn","Badge","AlertTriangle","Toggle","allowOnTimeout","sectionKey","Select","SelectTrigger","SelectValue","SelectContent","SelectItem"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{o as t}from"./index-
|
|
1
|
+
import{o as t}from"./index-DY2Zglku.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const h=t("QrCode",[["rect",{width:"5",height:"5",x:"3",y:"3",rx:"1",key:"1tu5fj"}],["rect",{width:"5",height:"5",x:"16",y:"3",rx:"1",key:"1v8r4q"}],["rect",{width:"5",height:"5",x:"3",y:"16",rx:"1",key:"1x03jg"}],["path",{d:"M21 16h-3a2 2 0 0 0-2 2v3",key:"177gqh"}],["path",{d:"M21 21v.01",key:"ents32"}],["path",{d:"M12 7v3a2 2 0 0 1-2 2H7",key:"8crl2c"}],["path",{d:"M3 12h.01",key:"nlz23k"}],["path",{d:"M12 3h.01",key:"n36tog"}],["path",{d:"M12 16v.01",key:"133mhm"}],["path",{d:"M16 12h1",key:"1slzba"}],["path",{d:"M21 12v.01",key:"1lwtk9"}],["path",{d:"M12 21v-1",key:"1880an"}]]);export{h as Q};
|
|
7
|
-
//# sourceMappingURL=qr-code-
|
|
7
|
+
//# sourceMappingURL=qr-code-C-ACsKv_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-code-
|
|
1
|
+
{"version":3,"file":"qr-code-C-ACsKv_.js","sources":["../../node_modules/lucide-react/dist/esm/icons/qr-code.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 QrCode = createLucideIcon(\"QrCode\", [\n [\"rect\", { width: \"5\", height: \"5\", x: \"3\", y: \"3\", rx: \"1\", key: \"1tu5fj\" }],\n [\"rect\", { width: \"5\", height: \"5\", x: \"16\", y: \"3\", rx: \"1\", key: \"1v8r4q\" }],\n [\"rect\", { width: \"5\", height: \"5\", x: \"3\", y: \"16\", rx: \"1\", key: \"1x03jg\" }],\n [\"path\", { d: \"M21 16h-3a2 2 0 0 0-2 2v3\", key: \"177gqh\" }],\n [\"path\", { d: \"M21 21v.01\", key: \"ents32\" }],\n [\"path\", { d: \"M12 7v3a2 2 0 0 1-2 2H7\", key: \"8crl2c\" }],\n [\"path\", { d: \"M3 12h.01\", key: \"nlz23k\" }],\n [\"path\", { d: \"M12 3h.01\", key: \"n36tog\" }],\n [\"path\", { d: \"M12 16v.01\", key: \"133mhm\" }],\n [\"path\", { d: \"M16 12h1\", key: \"1slzba\" }],\n [\"path\", { d: \"M21 12v.01\", key: \"1lwtk9\" }],\n [\"path\", { d: \"M12 21v-1\", key: \"1880an\" }]\n]);\n\nexport { QrCode as default };\n//# sourceMappingURL=qr-code.js.map\n"],"names":["QrCode","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CAAC,OAAQ,CAAE,MAAO,IAAK,OAAQ,IAAK,EAAG,IAAK,EAAG,IAAK,GAAI,IAAK,IAAK,QAAQ,CAAE,EAC5E,CAAC,OAAQ,CAAE,MAAO,IAAK,OAAQ,IAAK,EAAG,KAAM,EAAG,IAAK,GAAI,IAAK,IAAK,QAAQ,CAAE,EAC7E,CAAC,OAAQ,CAAE,MAAO,IAAK,OAAQ,IAAK,EAAG,IAAK,EAAG,KAAM,GAAI,IAAK,IAAK,QAAQ,CAAE,EAC7E,CAAC,OAAQ,CAAE,EAAG,4BAA6B,IAAK,QAAQ,CAAE,EAC1D,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,EAC3C,CAAC,OAAQ,CAAE,EAAG,0BAA2B,IAAK,QAAQ,CAAE,EACxD,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,QAAQ,CAAE,EAC1C,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,QAAQ,CAAE,EAC1C,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,EAC3C,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,EACzC,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,EAC3C,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,QAAQ,CAAE,CAC5C,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{o as e}from"./index-
|
|
1
|
+
import{o as e}from"./index-DY2Zglku.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const a=e("RefreshCcw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]);export{a as R};
|
|
7
|
-
//# sourceMappingURL=refresh-ccw-
|
|
7
|
+
//# sourceMappingURL=refresh-ccw-7HdadG5V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-ccw-
|
|
1
|
+
{"version":3,"file":"refresh-ccw-7HdadG5V.js","sources":["../../node_modules/lucide-react/dist/esm/icons/refresh-ccw.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 RefreshCcw = createLucideIcon(\"RefreshCcw\", [\n [\"path\", { d: \"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\", key: \"14sxne\" }],\n [\"path\", { d: \"M3 3v5h5\", key: \"1xhq8a\" }],\n [\"path\", { d: \"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\", key: \"1hlbsb\" }],\n [\"path\", { d: \"M16 16h5v5\", key: \"ccwih5\" }]\n]);\n\nexport { RefreshCcw as default };\n//# sourceMappingURL=refresh-ccw.js.map\n"],"names":["RefreshCcw","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAaC,EAAiB,aAAc,CAChD,CAAC,OAAQ,CAAE,EAAG,qDAAsD,IAAK,QAAQ,CAAE,EACnF,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,EACzC,CAAC,OAAQ,CAAE,EAAG,sDAAuD,IAAK,QAAQ,CAAE,EACpF,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,CAC7C,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{o as B,b as K,d as y,a as w,e as v,u as D,f as M,j as e,l as E,B as d,T as L,S as q,g as A,h as F,i as V,k as _,m as h,n as b,D as Q,t as $,v as H,w as U,x as Z,y as G}from"./index-
|
|
1
|
+
import{o as B,b as K,d as y,a as w,e as v,u as D,f as M,j as e,l as E,B as d,T as L,S as q,g as A,h as F,i as V,k as _,m as h,n as b,D as Q,t as $,v as H,w as U,x as Z,y as G}from"./index-DY2Zglku.js";import{r as x}from"./react-DlP5eolq.js";import{D as J}from"./data-table-BLYG79TX.js";import{E as W}from"./empty-state-96qpPTNg.js";import{S as X}from"./status-badge-D0nbFSom.js";import{C as Y}from"./confirm-dialog-CgpJL9oN.js";import{C as ee}from"./clock-ZPVXnd6V.js";import{L as se}from"./loader-circle-D8L1w9c1.js";import{R as te}from"./refresh-ccw-7HdadG5V.js";import{B as ae}from"./bell-D7iS7ctN.js";import"./table-_WsrnZ_T.js";import"./arrow-up-CAiQ2jy-.js";import"./arrow-down-DxlRHrs8.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const ne=B("BellOff",[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0",key:"vwvbt9"}],["path",{d:"M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742",key:"178tsu"}],["path",{d:"m2 2 20 20",key:"1ooewy"}],["path",{d:"M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05",key:"1hqiys"}]]),C={all:["reminders"],list:s=>["reminders","list",s]};function ie(s){return K({queryKey:C.list(s),queryFn:()=>y.listReminders(s),refetchInterval:5e3,refetchIntervalInBackground:!1})}function re(){const s=w();return v({mutationFn:a=>y.cancelReminder(a),onSuccess:()=>s.invalidateQueries({queryKey:C.all})})}function le(){const s=w();return v({mutationFn:({id:a,body:i})=>y.snoozeReminder(a,i),onSuccess:()=>s.invalidateQueries({queryKey:C.all})})}const oe=["pending","firing","fired","cancelled","failed"],ce=[{duration:"5m",i18nKey:"snooze.preset5m"},{duration:"30m",i18nKey:"snooze.preset30m"},{duration:"1h",i18nKey:"snooze.preset1h"},{duration:"1d",i18nKey:"snooze.preset1d"}];function ve(){const{t:s}=D(["reminders","common"]),[a,i]=M(),r=a.get("status")??"pending",l=x.useMemo(()=>({status:r,limit:200}),[r]),{data:o,isLoading:z,isFetching:N,refetch:T}=ie(l),P=o?.reminders??[],m=re(),c=le(),[f,S]=x.useState(null),[p,g]=x.useState(null);function k(t){const n=new URLSearchParams(a);for(const[u,j]of Object.entries(t))j==null||j===""?n.delete(u):n.set(u,j);i(n,{replace:!0})}async function R(){if(f!=null)try{await m.mutateAsync(f),h.success(s("toast.cancelled"))}catch(t){const{message:n}=b(t,s);throw h.error(n),t}}async function O(t){if(p!=null)try{await c.mutateAsync({id:p,body:{duration:t}}),h.success(s("snooze.toastOk",{duration:t})),g(null)}catch(n){const{message:u}=b(n,s);h.error(u)}}const I=x.useMemo(()=>[{id:"id",header:s("col.id"),cell:t=>e.jsxs("span",{className:"tabular-nums text-text-dim",children:["#",t.id]}),headClassName:"w-16"},{id:"fireAt",header:s("col.fireAt"),cell:t=>e.jsx("span",{className:"font-medium tabular-nums",children:me(t.fire_at)}),headClassName:"w-40"},{id:"text",header:s("col.text"),cell:t=>e.jsx("span",{className:"line-clamp-2 text-text-dim",children:t.text}),asCardTitle:!0},{id:"status",header:s("col.status"),cell:t=>e.jsx(X,{status:t.status,children:s(`status.${t.status}`,{defaultValue:t.status})}),headClassName:"w-28"},{id:"platform",header:s("col.platform"),cell:t=>e.jsx("span",{className:"text-text-dim",children:t.platform}),headClassName:"w-24",hideOnMobile:!0},{id:"recurrence",header:s("col.recurrence"),cell:t=>t.recurrence_label?e.jsx(E,{variant:"info",children:t.recurrence_label}):e.jsx("span",{className:"text-text-muted",children:"—"}),headClassName:"w-36",hideOnMobile:!0},{id:"source",header:s("col.source"),cell:t=>e.jsx("span",{className:"text-text-dim",children:s(`source.${t.source}`,{defaultValue:t.source})}),headClassName:"w-24",hideOnMobile:!0},{id:"actions",header:"",cell:t=>t.status==="pending"?e.jsxs("div",{className:"flex justify-end gap-1",children:[e.jsxs(d,{variant:"ghost",size:"sm",onClick:n=>{n.stopPropagation(),g(t.id)},disabled:c.isPending||m.isPending,children:[e.jsx(ee,{className:"h-3 w-3"}),s("action.snooze")]}),e.jsxs(d,{variant:"outline",size:"sm",onClick:n=>{n.stopPropagation(),S(t.id)},disabled:m.isPending||c.isPending,children:[e.jsx(ne,{className:"h-3 w-3"}),s("action.cancel")]})]}):null,headClassName:"w-48"}],[s,m.isPending,c.isPending]);return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(L,{}),e.jsxs("main",{className:"mx-auto flex w-full max-w-7xl flex-1 flex-col gap-4 px-3 py-4 sm:px-4 pb-safe",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("pageTitle")}),e.jsxs(d,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>T(),disabled:N,"aria-label":s("actions.refresh",{ns:"common"}),children:[N?e.jsx(se,{className:"h-4 w-4 animate-spin"}):e.jsx(te,{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-center gap-2",children:[e.jsx("label",{className:"text-sm text-text-dim",htmlFor:"status-filter",children:s("filter.status")}),e.jsxs(q,{value:r,onValueChange:t=>k({status:t}),children:[e.jsx(A,{id:"status-filter",className:"w-[160px]",children:e.jsx(F,{})}),e.jsx(V,{children:oe.map(t=>e.jsx(_,{value:t,children:s(`status.${t}`,{defaultValue:t})},t))})]})]}),e.jsx(J,{columns:I,rows:P,getRowId:t=>String(t.id),loading:z,emptyState:e.jsx(W,{icon:e.jsx(ae,{}),title:s("empty.title"),description:s("empty.description")})}),e.jsx(Y,{open:f!=null,onOpenChange:t=>{t||S(null)},title:s("action.confirmCancel"),description:s("action.confirmCancelDesc"),intent:"danger",confirmLabel:s("action.cancel"),onConfirm:R}),e.jsx(de,{open:p!=null,busy:c.isPending,onOpenChange:t=>{t||g(null)},onPick:O})]})]})}function de({open:s,busy:a,onOpenChange:i,onPick:r}){const{t:l}=D(["reminders","common"]);return e.jsx(Q,{open:s,onOpenChange:a?()=>{}:i,children:e.jsxs($,{children:[e.jsxs(H,{children:[e.jsx(U,{children:l("snooze.title")}),e.jsx(Z,{children:l("snooze.description")})]}),e.jsx("div",{className:"grid grid-cols-2 gap-2",children:ce.map(o=>e.jsx(d,{type:"button",variant:"outline",disabled:a,onClick:()=>void r(o.duration),children:l(o.i18nKey)},o.duration))}),e.jsx(G,{children:e.jsx(d,{type:"button",variant:"secondary",disabled:a,onClick:()=>i(!1),children:l("actions.cancel",{ns:"common"})})})]})})}function me(s){try{const a=new Date(s);if(Number.isNaN(a.getTime()))return s;const i=new Date;return a.toDateString()===i.toDateString()?a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"}):a.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return s}}export{ve as default};
|
|
7
|
-
//# sourceMappingURL=reminders-
|
|
7
|
+
//# sourceMappingURL=reminders-BDeiVYTC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reminders-BR6noe6I.js","sources":["../../node_modules/lucide-react/dist/esm/icons/bell-off.js","../../src/hooks/use-reminders.ts","../../src/routes/reminders.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 BellOff = createLucideIcon(\"BellOff\", [\n [\"path\", { d: \"M10.268 21a2 2 0 0 0 3.464 0\", key: \"vwvbt9\" }],\n [\n \"path\",\n {\n d: \"M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742\",\n key: \"178tsu\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }],\n [\"path\", { d: \"M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05\", key: \"1hqiys\" }]\n]);\n\nexport { BellOff as default };\n//# sourceMappingURL=bell-off.js.map\n","/**\n * useReminders — react-query wrappers for the reminders domain.\n *\n * useReminders(query) — list pending (default) or by status\n * useCancelReminder() — POST /api/reminders/:id/cancel\n * useSnoozeReminder() — POST /api/reminders/:id/snooze\n *\n * Reminders aren't on the SSE event-bus today; the page combines a\n * 5s react-query poll with a manual Refresh button. When a snooze\n * mutation lands, both the original AND the new reminder change\n * (one cancelled, one pending) — invalidating the full 'reminders'\n * key catches both.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type {\n CancelReminderResponse,\n ListRemindersQuery,\n ListRemindersResponse,\n SnoozeReminderBody,\n SnoozeReminderResponse,\n} from '@/types/api'\n\nexport const remindersKeys = {\n all: ['reminders'] as const,\n list: (q: ListRemindersQuery) => ['reminders', 'list', q] as const,\n}\n\nexport function useReminders(query: ListRemindersQuery) {\n return useQuery<ListRemindersResponse>({\n queryKey: remindersKeys.list(query),\n queryFn: () => api.listReminders(query),\n refetchInterval: 5000,\n refetchIntervalInBackground: false,\n })\n}\n\nexport function useInvalidateReminders() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: remindersKeys.all })\n}\n\nexport function useCancelReminder() {\n const qc = useQueryClient()\n return useMutation<CancelReminderResponse, Error, number>({\n mutationFn: (id) => api.cancelReminder(id),\n onSuccess: () => qc.invalidateQueries({ queryKey: remindersKeys.all }),\n })\n}\n\nexport function useSnoozeReminder() {\n const qc = useQueryClient()\n return useMutation<\n SnoozeReminderResponse,\n Error,\n { id: number; body: SnoozeReminderBody }\n >({\n mutationFn: ({ id, body }) => api.snoozeReminder(id, body),\n onSuccess: () => qc.invalidateQueries({ queryKey: remindersKeys.all }),\n })\n}\n","/**\n * /reminders — queued-reminder list with cancel + snooze.\n *\n * Composition: Topbar + filter row + DataTable + ConfirmDialog\n * (cancel) + a custom <SnoozeDialog> with duration presets that\n * map to the backend's parseDuration vocabulary.\n *\n * Mutations only enabled on `pending` rows; firing/fired/cancelled/\n * failed stay read-only. The DataTable's last column is per-row\n * action buttons.\n *\n * No SSE channel for reminders — 5s react-query poll covers the\n * common case (reminders fire on minute boundaries; latency to\n * surface a firing transition is rarely UX-critical).\n */\n\nimport { useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Bell, BellOff, Clock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useReminders,\n useCancelReminder,\n useSnoozeReminder,\n} from '@/hooks/use-reminders'\nimport { describeError } from '@/lib/api/errors'\nimport type { Reminder, ReminderStatus } from '@/types/api'\n\nconst STATUS_OPTIONS: ReminderStatus[] = ['pending', 'firing', 'fired', 'cancelled', 'failed']\n\ninterface SnoozePreset {\n /** Backend parseDuration string. */\n duration: string\n /** i18n key (inside the `snooze` section). */\n i18nKey: string\n}\n\nconst SNOOZE_PRESETS: SnoozePreset[] = [\n { duration: '5m', i18nKey: 'snooze.preset5m' },\n { duration: '30m', i18nKey: 'snooze.preset30m' },\n { duration: '1h', i18nKey: 'snooze.preset1h' },\n { duration: '1d', i18nKey: 'snooze.preset1d' },\n]\n\nexport default function RemindersRoute(): JSX.Element {\n const { t } = useTranslation(['reminders', 'common'])\n const [params, setParams] = useSearchParams()\n const status = (params.get('status') as ReminderStatus | null) ?? 'pending'\n\n const query = useMemo(\n () => ({ status, limit: 200 }),\n [status],\n )\n const { data, isLoading, isFetching, refetch } = useReminders(query)\n const reminders = data?.reminders ?? []\n\n const cancel = useCancelReminder()\n const snooze = useSnoozeReminder()\n\n const [confirmCancelId, setConfirmCancelId] = useState<number | null>(null)\n const [snoozeTargetId, setSnoozeTargetId] = useState<number | null>(null)\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 setParams(next, { replace: true })\n }\n\n async function onConfirmCancel(): Promise<void> {\n if (confirmCancelId == null) return\n try {\n await cancel.mutateAsync(confirmCancelId)\n toast.success(t('toast.cancelled'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n async function onConfirmSnooze(duration: string): Promise<void> {\n if (snoozeTargetId == null) return\n try {\n await snooze.mutateAsync({ id: snoozeTargetId, body: { duration } })\n toast.success(t('snooze.toastOk', { duration }))\n setSnoozeTargetId(null)\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n const columns: DataTableColumn<Reminder>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'fireAt',\n header: t('col.fireAt'),\n cell: (r) => <span className=\"font-medium tabular-nums\">{formatTime(r.fire_at)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'text',\n header: t('col.text'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.text}</span>,\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('col.status'),\n cell: (r) => (\n <StatusBadge status={r.status}>\n {t(`status.${r.status}`, { defaultValue: r.status })}\n </StatusBadge>\n ),\n headClassName: 'w-28',\n },\n {\n id: 'platform',\n header: t('col.platform'),\n cell: (r) => <span className=\"text-text-dim\">{r.platform}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'recurrence',\n header: t('col.recurrence'),\n cell: (r) =>\n r.recurrence_label ? (\n <Badge variant=\"info\">{r.recurrence_label}</Badge>\n ) : (\n <span className=\"text-text-muted\">—</span>\n ),\n headClassName: 'w-36',\n hideOnMobile: true,\n },\n {\n id: 'source',\n header: t('col.source'),\n cell: (r) => (\n <span className=\"text-text-dim\">{t(`source.${r.source}`, { defaultValue: r.source })}</span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'actions',\n header: '',\n cell: (r) =>\n r.status === 'pending' ? (\n <div className=\"flex justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n setSnoozeTargetId(r.id)\n }}\n disabled={snooze.isPending || cancel.isPending}\n >\n <Clock className=\"h-3 w-3\" />\n {t('action.snooze')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n setConfirmCancelId(r.id)\n }}\n disabled={cancel.isPending || snooze.isPending}\n >\n <BellOff className=\"h-3 w-3\" />\n {t('action.cancel')}\n </Button>\n </div>\n ) : null,\n headClassName: 'w-48',\n },\n ],\n [t, cancel.isPending, snooze.isPending],\n )\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <main className=\"mx-auto flex w-full max-w-7xl flex-1 flex-col gap-4 px-3 py-4 sm:px-4 pb-safe\">\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('pageTitle')}</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-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"status-filter\">\n {t('filter.status')}\n </label>\n <Select\n value={status}\n onValueChange={(v) => patchParams({ status: v })}\n >\n <SelectTrigger id=\"status-filter\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`status.${s}`, { defaultValue: s })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <DataTable\n columns={columns}\n rows={reminders}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Bell />}\n title={t('empty.title')}\n description={t('empty.description')}\n />\n }\n />\n\n <ConfirmDialog\n open={confirmCancelId != null}\n onOpenChange={(open) => {\n if (!open) setConfirmCancelId(null)\n }}\n title={t('action.confirmCancel')}\n description={t('action.confirmCancelDesc')}\n intent=\"danger\"\n confirmLabel={t('action.cancel')}\n onConfirm={onConfirmCancel}\n />\n\n <SnoozeDialog\n open={snoozeTargetId != null}\n busy={snooze.isPending}\n onOpenChange={(open) => {\n if (!open) setSnoozeTargetId(null)\n }}\n onPick={onConfirmSnooze}\n />\n </main>\n </div>\n )\n}\n\ninterface SnoozeDialogProps {\n open: boolean\n busy: boolean\n onOpenChange: (open: boolean) => void\n onPick: (duration: string) => void | Promise<void>\n}\n\nfunction SnoozeDialog({ open, busy, onOpenChange, onPick }: SnoozeDialogProps): JSX.Element {\n const { t } = useTranslation(['reminders', 'common'])\n return (\n <Dialog open={open} onOpenChange={busy ? () => {} : onOpenChange}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{t('snooze.title')}</DialogTitle>\n <DialogDescription>{t('snooze.description')}</DialogDescription>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-2\">\n {SNOOZE_PRESETS.map((p) => (\n <Button\n key={p.duration}\n type=\"button\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => void onPick(p.duration)}\n >\n {t(p.i18nKey)}\n </Button>\n ))}\n </div>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={busy}\n onClick={() => onOpenChange(false)}\n >\n {t('actions.cancel', { ns: 'common' })}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction formatTime(iso: string): string {\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n const now = new Date()\n const sameDay = d.toDateString() === now.toDateString()\n if (sameDay) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })\n }\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["BellOff","createLucideIcon","remindersKeys","q","useReminders","query","useQuery","api","useCancelReminder","qc","useQueryClient","useMutation","id","useSnoozeReminder","body","STATUS_OPTIONS","SNOOZE_PRESETS","RemindersRoute","t","useTranslation","params","setParams","useSearchParams","status","useMemo","data","isLoading","isFetching","refetch","reminders","cancel","snooze","confirmCancelId","setConfirmCancelId","useState","snoozeTargetId","setSnoozeTargetId","patchParams","patch","next","k","v","onConfirmCancel","toast","err","message","describeError","onConfirmSnooze","duration","columns","r","jsxs","jsx","formatTime","StatusBadge","Badge","Button","e","Clock","Topbar","Loader2","RefreshCcw","Select","SelectTrigger","SelectValue","SelectContent","s","SelectItem","DataTable","EmptyState","Bell","ConfirmDialog","open","SnoozeDialog","busy","onOpenChange","onPick","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","p","DialogFooter","iso","d","now"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"reminders-BDeiVYTC.js","sources":["../../node_modules/lucide-react/dist/esm/icons/bell-off.js","../../src/hooks/use-reminders.ts","../../src/routes/reminders.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 BellOff = createLucideIcon(\"BellOff\", [\n [\"path\", { d: \"M10.268 21a2 2 0 0 0 3.464 0\", key: \"vwvbt9\" }],\n [\n \"path\",\n {\n d: \"M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742\",\n key: \"178tsu\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }],\n [\"path\", { d: \"M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05\", key: \"1hqiys\" }]\n]);\n\nexport { BellOff as default };\n//# sourceMappingURL=bell-off.js.map\n","/**\n * useReminders — react-query wrappers for the reminders domain.\n *\n * useReminders(query) — list pending (default) or by status\n * useCancelReminder() — POST /api/reminders/:id/cancel\n * useSnoozeReminder() — POST /api/reminders/:id/snooze\n *\n * Reminders aren't on the SSE event-bus today; the page combines a\n * 5s react-query poll with a manual Refresh button. When a snooze\n * mutation lands, both the original AND the new reminder change\n * (one cancelled, one pending) — invalidating the full 'reminders'\n * key catches both.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type {\n CancelReminderResponse,\n ListRemindersQuery,\n ListRemindersResponse,\n SnoozeReminderBody,\n SnoozeReminderResponse,\n} from '@/types/api'\n\nexport const remindersKeys = {\n all: ['reminders'] as const,\n list: (q: ListRemindersQuery) => ['reminders', 'list', q] as const,\n}\n\nexport function useReminders(query: ListRemindersQuery) {\n return useQuery<ListRemindersResponse>({\n queryKey: remindersKeys.list(query),\n queryFn: () => api.listReminders(query),\n refetchInterval: 5000,\n refetchIntervalInBackground: false,\n })\n}\n\nexport function useInvalidateReminders() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: remindersKeys.all })\n}\n\nexport function useCancelReminder() {\n const qc = useQueryClient()\n return useMutation<CancelReminderResponse, Error, number>({\n mutationFn: (id) => api.cancelReminder(id),\n onSuccess: () => qc.invalidateQueries({ queryKey: remindersKeys.all }),\n })\n}\n\nexport function useSnoozeReminder() {\n const qc = useQueryClient()\n return useMutation<\n SnoozeReminderResponse,\n Error,\n { id: number; body: SnoozeReminderBody }\n >({\n mutationFn: ({ id, body }) => api.snoozeReminder(id, body),\n onSuccess: () => qc.invalidateQueries({ queryKey: remindersKeys.all }),\n })\n}\n","/**\n * /reminders — queued-reminder list with cancel + snooze.\n *\n * Composition: Topbar + filter row + DataTable + ConfirmDialog\n * (cancel) + a custom <SnoozeDialog> with duration presets that\n * map to the backend's parseDuration vocabulary.\n *\n * Mutations only enabled on `pending` rows; firing/fired/cancelled/\n * failed stay read-only. The DataTable's last column is per-row\n * action buttons.\n *\n * No SSE channel for reminders — 5s react-query poll covers the\n * common case (reminders fire on minute boundaries; latency to\n * surface a firing transition is rarely UX-critical).\n */\n\nimport { useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Bell, BellOff, Clock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useReminders,\n useCancelReminder,\n useSnoozeReminder,\n} from '@/hooks/use-reminders'\nimport { describeError } from '@/lib/api/errors'\nimport type { Reminder, ReminderStatus } from '@/types/api'\n\nconst STATUS_OPTIONS: ReminderStatus[] = ['pending', 'firing', 'fired', 'cancelled', 'failed']\n\ninterface SnoozePreset {\n /** Backend parseDuration string. */\n duration: string\n /** i18n key (inside the `snooze` section). */\n i18nKey: string\n}\n\nconst SNOOZE_PRESETS: SnoozePreset[] = [\n { duration: '5m', i18nKey: 'snooze.preset5m' },\n { duration: '30m', i18nKey: 'snooze.preset30m' },\n { duration: '1h', i18nKey: 'snooze.preset1h' },\n { duration: '1d', i18nKey: 'snooze.preset1d' },\n]\n\nexport default function RemindersRoute(): JSX.Element {\n const { t } = useTranslation(['reminders', 'common'])\n const [params, setParams] = useSearchParams()\n const status = (params.get('status') as ReminderStatus | null) ?? 'pending'\n\n const query = useMemo(\n () => ({ status, limit: 200 }),\n [status],\n )\n const { data, isLoading, isFetching, refetch } = useReminders(query)\n const reminders = data?.reminders ?? []\n\n const cancel = useCancelReminder()\n const snooze = useSnoozeReminder()\n\n const [confirmCancelId, setConfirmCancelId] = useState<number | null>(null)\n const [snoozeTargetId, setSnoozeTargetId] = useState<number | null>(null)\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 setParams(next, { replace: true })\n }\n\n async function onConfirmCancel(): Promise<void> {\n if (confirmCancelId == null) return\n try {\n await cancel.mutateAsync(confirmCancelId)\n toast.success(t('toast.cancelled'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n async function onConfirmSnooze(duration: string): Promise<void> {\n if (snoozeTargetId == null) return\n try {\n await snooze.mutateAsync({ id: snoozeTargetId, body: { duration } })\n toast.success(t('snooze.toastOk', { duration }))\n setSnoozeTargetId(null)\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n const columns: DataTableColumn<Reminder>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'fireAt',\n header: t('col.fireAt'),\n cell: (r) => <span className=\"font-medium tabular-nums\">{formatTime(r.fire_at)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'text',\n header: t('col.text'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.text}</span>,\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('col.status'),\n cell: (r) => (\n <StatusBadge status={r.status}>\n {t(`status.${r.status}`, { defaultValue: r.status })}\n </StatusBadge>\n ),\n headClassName: 'w-28',\n },\n {\n id: 'platform',\n header: t('col.platform'),\n cell: (r) => <span className=\"text-text-dim\">{r.platform}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'recurrence',\n header: t('col.recurrence'),\n cell: (r) =>\n r.recurrence_label ? (\n <Badge variant=\"info\">{r.recurrence_label}</Badge>\n ) : (\n <span className=\"text-text-muted\">—</span>\n ),\n headClassName: 'w-36',\n hideOnMobile: true,\n },\n {\n id: 'source',\n header: t('col.source'),\n cell: (r) => (\n <span className=\"text-text-dim\">{t(`source.${r.source}`, { defaultValue: r.source })}</span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'actions',\n header: '',\n cell: (r) =>\n r.status === 'pending' ? (\n <div className=\"flex justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n setSnoozeTargetId(r.id)\n }}\n disabled={snooze.isPending || cancel.isPending}\n >\n <Clock className=\"h-3 w-3\" />\n {t('action.snooze')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n setConfirmCancelId(r.id)\n }}\n disabled={cancel.isPending || snooze.isPending}\n >\n <BellOff className=\"h-3 w-3\" />\n {t('action.cancel')}\n </Button>\n </div>\n ) : null,\n headClassName: 'w-48',\n },\n ],\n [t, cancel.isPending, snooze.isPending],\n )\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <main className=\"mx-auto flex w-full max-w-7xl flex-1 flex-col gap-4 px-3 py-4 sm:px-4 pb-safe\">\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('pageTitle')}</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-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"status-filter\">\n {t('filter.status')}\n </label>\n <Select\n value={status}\n onValueChange={(v) => patchParams({ status: v })}\n >\n <SelectTrigger id=\"status-filter\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`status.${s}`, { defaultValue: s })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <DataTable\n columns={columns}\n rows={reminders}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Bell />}\n title={t('empty.title')}\n description={t('empty.description')}\n />\n }\n />\n\n <ConfirmDialog\n open={confirmCancelId != null}\n onOpenChange={(open) => {\n if (!open) setConfirmCancelId(null)\n }}\n title={t('action.confirmCancel')}\n description={t('action.confirmCancelDesc')}\n intent=\"danger\"\n confirmLabel={t('action.cancel')}\n onConfirm={onConfirmCancel}\n />\n\n <SnoozeDialog\n open={snoozeTargetId != null}\n busy={snooze.isPending}\n onOpenChange={(open) => {\n if (!open) setSnoozeTargetId(null)\n }}\n onPick={onConfirmSnooze}\n />\n </main>\n </div>\n )\n}\n\ninterface SnoozeDialogProps {\n open: boolean\n busy: boolean\n onOpenChange: (open: boolean) => void\n onPick: (duration: string) => void | Promise<void>\n}\n\nfunction SnoozeDialog({ open, busy, onOpenChange, onPick }: SnoozeDialogProps): JSX.Element {\n const { t } = useTranslation(['reminders', 'common'])\n return (\n <Dialog open={open} onOpenChange={busy ? () => {} : onOpenChange}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{t('snooze.title')}</DialogTitle>\n <DialogDescription>{t('snooze.description')}</DialogDescription>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-2\">\n {SNOOZE_PRESETS.map((p) => (\n <Button\n key={p.duration}\n type=\"button\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => void onPick(p.duration)}\n >\n {t(p.i18nKey)}\n </Button>\n ))}\n </div>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={busy}\n onClick={() => onOpenChange(false)}\n >\n {t('actions.cancel', { ns: 'common' })}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction formatTime(iso: string): string {\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n const now = new Date()\n const sameDay = d.toDateString() === now.toDateString()\n if (sameDay) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })\n }\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["BellOff","createLucideIcon","remindersKeys","q","useReminders","query","useQuery","api","useCancelReminder","qc","useQueryClient","useMutation","id","useSnoozeReminder","body","STATUS_OPTIONS","SNOOZE_PRESETS","RemindersRoute","t","useTranslation","params","setParams","useSearchParams","status","useMemo","data","isLoading","isFetching","refetch","reminders","cancel","snooze","confirmCancelId","setConfirmCancelId","useState","snoozeTargetId","setSnoozeTargetId","patchParams","patch","next","k","v","onConfirmCancel","toast","err","message","describeError","onConfirmSnooze","duration","columns","r","jsxs","jsx","formatTime","StatusBadge","Badge","Button","e","Clock","Topbar","Loader2","RefreshCcw","Select","SelectTrigger","SelectValue","SelectContent","s","SelectItem","DataTable","EmptyState","Bell","ConfirmDialog","open","SnoozeDialog","busy","onOpenChange","onPick","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","p","DialogFooter","iso","d","now"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{o as a}from"./index-
|
|
1
|
+
import{o as a}from"./index-DY2Zglku.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const t=a("Save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);export{t as S};
|
|
7
|
-
//# sourceMappingURL=save-
|
|
7
|
+
//# sourceMappingURL=save-BYFmz9gD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save-
|
|
1
|
+
{"version":3,"file":"save-BYFmz9gD.js","sources":["../../node_modules/lucide-react/dist/esm/icons/save.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 Save = createLucideIcon(\"Save\", [\n [\n \"path\",\n {\n d: \"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z\",\n key: \"1c8476\"\n }\n ],\n [\"path\", { d: \"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7\", key: \"1ydtos\" }],\n [\"path\", { d: \"M7 3v4a1 1 0 0 0 1 1h7\", key: \"t51u73\" }]\n]);\n\nexport { Save as default };\n//# sourceMappingURL=save.js.map\n"],"names":["Save","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAOC,EAAiB,OAAQ,CACpC,CACE,OACA,CACE,EAAG,qGACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,4CAA6C,IAAK,QAAQ,CAAE,EAC1E,CAAC,OAAQ,CAAE,EAAG,yBAA0B,IAAK,QAAQ,CAAE,CACzD,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{o as f,b as g,d as N,u as j,f as y,j as s,l as d,B as b,L as w,I as C}from"./index-
|
|
1
|
+
import{o as f,b as g,d as N,u as j,f as y,j as s,l as d,B as b,L as w,I as C}from"./index-DY2Zglku.js";import{r as v}from"./react-DlP5eolq.js";import{D as k}from"./data-table-BLYG79TX.js";import{E as R}from"./empty-state-96qpPTNg.js";import{L as S}from"./loader-circle-D8L1w9c1.js";import{R as L}from"./refresh-ccw-7HdadG5V.js";import"./table-_WsrnZ_T.js";import"./arrow-up-CAiQ2jy-.js";import"./arrow-down-DxlRHrs8.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const M=f("CalendarClock",[["path",{d:"M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5",key:"1osxxc"}],["path",{d:"M16 2v4",key:"4m81vk"}],["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M3 10h5",key:"r794hk"}],["path",{d:"M17.5 17.5 16 16.3V14",key:"akvzfd"}],["circle",{cx:"16",cy:"16",r:"6",key:"qoo3c4"}]]),T={all:["schedules"],list:e=>["schedules","list",e]};function E(e){return g({queryKey:T.list(e),queryFn:()=>N.listSchedules(e)})}function O(){const{t:e}=j(["tasks","common"]),[t,i]=y(),l=t.get("agent")??"",{data:m,isLoading:o,isFetching:n,refetch:h}=E(l?{agent:l}:{}),u=m?.schedules??[];function x(a){const c=new URLSearchParams(t);a?c.set("agent",a):c.delete("agent"),i(c,{replace:!0})}const p=v.useMemo(()=>[{id:"id",header:e("schedules.col.id"),cell:a=>s.jsxs("span",{className:"tabular-nums text-text-dim",children:["#",a.id]}),headClassName:"w-16"},{id:"name",header:e("schedules.col.name"),cell:a=>s.jsx("span",{className:"font-medium",children:a.name}),asCardTitle:!0},{id:"agent",header:e("schedules.col.agent"),cell:a=>s.jsx("span",{className:"text-text-dim",children:a.agent}),headClassName:"w-32"},{id:"cron",header:e("schedules.col.cron"),cell:a=>s.jsx("code",{className:"rounded bg-surface-2 px-1.5 py-0.5 text-xs",children:a.cron}),headClassName:"w-32"},{id:"prompt",header:e("schedules.col.prompt"),cell:a=>s.jsx("span",{className:"line-clamp-2 text-text-dim",children:a.prompt}),hideOnMobile:!0},{id:"enabled",header:e("schedules.col.enabled"),cell:a=>a.enabled?s.jsx(d,{variant:"success",children:e("schedules.enabled")}):s.jsx(d,{variant:"outline",children:e("schedules.disabled")}),headClassName:"w-20"},{id:"nextRun",header:e("schedules.col.nextRun"),cell:a=>s.jsx("span",{className:"text-text-dim",children:r(a.next_run)}),headClassName:"w-40"},{id:"lastRun",header:e("schedules.col.lastRun"),cell:a=>s.jsx("span",{className:"text-text-dim",children:r(a.last_run)}),headClassName:"w-40",hideOnMobile:!0}],[e]);return s.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[s.jsxs("header",{className:"flex flex-col gap-1",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[s.jsx("h1",{className:"text-xl font-semibold",children:e("schedules.title")}),s.jsxs(b,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>h(),disabled:n,"aria-label":e("actions.refresh",{ns:"common"}),children:[n?s.jsx(S,{className:"h-4 w-4 animate-spin"}):s.jsx(L,{className:"h-4 w-4"}),s.jsx("span",{className:"hidden sm:inline",children:e("actions.refresh",{ns:"common"})})]})]}),s.jsx("p",{className:"text-sm text-text-dim",children:e("schedules.subtitle")})]}),s.jsx("div",{className:"flex flex-wrap items-end gap-2",children:s.jsxs("div",{className:"flex flex-col gap-1",children:[s.jsx(w,{htmlFor:"agent-filter",className:"text-xs text-text-dim",children:e("schedules.filter.agent")}),s.jsx(C,{id:"agent-filter",value:l,onChange:a=>x(a.target.value),placeholder:e("schedules.filter.agentAny"),className:"w-48"})]})}),s.jsx(k,{columns:p,rows:u,getRowId:a=>String(a.id),loading:o,emptyState:s.jsx(R,{icon:s.jsx(M,{}),title:e("schedules.empty.title"),description:e("schedules.empty.description")})})]})}function r(e){if(e==null)return"—";try{const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return e}}export{O as default};
|
|
7
|
-
//# sourceMappingURL=schedules-
|
|
7
|
+
//# sourceMappingURL=schedules-BddzBrxI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedules-Brp1Fomd.js","sources":["../../node_modules/lucide-react/dist/esm/icons/calendar-clock.js","../../src/hooks/use-schedules.ts","../../src/routes/tasks/schedules.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 CalendarClock = createLucideIcon(\"CalendarClock\", [\n [\"path\", { d: \"M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5\", key: \"1osxxc\" }],\n [\"path\", { d: \"M16 2v4\", key: \"4m81vk\" }],\n [\"path\", { d: \"M8 2v4\", key: \"1cmpym\" }],\n [\"path\", { d: \"M3 10h5\", key: \"r794hk\" }],\n [\"path\", { d: \"M17.5 17.5 16 16.3V14\", key: \"akvzfd\" }],\n [\"circle\", { cx: \"16\", cy: \"16\", r: \"6\", key: \"qoo3c4\" }]\n]);\n\nexport { CalendarClock as default };\n//# sourceMappingURL=calendar-clock.js.map\n","/**\n * useSchedules — react-query wrapper for /api/schedules.\n *\n * Read-only: schedule create / delete / toggle isn't exposed via\n * /api today (only via CLI). When that lands the mutations slot in\n * here next to useSchedules.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListSchedulesQuery, ListSchedulesResponse } from '@/types/api'\n\nexport const schedulesKeys = {\n all: ['schedules'] as const,\n list: (q: ListSchedulesQuery) => ['schedules', 'list', q] as const,\n}\n\nexport function useSchedules(query: ListSchedulesQuery) {\n return useQuery<ListSchedulesResponse>({\n queryKey: schedulesKeys.list(query),\n queryFn: () => api.listSchedules(query),\n })\n}\n\nexport function useInvalidateSchedules() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: schedulesKeys.all })\n}\n","/**\n * /tasks/schedules — cron-style triggers list. Read-only; mutations\n * (create / delete / toggle) only exist via CLI today; the page\n * shows what's scheduled and when the next run lands.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { CalendarClock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\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 { Label } from '@/components/ui/label'\nimport { useSchedules } from '@/hooks/use-schedules'\nimport type { Schedule } from '@/types/api'\n\nexport default function SchedulesRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n\n const { data, isLoading, isFetching, refetch } = useSchedules(\n agent ? { agent } : {},\n )\n const schedules = data?.schedules ?? []\n\n function setAgentFilter(v: string): void {\n const next = new URLSearchParams(params)\n if (!v) next.delete('agent')\n else next.set('agent', v)\n setParams(next, { replace: true })\n }\n\n const columns: DataTableColumn<Schedule>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('schedules.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'name',\n header: t('schedules.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'agent',\n header: t('schedules.col.agent'),\n cell: (r) => <span className=\"text-text-dim\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'cron',\n header: t('schedules.col.cron'),\n cell: (r) => <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs\">{r.cron}</code>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('schedules.col.prompt'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>,\n hideOnMobile: true,\n },\n {\n id: 'enabled',\n header: t('schedules.col.enabled'),\n cell: (r) =>\n r.enabled ? (\n <Badge variant=\"success\">{t('schedules.enabled')}</Badge>\n ) : (\n <Badge variant=\"outline\">{t('schedules.disabled')}</Badge>\n ),\n headClassName: 'w-20',\n },\n {\n id: 'nextRun',\n header: t('schedules.col.nextRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.next_run)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'lastRun',\n header: t('schedules.col.lastRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.last_run)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n ],\n [t],\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('schedules.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('schedules.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent-filter\" className=\"text-xs text-text-dim\">\n {t('schedules.filter.agent')}\n </Label>\n <Input\n id=\"agent-filter\"\n value={agent}\n onChange={(e) => setAgentFilter(e.target.value)}\n placeholder={t('schedules.filter.agentAny')}\n className=\"w-48\"\n />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n rows={schedules}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<CalendarClock />}\n title={t('schedules.empty.title')}\n description={t('schedules.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string | undefined | null): string {\n if (iso == null) return '—'\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["CalendarClock","createLucideIcon","schedulesKeys","q","useSchedules","query","useQuery","api","SchedulesRoute","t","useTranslation","params","setParams","useSearchParams","agent","data","isLoading","isFetching","refetch","schedules","setAgentFilter","v","next","columns","useMemo","r","jsxs","jsx","Badge","formatTime","Button","Loader2","RefreshCcw","Label","Input","e","DataTable","EmptyState","iso","d"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"schedules-BddzBrxI.js","sources":["../../node_modules/lucide-react/dist/esm/icons/calendar-clock.js","../../src/hooks/use-schedules.ts","../../src/routes/tasks/schedules.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 CalendarClock = createLucideIcon(\"CalendarClock\", [\n [\"path\", { d: \"M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5\", key: \"1osxxc\" }],\n [\"path\", { d: \"M16 2v4\", key: \"4m81vk\" }],\n [\"path\", { d: \"M8 2v4\", key: \"1cmpym\" }],\n [\"path\", { d: \"M3 10h5\", key: \"r794hk\" }],\n [\"path\", { d: \"M17.5 17.5 16 16.3V14\", key: \"akvzfd\" }],\n [\"circle\", { cx: \"16\", cy: \"16\", r: \"6\", key: \"qoo3c4\" }]\n]);\n\nexport { CalendarClock as default };\n//# sourceMappingURL=calendar-clock.js.map\n","/**\n * useSchedules — react-query wrapper for /api/schedules.\n *\n * Read-only: schedule create / delete / toggle isn't exposed via\n * /api today (only via CLI). When that lands the mutations slot in\n * here next to useSchedules.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListSchedulesQuery, ListSchedulesResponse } from '@/types/api'\n\nexport const schedulesKeys = {\n all: ['schedules'] as const,\n list: (q: ListSchedulesQuery) => ['schedules', 'list', q] as const,\n}\n\nexport function useSchedules(query: ListSchedulesQuery) {\n return useQuery<ListSchedulesResponse>({\n queryKey: schedulesKeys.list(query),\n queryFn: () => api.listSchedules(query),\n })\n}\n\nexport function useInvalidateSchedules() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: schedulesKeys.all })\n}\n","/**\n * /tasks/schedules — cron-style triggers list. Read-only; mutations\n * (create / delete / toggle) only exist via CLI today; the page\n * shows what's scheduled and when the next run lands.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { CalendarClock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\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 { Label } from '@/components/ui/label'\nimport { useSchedules } from '@/hooks/use-schedules'\nimport type { Schedule } from '@/types/api'\n\nexport default function SchedulesRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n\n const { data, isLoading, isFetching, refetch } = useSchedules(\n agent ? { agent } : {},\n )\n const schedules = data?.schedules ?? []\n\n function setAgentFilter(v: string): void {\n const next = new URLSearchParams(params)\n if (!v) next.delete('agent')\n else next.set('agent', v)\n setParams(next, { replace: true })\n }\n\n const columns: DataTableColumn<Schedule>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('schedules.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'name',\n header: t('schedules.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'agent',\n header: t('schedules.col.agent'),\n cell: (r) => <span className=\"text-text-dim\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'cron',\n header: t('schedules.col.cron'),\n cell: (r) => <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs\">{r.cron}</code>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('schedules.col.prompt'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>,\n hideOnMobile: true,\n },\n {\n id: 'enabled',\n header: t('schedules.col.enabled'),\n cell: (r) =>\n r.enabled ? (\n <Badge variant=\"success\">{t('schedules.enabled')}</Badge>\n ) : (\n <Badge variant=\"outline\">{t('schedules.disabled')}</Badge>\n ),\n headClassName: 'w-20',\n },\n {\n id: 'nextRun',\n header: t('schedules.col.nextRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.next_run)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'lastRun',\n header: t('schedules.col.lastRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.last_run)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n ],\n [t],\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('schedules.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('schedules.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent-filter\" className=\"text-xs text-text-dim\">\n {t('schedules.filter.agent')}\n </Label>\n <Input\n id=\"agent-filter\"\n value={agent}\n onChange={(e) => setAgentFilter(e.target.value)}\n placeholder={t('schedules.filter.agentAny')}\n className=\"w-48\"\n />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n rows={schedules}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<CalendarClock />}\n title={t('schedules.empty.title')}\n description={t('schedules.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string | undefined | null): string {\n if (iso == null) return '—'\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["CalendarClock","createLucideIcon","schedulesKeys","q","useSchedules","query","useQuery","api","SchedulesRoute","t","useTranslation","params","setParams","useSearchParams","agent","data","isLoading","isFetching","refetch","schedules","setAgentFilter","v","next","columns","useMemo","r","jsxs","jsx","Badge","formatTime","Button","Loader2","RefreshCcw","Label","Input","e","DataTable","EmptyState","iso","d"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{o as E,u as R,j as e,B as j,l as b,X as P,m as g,L as I,I as L}from"./index-
|
|
1
|
+
import{o as E,u as R,j as e,B as j,l as b,X as P,m as g,L as I,I as L}from"./index-DY2Zglku.js";import{r as C}from"./react-DlP5eolq.js";import{u as _,s as z,b as F,t as U}from"./use-settings-Co-iafXD.js";import{L as T}from"./loader-circle-D8L1w9c1.js";import{R as V}from"./refresh-ccw-7HdadG5V.js";import{S as q}from"./save-BYFmz9gD.js";import{A as J}from"./arrow-up-CAiQ2jy-.js";import{A as W}from"./arrow-down-DxlRHrs8.js";import{C as G}from"./circle-check-COhM5JsK.js";import{C as Q}from"./circle-x-7VaoZ7dY.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -14,4 +14,4 @@ import{o as E,u as R,j as e,B as j,l as b,X as P,m as g,L as I,I as L}from"./ind
|
|
|
14
14
|
* This source code is licensed under the ISC license.
|
|
15
15
|
* See the LICENSE file in the root directory of this source tree.
|
|
16
16
|
*/const Z=E("Key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);function $(r,a){const o=(r??"").split(",").map(i=>i.trim()).filter(Boolean),h=new Set(a.map(i=>i.name)),m=new Set,f=[];for(const i of o)!h.has(i)||m.has(i)||(f.push({name:i,enabled:!0}),m.add(i));for(const i of a)m.has(i.name)||f.push({name:i.name,enabled:!1});return f}function M(r){return r.filter(a=>a.enabled).map(a=>a.name).join(",")}function me(){const{t:r}=R(["settings","common"]),a=_({reveal:!1}),o=z(),h=F(),m=U(),f=a.data?.env??{},i=o.data?.providers??[],y=o.data?.chain??[],d=C.useMemo(()=>({chain:$(f.IMHUB_WEB_SEARCH_PROVIDERS,i),keys:{},keysTouched:{}}),[f.IMHUB_WEB_SEARCH_PROVIDERS,i]),[l,u]=C.useState(d),[k,S]=C.useState("");C.useEffect(()=>{const s=JSON.stringify(d);s!==k&&(u(d),S(s))},[d,k]);const N=JSON.stringify(l.chain)!==JSON.stringify(d.chain)||Object.keys(l.keysTouched).length>0;function w(s,t){u(c=>{const n=[...c.chain],x=s+t;if(x<0||x>=n.length)return c;const v=n[s],B=n[x];return!v||!B?c:(n[s]=B,n[x]=v,{...c,chain:n})})}function K(s,t){u(c=>{const n=[...c.chain],x=n[s];return x?(n[s]={...x,enabled:t},{...c,chain:n}):c})}function p(s,t){u(c=>({...c,keys:{...c.keys,[s]:t},keysTouched:{...c.keysTouched,[s]:!0}}))}function A(s){u(t=>({...t,keys:{...t.keys,[s]:""},keysTouched:{...t.keysTouched,[s]:!0}}))}async function H(){const s={},t=M(l.chain),c=M(d.chain);t!==c&&(s.IMHUB_WEB_SEARCH_PROVIDERS=t||null);for(const[n,x]of Object.entries(l.keysTouched)){if(!x)continue;const v=l.keys[n]??"";s[n]=v.trim()===""?null:v}if(Object.keys(s).length!==0)try{await h.mutateAsync({updates:s}),o.refetch(),g.success(r("search.savedToast")),u(n=>({...n,keys:{},keysTouched:{}}))}catch(n){g.error(n?.message??String(n))}}function O(){u(d)}async function D(s){try{const t=await m.mutateAsync({provider:s,query:"agim ping"});t.ok?g.success(r("search.testOk",{provider:s,hits:t.hits,ms:t.latencyMs})):g.error(r("search.testFail",{provider:s,error:t.error??"unknown error"}))}catch(t){g.error(t?.message??String(t))}}return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-6",children:[e.jsxs("header",{className:"flex items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:r("search.title")}),e.jsxs(j,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>{a.refetch(),o.refetch()},disabled:a.isFetching||o.isFetching,"aria-label":r("actions.refresh",{ns:"common"}),children:[a.isFetching||o.isFetching?e.jsx(T,{className:"h-4 w-4 animate-spin"}):e.jsx(V,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:r("search.refresh")})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:r("search.subtitle")}),e.jsx(ee,{live:y}),a.isLoading||o.isLoading?e.jsx("div",{className:"h-64 rounded-md bg-surface-2 animate-pulse"}):e.jsx("section",{className:"flex flex-col gap-2",children:l.chain.map((s,t)=>{const c=i.find(n=>n.name===s.name);return e.jsx(se,{info:c,entry:s,idx:t,count:l.chain.length,keyValue:c?.envKey?l.keys[c.envKey]:void 0,keyTouched:c?.envKey?!!l.keysTouched[c.envKey]:!1,onMove:n=>w(t,n),onToggle:n=>K(t,n),onSetKey:n=>c?.envKey&&p(c.envKey,n),onClearKey:()=>c?.envKey&&A(c.envKey),onTest:()=>D(s.name),testing:m.isPending},s.name)})}),N&&e.jsxs("div",{className:"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow",children:[e.jsx(b,{variant:"info",children:r("search.unsaved")}),e.jsxs(j,{variant:"ghost",size:"sm",className:"ml-auto",onClick:O,disabled:h.isPending,children:[e.jsx(P,{className:"h-4 w-4"})," ",r("search.discard")]}),e.jsxs(j,{size:"sm",onClick:()=>void H(),disabled:h.isPending,children:[h.isPending?e.jsx(T,{className:"h-4 w-4 animate-spin"}):e.jsx(q,{className:"h-4 w-4"}),r("search.save")]})]})]})}function ee({live:r}){const{t:a}=R("settings");return e.jsxs("section",{className:"rounded-md border border-border bg-surface px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx(Y,{className:"h-4 w-4 text-text-dim"}),e.jsx("span",{className:"text-text-dim",children:a("search.chainLabel")}),r.length===0?e.jsx(b,{variant:"outline",children:a("search.chainEmpty")}):e.jsx("span",{className:"font-mono text-xs",children:r.join(" → ")})]}),e.jsx("p",{className:"mt-1 text-[11px] text-text-dim",children:a("search.chainHint")})]})}function se(r){const{t:a}=R("settings"),{info:o,entry:h,idx:m,count:f,keyValue:i,keyTouched:y,onMove:d,onToggle:l,onSetKey:u,onClearKey:k,onTest:S,testing:N}=r,w=!!o?.envKey,K=!!o?.available||y&&!!i;return e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{className:"flex flex-col",children:[e.jsx("button",{type:"button",className:"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30",onClick:()=>d(-1),disabled:m===0,"aria-label":a("search.moveUp"),children:e.jsx(J,{className:"h-4 w-4"})}),e.jsx("button",{type:"button",className:"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30",onClick:()=>d(1),disabled:m===f-1,"aria-label":a("search.moveDown"),children:e.jsx(W,{className:"h-4 w-4"})})]}),e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:h.enabled,onChange:p=>l(p.target.checked),className:"h-4 w-4 accent-accent cursor-pointer"}),e.jsx("span",{className:"font-mono text-sm",children:h.name})]}),w?K?e.jsxs(b,{variant:"success",children:[e.jsx(G,{className:"mr-1 h-3 w-3"})," ",a("search.configured")]}):e.jsxs(b,{variant:"warning",children:[e.jsx(Q,{className:"mr-1 h-3 w-3"})," ",a("search.missingKey")]}):e.jsx(b,{variant:"outline",children:a("search.noKey")}),e.jsx("div",{className:"ml-auto flex items-center gap-1",children:e.jsxs(j,{size:"sm",variant:"ghost",onClick:S,disabled:N||!h.enabled,"aria-label":a("search.testBtn"),children:[N?e.jsx(T,{className:"h-4 w-4 animate-spin"}):e.jsx(X,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:a("search.testBtn")})]})})]}),w&&e.jsxs("div",{className:"mt-3 flex flex-col gap-1",children:[e.jsxs(I,{className:"text-xs font-medium",children:[e.jsx(Z,{className:"mr-1 inline h-3 w-3"}),o?.envKey]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(L,{type:"password",value:i??"",onChange:p=>u(p.target.value),placeholder:o?.available&&!y?a("search.placeholderConfigured"):a("search.placeholderEnter"),className:"font-mono text-xs"}),o?.available&&!y&&e.jsx(j,{size:"sm",variant:"ghost",onClick:k,"aria-label":a("search.clearKey"),children:e.jsx(P,{className:"h-4 w-4"})})]}),e.jsx("p",{className:"text-[11px] text-text-dim",children:a("search.keyHint",{envKey:o?.envKey})})]})]})}export{me as default};
|
|
17
|
-
//# sourceMappingURL=search-
|
|
17
|
+
//# sourceMappingURL=search-B8PlbtLg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-nk8nyLFI.js","sources":["../../node_modules/lucide-react/dist/esm/icons/flask-conical.js","../../node_modules/lucide-react/dist/esm/icons/globe.js","../../node_modules/lucide-react/dist/esm/icons/key.js","../../src/routes/settings/search.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 FlaskConical = createLucideIcon(\"FlaskConical\", [\n [\n \"path\",\n {\n d: \"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2\",\n key: \"18mbvz\"\n }\n ],\n [\"path\", { d: \"M6.453 15h11.094\", key: \"3shlmq\" }],\n [\"path\", { d: \"M8.5 2h7\", key: \"csnxdl\" }]\n]);\n\nexport { FlaskConical as default };\n//# sourceMappingURL=flask-conical.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 Globe = createLucideIcon(\"Globe\", [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"path\", { d: \"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", key: \"13o1zl\" }],\n [\"path\", { d: \"M2 12h20\", key: \"9i4pu4\" }]\n]);\n\nexport { Globe as default };\n//# sourceMappingURL=globe.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 Key = createLucideIcon(\"Key\", [\n [\"path\", { d: \"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4\", key: \"g0fldk\" }],\n [\"path\", { d: \"m21 2-9.6 9.6\", key: \"1j0ho8\" }],\n [\"circle\", { cx: \"7.5\", cy: \"15.5\", r: \"5.5\", key: \"yqb3hr\" }]\n]);\n\nexport { Key as default };\n//# sourceMappingURL=key.js.map\n","/**\n * /settings/search — web_search provider config (v1.2.126).\n *\n * One row per registered provider (Tavily / Brave / Serper / Exa /\n * DuckDuckGo / Metaso): API-key input (masked-by-default), enable\n * toggle (= \"is this name in the chain?\"), reorder via up / down,\n * one-shot test button. The chain preview at the top shows what\n * IMHUB_WEB_SEARCH_PROVIDERS resolves to right now.\n *\n * Persistence: API keys go via PUT /api/env (which masks GET +\n * refuses echoed masks on PUT); the chain order is a single CSV\n * env key — same endpoint. Save is one round-trip per dirty key.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n ArrowDown, ArrowUp, CheckCircle2, FlaskConical, Globe, Key,\n Loader2, RefreshCcw, Save, 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 useEnv,\n useSearchProviders,\n useTestSearchProvider,\n useUpdateEnv,\n} from '@/hooks/use-settings'\nimport type { SearchProviderInfo } from '@/types/api'\n\ninterface ChainEntry {\n name: string\n enabled: boolean\n}\n\ninterface Draft {\n /** Provider chain — every known provider, with `enabled`\n * reflecting \"is name in the CSV\". Order is the row order. */\n chain: ChainEntry[]\n /** Each provider's API-key input. Keyed by envKey (e.g.\n * TAVILY_API_KEY). Holds either '' (clear) or the new plaintext\n * the operator typed. We never load values back from the server. */\n keys: Record<string, string>\n /** Tracks which key fields the operator actually touched — so\n * Save only sends the diff and untouched masked keys are left\n * alone. */\n keysTouched: Record<string, boolean>\n}\n\nfunction envChainOrder(envValue: string | undefined, providers: SearchProviderInfo[]): ChainEntry[] {\n // Parse the CSV chain; preserve operator order; tail-append any\n // provider not in the CSV as disabled so they remain renderable.\n const raw = (envValue ?? '').split(',').map((s) => s.trim()).filter(Boolean)\n const known = new Set(providers.map((p) => p.name))\n const seen = new Set<string>()\n const out: ChainEntry[] = []\n for (const name of raw) {\n if (!known.has(name) || seen.has(name)) continue\n out.push({ name, enabled: true })\n seen.add(name)\n }\n for (const p of providers) {\n if (seen.has(p.name)) continue\n out.push({ name: p.name, enabled: false })\n }\n return out\n}\n\nfunction chainToCsv(chain: ChainEntry[]): string {\n return chain.filter((c) => c.enabled).map((c) => c.name).join(',')\n}\n\nexport default function SettingsSearchRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv({ reveal: false })\n const providersQuery = useSearchProviders()\n const updateEnv = useUpdateEnv()\n const testProvider = useTestSearchProvider()\n\n const envData = envQuery.data?.env ?? {}\n const providers = providersQuery.data?.providers ?? []\n const live = providersQuery.data?.chain ?? []\n\n const initial = useMemo<Draft>(\n () => ({\n chain: envChainOrder(envData.IMHUB_WEB_SEARCH_PROVIDERS, providers),\n keys: {},\n keysTouched: {},\n }),\n [envData.IMHUB_WEB_SEARCH_PROVIDERS, providers],\n )\n\n const [draft, setDraft] = useState<Draft>(initial)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n const hash = JSON.stringify(initial)\n if (hash !== syncedHash) {\n setDraft(initial)\n setSyncedHash(hash)\n }\n }, [initial, syncedHash])\n\n const isDirty =\n JSON.stringify(draft.chain) !== JSON.stringify(initial.chain) ||\n Object.keys(draft.keysTouched).length > 0\n\n function moveRow(idx: number, delta: -1 | 1): void {\n setDraft((d) => {\n const next = [...d.chain]\n const swap = idx + delta\n if (swap < 0 || swap >= next.length) return d\n const a = next[idx]\n const b = next[swap]\n if (!a || !b) return d\n next[idx] = b\n next[swap] = a\n return { ...d, chain: next }\n })\n }\n\n function toggleRow(idx: number, enabled: boolean): void {\n setDraft((d) => {\n const next = [...d.chain]\n const row = next[idx]\n if (!row) return d\n next[idx] = { ...row, enabled }\n return { ...d, chain: next }\n })\n }\n\n function setKey(envKey: string, value: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: value },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n function clearKey(envKey: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: '' },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n const nextCsv = chainToCsv(draft.chain)\n const initialCsv = chainToCsv(initial.chain)\n if (nextCsv !== initialCsv) {\n updates.IMHUB_WEB_SEARCH_PROVIDERS = nextCsv || null\n }\n for (const [envKey, touched] of Object.entries(draft.keysTouched)) {\n if (!touched) continue\n const v = draft.keys[envKey] ?? ''\n updates[envKey] = v.trim() === '' ? null : v\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n // Refetch both — env keys changed availability, chain order changed.\n void providersQuery.refetch()\n toast.success(t('search.savedToast'))\n setDraft((d) => ({ ...d, keys: {}, keysTouched: {} }))\n } catch (err) {\n toast.error(((err as Error)?.message ?? String(err)))\n }\n }\n\n function onDiscard(): void {\n setDraft(initial)\n }\n\n async function onTest(provider: string): Promise<void> {\n try {\n const result = await testProvider.mutateAsync({\n provider,\n // Short, harmless probe — the dispatcher only needs to\n // verify the upstream auth + transport.\n query: 'agim ping',\n })\n if (result.ok) {\n toast.success(\n t('search.testOk', { provider, hits: result.hits, ms: result.latencyMs }),\n )\n } else {\n toast.error(\n t('search.testFail', { provider, error: result.error ?? 'unknown error' }),\n )\n }\n } catch (err) {\n toast.error((err as Error)?.message ?? String(err))\n }\n }\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\n <header className=\"flex items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('search.title')}</h1>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={() => {\n void envQuery.refetch()\n void providersQuery.refetch()\n }}\n disabled={envQuery.isFetching || providersQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || providersQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.refresh')}</span>\n </Button>\n </header>\n <p className=\"text-sm text-text-dim\">{t('search.subtitle')}</p>\n\n <ChainPreview live={live} />\n\n {envQuery.isLoading || providersQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <section className=\"flex flex-col gap-2\">\n {draft.chain.map((entry, idx) => {\n const info = providers.find((p) => p.name === entry.name)\n return (\n <ProviderRow\n key={entry.name}\n info={info}\n entry={entry}\n idx={idx}\n count={draft.chain.length}\n keyValue={info?.envKey ? draft.keys[info.envKey] : undefined}\n keyTouched={info?.envKey ? !!draft.keysTouched[info.envKey] : false}\n onMove={(d) => moveRow(idx, d)}\n onToggle={(v) => toggleRow(idx, v)}\n onSetKey={(v) => info?.envKey && setKey(info.envKey, v)}\n onClearKey={() => info?.envKey && clearKey(info.envKey)}\n onTest={() => onTest(entry.name)}\n testing={testProvider.isPending}\n />\n )\n })}\n </section>\n )}\n\n {isDirty && (\n <div className=\"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow\">\n <Badge variant=\"info\">{t('search.unsaved')}</Badge>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" /> {t('search.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 {t('search.save')}\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nfunction ChainPreview({ live }: { live: string[] }): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm\">\n <Globe className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-text-dim\">{t('search.chainLabel')}</span>\n {live.length === 0 ? (\n <Badge variant=\"outline\">{t('search.chainEmpty')}</Badge>\n ) : (\n <span className=\"font-mono text-xs\">{live.join(' → ')}</span>\n )}\n </div>\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('search.chainHint')}</p>\n </section>\n )\n}\n\ninterface ProviderRowProps {\n info: SearchProviderInfo | undefined\n entry: ChainEntry\n idx: number\n count: number\n keyValue: string | undefined\n keyTouched: boolean\n onMove: (delta: -1 | 1) => void\n onToggle: (v: boolean) => void\n onSetKey: (v: string) => void\n onClearKey: () => void\n onTest: () => void\n testing: boolean\n}\n\nfunction ProviderRow(props: ProviderRowProps): JSX.Element {\n const { t } = useTranslation('settings')\n const { info, entry, idx, count, keyValue, keyTouched, onMove, onToggle,\n onSetKey, onClearKey, onTest, testing } = props\n const needsKey = !!info?.envKey\n const available = !!info?.available || (keyTouched && !!keyValue)\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex flex-col\">\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(-1)}\n disabled={idx === 0}\n aria-label={t('search.moveUp')}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(1)}\n disabled={idx === count - 1}\n aria-label={t('search.moveDown')}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={entry.enabled}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-mono text-sm\">{entry.name}</span>\n </label>\n\n {!needsKey ? (\n <Badge variant=\"outline\">{t('search.noKey')}</Badge>\n ) : available ? (\n <Badge variant=\"success\">\n <CheckCircle2 className=\"mr-1 h-3 w-3\" /> {t('search.configured')}\n </Badge>\n ) : (\n <Badge variant=\"warning\">\n <XCircle className=\"mr-1 h-3 w-3\" /> {t('search.missingKey')}\n </Badge>\n )}\n\n <div className=\"ml-auto flex items-center gap-1\">\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onTest}\n disabled={testing || !entry.enabled}\n aria-label={t('search.testBtn')}\n >\n {testing\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <FlaskConical className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.testBtn')}</span>\n </Button>\n </div>\n </div>\n\n {needsKey && (\n <div className=\"mt-3 flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">\n <Key className=\"mr-1 inline h-3 w-3\" />\n {info?.envKey}\n </Label>\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"password\"\n value={keyValue ?? ''}\n onChange={(e) => onSetKey(e.target.value)}\n placeholder={\n info?.available && !keyTouched\n ? t('search.placeholderConfigured')\n : t('search.placeholderEnter')\n }\n className=\"font-mono text-xs\"\n />\n {info?.available && !keyTouched && (\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onClearKey}\n aria-label={t('search.clearKey')}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n <p className=\"text-[11px] text-text-dim\">\n {t('search.keyHint', { envKey: info?.envKey })}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],"names":["FlaskConical","createLucideIcon","Globe","Key","envChainOrder","envValue","providers","raw","s","known","p","seen","out","name","chainToCsv","chain","c","SettingsSearchRoute","t","useTranslation","envQuery","useEnv","providersQuery","useSearchProviders","updateEnv","useUpdateEnv","testProvider","useTestSearchProvider","envData","live","initial","useMemo","draft","setDraft","useState","syncedHash","setSyncedHash","useEffect","hash","isDirty","moveRow","idx","delta","d","next","swap","a","b","toggleRow","enabled","row","setKey","envKey","value","clearKey","onSave","updates","nextCsv","initialCsv","touched","toast","err","onDiscard","onTest","provider","result","jsxs","jsx","Button","Loader2","RefreshCcw","ChainPreview","entry","info","ProviderRow","v","Badge","X","Save","props","count","keyValue","keyTouched","onMove","onToggle","onSetKey","onClearKey","testing","needsKey","available","ArrowUp","ArrowDown","e","CheckCircle2","XCircle","Label","Input"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
1
|
+
{"version":3,"file":"search-B8PlbtLg.js","sources":["../../node_modules/lucide-react/dist/esm/icons/flask-conical.js","../../node_modules/lucide-react/dist/esm/icons/globe.js","../../node_modules/lucide-react/dist/esm/icons/key.js","../../src/routes/settings/search.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 FlaskConical = createLucideIcon(\"FlaskConical\", [\n [\n \"path\",\n {\n d: \"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2\",\n key: \"18mbvz\"\n }\n ],\n [\"path\", { d: \"M6.453 15h11.094\", key: \"3shlmq\" }],\n [\"path\", { d: \"M8.5 2h7\", key: \"csnxdl\" }]\n]);\n\nexport { FlaskConical as default };\n//# sourceMappingURL=flask-conical.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 Globe = createLucideIcon(\"Globe\", [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"path\", { d: \"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", key: \"13o1zl\" }],\n [\"path\", { d: \"M2 12h20\", key: \"9i4pu4\" }]\n]);\n\nexport { Globe as default };\n//# sourceMappingURL=globe.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 Key = createLucideIcon(\"Key\", [\n [\"path\", { d: \"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4\", key: \"g0fldk\" }],\n [\"path\", { d: \"m21 2-9.6 9.6\", key: \"1j0ho8\" }],\n [\"circle\", { cx: \"7.5\", cy: \"15.5\", r: \"5.5\", key: \"yqb3hr\" }]\n]);\n\nexport { Key as default };\n//# sourceMappingURL=key.js.map\n","/**\n * /settings/search — web_search provider config (v1.2.126).\n *\n * One row per registered provider (Tavily / Brave / Serper / Exa /\n * DuckDuckGo / Metaso): API-key input (masked-by-default), enable\n * toggle (= \"is this name in the chain?\"), reorder via up / down,\n * one-shot test button. The chain preview at the top shows what\n * IMHUB_WEB_SEARCH_PROVIDERS resolves to right now.\n *\n * Persistence: API keys go via PUT /api/env (which masks GET +\n * refuses echoed masks on PUT); the chain order is a single CSV\n * env key — same endpoint. Save is one round-trip per dirty key.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n ArrowDown, ArrowUp, CheckCircle2, FlaskConical, Globe, Key,\n Loader2, RefreshCcw, Save, 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 useEnv,\n useSearchProviders,\n useTestSearchProvider,\n useUpdateEnv,\n} from '@/hooks/use-settings'\nimport type { SearchProviderInfo } from '@/types/api'\n\ninterface ChainEntry {\n name: string\n enabled: boolean\n}\n\ninterface Draft {\n /** Provider chain — every known provider, with `enabled`\n * reflecting \"is name in the CSV\". Order is the row order. */\n chain: ChainEntry[]\n /** Each provider's API-key input. Keyed by envKey (e.g.\n * TAVILY_API_KEY). Holds either '' (clear) or the new plaintext\n * the operator typed. We never load values back from the server. */\n keys: Record<string, string>\n /** Tracks which key fields the operator actually touched — so\n * Save only sends the diff and untouched masked keys are left\n * alone. */\n keysTouched: Record<string, boolean>\n}\n\nfunction envChainOrder(envValue: string | undefined, providers: SearchProviderInfo[]): ChainEntry[] {\n // Parse the CSV chain; preserve operator order; tail-append any\n // provider not in the CSV as disabled so they remain renderable.\n const raw = (envValue ?? '').split(',').map((s) => s.trim()).filter(Boolean)\n const known = new Set(providers.map((p) => p.name))\n const seen = new Set<string>()\n const out: ChainEntry[] = []\n for (const name of raw) {\n if (!known.has(name) || seen.has(name)) continue\n out.push({ name, enabled: true })\n seen.add(name)\n }\n for (const p of providers) {\n if (seen.has(p.name)) continue\n out.push({ name: p.name, enabled: false })\n }\n return out\n}\n\nfunction chainToCsv(chain: ChainEntry[]): string {\n return chain.filter((c) => c.enabled).map((c) => c.name).join(',')\n}\n\nexport default function SettingsSearchRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv({ reveal: false })\n const providersQuery = useSearchProviders()\n const updateEnv = useUpdateEnv()\n const testProvider = useTestSearchProvider()\n\n const envData = envQuery.data?.env ?? {}\n const providers = providersQuery.data?.providers ?? []\n const live = providersQuery.data?.chain ?? []\n\n const initial = useMemo<Draft>(\n () => ({\n chain: envChainOrder(envData.IMHUB_WEB_SEARCH_PROVIDERS, providers),\n keys: {},\n keysTouched: {},\n }),\n [envData.IMHUB_WEB_SEARCH_PROVIDERS, providers],\n )\n\n const [draft, setDraft] = useState<Draft>(initial)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n const hash = JSON.stringify(initial)\n if (hash !== syncedHash) {\n setDraft(initial)\n setSyncedHash(hash)\n }\n }, [initial, syncedHash])\n\n const isDirty =\n JSON.stringify(draft.chain) !== JSON.stringify(initial.chain) ||\n Object.keys(draft.keysTouched).length > 0\n\n function moveRow(idx: number, delta: -1 | 1): void {\n setDraft((d) => {\n const next = [...d.chain]\n const swap = idx + delta\n if (swap < 0 || swap >= next.length) return d\n const a = next[idx]\n const b = next[swap]\n if (!a || !b) return d\n next[idx] = b\n next[swap] = a\n return { ...d, chain: next }\n })\n }\n\n function toggleRow(idx: number, enabled: boolean): void {\n setDraft((d) => {\n const next = [...d.chain]\n const row = next[idx]\n if (!row) return d\n next[idx] = { ...row, enabled }\n return { ...d, chain: next }\n })\n }\n\n function setKey(envKey: string, value: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: value },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n function clearKey(envKey: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: '' },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n const nextCsv = chainToCsv(draft.chain)\n const initialCsv = chainToCsv(initial.chain)\n if (nextCsv !== initialCsv) {\n updates.IMHUB_WEB_SEARCH_PROVIDERS = nextCsv || null\n }\n for (const [envKey, touched] of Object.entries(draft.keysTouched)) {\n if (!touched) continue\n const v = draft.keys[envKey] ?? ''\n updates[envKey] = v.trim() === '' ? null : v\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n // Refetch both — env keys changed availability, chain order changed.\n void providersQuery.refetch()\n toast.success(t('search.savedToast'))\n setDraft((d) => ({ ...d, keys: {}, keysTouched: {} }))\n } catch (err) {\n toast.error(((err as Error)?.message ?? String(err)))\n }\n }\n\n function onDiscard(): void {\n setDraft(initial)\n }\n\n async function onTest(provider: string): Promise<void> {\n try {\n const result = await testProvider.mutateAsync({\n provider,\n // Short, harmless probe — the dispatcher only needs to\n // verify the upstream auth + transport.\n query: 'agim ping',\n })\n if (result.ok) {\n toast.success(\n t('search.testOk', { provider, hits: result.hits, ms: result.latencyMs }),\n )\n } else {\n toast.error(\n t('search.testFail', { provider, error: result.error ?? 'unknown error' }),\n )\n }\n } catch (err) {\n toast.error((err as Error)?.message ?? String(err))\n }\n }\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\n <header className=\"flex items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('search.title')}</h1>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={() => {\n void envQuery.refetch()\n void providersQuery.refetch()\n }}\n disabled={envQuery.isFetching || providersQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || providersQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.refresh')}</span>\n </Button>\n </header>\n <p className=\"text-sm text-text-dim\">{t('search.subtitle')}</p>\n\n <ChainPreview live={live} />\n\n {envQuery.isLoading || providersQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <section className=\"flex flex-col gap-2\">\n {draft.chain.map((entry, idx) => {\n const info = providers.find((p) => p.name === entry.name)\n return (\n <ProviderRow\n key={entry.name}\n info={info}\n entry={entry}\n idx={idx}\n count={draft.chain.length}\n keyValue={info?.envKey ? draft.keys[info.envKey] : undefined}\n keyTouched={info?.envKey ? !!draft.keysTouched[info.envKey] : false}\n onMove={(d) => moveRow(idx, d)}\n onToggle={(v) => toggleRow(idx, v)}\n onSetKey={(v) => info?.envKey && setKey(info.envKey, v)}\n onClearKey={() => info?.envKey && clearKey(info.envKey)}\n onTest={() => onTest(entry.name)}\n testing={testProvider.isPending}\n />\n )\n })}\n </section>\n )}\n\n {isDirty && (\n <div className=\"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow\">\n <Badge variant=\"info\">{t('search.unsaved')}</Badge>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" /> {t('search.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 {t('search.save')}\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nfunction ChainPreview({ live }: { live: string[] }): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm\">\n <Globe className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-text-dim\">{t('search.chainLabel')}</span>\n {live.length === 0 ? (\n <Badge variant=\"outline\">{t('search.chainEmpty')}</Badge>\n ) : (\n <span className=\"font-mono text-xs\">{live.join(' → ')}</span>\n )}\n </div>\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('search.chainHint')}</p>\n </section>\n )\n}\n\ninterface ProviderRowProps {\n info: SearchProviderInfo | undefined\n entry: ChainEntry\n idx: number\n count: number\n keyValue: string | undefined\n keyTouched: boolean\n onMove: (delta: -1 | 1) => void\n onToggle: (v: boolean) => void\n onSetKey: (v: string) => void\n onClearKey: () => void\n onTest: () => void\n testing: boolean\n}\n\nfunction ProviderRow(props: ProviderRowProps): JSX.Element {\n const { t } = useTranslation('settings')\n const { info, entry, idx, count, keyValue, keyTouched, onMove, onToggle,\n onSetKey, onClearKey, onTest, testing } = props\n const needsKey = !!info?.envKey\n const available = !!info?.available || (keyTouched && !!keyValue)\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex flex-col\">\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(-1)}\n disabled={idx === 0}\n aria-label={t('search.moveUp')}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(1)}\n disabled={idx === count - 1}\n aria-label={t('search.moveDown')}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={entry.enabled}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-mono text-sm\">{entry.name}</span>\n </label>\n\n {!needsKey ? (\n <Badge variant=\"outline\">{t('search.noKey')}</Badge>\n ) : available ? (\n <Badge variant=\"success\">\n <CheckCircle2 className=\"mr-1 h-3 w-3\" /> {t('search.configured')}\n </Badge>\n ) : (\n <Badge variant=\"warning\">\n <XCircle className=\"mr-1 h-3 w-3\" /> {t('search.missingKey')}\n </Badge>\n )}\n\n <div className=\"ml-auto flex items-center gap-1\">\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onTest}\n disabled={testing || !entry.enabled}\n aria-label={t('search.testBtn')}\n >\n {testing\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <FlaskConical className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.testBtn')}</span>\n </Button>\n </div>\n </div>\n\n {needsKey && (\n <div className=\"mt-3 flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">\n <Key className=\"mr-1 inline h-3 w-3\" />\n {info?.envKey}\n </Label>\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"password\"\n value={keyValue ?? ''}\n onChange={(e) => onSetKey(e.target.value)}\n placeholder={\n info?.available && !keyTouched\n ? t('search.placeholderConfigured')\n : t('search.placeholderEnter')\n }\n className=\"font-mono text-xs\"\n />\n {info?.available && !keyTouched && (\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onClearKey}\n aria-label={t('search.clearKey')}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n <p className=\"text-[11px] text-text-dim\">\n {t('search.keyHint', { envKey: info?.envKey })}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],"names":["FlaskConical","createLucideIcon","Globe","Key","envChainOrder","envValue","providers","raw","s","known","p","seen","out","name","chainToCsv","chain","c","SettingsSearchRoute","t","useTranslation","envQuery","useEnv","providersQuery","useSearchProviders","updateEnv","useUpdateEnv","testProvider","useTestSearchProvider","envData","live","initial","useMemo","draft","setDraft","useState","syncedHash","setSyncedHash","useEffect","hash","isDirty","moveRow","idx","delta","d","next","swap","a","b","toggleRow","enabled","row","setKey","envKey","value","clearKey","onSave","updates","nextCsv","initialCsv","touched","toast","err","onDiscard","onTest","provider","result","jsxs","jsx","Button","Loader2","RefreshCcw","ChainPreview","entry","info","ProviderRow","v","Badge","X","Save","props","count","keyValue","keyTouched","onMove","onToggle","onSetKey","onClearKey","testing","needsKey","available","ArrowUp","ArrowDown","e","CheckCircle2","XCircle","Label","Input"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|