agim-cli 1.2.42 → 1.2.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/core/viewer-config.d.ts +35 -3
  2. package/dist/core/viewer-config.d.ts.map +1 -1
  3. package/dist/core/viewer-config.js +47 -3
  4. package/dist/core/viewer-config.js.map +1 -1
  5. package/dist/core/viewer-remote.d.ts.map +1 -1
  6. package/dist/core/viewer-remote.js +4 -3
  7. package/dist/core/viewer-remote.js.map +1 -1
  8. package/dist/web/public/assets/{a2a-Dvz7ig4m.js → a2a-Bg_mT0z1.js} +2 -2
  9. package/dist/web/public/assets/{a2a-Dvz7ig4m.js.map → a2a-Bg_mT0z1.js.map} +1 -1
  10. package/dist/web/public/assets/{activity-BJqk7b-v.js → activity-U9AaNlDt.js} +2 -2
  11. package/dist/web/public/assets/{activity-BJqk7b-v.js.map → activity-U9AaNlDt.js.map} +1 -1
  12. package/dist/web/public/assets/{admins-DeiNQcMh.js → admins-BTC_YEK9.js} +2 -2
  13. package/dist/web/public/assets/{admins-DeiNQcMh.js.map → admins-BTC_YEK9.js.map} +1 -1
  14. package/dist/web/public/assets/{agents-DJGdXKLv.js → agents-o9odac79.js} +2 -2
  15. package/dist/web/public/assets/{agents-DJGdXKLv.js.map → agents-o9odac79.js.map} +1 -1
  16. package/dist/web/public/assets/{approvals-BOJ9et5G.js → approvals-Boj7iXz4.js} +2 -2
  17. package/dist/web/public/assets/{approvals-BOJ9et5G.js.map → approvals-Boj7iXz4.js.map} +1 -1
  18. package/dist/web/public/assets/{audit-Dnnevx6v.js → audit-MprCFoy_.js} +2 -2
  19. package/dist/web/public/assets/{audit-Dnnevx6v.js.map → audit-MprCFoy_.js.map} +1 -1
  20. package/dist/web/public/assets/{bgjobs-BZAxNFpA.js → bgjobs-B6XvxURz.js} +2 -2
  21. package/dist/web/public/assets/{bgjobs-BZAxNFpA.js.map → bgjobs-B6XvxURz.js.map} +1 -1
  22. package/dist/web/public/assets/{brain-fC59o4ZL.js → brain-C5HejASN.js} +2 -2
  23. package/dist/web/public/assets/{brain-fC59o4ZL.js.map → brain-C5HejASN.js.map} +1 -1
  24. package/dist/web/public/assets/{briefcase-DJRv77W6.js → briefcase-DkCTxO1A.js} +2 -2
  25. package/dist/web/public/assets/{briefcase-DJRv77W6.js.map → briefcase-DkCTxO1A.js.map} +1 -1
  26. package/dist/web/public/assets/{chevron-right-DzyVDoQs.js → chevron-right-DEyrYFz4.js} +2 -2
  27. package/dist/web/public/assets/{chevron-right-DzyVDoQs.js.map → chevron-right-DEyrYFz4.js.map} +1 -1
  28. package/dist/web/public/assets/{circle-check-B7B_CkmJ.js → circle-check-bADFEXGw.js} +2 -2
  29. package/dist/web/public/assets/{circle-check-B7B_CkmJ.js.map → circle-check-bADFEXGw.js.map} +1 -1
  30. package/dist/web/public/assets/{circle-check-big-dmFsJv6U.js → circle-check-big-U4xrkHiS.js} +2 -2
  31. package/dist/web/public/assets/{circle-check-big-dmFsJv6U.js.map → circle-check-big-U4xrkHiS.js.map} +1 -1
  32. package/dist/web/public/assets/{circle-x-epC-av6L.js → circle-x-C6qY5UUl.js} +2 -2
  33. package/dist/web/public/assets/{circle-x-epC-av6L.js.map → circle-x-C6qY5UUl.js.map} +1 -1
  34. package/dist/web/public/assets/{confirm-dialog-BEzhDlk7.js → confirm-dialog-B53cbWzX.js} +2 -2
  35. package/dist/web/public/assets/{confirm-dialog-BEzhDlk7.js.map → confirm-dialog-B53cbWzX.js.map} +1 -1
  36. package/dist/web/public/assets/{data-table-B_BxQrSj.js → data-table-BKTemkw9.js} +2 -2
  37. package/dist/web/public/assets/{data-table-B_BxQrSj.js.map → data-table-BKTemkw9.js.map} +1 -1
  38. package/dist/web/public/assets/{dialog-BAZNQtMt.js → dialog-Cevi7KvQ.js} +2 -2
  39. package/dist/web/public/assets/{dialog-BAZNQtMt.js.map → dialog-Cevi7KvQ.js.map} +1 -1
  40. package/dist/web/public/assets/{download-C08F-eA4.js → download-DwJNwdwV.js} +2 -2
  41. package/dist/web/public/assets/{download-C08F-eA4.js.map → download-DwJNwdwV.js.map} +1 -1
  42. package/dist/web/public/assets/{email-qzOnhITw.js → email-CZVaRhG_.js} +2 -2
  43. package/dist/web/public/assets/{email-qzOnhITw.js.map → email-CZVaRhG_.js.map} +1 -1
  44. package/dist/web/public/assets/{empty-state-BKs1qKHx.js → empty-state-C87S9Wor.js} +2 -2
  45. package/dist/web/public/assets/{empty-state-BKs1qKHx.js.map → empty-state-C87S9Wor.js.map} +1 -1
  46. package/dist/web/public/assets/{external-link-HML--jya.js → external-link-CIM1wjHP.js} +2 -2
  47. package/dist/web/public/assets/{external-link-HML--jya.js.map → external-link-CIM1wjHP.js.map} +1 -1
  48. package/dist/web/public/assets/{eye-C20O3zvh.js → eye-TUAOhz72.js} +2 -2
  49. package/dist/web/public/assets/{eye-C20O3zvh.js.map → eye-TUAOhz72.js.map} +1 -1
  50. package/dist/web/public/assets/{facts-B7VlqoVs.js → facts-ByM1VHCp.js} +2 -2
  51. package/dist/web/public/assets/{facts-B7VlqoVs.js.map → facts-ByM1VHCp.js.map} +1 -1
  52. package/dist/web/public/assets/{health-IF_VcgWW.js → health-B4GF06J-.js} +2 -2
  53. package/dist/web/public/assets/{health-IF_VcgWW.js.map → health-B4GF06J-.js.map} +1 -1
  54. package/dist/web/public/assets/{hot-D-aKQ8y2.js → hot-O9Z19xXa.js} +2 -2
  55. package/dist/web/public/assets/{hot-D-aKQ8y2.js.map → hot-O9Z19xXa.js.map} +1 -1
  56. package/dist/web/public/assets/{index-B48azMO-.js → index-CITyis5g.js} +4 -4
  57. package/dist/web/public/assets/{index-B48azMO-.js.map → index-CITyis5g.js.map} +1 -1
  58. package/dist/web/public/assets/{installed-CkiQRc89.js → installed-CM34mlDU.js} +2 -2
  59. package/dist/web/public/assets/{installed-CkiQRc89.js.map → installed-CM34mlDU.js.map} +1 -1
  60. package/dist/web/public/assets/{jobs-g1Wl8_zd.js → jobs-DwA9Ip87.js} +2 -2
  61. package/dist/web/public/assets/{jobs-g1Wl8_zd.js.map → jobs-DwA9Ip87.js.map} +1 -1
  62. package/dist/web/public/assets/{layout-D_CUXsIR.js → layout-BxrFilDN.js} +2 -2
  63. package/dist/web/public/assets/{layout-D_CUXsIR.js.map → layout-BxrFilDN.js.map} +1 -1
  64. package/dist/web/public/assets/{layout-ChcpcCB8.js → layout-CR5gLapT.js} +2 -2
  65. package/dist/web/public/assets/{layout-ChcpcCB8.js.map → layout-CR5gLapT.js.map} +1 -1
  66. package/dist/web/public/assets/{layout-CjLzz--E.js → layout-CcJQxFci.js} +2 -2
  67. package/dist/web/public/assets/{layout-CjLzz--E.js.map → layout-CcJQxFci.js.map} +1 -1
  68. package/dist/web/public/assets/{layout-CS9s-Q0a.js → layout-DRRveTaR.js} +2 -2
  69. package/dist/web/public/assets/{layout-CS9s-Q0a.js.map → layout-DRRveTaR.js.map} +1 -1
  70. package/dist/web/public/assets/{layout-fTZD9lJN.js → layout-zotlJ5fW.js} +2 -2
  71. package/dist/web/public/assets/{layout-fTZD9lJN.js.map → layout-zotlJ5fW.js.map} +1 -1
  72. package/dist/web/public/assets/{loader-circle-DBf4xFjo.js → loader-circle-BFp6Oc8_.js} +2 -2
  73. package/dist/web/public/assets/{loader-circle-DBf4xFjo.js.map → loader-circle-BFp6Oc8_.js.map} +1 -1
  74. package/dist/web/public/assets/{map-pin-BYR0Sf5r.js → map-pin-DPqnsBIG.js} +2 -2
  75. package/dist/web/public/assets/{map-pin-BYR0Sf5r.js.map → map-pin-DPqnsBIG.js.map} +1 -1
  76. package/dist/web/public/assets/{memos-ChfapnyG.js → memos-DCACQN26.js} +2 -2
  77. package/dist/web/public/assets/{memos-ChfapnyG.js.map → memos-DCACQN26.js.map} +1 -1
  78. package/dist/web/public/assets/{messengers-zBIYakPW.js → messengers-CwWP1qI4.js} +2 -2
  79. package/dist/web/public/assets/{messengers-zBIYakPW.js.map → messengers-CwWP1qI4.js.map} +1 -1
  80. package/dist/web/public/assets/{network-CEqTiog6.js → network-CrMEVz4-.js} +2 -2
  81. package/dist/web/public/assets/{network-CEqTiog6.js.map → network-CrMEVz4-.js.map} +1 -1
  82. package/dist/web/public/assets/{outbox-D4IQcACJ.js → outbox-C7FpZzwS.js} +2 -2
  83. package/dist/web/public/assets/{outbox-D4IQcACJ.js.map → outbox-C7FpZzwS.js.map} +1 -1
  84. package/dist/web/public/assets/{pagination-R9mql8VB.js → pagination-Cgt8W6EX.js} +2 -2
  85. package/dist/web/public/assets/{pagination-R9mql8VB.js.map → pagination-Cgt8W6EX.js.map} +1 -1
  86. package/dist/web/public/assets/{persona-Cj_C5frK.js → persona-Bjty6uqM.js} +2 -2
  87. package/dist/web/public/assets/{persona-Cj_C5frK.js.map → persona-Bjty6uqM.js.map} +1 -1
  88. package/dist/web/public/assets/{play-CTWqoMcj.js → play-CEoS1N8t.js} +2 -2
  89. package/dist/web/public/assets/{play-CTWqoMcj.js.map → play-CEoS1N8t.js.map} +1 -1
  90. package/dist/web/public/assets/{policy-BKEZ2ziu.js → policy-DwI-ov9J.js} +2 -2
  91. package/dist/web/public/assets/{policy-BKEZ2ziu.js.map → policy-DwI-ov9J.js.map} +1 -1
  92. package/dist/web/public/assets/{refresh-ccw-Clj5sQNF.js → refresh-ccw-CPSYEbgh.js} +2 -2
  93. package/dist/web/public/assets/{refresh-ccw-Clj5sQNF.js.map → refresh-ccw-CPSYEbgh.js.map} +1 -1
  94. package/dist/web/public/assets/{reminders-C5bVkLgj.js → reminders-X-QFSWvZ.js} +2 -2
  95. package/dist/web/public/assets/{reminders-C5bVkLgj.js.map → reminders-X-QFSWvZ.js.map} +1 -1
  96. package/dist/web/public/assets/{save-CAFra1jb.js → save-tS3IBPf3.js} +2 -2
  97. package/dist/web/public/assets/{save-CAFra1jb.js.map → save-tS3IBPf3.js.map} +1 -1
  98. package/dist/web/public/assets/{schedules-KQCD7978.js → schedules-CfCNvhCV.js} +2 -2
  99. package/dist/web/public/assets/{schedules-KQCD7978.js.map → schedules-CfCNvhCV.js.map} +1 -1
  100. package/dist/web/public/assets/{search-bDqizeZv.js → search-BPvPCccn.js} +2 -2
  101. package/dist/web/public/assets/{search-bDqizeZv.js.map → search-BPvPCccn.js.map} +1 -1
  102. package/dist/web/public/assets/{service-DTeMmX2u.js → service-DSitDFzD.js} +2 -2
  103. package/dist/web/public/assets/{service-DTeMmX2u.js.map → service-DSitDFzD.js.map} +1 -1
  104. package/dist/web/public/assets/{status-badge-_3Ve0Jef.js → status-badge-HSHkhdur.js} +2 -2
  105. package/dist/web/public/assets/{status-badge-_3Ve0Jef.js.map → status-badge-HSHkhdur.js.map} +1 -1
  106. package/dist/web/public/assets/{subtasks-DROeZcfT.js → subtasks-eKq_P0v6.js} +2 -2
  107. package/dist/web/public/assets/{subtasks-DROeZcfT.js.map → subtasks-eKq_P0v6.js.map} +1 -1
  108. package/dist/web/public/assets/{table-CZwKizat.js → table-BBISYTH-.js} +2 -2
  109. package/dist/web/public/assets/{table-CZwKizat.js.map → table-BBISYTH-.js.map} +1 -1
  110. package/dist/web/public/assets/{topn-DpErN6N8.js → topn-JA8llaUI.js} +2 -2
  111. package/dist/web/public/assets/{topn-DpErN6N8.js.map → topn-JA8llaUI.js.map} +1 -1
  112. package/dist/web/public/assets/{trash-2-BkZrVFdH.js → trash-2-D8F96aYm.js} +2 -2
  113. package/dist/web/public/assets/{trash-2-BkZrVFdH.js.map → trash-2-D8F96aYm.js.map} +1 -1
  114. package/dist/web/public/assets/{use-memory-CElm89Hg.js → use-memory-BDa1R8Tf.js} +2 -2
  115. package/dist/web/public/assets/{use-memory-CElm89Hg.js.map → use-memory-BDa1R8Tf.js.map} +1 -1
  116. package/dist/web/public/assets/{use-observability-D8qHfj0S.js → use-observability-XgxLMQsY.js} +2 -2
  117. package/dist/web/public/assets/{use-observability-D8qHfj0S.js.map → use-observability-XgxLMQsY.js.map} +1 -1
  118. package/dist/web/public/assets/{use-settings-DgU8YmGj.js → use-settings-Bc4s-JUD.js} +2 -2
  119. package/dist/web/public/assets/{use-settings-DgU8YmGj.js.map → use-settings-Bc4s-JUD.js.map} +1 -1
  120. package/dist/web/public/assets/{use-skills-Cp_ok0Uq.js → use-skills-DCOYAQ8s.js} +2 -2
  121. package/dist/web/public/assets/{use-skills-Cp_ok0Uq.js.map → use-skills-DCOYAQ8s.js.map} +1 -1
  122. package/dist/web/public/assets/{use-workspace-Dc6rNi5d.js → use-workspace-JalfqH6H.js} +2 -2
  123. package/dist/web/public/assets/{use-workspace-Dc6rNi5d.js.map → use-workspace-JalfqH6H.js.map} +1 -1
  124. package/dist/web/public/assets/{useQuery-BjFqOBV3.js → useQuery-DFryK9Pn.js} +2 -2
  125. package/dist/web/public/assets/{useQuery-BjFqOBV3.js.map → useQuery-DFryK9Pn.js.map} +1 -1
  126. package/dist/web/public/assets/{vector-Bb3l7TOa.js → vector-G2dN_aut.js} +2 -2
  127. package/dist/web/public/assets/{vector-Bb3l7TOa.js.map → vector-G2dN_aut.js.map} +1 -1
  128. package/dist/web/public/assets/viewer-DliYQkQQ.js +12 -0
  129. package/dist/web/public/assets/viewer-DliYQkQQ.js.map +1 -0
  130. package/dist/web/public/assets/{workspace-Du8va-tD.js → workspace-BaLZIgGn.js} +2 -2
  131. package/dist/web/public/assets/{workspace-Du8va-tD.js.map → workspace-BaLZIgGn.js.map} +1 -1
  132. package/dist/web/public/assets/{workspaces-BjgDtEnF.js → workspaces-cX_rV523.js} +2 -2
  133. package/dist/web/public/assets/{workspaces-BjgDtEnF.js.map → workspaces-cX_rV523.js.map} +1 -1
  134. package/dist/web/public/assets/{x-CHe1y-sG.js → x-D5196-9X.js} +2 -2
  135. package/dist/web/public/assets/{x-CHe1y-sG.js.map → x-D5196-9X.js.map} +1 -1
  136. package/dist/web/public/index.html +1 -1
  137. package/package.json +1 -1
  138. package/dist/web/public/assets/viewer-BeyW50lw.js +0 -12
  139. package/dist/web/public/assets/viewer-BeyW50lw.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import{x as _,ac as N,Q as e,c as L,L as g,I as D,S as M,l as B,m as E,j as F,k as j,B as v,r as b}from"./index-B48azMO-.js";import{e as o}from"./react-C9F3QeMB.js";import{E as I}from"./empty-state-BKs1qKHx.js";import{D as T,a as V,d as O,e as R,b as A}from"./dialog-BAZNQtMt.js";import{a as z,u as Q}from"./use-skills-Cp_ok0Uq.js";import{L as $}from"./loader-circle-DBf4xFjo.js";import{R as q}from"./refresh-ccw-Clj5sQNF.js";import{S as H}from"./search-bDqizeZv.js";import"./x-CHe1y-sG.js";import"./useQuery-BjFqOBV3.js";/**
1
+ import{x as _,ac as N,Q as e,c as L,L as g,I as D,S as M,l as B,m as E,j as F,k as j,B as v,r as b}from"./index-CITyis5g.js";import{e as o}from"./react-C9F3QeMB.js";import{E as I}from"./empty-state-C87S9Wor.js";import{D as T,a as V,d as O,e as R,b as A}from"./dialog-Cevi7KvQ.js";import{a as z,u as Q}from"./use-skills-DCOYAQ8s.js";import{L as $}from"./loader-circle-BFp6Oc8_.js";import{R as q}from"./refresh-ccw-CPSYEbgh.js";import{S as H}from"./search-BPvPCccn.js";import"./x-D5196-9X.js";import"./useQuery-DFryK9Pn.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 G=_("BookOpen",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]);function ae(){const{t}=N(["skills","common"]),{data:d,isLoading:c,isFetching:n,refetch:m}=z(),r=d?.skills??[],i=d?.sources,[u,w]=o.useState(""),[x,y]=o.useState(null),[k,p]=o.useState(null),S=o.useMemo(()=>{const s=new Set;for(const a of r)s.add(a.category);return Array.from(s).sort()},[r]),f=o.useMemo(()=>{const s=u.trim().toLowerCase();return r.filter(a=>x&&a.category!==x?!1:s?a.slug.toLowerCase().includes(s)||a.name.toLowerCase().includes(s)||a.description.toLowerCase().includes(s):!0)},[r,u,x]),C=o.useMemo(()=>{const s=new Map;for(const a of f){const l=s.get(a.category)??[];l.push(a),s.set(a.category,l)}return Array.from(s.entries()).sort(([a],[l])=>a.localeCompare(l))},[f]);return e.jsxs("div",{className:"mx-auto flex max-w-5xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:t("installed.title")}),e.jsxs(L,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>m(),disabled:n,"aria-label":t("actions.refresh",{ns:"common"}),children:[n?e.jsx($,{className:"h-4 w-4 animate-spin"}):e.jsx(q,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:t("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:t("installed.subtitle")})]}),i&&e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3 text-xs text-text-dim",children:[e.jsx("div",{className:"font-medium text-text mb-1",children:t("installed.sources.title")}),e.jsxs("div",{className:"font-mono",children:[e.jsxs("div",{children:[t("installed.sources.claude"),": ",e.jsx("span",{className:"text-text",children:i.claude})]}),e.jsxs("div",{children:[t("installed.sources.opencode"),": ",e.jsx("span",{className:"text-text",children:i.opencode})]})]})]}),r.length>0&&e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{className:"relative flex-1 min-w-[180px] max-w-md",children:[e.jsx(g,{htmlFor:"skill-search",className:"text-xs text-text-dim",children:t("installed.filter.search")}),e.jsx(H,{className:"pointer-events-none absolute left-2 bottom-2 h-4 w-4 text-text-muted"}),e.jsx(D,{id:"skill-search",value:u,onChange:s=>w(s.target.value),className:"pl-7 mt-1"})]}),e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(g,{htmlFor:"skill-cat",className:"text-xs text-text-dim",children:t("installed.filter.category")}),e.jsxs(M,{value:x??"__any__",onValueChange:s=>y(s==="__any__"?null:s),children:[e.jsx(B,{id:"skill-cat",className:"w-[160px]",children:e.jsx(E,{})}),e.jsxs(F,{children:[e.jsx(j,{value:"__any__",children:t("installed.filter.categoryAny")}),S.map(s=>e.jsx(j,{value:s,children:s},s))]})]})]})]}),c?e.jsx("div",{className:"h-48 w-full rounded-md bg-surface-2 animate-pulse"}):r.length===0?e.jsx(I,{icon:e.jsx(G,{}),title:t("installed.empty.title"),description:t("installed.empty.description")}):e.jsx("div",{className:"flex flex-col gap-3",children:C.map(([s,a])=>e.jsxs("section",{children:[e.jsxs("h2",{className:"mb-2 text-sm font-medium uppercase tracking-wide text-text-dim",children:[s," · ",a.length]}),e.jsx("ul",{className:"flex flex-col gap-2",children:a.map(l=>e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>p(l.slug),className:b("flex w-full flex-col gap-1 rounded-md border border-border bg-surface px-3 py-2 text-left","transition-colors hover:border-border-strong hover:bg-surface-hover"),children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-medium text-text",children:l.name}),e.jsx("span",{className:"text-text-muted text-xs font-mono",children:l.slug}),e.jsx("div",{className:"ml-auto flex gap-1",children:l.agents.map(h=>e.jsx(v,{variant:"secondary",children:t(`installed.agentTag.${h}`,{defaultValue:h})},h))})]}),e.jsx("p",{className:"line-clamp-2 text-sm text-text-dim",children:l.description})]})},l.slug))})]},s))}),e.jsx(J,{slug:k,onOpenChange:s=>{s||p(null)}})]})}function J({slug:t,onOpenChange:d}){const{t:c}=N("skills"),{data:n,isLoading:m,isError:r}=Q(t);return e.jsx(T,{open:t!=null,onOpenChange:d,children:e.jsxs(V,{className:"sm:max-w-3xl",children:[e.jsxs(O,{children:[e.jsx(R,{children:n?.name??t??""}),n&&e.jsx(A,{children:n.description})]}),m?e.jsx("div",{className:"text-sm text-text-dim",children:c("installed.detail.loading")}):r?e.jsx("div",{className:"text-sm text-danger",children:c("installed.detail.loadFailed")}):n?e.jsxs(e.Fragment,{children:[e.jsxs("dl",{className:"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs",children:[e.jsx("dt",{className:"text-text-dim",children:c("installed.detail.agentsLabel")}),e.jsx("dd",{className:"flex gap-1",children:n.agents.map(i=>e.jsx(v,{variant:"secondary",children:c(`installed.agentTag.${i}`,{defaultValue:i})},i))}),e.jsx("dt",{className:"text-text-dim",children:"slug"}),e.jsx("dd",{className:"font-mono",children:n.slug})]}),e.jsx("pre",{className:b("mt-2 max-h-[60dvh] overflow-auto","rounded-md bg-surface-2 px-3 py-2","font-mono text-xs leading-5 text-text","whitespace-pre-wrap break-words"),children:n.content})]}):null]})})}export{ae as default};
7
- //# sourceMappingURL=installed-CkiQRc89.js.map
7
+ //# sourceMappingURL=installed-CM34mlDU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"installed-CkiQRc89.js","sources":["../../node_modules/lucide-react/dist/esm/icons/book-open.js","../../src/routes/skills/installed.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 BookOpen = createLucideIcon(\"BookOpen\", [\n [\"path\", { d: \"M12 7v14\", key: \"1akyts\" }],\n [\n \"path\",\n {\n d: \"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\",\n key: \"ruj8y\"\n }\n ]\n]);\n\nexport { BookOpen as default };\n//# sourceMappingURL=book-open.js.map\n","/**\n * /skills/installed — read-only browse of SKILL.md files the host\n * agents pick up. Search + category filter; row click opens a\n * detail Dialog with the raw SKILL.md content.\n *\n * Moved from /memory/skills in 2026-05 so skills sit at the top\n * level. The render logic is unchanged from the previous home —\n * only the `useTranslation` namespace and i18n key prefix shifted.\n *\n * No markdown rendering for now: we'd need marked + DOMPurify to\n * safely render arbitrary skill content, and the raw text is what\n * the host agent reads anyway.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { BookOpen, Loader2, RefreshCcw, Search } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\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 { useSkills, useSkillDetail } from '@/hooks/use-skills'\nimport type { SkillMeta } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function SkillsInstalledRoute(): JSX.Element {\n const { t } = useTranslation(['skills', 'common'])\n const { data, isLoading, isFetching, refetch } = useSkills()\n const skills = data?.skills ?? []\n const sources = data?.sources\n\n const [q, setQ] = useState('')\n const [cat, setCat] = useState<string | null>(null)\n const [openSlug, setOpenSlug] = useState<string | null>(null)\n\n const categories = useMemo(() => {\n const set = new Set<string>()\n for (const s of skills) set.add(s.category)\n return Array.from(set).sort()\n }, [skills])\n\n const filtered = useMemo(() => {\n const needle = q.trim().toLowerCase()\n return skills.filter((s) => {\n if (cat && s.category !== cat) return false\n if (!needle) return true\n return (\n s.slug.toLowerCase().includes(needle) ||\n s.name.toLowerCase().includes(needle) ||\n s.description.toLowerCase().includes(needle)\n )\n })\n }, [skills, q, cat])\n\n const grouped = useMemo(() => {\n const m = new Map<string, SkillMeta[]>()\n for (const s of filtered) {\n const arr = m.get(s.category) ?? []\n arr.push(s)\n m.set(s.category, arr)\n }\n return Array.from(m.entries()).sort(([a], [b]) => a.localeCompare(b))\n }, [filtered])\n\n return (\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('installed.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('installed.subtitle')}</p>\n </header>\n\n {sources && (\n <div className=\"rounded-md border border-border bg-surface p-3 text-xs text-text-dim\">\n <div className=\"font-medium text-text mb-1\">{t('installed.sources.title')}</div>\n <div className=\"font-mono\">\n <div>{t('installed.sources.claude')}: <span className=\"text-text\">{sources.claude}</span></div>\n <div>{t('installed.sources.opencode')}: <span className=\"text-text\">{sources.opencode}</span></div>\n </div>\n </div>\n )}\n\n {skills.length > 0 && (\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"relative flex-1 min-w-[180px] max-w-md\">\n <Label htmlFor=\"skill-search\" className=\"text-xs text-text-dim\">\n {t('installed.filter.search')}\n </Label>\n <Search className=\"pointer-events-none absolute left-2 bottom-2 h-4 w-4 text-text-muted\" />\n <Input\n id=\"skill-search\"\n value={q}\n onChange={(e) => setQ(e.target.value)}\n className=\"pl-7 mt-1\"\n />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"skill-cat\" className=\"text-xs text-text-dim\">\n {t('installed.filter.category')}\n </Label>\n <Select\n value={cat ?? '__any__'}\n onValueChange={(v) => setCat(v === '__any__' ? null : v)}\n >\n <SelectTrigger id=\"skill-cat\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('installed.filter.categoryAny')}</SelectItem>\n {categories.map((c) => (\n <SelectItem key={c} value={c}>{c}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n )}\n\n {isLoading ? (\n <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : skills.length === 0 ? (\n <EmptyState\n icon={<BookOpen />}\n title={t('installed.empty.title')}\n description={t('installed.empty.description')}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {grouped.map(([category, list]) => (\n <section key={category}>\n <h2 className=\"mb-2 text-sm font-medium uppercase tracking-wide text-text-dim\">\n {category} · {list.length}\n </h2>\n <ul className=\"flex flex-col gap-2\">\n {list.map((s) => (\n <li key={s.slug}>\n <button\n type=\"button\"\n onClick={() => setOpenSlug(s.slug)}\n className={cn(\n 'flex w-full flex-col gap-1 rounded-md border border-border bg-surface px-3 py-2 text-left',\n 'transition-colors hover:border-border-strong hover:bg-surface-hover',\n )}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{s.name}</span>\n <span className=\"text-text-muted text-xs font-mono\">{s.slug}</span>\n <div className=\"ml-auto flex gap-1\">\n {s.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">\n {t(`installed.agentTag.${a}`, { defaultValue: a })}\n </Badge>\n ))}\n </div>\n </div>\n <p className=\"line-clamp-2 text-sm text-text-dim\">{s.description}</p>\n </button>\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n\n <SkillDetailDialog\n slug={openSlug}\n onOpenChange={(open) => { if (!open) setOpenSlug(null) }}\n />\n </div>\n )\n}\n\ninterface SkillDetailDialogProps {\n slug: string | null\n onOpenChange: (open: boolean) => void\n}\n\nfunction SkillDetailDialog({ slug, onOpenChange }: SkillDetailDialogProps): JSX.Element {\n const { t } = useTranslation('skills')\n const { data, isLoading, isError } = useSkillDetail(slug)\n return (\n <Dialog open={slug != null} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-3xl\">\n <DialogHeader>\n <DialogTitle>{data?.name ?? slug ?? ''}</DialogTitle>\n {data && <DialogDescription>{data.description}</DialogDescription>}\n </DialogHeader>\n {isLoading ? (\n <div className=\"text-sm text-text-dim\">{t('installed.detail.loading')}</div>\n ) : isError ? (\n <div className=\"text-sm text-danger\">{t('installed.detail.loadFailed')}</div>\n ) : data ? (\n <>\n <dl className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs\">\n <dt className=\"text-text-dim\">{t('installed.detail.agentsLabel')}</dt>\n <dd className=\"flex gap-1\">\n {data.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">\n {t(`installed.agentTag.${a}`, { defaultValue: a })}\n </Badge>\n ))}\n </dd>\n <dt className=\"text-text-dim\">slug</dt>\n <dd className=\"font-mono\">{data.slug}</dd>\n </dl>\n <pre\n className={cn(\n 'mt-2 max-h-[60dvh] overflow-auto',\n 'rounded-md bg-surface-2 px-3 py-2',\n 'font-mono text-xs leading-5 text-text',\n 'whitespace-pre-wrap break-words',\n )}\n >\n {data.content}\n </pre>\n </>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n"],"names":["BookOpen","createLucideIcon","SkillsInstalledRoute","useTranslation","data","isLoading","isFetching","refetch","useSkills","skills","sources","q","setQ","useState","cat","setCat","openSlug","setOpenSlug","categories","useMemo","set","s","filtered","needle","grouped","m","arr","b","jsxs","jsx","Button","Loader2","RefreshCcw","Label","Search","Input","e","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","c","EmptyState","category","list","cn","a","Badge","SkillDetailDialog","open","slug","onOpenChange","t","isError","useSkillDetail","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","Fragment"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"installed-CM34mlDU.js","sources":["../../node_modules/lucide-react/dist/esm/icons/book-open.js","../../src/routes/skills/installed.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 BookOpen = createLucideIcon(\"BookOpen\", [\n [\"path\", { d: \"M12 7v14\", key: \"1akyts\" }],\n [\n \"path\",\n {\n d: \"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\",\n key: \"ruj8y\"\n }\n ]\n]);\n\nexport { BookOpen as default };\n//# sourceMappingURL=book-open.js.map\n","/**\n * /skills/installed — read-only browse of SKILL.md files the host\n * agents pick up. Search + category filter; row click opens a\n * detail Dialog with the raw SKILL.md content.\n *\n * Moved from /memory/skills in 2026-05 so skills sit at the top\n * level. The render logic is unchanged from the previous home —\n * only the `useTranslation` namespace and i18n key prefix shifted.\n *\n * No markdown rendering for now: we'd need marked + DOMPurify to\n * safely render arbitrary skill content, and the raw text is what\n * the host agent reads anyway.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { BookOpen, Loader2, RefreshCcw, Search } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\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 { useSkills, useSkillDetail } from '@/hooks/use-skills'\nimport type { SkillMeta } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function SkillsInstalledRoute(): JSX.Element {\n const { t } = useTranslation(['skills', 'common'])\n const { data, isLoading, isFetching, refetch } = useSkills()\n const skills = data?.skills ?? []\n const sources = data?.sources\n\n const [q, setQ] = useState('')\n const [cat, setCat] = useState<string | null>(null)\n const [openSlug, setOpenSlug] = useState<string | null>(null)\n\n const categories = useMemo(() => {\n const set = new Set<string>()\n for (const s of skills) set.add(s.category)\n return Array.from(set).sort()\n }, [skills])\n\n const filtered = useMemo(() => {\n const needle = q.trim().toLowerCase()\n return skills.filter((s) => {\n if (cat && s.category !== cat) return false\n if (!needle) return true\n return (\n s.slug.toLowerCase().includes(needle) ||\n s.name.toLowerCase().includes(needle) ||\n s.description.toLowerCase().includes(needle)\n )\n })\n }, [skills, q, cat])\n\n const grouped = useMemo(() => {\n const m = new Map<string, SkillMeta[]>()\n for (const s of filtered) {\n const arr = m.get(s.category) ?? []\n arr.push(s)\n m.set(s.category, arr)\n }\n return Array.from(m.entries()).sort(([a], [b]) => a.localeCompare(b))\n }, [filtered])\n\n return (\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('installed.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('installed.subtitle')}</p>\n </header>\n\n {sources && (\n <div className=\"rounded-md border border-border bg-surface p-3 text-xs text-text-dim\">\n <div className=\"font-medium text-text mb-1\">{t('installed.sources.title')}</div>\n <div className=\"font-mono\">\n <div>{t('installed.sources.claude')}: <span className=\"text-text\">{sources.claude}</span></div>\n <div>{t('installed.sources.opencode')}: <span className=\"text-text\">{sources.opencode}</span></div>\n </div>\n </div>\n )}\n\n {skills.length > 0 && (\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"relative flex-1 min-w-[180px] max-w-md\">\n <Label htmlFor=\"skill-search\" className=\"text-xs text-text-dim\">\n {t('installed.filter.search')}\n </Label>\n <Search className=\"pointer-events-none absolute left-2 bottom-2 h-4 w-4 text-text-muted\" />\n <Input\n id=\"skill-search\"\n value={q}\n onChange={(e) => setQ(e.target.value)}\n className=\"pl-7 mt-1\"\n />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"skill-cat\" className=\"text-xs text-text-dim\">\n {t('installed.filter.category')}\n </Label>\n <Select\n value={cat ?? '__any__'}\n onValueChange={(v) => setCat(v === '__any__' ? null : v)}\n >\n <SelectTrigger id=\"skill-cat\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('installed.filter.categoryAny')}</SelectItem>\n {categories.map((c) => (\n <SelectItem key={c} value={c}>{c}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n )}\n\n {isLoading ? (\n <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : skills.length === 0 ? (\n <EmptyState\n icon={<BookOpen />}\n title={t('installed.empty.title')}\n description={t('installed.empty.description')}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {grouped.map(([category, list]) => (\n <section key={category}>\n <h2 className=\"mb-2 text-sm font-medium uppercase tracking-wide text-text-dim\">\n {category} · {list.length}\n </h2>\n <ul className=\"flex flex-col gap-2\">\n {list.map((s) => (\n <li key={s.slug}>\n <button\n type=\"button\"\n onClick={() => setOpenSlug(s.slug)}\n className={cn(\n 'flex w-full flex-col gap-1 rounded-md border border-border bg-surface px-3 py-2 text-left',\n 'transition-colors hover:border-border-strong hover:bg-surface-hover',\n )}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{s.name}</span>\n <span className=\"text-text-muted text-xs font-mono\">{s.slug}</span>\n <div className=\"ml-auto flex gap-1\">\n {s.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">\n {t(`installed.agentTag.${a}`, { defaultValue: a })}\n </Badge>\n ))}\n </div>\n </div>\n <p className=\"line-clamp-2 text-sm text-text-dim\">{s.description}</p>\n </button>\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n\n <SkillDetailDialog\n slug={openSlug}\n onOpenChange={(open) => { if (!open) setOpenSlug(null) }}\n />\n </div>\n )\n}\n\ninterface SkillDetailDialogProps {\n slug: string | null\n onOpenChange: (open: boolean) => void\n}\n\nfunction SkillDetailDialog({ slug, onOpenChange }: SkillDetailDialogProps): JSX.Element {\n const { t } = useTranslation('skills')\n const { data, isLoading, isError } = useSkillDetail(slug)\n return (\n <Dialog open={slug != null} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-3xl\">\n <DialogHeader>\n <DialogTitle>{data?.name ?? slug ?? ''}</DialogTitle>\n {data && <DialogDescription>{data.description}</DialogDescription>}\n </DialogHeader>\n {isLoading ? (\n <div className=\"text-sm text-text-dim\">{t('installed.detail.loading')}</div>\n ) : isError ? (\n <div className=\"text-sm text-danger\">{t('installed.detail.loadFailed')}</div>\n ) : data ? (\n <>\n <dl className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs\">\n <dt className=\"text-text-dim\">{t('installed.detail.agentsLabel')}</dt>\n <dd className=\"flex gap-1\">\n {data.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">\n {t(`installed.agentTag.${a}`, { defaultValue: a })}\n </Badge>\n ))}\n </dd>\n <dt className=\"text-text-dim\">slug</dt>\n <dd className=\"font-mono\">{data.slug}</dd>\n </dl>\n <pre\n className={cn(\n 'mt-2 max-h-[60dvh] overflow-auto',\n 'rounded-md bg-surface-2 px-3 py-2',\n 'font-mono text-xs leading-5 text-text',\n 'whitespace-pre-wrap break-words',\n )}\n >\n {data.content}\n </pre>\n </>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n"],"names":["BookOpen","createLucideIcon","SkillsInstalledRoute","useTranslation","data","isLoading","isFetching","refetch","useSkills","skills","sources","q","setQ","useState","cat","setCat","openSlug","setOpenSlug","categories","useMemo","set","s","filtered","needle","grouped","m","arr","b","jsxs","jsx","Button","Loader2","RefreshCcw","Label","Search","Input","e","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","c","EmptyState","category","list","cn","a","Badge","SkillDetailDialog","open","slug","onOpenChange","t","isError","useSkillDetail","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","Fragment"],"mappings":"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","x_google_ignoreList":[0]}
@@ -1,2 +1,2 @@
1
- import{aa as D,a9 as J,q as M,ac as I,ab as Q,Q as t,r as A,c as P,S as V,l as z,m as K,j as U,k as T,B as G,a2 as O,z as X}from"./index-B48azMO-.js";import{e as u}from"./react-C9F3QeMB.js";import{D as Y}from"./data-table-B_BxQrSj.js";import{E as H}from"./empty-state-BKs1qKHx.js";import{S as W}from"./status-badge-_3Ve0Jef.js";import{P as Z}from"./pagination-R9mql8VB.js";import{C as ee}from"./confirm-dialog-BEzhDlk7.js";import{u as te}from"./useQuery-BjFqOBV3.js";import{u as se}from"./use-event-stream-BGeFcayX.js";import{L as ae}from"./loader-circle-DBf4xFjo.js";import{R as ne}from"./refresh-ccw-Clj5sQNF.js";import{C as ie}from"./circle-x-epC-av6L.js";import{B as oe}from"./briefcase-DJRv77W6.js";import"./table-CZwKizat.js";import"./chevron-right-DzyVDoQs.js";import"./dialog-BAZNQtMt.js";import"./x-CHe1y-sG.js";const N={all:["jobs"],list:e=>["jobs","list",e]};function re(e){return te({queryKey:N.list(e),queryFn:()=>M.listJobs(e)})}function le(){const e=D();return()=>e.invalidateQueries({queryKey:N.all})}function ce(){const e=D();return J({mutationFn:a=>M.batchCancelJobs(a),onSuccess:()=>e.invalidateQueries({queryKey:N.all})})}const de=[10,20,50,100],E=20,me=["pending","running","completed","delivered","failed","cancelled","interrupted","replaced","abandoned"],ue={connected:"text-success",connecting:"text-text-dim",reconnecting:"text-warning",closed:"text-text-muted"};function Ae(){const{t:e}=I(["tasks","common"]),[a,i]=Q(),d=le(),r=Math.max(1,Number(a.get("page"))||1),l=Number(a.get("per_page"))||E,m=a.get("status")??null,L=u.useMemo(()=>({limit:l,offset:(r-1)*l,...m?{status:m}:{}}),[r,l,m]),{data:S,isLoading:R,isFetching:C,refetch:$}=re(L),h=S?.jobs??[],n=S?.stats,v=se({job:()=>d()}),[p,w]=u.useState(new Set),[B,y]=u.useState(!1),_=ce();async function F(){const s=Array.from(p).map(Number).filter(Number.isFinite);if(s.length!==0)try{await _.mutateAsync({ids:s}),w(new Set),O.success(e("states.saved",{ns:"common"}))}catch(o){const{message:c}=X(o,e);throw O.error(c),o}}function f(s){const o=new URLSearchParams(a);for(const[c,j]of Object.entries(s))j==null||j===""?o.delete(c):o.set(c,j);Object.keys(s).some(c=>c!=="page")&&o.delete("page"),i(o,{replace:!1})}const g=n?n.pending+n.running+n.completed+n.failed+(n.delivered??0):void 0,k=g?Math.max(1,Math.ceil(g/l)):h.length===l?r+1:r,q=u.useMemo(()=>[{id:"id",header:e("jobs.col.id"),cell:s=>t.jsxs("span",{className:"tabular-nums text-text-dim",children:["#",s.id]}),headClassName:"w-16",hideOnMobile:!1},{id:"agent",header:e("jobs.col.agent"),cell:s=>t.jsx("span",{className:"font-medium",children:s.agent}),headClassName:"w-32"},{id:"prompt",header:e("jobs.col.prompt"),cell:s=>t.jsx("span",{className:"line-clamp-2 text-text-dim",children:s.prompt}),asCardTitle:!0},{id:"status",header:e("jobs.col.status"),cell:s=>t.jsx(W,{status:s.status,children:e(`jobs.status.${s.status}`,{defaultValue:s.status})}),headClassName:"w-32"},{id:"createdAt",header:e("jobs.col.createdAt"),cell:s=>t.jsx("span",{className:"text-text-dim",children:he(s.created_at)}),headClassName:"w-40",hideOnMobile:!0},{id:"duration",header:e("jobs.col.duration"),cell:s=>t.jsx("span",{className:"tabular-nums text-text-dim",children:pe(s.duration_ms)}),headClassName:"w-24",hideOnMobile:!0},{id:"cost",header:e("jobs.col.cost"),cell:s=>t.jsx("span",{className:"tabular-nums text-text-dim",children:s.cost!=null?`$${s.cost.toFixed(4)}`:"—"}),headClassName:"w-24",hideOnMobile:!0}],[e]),b=p.size;return t.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[t.jsxs("header",{className:"flex flex-col gap-1",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[t.jsx("h1",{className:"text-xl font-semibold",children:e("jobs.title")}),t.jsx("span",{className:A("text-xs font-medium tabular-nums",ue[v]),children:e(`jobs.live.${v}`)}),t.jsxs(P,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>$(),disabled:C,"aria-label":e("actions.refresh",{ns:"common"}),children:[C?t.jsx(ae,{className:"h-4 w-4 animate-spin"}):t.jsx(ne,{className:"h-4 w-4"}),t.jsx("span",{className:"hidden sm:inline",children:e("actions.refresh",{ns:"common"})})]})]}),t.jsx("p",{className:"text-sm text-text-dim",children:e("jobs.subtitle")})]}),n&&t.jsxs("div",{className:"grid grid-cols-2 gap-2 sm:grid-cols-4",children:[t.jsx(x,{label:e("jobs.stats.pending"),value:n.pending,tone:"info"}),t.jsx(x,{label:e("jobs.stats.running"),value:n.running,tone:"warning"}),t.jsx(x,{label:e("jobs.stats.completed"),value:n.completed,tone:"success"}),t.jsx(x,{label:e("jobs.stats.failed"),value:n.failed,tone:"danger"})]}),t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("label",{className:"text-sm text-text-dim",htmlFor:"status-filter",children:e("jobs.filter.status")}),t.jsxs(V,{value:m??"__any__",onValueChange:s=>f({status:s==="__any__"?null:s}),children:[t.jsx(z,{id:"status-filter",className:"w-[160px]",children:t.jsx(K,{})}),t.jsxs(U,{children:[t.jsx(T,{value:"__any__",children:e("jobs.filter.statusAny")}),me.map(s=>t.jsx(T,{value:s,children:e(`jobs.status.${s}`,{defaultValue:s})},s))]})]})]}),b>0&&t.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[t.jsx(G,{variant:"secondary",children:e("jobs.batch.selected",{count:b})}),t.jsxs(P,{variant:"destructive",size:"sm",onClick:()=>y(!0),disabled:_.isPending,children:[t.jsx(ie,{className:"h-4 w-4"}),e("jobs.batch.cancel")]})]})]}),t.jsx(Y,{columns:q,rows:h,getRowId:s=>String(s.id),loading:R,selection:p,onSelectionChange:w,emptyState:t.jsx(H,{icon:t.jsx(oe,{}),title:e("jobs.empty.title"),description:e("jobs.empty.description")})}),(h.length>0||r>1)&&t.jsx(Z,{page:r,totalPages:k,totalRows:g,perPage:l,perPageOptions:de,onPageChange:s=>f({page:s===1?null:String(s)}),onPerPageChange:s=>f({per_page:s===E?null:String(s)})}),t.jsx(ee,{open:B,onOpenChange:y,title:e("jobs.batch.confirmCancel",{count:b}),description:e("jobs.batch.confirmCancelDesc"),intent:"danger",confirmLabel:e("jobs.batch.cancel"),onConfirm:F})]})}const xe={info:"border-info/30 text-info",success:"border-success/30 text-success",warning:"border-warning/30 text-warning",danger:"border-danger/30 text-danger"};function x({label:e,value:a,tone:i}){return t.jsxs("div",{className:A("rounded-md border bg-surface px-3 py-2",xe[i]),children:[t.jsx("div",{className:"text-xs uppercase tracking-wide text-text-dim",children:e}),t.jsx("div",{className:"text-2xl font-semibold tabular-nums",children:a})]})}function he(e){try{const a=new Date(e);if(Number.isNaN(a.getTime()))return e;const i=new Date;return a.toDateString()===i.toDateString()?a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit"}):a.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return e}}function pe(e){if(e==null)return"—";if(e<1e3)return`${e}ms`;const a=e/1e3;if(a<60)return`${a.toFixed(1)}s`;const i=Math.floor(a/60),d=Math.round(a-i*60);return`${i}m${d}s`}export{Ae as default};
2
- //# sourceMappingURL=jobs-g1Wl8_zd.js.map
1
+ import{aa as D,a9 as J,q as M,ac as I,ab as Q,Q as t,r as A,c as P,S as V,l as z,m as K,j as U,k as T,B as G,a2 as O,z as X}from"./index-CITyis5g.js";import{e as u}from"./react-C9F3QeMB.js";import{D as Y}from"./data-table-BKTemkw9.js";import{E as H}from"./empty-state-C87S9Wor.js";import{S as W}from"./status-badge-HSHkhdur.js";import{P as Z}from"./pagination-Cgt8W6EX.js";import{C as ee}from"./confirm-dialog-B53cbWzX.js";import{u as te}from"./useQuery-DFryK9Pn.js";import{u as se}from"./use-event-stream-BGeFcayX.js";import{L as ae}from"./loader-circle-BFp6Oc8_.js";import{R as ne}from"./refresh-ccw-CPSYEbgh.js";import{C as ie}from"./circle-x-C6qY5UUl.js";import{B as oe}from"./briefcase-DkCTxO1A.js";import"./table-BBISYTH-.js";import"./chevron-right-DEyrYFz4.js";import"./dialog-Cevi7KvQ.js";import"./x-D5196-9X.js";const N={all:["jobs"],list:e=>["jobs","list",e]};function re(e){return te({queryKey:N.list(e),queryFn:()=>M.listJobs(e)})}function le(){const e=D();return()=>e.invalidateQueries({queryKey:N.all})}function ce(){const e=D();return J({mutationFn:a=>M.batchCancelJobs(a),onSuccess:()=>e.invalidateQueries({queryKey:N.all})})}const de=[10,20,50,100],E=20,me=["pending","running","completed","delivered","failed","cancelled","interrupted","replaced","abandoned"],ue={connected:"text-success",connecting:"text-text-dim",reconnecting:"text-warning",closed:"text-text-muted"};function Ae(){const{t:e}=I(["tasks","common"]),[a,i]=Q(),d=le(),r=Math.max(1,Number(a.get("page"))||1),l=Number(a.get("per_page"))||E,m=a.get("status")??null,L=u.useMemo(()=>({limit:l,offset:(r-1)*l,...m?{status:m}:{}}),[r,l,m]),{data:S,isLoading:R,isFetching:C,refetch:$}=re(L),h=S?.jobs??[],n=S?.stats,v=se({job:()=>d()}),[p,w]=u.useState(new Set),[B,y]=u.useState(!1),_=ce();async function F(){const s=Array.from(p).map(Number).filter(Number.isFinite);if(s.length!==0)try{await _.mutateAsync({ids:s}),w(new Set),O.success(e("states.saved",{ns:"common"}))}catch(o){const{message:c}=X(o,e);throw O.error(c),o}}function f(s){const o=new URLSearchParams(a);for(const[c,j]of Object.entries(s))j==null||j===""?o.delete(c):o.set(c,j);Object.keys(s).some(c=>c!=="page")&&o.delete("page"),i(o,{replace:!1})}const g=n?n.pending+n.running+n.completed+n.failed+(n.delivered??0):void 0,k=g?Math.max(1,Math.ceil(g/l)):h.length===l?r+1:r,q=u.useMemo(()=>[{id:"id",header:e("jobs.col.id"),cell:s=>t.jsxs("span",{className:"tabular-nums text-text-dim",children:["#",s.id]}),headClassName:"w-16",hideOnMobile:!1},{id:"agent",header:e("jobs.col.agent"),cell:s=>t.jsx("span",{className:"font-medium",children:s.agent}),headClassName:"w-32"},{id:"prompt",header:e("jobs.col.prompt"),cell:s=>t.jsx("span",{className:"line-clamp-2 text-text-dim",children:s.prompt}),asCardTitle:!0},{id:"status",header:e("jobs.col.status"),cell:s=>t.jsx(W,{status:s.status,children:e(`jobs.status.${s.status}`,{defaultValue:s.status})}),headClassName:"w-32"},{id:"createdAt",header:e("jobs.col.createdAt"),cell:s=>t.jsx("span",{className:"text-text-dim",children:he(s.created_at)}),headClassName:"w-40",hideOnMobile:!0},{id:"duration",header:e("jobs.col.duration"),cell:s=>t.jsx("span",{className:"tabular-nums text-text-dim",children:pe(s.duration_ms)}),headClassName:"w-24",hideOnMobile:!0},{id:"cost",header:e("jobs.col.cost"),cell:s=>t.jsx("span",{className:"tabular-nums text-text-dim",children:s.cost!=null?`$${s.cost.toFixed(4)}`:"—"}),headClassName:"w-24",hideOnMobile:!0}],[e]),b=p.size;return t.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[t.jsxs("header",{className:"flex flex-col gap-1",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[t.jsx("h1",{className:"text-xl font-semibold",children:e("jobs.title")}),t.jsx("span",{className:A("text-xs font-medium tabular-nums",ue[v]),children:e(`jobs.live.${v}`)}),t.jsxs(P,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>$(),disabled:C,"aria-label":e("actions.refresh",{ns:"common"}),children:[C?t.jsx(ae,{className:"h-4 w-4 animate-spin"}):t.jsx(ne,{className:"h-4 w-4"}),t.jsx("span",{className:"hidden sm:inline",children:e("actions.refresh",{ns:"common"})})]})]}),t.jsx("p",{className:"text-sm text-text-dim",children:e("jobs.subtitle")})]}),n&&t.jsxs("div",{className:"grid grid-cols-2 gap-2 sm:grid-cols-4",children:[t.jsx(x,{label:e("jobs.stats.pending"),value:n.pending,tone:"info"}),t.jsx(x,{label:e("jobs.stats.running"),value:n.running,tone:"warning"}),t.jsx(x,{label:e("jobs.stats.completed"),value:n.completed,tone:"success"}),t.jsx(x,{label:e("jobs.stats.failed"),value:n.failed,tone:"danger"})]}),t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("label",{className:"text-sm text-text-dim",htmlFor:"status-filter",children:e("jobs.filter.status")}),t.jsxs(V,{value:m??"__any__",onValueChange:s=>f({status:s==="__any__"?null:s}),children:[t.jsx(z,{id:"status-filter",className:"w-[160px]",children:t.jsx(K,{})}),t.jsxs(U,{children:[t.jsx(T,{value:"__any__",children:e("jobs.filter.statusAny")}),me.map(s=>t.jsx(T,{value:s,children:e(`jobs.status.${s}`,{defaultValue:s})},s))]})]})]}),b>0&&t.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[t.jsx(G,{variant:"secondary",children:e("jobs.batch.selected",{count:b})}),t.jsxs(P,{variant:"destructive",size:"sm",onClick:()=>y(!0),disabled:_.isPending,children:[t.jsx(ie,{className:"h-4 w-4"}),e("jobs.batch.cancel")]})]})]}),t.jsx(Y,{columns:q,rows:h,getRowId:s=>String(s.id),loading:R,selection:p,onSelectionChange:w,emptyState:t.jsx(H,{icon:t.jsx(oe,{}),title:e("jobs.empty.title"),description:e("jobs.empty.description")})}),(h.length>0||r>1)&&t.jsx(Z,{page:r,totalPages:k,totalRows:g,perPage:l,perPageOptions:de,onPageChange:s=>f({page:s===1?null:String(s)}),onPerPageChange:s=>f({per_page:s===E?null:String(s)})}),t.jsx(ee,{open:B,onOpenChange:y,title:e("jobs.batch.confirmCancel",{count:b}),description:e("jobs.batch.confirmCancelDesc"),intent:"danger",confirmLabel:e("jobs.batch.cancel"),onConfirm:F})]})}const xe={info:"border-info/30 text-info",success:"border-success/30 text-success",warning:"border-warning/30 text-warning",danger:"border-danger/30 text-danger"};function x({label:e,value:a,tone:i}){return t.jsxs("div",{className:A("rounded-md border bg-surface px-3 py-2",xe[i]),children:[t.jsx("div",{className:"text-xs uppercase tracking-wide text-text-dim",children:e}),t.jsx("div",{className:"text-2xl font-semibold tabular-nums",children:a})]})}function he(e){try{const a=new Date(e);if(Number.isNaN(a.getTime()))return e;const i=new Date;return a.toDateString()===i.toDateString()?a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit"}):a.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return e}}function pe(e){if(e==null)return"—";if(e<1e3)return`${e}ms`;const a=e/1e3;if(a<60)return`${a.toFixed(1)}s`;const i=Math.floor(a/60),d=Math.round(a-i*60);return`${i}m${d}s`}export{Ae as default};
2
+ //# sourceMappingURL=jobs-DwA9Ip87.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jobs-g1Wl8_zd.js","sources":["../../src/hooks/use-jobs.ts","../../src/routes/tasks/jobs.tsx"],"sourcesContent":["/**\n * useJobs — react-query wrappers for the jobs domain.\n *\n * useJobs(query) — list with filter / pagination\n * useBatchCancelJobs() — bulk cancel mutation\n * useBatchRunJobs() — bulk re-run mutation\n *\n * Query keys: `['jobs', 'list', stableQuery]` so SSE invalidation\n * via useEventStream({ job: () => qc.invalidateQueries(['jobs']) })\n * clears every list view at once. The serialised query object is\n * stable across renders (react-query handles deep equality), so\n * keying on it directly is safe.\n *\n * Why hooks not raw `useQuery`: every route hits the same listJobs\n * endpoint with similar shapes; centralising the key + error toast\n * (caller-supplied) here keeps the route files focused on layout.\n */\n\nimport { useQueryClient, useMutation, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { BatchJobBody, ListJobsQuery, ListJobsResponse } from '@/types/api'\n\nexport const jobsKeys = {\n all: ['jobs'] as const,\n list: (q: ListJobsQuery) => ['jobs', 'list', q] as const,\n}\n\nexport function useJobs(query: ListJobsQuery) {\n return useQuery<ListJobsResponse>({\n queryKey: jobsKeys.list(query),\n queryFn: () => api.listJobs(query),\n })\n}\n\n/** Force-refresh every jobs list view. Call this from the SSE\n * `job` handler in route files. */\nexport function useInvalidateJobs() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: jobsKeys.all })\n}\n\nexport function useBatchCancelJobs() {\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (body: BatchJobBody) => api.batchCancelJobs(body),\n onSuccess: () => qc.invalidateQueries({ queryKey: jobsKeys.all }),\n })\n}\n\nexport function useBatchRunJobs() {\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (body: BatchJobBody) => api.batchRunJobs(body),\n onSuccess: () => qc.invalidateQueries({ queryKey: jobsKeys.all }),\n })\n}\n","/**\n * /tasks/jobs — the long-running agent invocations list.\n *\n * Composes every PR-8/9 primitive: DataTable + StatusBadge + Pagination\n * + ConfirmDialog + EmptyState + useEventStream. The route is a thin\n * presenter: data via useJobs (react-query), URL state via useSearchParams,\n * live refresh via the SSE `job` event invalidating the jobs query key.\n *\n * URL contract (all params optional):\n * ?page=2&per_page=20&status=running\n *\n * Why URL state rather than zustand:\n * * deep-link friendly (\"show me failed jobs page 3\")\n * * back/forward browser history navigates filter state\n * * no risk of a leftover filter showing wrong rows after route swap\n *\n * Mobile: the DataTable already swaps to a card view below md. The\n * stats strip wraps; the filter row stacks vertically.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Briefcase, Loader2, RefreshCcw, XCircle } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { Pagination } from '@/components/common/pagination'\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useJobs,\n useInvalidateJobs,\n useBatchCancelJobs,\n} from '@/hooks/use-jobs'\nimport { useEventStream, type SseStatus } from '@/hooks/use-event-stream'\nimport type { Job, JobStatus, ListJobsQuery } from '@/types/api'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\nconst PER_PAGE_OPTIONS = [10, 20, 50, 100]\nconst DEFAULT_PER_PAGE = 20\n\nconst STATUS_OPTIONS: JobStatus[] = [\n 'pending',\n 'running',\n 'completed',\n 'delivered',\n 'failed',\n 'cancelled',\n 'interrupted',\n 'replaced',\n 'abandoned',\n]\n\nconst LIVE_STATUS_CLASS: Record<SseStatus, string> = {\n connected: 'text-success',\n connecting: 'text-text-dim',\n reconnecting: 'text-warning',\n closed: 'text-text-muted',\n}\n\nexport default function JobsRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const invalidateJobs = useInvalidateJobs()\n\n // ─── URL state ───\n const page = Math.max(1, Number(params.get('page')) || 1)\n const perPage = Number(params.get('per_page')) || DEFAULT_PER_PAGE\n const status = (params.get('status') as JobStatus | null) ?? null\n\n const query: ListJobsQuery = useMemo(\n () => ({\n limit: perPage,\n offset: (page - 1) * perPage,\n ...(status ? { status } : {}),\n }),\n [page, perPage, status],\n )\n\n const { data, isLoading, isFetching, refetch } = useJobs(query)\n const jobs = data?.jobs ?? []\n const stats = data?.stats\n\n // ─── Live refresh via SSE ───\n const live = useEventStream({\n job: () => invalidateJobs(),\n })\n\n // ─── Selection ───\n const [selection, setSelection] = useState<Set<string>>(new Set())\n\n // ─── Confirm dialog ───\n const [confirmCancelOpen, setConfirmCancelOpen] = useState(false)\n const batchCancel = useBatchCancelJobs()\n\n async function onConfirmBatchCancel(): Promise<void> {\n const ids = Array.from(selection).map(Number).filter(Number.isFinite)\n if (ids.length === 0) return\n try {\n await batchCancel.mutateAsync({ ids })\n setSelection(new Set())\n toast.success(t('states.saved', { ns: 'common' }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err // keep dialog open\n }\n }\n\n // ─── URL writers (single source of truth for filter state) ───\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 // Any filter change implicitly resets to page 1.\n if (Object.keys(patch).some((k) => k !== 'page')) next.delete('page')\n setParams(next, { replace: false })\n }\n\n // ─── Derived ───\n // The backend doesn't ship a true total-count field. Until it does\n // we infer pagination from the page itself: if a page returns a\n // full page, assume there's at least one more; otherwise this is\n // the last one. Stats give an exact total of pending/running but\n // not \"all kinds\", so the page label stays \"Page X of N\" with N\n // derived conservatively.\n const totalRows = stats\n ? (stats.pending + stats.running + stats.completed + stats.failed + (stats.delivered ?? 0))\n : undefined\n const totalPages = totalRows\n ? Math.max(1, Math.ceil(totalRows / perPage))\n : jobs.length === perPage ? page + 1 : page\n\n // ─── Columns ───\n const columns: DataTableColumn<Job>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('jobs.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n hideOnMobile: false,\n },\n {\n id: 'agent',\n header: t('jobs.col.agent'),\n cell: (r) => <span className=\"font-medium\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('jobs.col.prompt'),\n cell: (r) => (\n <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>\n ),\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('jobs.col.status'),\n cell: (r) => (\n <StatusBadge status={r.status}>\n {t(`jobs.status.${r.status}`, { defaultValue: r.status })}\n </StatusBadge>\n ),\n headClassName: 'w-32',\n },\n {\n id: 'createdAt',\n header: t('jobs.col.createdAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.created_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'duration',\n header: t('jobs.col.duration'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">{formatDuration(r.duration_ms)}</span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'cost',\n header: t('jobs.col.cost'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">\n {r.cost != null ? `$${r.cost.toFixed(4)}` : '—'}\n </span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n ],\n [t],\n )\n\n const selectedCount = selection.size\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n {/* Page header */}\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('jobs.title')}</h1>\n <span className={cn('text-xs font-medium tabular-nums', LIVE_STATUS_CLASS[live])}>\n {t(`jobs.live.${live}`)}\n </span>\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('jobs.subtitle')}</p>\n </header>\n\n {/* Stats KPI strip */}\n {stats && (\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <StatCard label={t('jobs.stats.pending')} value={stats.pending} tone=\"info\" />\n <StatCard label={t('jobs.stats.running')} value={stats.running} tone=\"warning\" />\n <StatCard label={t('jobs.stats.completed')} value={stats.completed} tone=\"success\" />\n <StatCard label={t('jobs.stats.failed')} value={stats.failed} tone=\"danger\" />\n </div>\n )}\n\n {/* Filter row */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"status-filter\">\n {t('jobs.filter.status')}\n </label>\n <Select\n value={status ?? '__any__'}\n onValueChange={(v) => patchParams({ status: v === '__any__' ? null : v })}\n >\n <SelectTrigger id=\"status-filter\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('jobs.filter.statusAny')}</SelectItem>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`jobs.status.${s}`, { defaultValue: s })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Bulk action bar — appears when rows are selected. */}\n {selectedCount > 0 && (\n <div className=\"ml-auto flex items-center gap-2\">\n <Badge variant=\"secondary\">\n {t('jobs.batch.selected', { count: selectedCount })}\n </Badge>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setConfirmCancelOpen(true)}\n disabled={batchCancel.isPending}\n >\n <XCircle className=\"h-4 w-4\" />\n {t('jobs.batch.cancel')}\n </Button>\n </div>\n )}\n </div>\n\n {/* Table */}\n <DataTable\n columns={columns}\n rows={jobs}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n selection={selection}\n onSelectionChange={setSelection}\n emptyState={\n <EmptyState\n icon={<Briefcase />}\n title={t('jobs.empty.title')}\n description={t('jobs.empty.description')}\n />\n }\n />\n\n {/* Footer pagination */}\n {(jobs.length > 0 || page > 1) && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalRows={totalRows}\n perPage={perPage}\n perPageOptions={PER_PAGE_OPTIONS}\n onPageChange={(p) => patchParams({ page: p === 1 ? null : String(p) })}\n onPerPageChange={(pp) => patchParams({ per_page: pp === DEFAULT_PER_PAGE ? null : String(pp) })}\n />\n )}\n\n <ConfirmDialog\n open={confirmCancelOpen}\n onOpenChange={setConfirmCancelOpen}\n title={t('jobs.batch.confirmCancel', { count: selectedCount })}\n description={t('jobs.batch.confirmCancelDesc')}\n intent=\"danger\"\n confirmLabel={t('jobs.batch.cancel')}\n onConfirm={onConfirmBatchCancel}\n />\n </div>\n )\n}\n\n// ─── Helpers ───\n\ninterface StatCardProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<StatCardProps['tone'], string> = {\n info: 'border-info/30 text-info',\n success: 'border-success/30 text-success',\n warning: 'border-warning/30 text-warning',\n danger: 'border-danger/30 text-danger',\n}\n\nfunction StatCard({ label, value, tone }: StatCardProps): JSX.Element {\n return (\n <div\n className={cn(\n 'rounded-md border bg-surface px-3 py-2',\n TONE_STYLES[tone],\n )}\n >\n <div className=\"text-xs uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-2xl font-semibold tabular-nums\">{value}</div>\n </div>\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', second: '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\nfunction formatDuration(ms: number | undefined): string {\n if (ms == null) return '—'\n if (ms < 1000) return `${ms}ms`\n const s = ms / 1000\n if (s < 60) return `${s.toFixed(1)}s`\n const m = Math.floor(s / 60)\n const rs = Math.round(s - m * 60)\n return `${m}m${rs}s`\n}\n"],"names":["jobsKeys","q","useJobs","query","useQuery","api","useInvalidateJobs","qc","useQueryClient","useBatchCancelJobs","useMutation","body","PER_PAGE_OPTIONS","DEFAULT_PER_PAGE","STATUS_OPTIONS","LIVE_STATUS_CLASS","JobsRoute","t","useTranslation","params","setParams","useSearchParams","invalidateJobs","page","perPage","status","useMemo","data","isLoading","isFetching","refetch","jobs","stats","live","useEventStream","selection","setSelection","useState","confirmCancelOpen","setConfirmCancelOpen","batchCancel","onConfirmBatchCancel","ids","toast","err","message","describeError","patchParams","patch","next","k","v","totalRows","totalPages","columns","r","jsxs","jsx","StatusBadge","formatTime","formatDuration","selectedCount","cn","Button","Loader2","RefreshCcw","StatCard","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Badge","XCircle","DataTable","EmptyState","Briefcase","Pagination","p","pp","ConfirmDialog","TONE_STYLES","label","value","tone","iso","d","now","ms","s","m","rs"],"mappings":"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"}
1
+ {"version":3,"file":"jobs-DwA9Ip87.js","sources":["../../src/hooks/use-jobs.ts","../../src/routes/tasks/jobs.tsx"],"sourcesContent":["/**\n * useJobs — react-query wrappers for the jobs domain.\n *\n * useJobs(query) — list with filter / pagination\n * useBatchCancelJobs() — bulk cancel mutation\n * useBatchRunJobs() — bulk re-run mutation\n *\n * Query keys: `['jobs', 'list', stableQuery]` so SSE invalidation\n * via useEventStream({ job: () => qc.invalidateQueries(['jobs']) })\n * clears every list view at once. The serialised query object is\n * stable across renders (react-query handles deep equality), so\n * keying on it directly is safe.\n *\n * Why hooks not raw `useQuery`: every route hits the same listJobs\n * endpoint with similar shapes; centralising the key + error toast\n * (caller-supplied) here keeps the route files focused on layout.\n */\n\nimport { useQueryClient, useMutation, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { BatchJobBody, ListJobsQuery, ListJobsResponse } from '@/types/api'\n\nexport const jobsKeys = {\n all: ['jobs'] as const,\n list: (q: ListJobsQuery) => ['jobs', 'list', q] as const,\n}\n\nexport function useJobs(query: ListJobsQuery) {\n return useQuery<ListJobsResponse>({\n queryKey: jobsKeys.list(query),\n queryFn: () => api.listJobs(query),\n })\n}\n\n/** Force-refresh every jobs list view. Call this from the SSE\n * `job` handler in route files. */\nexport function useInvalidateJobs() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: jobsKeys.all })\n}\n\nexport function useBatchCancelJobs() {\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (body: BatchJobBody) => api.batchCancelJobs(body),\n onSuccess: () => qc.invalidateQueries({ queryKey: jobsKeys.all }),\n })\n}\n\nexport function useBatchRunJobs() {\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (body: BatchJobBody) => api.batchRunJobs(body),\n onSuccess: () => qc.invalidateQueries({ queryKey: jobsKeys.all }),\n })\n}\n","/**\n * /tasks/jobs — the long-running agent invocations list.\n *\n * Composes every PR-8/9 primitive: DataTable + StatusBadge + Pagination\n * + ConfirmDialog + EmptyState + useEventStream. The route is a thin\n * presenter: data via useJobs (react-query), URL state via useSearchParams,\n * live refresh via the SSE `job` event invalidating the jobs query key.\n *\n * URL contract (all params optional):\n * ?page=2&per_page=20&status=running\n *\n * Why URL state rather than zustand:\n * * deep-link friendly (\"show me failed jobs page 3\")\n * * back/forward browser history navigates filter state\n * * no risk of a leftover filter showing wrong rows after route swap\n *\n * Mobile: the DataTable already swaps to a card view below md. The\n * stats strip wraps; the filter row stacks vertically.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Briefcase, Loader2, RefreshCcw, XCircle } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { Pagination } from '@/components/common/pagination'\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useJobs,\n useInvalidateJobs,\n useBatchCancelJobs,\n} from '@/hooks/use-jobs'\nimport { useEventStream, type SseStatus } from '@/hooks/use-event-stream'\nimport type { Job, JobStatus, ListJobsQuery } from '@/types/api'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\nconst PER_PAGE_OPTIONS = [10, 20, 50, 100]\nconst DEFAULT_PER_PAGE = 20\n\nconst STATUS_OPTIONS: JobStatus[] = [\n 'pending',\n 'running',\n 'completed',\n 'delivered',\n 'failed',\n 'cancelled',\n 'interrupted',\n 'replaced',\n 'abandoned',\n]\n\nconst LIVE_STATUS_CLASS: Record<SseStatus, string> = {\n connected: 'text-success',\n connecting: 'text-text-dim',\n reconnecting: 'text-warning',\n closed: 'text-text-muted',\n}\n\nexport default function JobsRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const invalidateJobs = useInvalidateJobs()\n\n // ─── URL state ───\n const page = Math.max(1, Number(params.get('page')) || 1)\n const perPage = Number(params.get('per_page')) || DEFAULT_PER_PAGE\n const status = (params.get('status') as JobStatus | null) ?? null\n\n const query: ListJobsQuery = useMemo(\n () => ({\n limit: perPage,\n offset: (page - 1) * perPage,\n ...(status ? { status } : {}),\n }),\n [page, perPage, status],\n )\n\n const { data, isLoading, isFetching, refetch } = useJobs(query)\n const jobs = data?.jobs ?? []\n const stats = data?.stats\n\n // ─── Live refresh via SSE ───\n const live = useEventStream({\n job: () => invalidateJobs(),\n })\n\n // ─── Selection ───\n const [selection, setSelection] = useState<Set<string>>(new Set())\n\n // ─── Confirm dialog ───\n const [confirmCancelOpen, setConfirmCancelOpen] = useState(false)\n const batchCancel = useBatchCancelJobs()\n\n async function onConfirmBatchCancel(): Promise<void> {\n const ids = Array.from(selection).map(Number).filter(Number.isFinite)\n if (ids.length === 0) return\n try {\n await batchCancel.mutateAsync({ ids })\n setSelection(new Set())\n toast.success(t('states.saved', { ns: 'common' }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err // keep dialog open\n }\n }\n\n // ─── URL writers (single source of truth for filter state) ───\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 // Any filter change implicitly resets to page 1.\n if (Object.keys(patch).some((k) => k !== 'page')) next.delete('page')\n setParams(next, { replace: false })\n }\n\n // ─── Derived ───\n // The backend doesn't ship a true total-count field. Until it does\n // we infer pagination from the page itself: if a page returns a\n // full page, assume there's at least one more; otherwise this is\n // the last one. Stats give an exact total of pending/running but\n // not \"all kinds\", so the page label stays \"Page X of N\" with N\n // derived conservatively.\n const totalRows = stats\n ? (stats.pending + stats.running + stats.completed + stats.failed + (stats.delivered ?? 0))\n : undefined\n const totalPages = totalRows\n ? Math.max(1, Math.ceil(totalRows / perPage))\n : jobs.length === perPage ? page + 1 : page\n\n // ─── Columns ───\n const columns: DataTableColumn<Job>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('jobs.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n hideOnMobile: false,\n },\n {\n id: 'agent',\n header: t('jobs.col.agent'),\n cell: (r) => <span className=\"font-medium\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('jobs.col.prompt'),\n cell: (r) => (\n <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>\n ),\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('jobs.col.status'),\n cell: (r) => (\n <StatusBadge status={r.status}>\n {t(`jobs.status.${r.status}`, { defaultValue: r.status })}\n </StatusBadge>\n ),\n headClassName: 'w-32',\n },\n {\n id: 'createdAt',\n header: t('jobs.col.createdAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.created_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'duration',\n header: t('jobs.col.duration'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">{formatDuration(r.duration_ms)}</span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'cost',\n header: t('jobs.col.cost'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">\n {r.cost != null ? `$${r.cost.toFixed(4)}` : '—'}\n </span>\n ),\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n ],\n [t],\n )\n\n const selectedCount = selection.size\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n {/* Page header */}\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('jobs.title')}</h1>\n <span className={cn('text-xs font-medium tabular-nums', LIVE_STATUS_CLASS[live])}>\n {t(`jobs.live.${live}`)}\n </span>\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('jobs.subtitle')}</p>\n </header>\n\n {/* Stats KPI strip */}\n {stats && (\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <StatCard label={t('jobs.stats.pending')} value={stats.pending} tone=\"info\" />\n <StatCard label={t('jobs.stats.running')} value={stats.running} tone=\"warning\" />\n <StatCard label={t('jobs.stats.completed')} value={stats.completed} tone=\"success\" />\n <StatCard label={t('jobs.stats.failed')} value={stats.failed} tone=\"danger\" />\n </div>\n )}\n\n {/* Filter row */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"status-filter\">\n {t('jobs.filter.status')}\n </label>\n <Select\n value={status ?? '__any__'}\n onValueChange={(v) => patchParams({ status: v === '__any__' ? null : v })}\n >\n <SelectTrigger id=\"status-filter\" className=\"w-[160px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('jobs.filter.statusAny')}</SelectItem>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`jobs.status.${s}`, { defaultValue: s })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Bulk action bar — appears when rows are selected. */}\n {selectedCount > 0 && (\n <div className=\"ml-auto flex items-center gap-2\">\n <Badge variant=\"secondary\">\n {t('jobs.batch.selected', { count: selectedCount })}\n </Badge>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setConfirmCancelOpen(true)}\n disabled={batchCancel.isPending}\n >\n <XCircle className=\"h-4 w-4\" />\n {t('jobs.batch.cancel')}\n </Button>\n </div>\n )}\n </div>\n\n {/* Table */}\n <DataTable\n columns={columns}\n rows={jobs}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n selection={selection}\n onSelectionChange={setSelection}\n emptyState={\n <EmptyState\n icon={<Briefcase />}\n title={t('jobs.empty.title')}\n description={t('jobs.empty.description')}\n />\n }\n />\n\n {/* Footer pagination */}\n {(jobs.length > 0 || page > 1) && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalRows={totalRows}\n perPage={perPage}\n perPageOptions={PER_PAGE_OPTIONS}\n onPageChange={(p) => patchParams({ page: p === 1 ? null : String(p) })}\n onPerPageChange={(pp) => patchParams({ per_page: pp === DEFAULT_PER_PAGE ? null : String(pp) })}\n />\n )}\n\n <ConfirmDialog\n open={confirmCancelOpen}\n onOpenChange={setConfirmCancelOpen}\n title={t('jobs.batch.confirmCancel', { count: selectedCount })}\n description={t('jobs.batch.confirmCancelDesc')}\n intent=\"danger\"\n confirmLabel={t('jobs.batch.cancel')}\n onConfirm={onConfirmBatchCancel}\n />\n </div>\n )\n}\n\n// ─── Helpers ───\n\ninterface StatCardProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<StatCardProps['tone'], string> = {\n info: 'border-info/30 text-info',\n success: 'border-success/30 text-success',\n warning: 'border-warning/30 text-warning',\n danger: 'border-danger/30 text-danger',\n}\n\nfunction StatCard({ label, value, tone }: StatCardProps): JSX.Element {\n return (\n <div\n className={cn(\n 'rounded-md border bg-surface px-3 py-2',\n TONE_STYLES[tone],\n )}\n >\n <div className=\"text-xs uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-2xl font-semibold tabular-nums\">{value}</div>\n </div>\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', second: '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\nfunction formatDuration(ms: number | undefined): string {\n if (ms == null) return '—'\n if (ms < 1000) return `${ms}ms`\n const s = ms / 1000\n if (s < 60) return `${s.toFixed(1)}s`\n const m = Math.floor(s / 60)\n const rs = Math.round(s - m * 60)\n return `${m}m${rs}s`\n}\n"],"names":["jobsKeys","q","useJobs","query","useQuery","api","useInvalidateJobs","qc","useQueryClient","useBatchCancelJobs","useMutation","body","PER_PAGE_OPTIONS","DEFAULT_PER_PAGE","STATUS_OPTIONS","LIVE_STATUS_CLASS","JobsRoute","t","useTranslation","params","setParams","useSearchParams","invalidateJobs","page","perPage","status","useMemo","data","isLoading","isFetching","refetch","jobs","stats","live","useEventStream","selection","setSelection","useState","confirmCancelOpen","setConfirmCancelOpen","batchCancel","onConfirmBatchCancel","ids","toast","err","message","describeError","patchParams","patch","next","k","v","totalRows","totalPages","columns","r","jsxs","jsx","StatusBadge","formatTime","formatDuration","selectedCount","cn","Button","Loader2","RefreshCcw","StatCard","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Badge","XCircle","DataTable","EmptyState","Briefcase","Pagination","p","pp","ConfirmDialog","TONE_STYLES","label","value","tone","iso","d","now","ms","s","m","rs"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{ac as o,Q as e,T as r,N as i,r as n,O as m}from"./index-B48azMO-.js";import"./react-C9F3QeMB.js";const u=[{to:"messengers",i18nKey:"tabs.messengers",mounted:!0},{to:"email",i18nKey:"tabs.email",mounted:!0},{to:"viewer",i18nKey:"tabs.viewer",mounted:!0},{to:"agents",i18nKey:"tabs.agents",mounted:!0},{to:"policy",i18nKey:"tabs.policy",mounted:!0},{to:"workspaces",i18nKey:"tabs.workspaces",mounted:!0},{to:"admins",i18nKey:"tabs.admins",mounted:!0},{to:"service",i18nKey:"tabs.service",mounted:!0}];function l(){const{t:s}=o("settings");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(r,{}),e.jsx("nav",{className:n("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:u.filter(t=>t.mounted).map(t=>e.jsx(i,{to:t.to,className:({isActive:a})=>n("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(m,{})})]})}export{l as default};
2
- //# sourceMappingURL=layout-D_CUXsIR.js.map
1
+ import{ac as o,Q as e,T as r,N as i,r as n,O as m}from"./index-CITyis5g.js";import"./react-C9F3QeMB.js";const u=[{to:"messengers",i18nKey:"tabs.messengers",mounted:!0},{to:"email",i18nKey:"tabs.email",mounted:!0},{to:"viewer",i18nKey:"tabs.viewer",mounted:!0},{to:"agents",i18nKey:"tabs.agents",mounted:!0},{to:"policy",i18nKey:"tabs.policy",mounted:!0},{to:"workspaces",i18nKey:"tabs.workspaces",mounted:!0},{to:"admins",i18nKey:"tabs.admins",mounted:!0},{to:"service",i18nKey:"tabs.service",mounted:!0}];function l(){const{t:s}=o("settings");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(r,{}),e.jsx("nav",{className:n("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:u.filter(t=>t.mounted).map(t=>e.jsx(i,{to:t.to,className:({isActive:a})=>n("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(m,{})})]})}export{l as default};
2
+ //# sourceMappingURL=layout-BxrFilDN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-D_CUXsIR.js","sources":["../../src/routes/settings/layout.tsx"],"sourcesContent":["/**\n * /settings — layout shell with sub-tab nav (environment / service).\n *\n * Workspaces / WeChat QR / Admin allowlist will land as additional\n * sub-tabs in PR-19b/c. The `mounted` flag gates their nav links.\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'messengers', i18nKey: 'tabs.messengers', mounted: true },\n { to: 'email', i18nKey: 'tabs.email', mounted: true },\n { to: 'viewer', i18nKey: 'tabs.viewer', mounted: true },\n { to: 'agents', i18nKey: 'tabs.agents', mounted: true },\n { to: 'policy', i18nKey: 'tabs.policy', mounted: true },\n { to: 'workspaces', i18nKey: 'tabs.workspaces', mounted: true },\n { to: 'admins', i18nKey: 'tabs.admins', mounted: true },\n // R13 follow-up — /settings/env catch-all editor removed; raw env-var\n // editing belongs in `~/.agim/env` on the host, not in a web form.\n { to: 'service', i18nKey: 'tabs.service', mounted: true },\n]\n\nexport default function SettingsLayout(): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","SettingsLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAkBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,aAAc,QAAS,kBAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,QAAc,QAAS,aAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,aAAc,QAAS,kBAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EAGzD,CAAE,GAAI,UAAc,QAAS,eAAmB,QAAS,EAAA,CAC3D,EAEA,SAAwBC,GAA8B,CACpD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,UAAU,EACvC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,oBACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,OAAQQ,GAAQA,EAAI,OAAO,EAAE,IAAKA,GAC1CH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
1
+ {"version":3,"file":"layout-BxrFilDN.js","sources":["../../src/routes/settings/layout.tsx"],"sourcesContent":["/**\n * /settings — layout shell with sub-tab nav (environment / service).\n *\n * Workspaces / WeChat QR / Admin allowlist will land as additional\n * sub-tabs in PR-19b/c. The `mounted` flag gates their nav links.\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'messengers', i18nKey: 'tabs.messengers', mounted: true },\n { to: 'email', i18nKey: 'tabs.email', mounted: true },\n { to: 'viewer', i18nKey: 'tabs.viewer', mounted: true },\n { to: 'agents', i18nKey: 'tabs.agents', mounted: true },\n { to: 'policy', i18nKey: 'tabs.policy', mounted: true },\n { to: 'workspaces', i18nKey: 'tabs.workspaces', mounted: true },\n { to: 'admins', i18nKey: 'tabs.admins', mounted: true },\n // R13 follow-up — /settings/env catch-all editor removed; raw env-var\n // editing belongs in `~/.agim/env` on the host, not in a web form.\n { to: 'service', i18nKey: 'tabs.service', mounted: true },\n]\n\nexport default function SettingsLayout(): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","SettingsLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAkBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,aAAc,QAAS,kBAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,QAAc,QAAS,aAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,aAAc,QAAS,kBAAmB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAc,QAAS,cAAmB,QAAS,EAAA,EAGzD,CAAE,GAAI,UAAc,QAAS,eAAmB,QAAS,EAAA,CAC3D,EAEA,SAAwBC,GAA8B,CACpD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,UAAU,EACvC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,oBACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,OAAQQ,GAAQA,EAAI,OAAO,EAAE,IAAKA,GAC1CH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
@@ -1,2 +1,2 @@
1
- import{ac as x,Q as e,S as d,l as u,m as p,j as h,k as i,ab as f,T as b,N as j,r as m,O as g}from"./index-B48azMO-.js";import{f as y}from"./use-memory-CElm89Hg.js";import"./react-C9F3QeMB.js";import"./useQuery-BjFqOBV3.js";function _({value:c,onChange:n,className:l}){const{t:r}=x("memory"),{data:o,isLoading:s}=y(),a=o?.users??[];return e.jsxs(d,{value:c||"__placeholder__",onValueChange:t=>n(t==="__placeholder__"?"":t),disabled:s||a.length===0,children:[e.jsx(u,{className:l??"h-9 w-72 max-w-full",children:e.jsx(p,{placeholder:r("userPicker.placeholder")})}),e.jsxs(h,{children:[e.jsx(i,{value:"__placeholder__",disabled:!0,children:r("userPicker.placeholder")}),a.map(t=>e.jsx(i,{value:t.user_key,children:e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-mono text-xs",children:t.user_key}),e.jsx("span",{className:"text-text-dim text-xs",children:r("userPicker.factsLabel",{count:t.fact_count})}),t.has_persona&&e.jsxs("span",{className:"text-accent text-xs",children:["·",r("userPicker.personaTag"),"·"]})]})},t.user_key))]})]})}const v=[{to:"facts",i18nKey:"tabs.facts",mounted:!0},{to:"persona",i18nKey:"tabs.persona",mounted:!0},{to:"vector",i18nKey:"tabs.vector",mounted:!0}];function T(){const{t:c}=x("memory"),[n,l]=f(),r=n.get("user")??"";function o(s){const a=new URLSearchParams(n);s?a.set("user",s):a.delete("user"),l(a,{replace:!0})}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(b,{}),e.jsx("div",{className:"border-b border-border bg-surface px-3 sm:px-4 py-2",children:e.jsxs("div",{className:"mx-auto flex max-w-7xl items-center gap-3",children:[e.jsx("span",{className:"text-sm text-text-dim shrink-0",children:c("userPicker.label")}),e.jsx(_,{value:r,onChange:o})]})}),e.jsx("nav",{className:m("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":c("pageTitle"),children:v.filter(s=>s.mounted).map(s=>e.jsx(j,{to:s.to,className:({isActive:a})=>m("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:c(s.i18nKey)},s.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(g,{})})]})}export{T as default};
2
- //# sourceMappingURL=layout-ChcpcCB8.js.map
1
+ import{ac as x,Q as e,S as d,l as u,m as p,j as h,k as i,ab as f,T as b,N as j,r as m,O as g}from"./index-CITyis5g.js";import{f as y}from"./use-memory-BDa1R8Tf.js";import"./react-C9F3QeMB.js";import"./useQuery-DFryK9Pn.js";function _({value:c,onChange:n,className:l}){const{t:r}=x("memory"),{data:o,isLoading:s}=y(),a=o?.users??[];return e.jsxs(d,{value:c||"__placeholder__",onValueChange:t=>n(t==="__placeholder__"?"":t),disabled:s||a.length===0,children:[e.jsx(u,{className:l??"h-9 w-72 max-w-full",children:e.jsx(p,{placeholder:r("userPicker.placeholder")})}),e.jsxs(h,{children:[e.jsx(i,{value:"__placeholder__",disabled:!0,children:r("userPicker.placeholder")}),a.map(t=>e.jsx(i,{value:t.user_key,children:e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-mono text-xs",children:t.user_key}),e.jsx("span",{className:"text-text-dim text-xs",children:r("userPicker.factsLabel",{count:t.fact_count})}),t.has_persona&&e.jsxs("span",{className:"text-accent text-xs",children:["·",r("userPicker.personaTag"),"·"]})]})},t.user_key))]})]})}const v=[{to:"facts",i18nKey:"tabs.facts",mounted:!0},{to:"persona",i18nKey:"tabs.persona",mounted:!0},{to:"vector",i18nKey:"tabs.vector",mounted:!0}];function T(){const{t:c}=x("memory"),[n,l]=f(),r=n.get("user")??"";function o(s){const a=new URLSearchParams(n);s?a.set("user",s):a.delete("user"),l(a,{replace:!0})}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(b,{}),e.jsx("div",{className:"border-b border-border bg-surface px-3 sm:px-4 py-2",children:e.jsxs("div",{className:"mx-auto flex max-w-7xl items-center gap-3",children:[e.jsx("span",{className:"text-sm text-text-dim shrink-0",children:c("userPicker.label")}),e.jsx(_,{value:r,onChange:o})]})}),e.jsx("nav",{className:m("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":c("pageTitle"),children:v.filter(s=>s.mounted).map(s=>e.jsx(j,{to:s.to,className:({isActive:a})=>m("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:c(s.i18nKey)},s.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(g,{})})]})}export{T as default};
2
+ //# sourceMappingURL=layout-CR5gLapT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-ChcpcCB8.js","sources":["../../src/components/memory/user-picker.tsx","../../src/routes/memory/layout.tsx"],"sourcesContent":["/**\n * MemoryUserPicker — Select for the user_key the /memory route\n * targets. Sources the option list from useMemoryUsers, shows the\n * per-user fact_count + persona-presence as inline metadata so the\n * operator can tell which users are interesting at a glance.\n *\n * Loading: while users fetch, the trigger shows the placeholder so\n * the page doesn't jump.\n *\n * Empty bank: no users → the dropdown is disabled with a tooltip\n * explaining there's nothing to pick. Avoids a confusing \"no items\"\n * dropdown state.\n */\n\nimport { useTranslation } from 'react-i18next'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useMemoryUsers } from '@/hooks/use-memory'\n\nexport interface MemoryUserPickerProps {\n value: string\n onChange: (next: string) => void\n className?: string\n}\n\nexport function MemoryUserPicker({ value, onChange, className }: MemoryUserPickerProps): JSX.Element {\n const { t } = useTranslation('memory')\n const { data, isLoading } = useMemoryUsers()\n const users = data?.users ?? []\n\n return (\n <Select\n value={value || '__placeholder__'}\n onValueChange={(v) => onChange(v === '__placeholder__' ? '' : v)}\n disabled={isLoading || users.length === 0}\n >\n <SelectTrigger className={className ?? 'h-9 w-72 max-w-full'}>\n <SelectValue placeholder={t('userPicker.placeholder')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__placeholder__\" disabled>\n {t('userPicker.placeholder')}\n </SelectItem>\n {users.map((u) => (\n <SelectItem key={u.user_key} value={u.user_key}>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{u.user_key}</span>\n <span className=\"text-text-dim text-xs\">\n {t('userPicker.factsLabel', { count: u.fact_count })}\n </span>\n {u.has_persona && (\n <span className=\"text-accent text-xs\">·{t('userPicker.personaTag')}·</span>\n )}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n","/**\n * /memory — layout shell shared by every sub-tab.\n *\n * Memory is partitioned per user_key. The picker lives in the\n * layout so it's the first thing the operator sees and stays put\n * across sub-tabs (facts / persona / vector / skills). The chosen\n * user is held in URL state (?user=) so sub-tabs read it via\n * useSearchParams.\n *\n * Sub-tabs: facts / persona / vector. Skills used to live here too\n * but was promoted to its own top-level /skills tab in 2026-05 — the\n * router keeps a redirect for old /memory/skills bookmarks.\n */\n\nimport { NavLink, Outlet, useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { MemoryUserPicker } from '@/components/memory/user-picker'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'facts', i18nKey: 'tabs.facts', mounted: true },\n { to: 'persona', i18nKey: 'tabs.persona', mounted: true },\n { to: 'vector', i18nKey: 'tabs.vector', mounted: true },\n // 'skills' was here; promoted to top-level /skills in 2026-05.\n]\n\nexport default function MemoryLayout(): JSX.Element {\n const { t } = useTranslation('memory')\n const [params, setParams] = useSearchParams()\n const userKey = params.get('user') ?? ''\n\n function setUserKey(next: string): void {\n const p = new URLSearchParams(params)\n if (!next) p.delete('user')\n else p.set('user', next)\n setParams(p, { replace: true })\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n {/* User picker — the primary navigation control for this page */}\n <div className=\"border-b border-border bg-surface px-3 sm:px-4 py-2\">\n <div className=\"mx-auto flex max-w-7xl items-center gap-3\">\n <span className=\"text-sm text-text-dim shrink-0\">{t('userPicker.label')}</span>\n <MemoryUserPicker value={userKey} onChange={setUserKey} />\n </div>\n </div>\n\n {/* Sub-tab nav */}\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["MemoryUserPicker","value","onChange","className","t","useTranslation","data","isLoading","useMemoryUsers","users","jsxs","Select","v","jsx","SelectTrigger","SelectValue","SelectContent","SelectItem","u","SUB_TABS","MemoryLayout","params","setParams","useSearchParams","userKey","setUserKey","next","p","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"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"}
1
+ {"version":3,"file":"layout-CR5gLapT.js","sources":["../../src/components/memory/user-picker.tsx","../../src/routes/memory/layout.tsx"],"sourcesContent":["/**\n * MemoryUserPicker — Select for the user_key the /memory route\n * targets. Sources the option list from useMemoryUsers, shows the\n * per-user fact_count + persona-presence as inline metadata so the\n * operator can tell which users are interesting at a glance.\n *\n * Loading: while users fetch, the trigger shows the placeholder so\n * the page doesn't jump.\n *\n * Empty bank: no users → the dropdown is disabled with a tooltip\n * explaining there's nothing to pick. Avoids a confusing \"no items\"\n * dropdown state.\n */\n\nimport { useTranslation } from 'react-i18next'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useMemoryUsers } from '@/hooks/use-memory'\n\nexport interface MemoryUserPickerProps {\n value: string\n onChange: (next: string) => void\n className?: string\n}\n\nexport function MemoryUserPicker({ value, onChange, className }: MemoryUserPickerProps): JSX.Element {\n const { t } = useTranslation('memory')\n const { data, isLoading } = useMemoryUsers()\n const users = data?.users ?? []\n\n return (\n <Select\n value={value || '__placeholder__'}\n onValueChange={(v) => onChange(v === '__placeholder__' ? '' : v)}\n disabled={isLoading || users.length === 0}\n >\n <SelectTrigger className={className ?? 'h-9 w-72 max-w-full'}>\n <SelectValue placeholder={t('userPicker.placeholder')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__placeholder__\" disabled>\n {t('userPicker.placeholder')}\n </SelectItem>\n {users.map((u) => (\n <SelectItem key={u.user_key} value={u.user_key}>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{u.user_key}</span>\n <span className=\"text-text-dim text-xs\">\n {t('userPicker.factsLabel', { count: u.fact_count })}\n </span>\n {u.has_persona && (\n <span className=\"text-accent text-xs\">·{t('userPicker.personaTag')}·</span>\n )}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n","/**\n * /memory — layout shell shared by every sub-tab.\n *\n * Memory is partitioned per user_key. The picker lives in the\n * layout so it's the first thing the operator sees and stays put\n * across sub-tabs (facts / persona / vector / skills). The chosen\n * user is held in URL state (?user=) so sub-tabs read it via\n * useSearchParams.\n *\n * Sub-tabs: facts / persona / vector. Skills used to live here too\n * but was promoted to its own top-level /skills tab in 2026-05 — the\n * router keeps a redirect for old /memory/skills bookmarks.\n */\n\nimport { NavLink, Outlet, useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { MemoryUserPicker } from '@/components/memory/user-picker'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'facts', i18nKey: 'tabs.facts', mounted: true },\n { to: 'persona', i18nKey: 'tabs.persona', mounted: true },\n { to: 'vector', i18nKey: 'tabs.vector', mounted: true },\n // 'skills' was here; promoted to top-level /skills in 2026-05.\n]\n\nexport default function MemoryLayout(): JSX.Element {\n const { t } = useTranslation('memory')\n const [params, setParams] = useSearchParams()\n const userKey = params.get('user') ?? ''\n\n function setUserKey(next: string): void {\n const p = new URLSearchParams(params)\n if (!next) p.delete('user')\n else p.set('user', next)\n setParams(p, { replace: true })\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n {/* User picker — the primary navigation control for this page */}\n <div className=\"border-b border-border bg-surface px-3 sm:px-4 py-2\">\n <div className=\"mx-auto flex max-w-7xl items-center gap-3\">\n <span className=\"text-sm text-text-dim shrink-0\">{t('userPicker.label')}</span>\n <MemoryUserPicker value={userKey} onChange={setUserKey} />\n </div>\n </div>\n\n {/* Sub-tab nav */}\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["MemoryUserPicker","value","onChange","className","t","useTranslation","data","isLoading","useMemoryUsers","users","jsxs","Select","v","jsx","SelectTrigger","SelectValue","SelectContent","SelectItem","u","SUB_TABS","MemoryLayout","params","setParams","useSearchParams","userKey","setUserKey","next","p","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{ac as n,Q as e,T as r,N as u,r as a,O as i}from"./index-B48azMO-.js";import"./react-C9F3QeMB.js";const b=[{to:"jobs",i18nKey:"subnav.jobs",mounted:!0},{to:"subtasks",i18nKey:"subnav.subtasks",mounted:!0},{to:"bgjobs",i18nKey:"subnav.bgjobs",mounted:!0},{to:"schedules",i18nKey:"subnav.schedules",mounted:!0},{to:"outbox",i18nKey:"subnav.outbox",mounted:!0},{to:"a2a",i18nKey:"subnav.a2a",mounted:!0}];function c(){const{t:s}=n("tasks");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(r,{}),e.jsx("nav",{className:a("sticky top-[var(--topbar-h,0)] z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:b.filter(t=>t.mounted).map(t=>e.jsx(u,{to:t.to,className:({isActive:o})=>a("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",o?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(i,{})})]})}export{c as default};
2
- //# sourceMappingURL=layout-CjLzz--E.js.map
1
+ import{ac as n,Q as e,T as r,N as u,r as a,O as i}from"./index-CITyis5g.js";import"./react-C9F3QeMB.js";const b=[{to:"jobs",i18nKey:"subnav.jobs",mounted:!0},{to:"subtasks",i18nKey:"subnav.subtasks",mounted:!0},{to:"bgjobs",i18nKey:"subnav.bgjobs",mounted:!0},{to:"schedules",i18nKey:"subnav.schedules",mounted:!0},{to:"outbox",i18nKey:"subnav.outbox",mounted:!0},{to:"a2a",i18nKey:"subnav.a2a",mounted:!0}];function c(){const{t:s}=n("tasks");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(r,{}),e.jsx("nav",{className:a("sticky top-[var(--topbar-h,0)] z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:b.filter(t=>t.mounted).map(t=>e.jsx(u,{to:t.to,className:({isActive:o})=>a("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",o?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(i,{})})]})}export{c as default};
2
+ //# sourceMappingURL=layout-CcJQxFci.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-CjLzz--E.js","sources":["../../src/routes/tasks/layout.tsx"],"sourcesContent":["/**\n * /tasks — layout shell shared by every sub-tab.\n *\n * Renders the application Topbar plus a sub-tab strip below it. The\n * actual sub-tab body comes from <Outlet /> — one file per tab under\n * routes/tasks/.\n *\n * Sub-tab order matches plan: jobs / subtasks / bgjobs / schedules /\n * outbox / a2a. Each sub-tab is gated on its route being mounted; in\n * PR-10a only `/tasks/jobs` is wired up so the others stay out of the\n * nav until their PRs land.\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'jobs', i18nKey: 'subnav.jobs', mounted: true },\n { to: 'subtasks', i18nKey: 'subnav.subtasks', mounted: true },\n { to: 'bgjobs', i18nKey: 'subnav.bgjobs', mounted: true },\n { to: 'schedules', i18nKey: 'subnav.schedules', mounted: true },\n { to: 'outbox', i18nKey: 'subnav.outbox', mounted: true },\n { to: 'a2a', i18nKey: 'subnav.a2a', mounted: true },\n]\n\nexport default function TasksLayout(): JSX.Element {\n const { t } = useTranslation('tasks')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-[var(--topbar-h,0)] z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","TasksLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAwBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,OAAa,QAAS,cAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,WAAa,QAAS,kBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAa,QAAS,gBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,YAAa,QAAS,mBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAa,QAAS,gBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,MAAa,QAAS,aAAoB,QAAS,EAAA,CAC3D,EAEA,SAAwBC,GAA2B,CACjD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,OAAO,EACpC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,sCACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,OAAQQ,GAAQA,EAAI,OAAO,EAAE,IAAKA,GAC1CH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
1
+ {"version":3,"file":"layout-CcJQxFci.js","sources":["../../src/routes/tasks/layout.tsx"],"sourcesContent":["/**\n * /tasks — layout shell shared by every sub-tab.\n *\n * Renders the application Topbar plus a sub-tab strip below it. The\n * actual sub-tab body comes from <Outlet /> — one file per tab under\n * routes/tasks/.\n *\n * Sub-tab order matches plan: jobs / subtasks / bgjobs / schedules /\n * outbox / a2a. Each sub-tab is gated on its route being mounted; in\n * PR-10a only `/tasks/jobs` is wired up so the others stay out of the\n * nav until their PRs land.\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n mounted: boolean\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'jobs', i18nKey: 'subnav.jobs', mounted: true },\n { to: 'subtasks', i18nKey: 'subnav.subtasks', mounted: true },\n { to: 'bgjobs', i18nKey: 'subnav.bgjobs', mounted: true },\n { to: 'schedules', i18nKey: 'subnav.schedules', mounted: true },\n { to: 'outbox', i18nKey: 'subnav.outbox', mounted: true },\n { to: 'a2a', i18nKey: 'subnav.a2a', mounted: true },\n]\n\nexport default function TasksLayout(): JSX.Element {\n const { t } = useTranslation('tasks')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-[var(--topbar-h,0)] z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.filter((tab) => tab.mounted).map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","TasksLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAwBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,OAAa,QAAS,cAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,WAAa,QAAS,kBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAa,QAAS,gBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,YAAa,QAAS,mBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,SAAa,QAAS,gBAAoB,QAAS,EAAA,EACzD,CAAE,GAAI,MAAa,QAAS,aAAoB,QAAS,EAAA,CAC3D,EAEA,SAAwBC,GAA2B,CACjD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,OAAO,EACpC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,sCACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,OAAQQ,GAAQA,EAAI,OAAO,EAAE,IAAKA,GAC1CH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
@@ -1,2 +1,2 @@
1
- import{ac as r,Q as e,T as n,N as i,r as a,O as l}from"./index-B48azMO-.js";import"./react-C9F3QeMB.js";const x=[{to:"installed",i18nKey:"subnav.installed"},{to:"hot",i18nKey:"subnav.hot"}];function m(){const{t:s}=r("skills");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(n,{}),e.jsx("nav",{className:a("sticky top-[var(--topbar-h,0)] z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:x.map(t=>e.jsx(i,{to:t.to,className:({isActive:o})=>a("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",o?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(l,{})})]})}export{m as default};
2
- //# sourceMappingURL=layout-CS9s-Q0a.js.map
1
+ import{ac as r,Q as e,T as n,N as i,r as a,O as l}from"./index-CITyis5g.js";import"./react-C9F3QeMB.js";const x=[{to:"installed",i18nKey:"subnav.installed"},{to:"hot",i18nKey:"subnav.hot"}];function m(){const{t:s}=r("skills");return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(n,{}),e.jsx("nav",{className:a("sticky top-[var(--topbar-h,0)] z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":s("pageTitle"),children:x.map(t=>e.jsx(i,{to:t.to,className:({isActive:o})=>a("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",o?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:s(t.i18nKey)},t.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(l,{})})]})}export{m as default};
2
+ //# sourceMappingURL=layout-DRRveTaR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-CS9s-Q0a.js","sources":["../../src/routes/skills/layout.tsx"],"sourcesContent":["/**\n * /skills — top-level skills tab.\n *\n * Promoted out of /memory in 2026-05: skills aren't memory facts and\n * the \"memory\" tab was getting crowded. Two sub-tabs:\n *\n * installed — local SKILL.md inventory the agents pick up\n * (moved verbatim from /memory/skills)\n * hot — read-only browse of the skillhub.cn hot list\n * (restored from v1; backend already had the proxy)\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'installed', i18nKey: 'subnav.installed' },\n { to: 'hot', i18nKey: 'subnav.hot' },\n]\n\nexport default function SkillsLayout(): JSX.Element {\n const { t } = useTranslation('skills')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-[var(--topbar-h,0)] z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","SkillsLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAsBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,YAAa,QAAS,kBAAA,EAC5B,CAAE,GAAI,MAAa,QAAS,YAAA,CAC9B,EAEA,SAAwBC,GAA4B,CAClD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,QAAQ,EACrC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,sCACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,IAAKQ,GACbH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
1
+ {"version":3,"file":"layout-DRRveTaR.js","sources":["../../src/routes/skills/layout.tsx"],"sourcesContent":["/**\n * /skills — top-level skills tab.\n *\n * Promoted out of /memory in 2026-05: skills aren't memory facts and\n * the \"memory\" tab was getting crowded. Two sub-tabs:\n *\n * installed — local SKILL.md inventory the agents pick up\n * (moved verbatim from /memory/skills)\n * hot — read-only browse of the skillhub.cn hot list\n * (restored from v1; backend already had the proxy)\n */\n\nimport { NavLink, Outlet } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport { cn } from '@/lib/utils'\n\ninterface SubTab {\n to: string\n i18nKey: string\n}\n\nconst SUB_TABS: SubTab[] = [\n { to: 'installed', i18nKey: 'subnav.installed' },\n { to: 'hot', i18nKey: 'subnav.hot' },\n]\n\nexport default function SkillsLayout(): JSX.Element {\n const { t } = useTranslation('skills')\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <nav\n className={cn(\n 'sticky top-[var(--topbar-h,0)] z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","SkillsLayout","t","useTranslation","jsxs","jsx","Topbar","cn","tab","NavLink","isActive","Outlet"],"mappings":"wGAsBA,MAAMA,EAAqB,CACzB,CAAE,GAAI,YAAa,QAAS,kBAAA,EAC5B,CAAE,GAAI,MAAa,QAAS,YAAA,CAC9B,EAEA,SAAwBC,GAA4B,CAClD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,QAAQ,EACrC,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,EAERD,EAAAA,IAAC,MAAA,CACC,UAAWE,EACT,sCACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAYL,EAAE,WAAW,EAExB,SAAAF,EAAS,IAAKQ,GACbH,EAAAA,IAACI,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAAR,EAAEM,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAH,EAAAA,IAACM,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
@@ -1,2 +1,2 @@
1
- import{ac as c,ab as x,Q as e,T as d,S as m,l as p,m as b,j as u,k as h,N as g,r as n,O as y}from"./index-B48azMO-.js";import"./react-C9F3QeMB.js";const f=[{to:"health",i18nKey:"tabs.health"},{to:"topn",i18nKey:"tabs.topn"},{to:"audit",i18nKey:"tabs.audit"}],j=[1,7,30,90];function N(){const{t}=c("observability"),[r,l]=x(),i=Math.max(1,Number(r.get("days"))||7);function o(s){const a=new URLSearchParams(r);s===7?a.delete("days"):a.set("days",String(s)),l(a,{replace:!0})}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(d,{}),e.jsx("div",{className:"border-b border-border bg-surface px-3 sm:px-4 py-2",children:e.jsxs("div",{className:"mx-auto flex max-w-7xl items-center gap-3",children:[e.jsx("span",{className:"text-sm text-text-dim shrink-0",children:t("window.label")}),e.jsxs(m,{value:String(i),onValueChange:s=>o(Number(s)),children:[e.jsx(p,{className:"w-32",children:e.jsx(b,{})}),e.jsx(u,{children:j.map(s=>e.jsx(h,{value:String(s),children:t("window.days",{count:s})},s))})]})]})}),e.jsx("nav",{className:n("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":t("pageTitle"),children:f.map(s=>e.jsx(g,{to:s.to,className:({isActive:a})=>n("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:t(s.i18nKey)},s.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(y,{})})]})}export{N as default};
2
- //# sourceMappingURL=layout-fTZD9lJN.js.map
1
+ import{ac as c,ab as x,Q as e,T as d,S as m,l as p,m as b,j as u,k as h,N as g,r as n,O as y}from"./index-CITyis5g.js";import"./react-C9F3QeMB.js";const f=[{to:"health",i18nKey:"tabs.health"},{to:"topn",i18nKey:"tabs.topn"},{to:"audit",i18nKey:"tabs.audit"}],j=[1,7,30,90];function N(){const{t}=c("observability"),[r,l]=x(),i=Math.max(1,Number(r.get("days"))||7);function o(s){const a=new URLSearchParams(r);s===7?a.delete("days"):a.set("days",String(s)),l(a,{replace:!0})}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(d,{}),e.jsx("div",{className:"border-b border-border bg-surface px-3 sm:px-4 py-2",children:e.jsxs("div",{className:"mx-auto flex max-w-7xl items-center gap-3",children:[e.jsx("span",{className:"text-sm text-text-dim shrink-0",children:t("window.label")}),e.jsxs(m,{value:String(i),onValueChange:s=>o(Number(s)),children:[e.jsx(p,{className:"w-32",children:e.jsx(b,{})}),e.jsx(u,{children:j.map(s=>e.jsx(h,{value:String(s),children:t("window.days",{count:s})},s))})]})]})}),e.jsx("nav",{className:n("sticky top-0 z-10","flex items-center gap-1","border-b border-border bg-surface","px-3 sm:px-4 py-1","overflow-x-auto"),"aria-label":t("pageTitle"),children:f.map(s=>e.jsx(g,{to:s.to,className:({isActive:a})=>n("shrink-0 rounded-md px-3 py-1.5 text-sm no-underline","transition-colors tap-target",a?"bg-accent-bg text-accent font-medium":"text-text-dim hover:bg-surface-hover hover:text-text"),children:t(s.i18nKey)},s.to))}),e.jsx("main",{className:"flex-1 px-3 sm:px-4 py-4 pb-safe",children:e.jsx(y,{})})]})}export{N as default};
2
+ //# sourceMappingURL=layout-zotlJ5fW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-fTZD9lJN.js","sources":["../../src/routes/observability/layout.tsx"],"sourcesContent":["/**\n * /observability — layout shell with sub-tab nav (health / topn /\n * audit) + a global time-window selector that every sub-tab reads\n * via useSearchParams (?days=).\n */\n\nimport { NavLink, Outlet, useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { cn } from '@/lib/utils'\n\nconst SUB_TABS = [\n { to: 'health', i18nKey: 'tabs.health' },\n { to: 'topn', i18nKey: 'tabs.topn' },\n { to: 'audit', i18nKey: 'tabs.audit' },\n]\n\nconst DAY_WINDOWS = [1, 7, 30, 90]\n\nexport default function ObservabilityLayout(): JSX.Element {\n const { t } = useTranslation('observability')\n const [params, setParams] = useSearchParams()\n const days = Math.max(1, Number(params.get('days')) || 7)\n\n function setDays(next: number): void {\n const p = new URLSearchParams(params)\n if (next === 7) p.delete('days')\n else p.set('days', String(next))\n setParams(p, { replace: true })\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <div className=\"border-b border-border bg-surface px-3 sm:px-4 py-2\">\n <div className=\"mx-auto flex max-w-7xl items-center gap-3\">\n <span className=\"text-sm text-text-dim shrink-0\">{t('window.label')}</span>\n <Select value={String(days)} onValueChange={(v) => setDays(Number(v))}>\n <SelectTrigger className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {DAY_WINDOWS.map((n) => (\n <SelectItem key={n} value={String(n)}>\n {t('window.days', { count: n })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","DAY_WINDOWS","ObservabilityLayout","useTranslation","params","setParams","useSearchParams","days","setDays","next","p","jsxs","jsx","Topbar","Select","v","SelectTrigger","SelectValue","SelectContent","n","SelectItem","cn","tab","NavLink","isActive","Outlet"],"mappings":"mJAkBA,MAAMA,EAAW,CACf,CAAE,GAAI,SAAU,QAAS,aAAA,EACzB,CAAE,GAAI,OAAU,QAAS,WAAA,EACzB,CAAE,GAAI,QAAU,QAAS,YAAA,CAC3B,EAEMC,EAAc,CAAC,EAAG,EAAG,GAAI,EAAE,EAEjC,SAAwBC,GAAmC,CACzD,KAAM,CAAE,CAAA,EAAMC,EAAe,eAAe,EACtC,CAACC,EAAQC,CAAS,EAAIC,EAAA,EACtBC,EAAO,KAAK,IAAI,EAAG,OAAOH,EAAO,IAAI,MAAM,CAAC,GAAK,CAAC,EAExD,SAASI,EAAQC,EAAoB,CACnC,MAAMC,EAAI,IAAI,gBAAgBN,CAAM,EAChCK,IAAS,EAAGC,EAAE,OAAO,MAAM,EAC1BA,EAAE,IAAI,OAAQ,OAAOD,CAAI,CAAC,EAC/BJ,EAAUK,EAAG,CAAE,QAAS,EAAA,CAAM,CAChC,CAEA,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,QAEP,MAAA,CAAI,UAAU,sDACb,SAAAF,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAC,MAAC,OAAA,CAAK,UAAU,iCAAkC,SAAA,EAAE,cAAc,EAAE,EACpED,EAAAA,KAACG,EAAA,CAAO,MAAO,OAAOP,CAAI,EAAG,cAAgBQ,GAAMP,EAAQ,OAAOO,CAAC,CAAC,EAClE,SAAA,CAAAH,MAACI,EAAA,CAAc,UAAU,OACvB,SAAAJ,MAACK,IAAY,EACf,EACAL,MAACM,GACE,SAAAjB,EAAY,IAAKkB,GAChBP,EAAAA,IAACQ,GAAmB,MAAO,OAAOD,CAAC,EAChC,SAAA,EAAE,cAAe,CAAE,MAAOA,EAAG,CAAA,EADfA,CAEjB,CACD,CAAA,CACH,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CACF,EAEAP,EAAAA,IAAC,MAAA,CACC,UAAWS,EACT,oBACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAY,EAAE,WAAW,EAExB,SAAArB,EAAS,IAAKsB,GACbV,EAAAA,IAACW,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAA,EAAEF,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAV,EAAAA,IAACa,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
1
+ {"version":3,"file":"layout-zotlJ5fW.js","sources":["../../src/routes/observability/layout.tsx"],"sourcesContent":["/**\n * /observability — layout shell with sub-tab nav (health / topn /\n * audit) + a global time-window selector that every sub-tab reads\n * via useSearchParams (?days=).\n */\n\nimport { NavLink, Outlet, useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Topbar } from '@/components/shell/topbar'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { cn } from '@/lib/utils'\n\nconst SUB_TABS = [\n { to: 'health', i18nKey: 'tabs.health' },\n { to: 'topn', i18nKey: 'tabs.topn' },\n { to: 'audit', i18nKey: 'tabs.audit' },\n]\n\nconst DAY_WINDOWS = [1, 7, 30, 90]\n\nexport default function ObservabilityLayout(): JSX.Element {\n const { t } = useTranslation('observability')\n const [params, setParams] = useSearchParams()\n const days = Math.max(1, Number(params.get('days')) || 7)\n\n function setDays(next: number): void {\n const p = new URLSearchParams(params)\n if (next === 7) p.delete('days')\n else p.set('days', String(next))\n setParams(p, { replace: true })\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <div className=\"border-b border-border bg-surface px-3 sm:px-4 py-2\">\n <div className=\"mx-auto flex max-w-7xl items-center gap-3\">\n <span className=\"text-sm text-text-dim shrink-0\">{t('window.label')}</span>\n <Select value={String(days)} onValueChange={(v) => setDays(Number(v))}>\n <SelectTrigger className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {DAY_WINDOWS.map((n) => (\n <SelectItem key={n} value={String(n)}>\n {t('window.days', { count: n })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <nav\n className={cn(\n 'sticky top-0 z-10',\n 'flex items-center gap-1',\n 'border-b border-border bg-surface',\n 'px-3 sm:px-4 py-1',\n 'overflow-x-auto',\n )}\n aria-label={t('pageTitle')}\n >\n {SUB_TABS.map((tab) => (\n <NavLink\n key={tab.to}\n to={tab.to}\n className={({ isActive }) =>\n cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-sm no-underline',\n 'transition-colors tap-target',\n isActive\n ? 'bg-accent-bg text-accent font-medium'\n : 'text-text-dim hover:bg-surface-hover hover:text-text',\n )\n }\n >\n {t(tab.i18nKey)}\n </NavLink>\n ))}\n </nav>\n\n <main className=\"flex-1 px-3 sm:px-4 py-4 pb-safe\">\n <Outlet />\n </main>\n </div>\n )\n}\n"],"names":["SUB_TABS","DAY_WINDOWS","ObservabilityLayout","useTranslation","params","setParams","useSearchParams","days","setDays","next","p","jsxs","jsx","Topbar","Select","v","SelectTrigger","SelectValue","SelectContent","n","SelectItem","cn","tab","NavLink","isActive","Outlet"],"mappings":"mJAkBA,MAAMA,EAAW,CACf,CAAE,GAAI,SAAU,QAAS,aAAA,EACzB,CAAE,GAAI,OAAU,QAAS,WAAA,EACzB,CAAE,GAAI,QAAU,QAAS,YAAA,CAC3B,EAEMC,EAAc,CAAC,EAAG,EAAG,GAAI,EAAE,EAEjC,SAAwBC,GAAmC,CACzD,KAAM,CAAE,CAAA,EAAMC,EAAe,eAAe,EACtC,CAACC,EAAQC,CAAS,EAAIC,EAAA,EACtBC,EAAO,KAAK,IAAI,EAAG,OAAOH,EAAO,IAAI,MAAM,CAAC,GAAK,CAAC,EAExD,SAASI,EAAQC,EAAoB,CACnC,MAAMC,EAAI,IAAI,gBAAgBN,CAAM,EAChCK,IAAS,EAAGC,EAAE,OAAO,MAAM,EAC1BA,EAAE,IAAI,OAAQ,OAAOD,CAAI,CAAC,EAC/BJ,EAAUK,EAAG,CAAE,QAAS,EAAA,CAAM,CAChC,CAEA,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,EAAO,QAEP,MAAA,CAAI,UAAU,sDACb,SAAAF,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAC,MAAC,OAAA,CAAK,UAAU,iCAAkC,SAAA,EAAE,cAAc,EAAE,EACpED,EAAAA,KAACG,EAAA,CAAO,MAAO,OAAOP,CAAI,EAAG,cAAgBQ,GAAMP,EAAQ,OAAOO,CAAC,CAAC,EAClE,SAAA,CAAAH,MAACI,EAAA,CAAc,UAAU,OACvB,SAAAJ,MAACK,IAAY,EACf,EACAL,MAACM,GACE,SAAAjB,EAAY,IAAKkB,GAChBP,EAAAA,IAACQ,GAAmB,MAAO,OAAOD,CAAC,EAChC,SAAA,EAAE,cAAe,CAAE,MAAOA,EAAG,CAAA,EADfA,CAEjB,CACD,CAAA,CACH,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CACF,EAEAP,EAAAA,IAAC,MAAA,CACC,UAAWS,EACT,oBACA,0BACA,oCACA,oBACA,iBAAA,EAEF,aAAY,EAAE,WAAW,EAExB,SAAArB,EAAS,IAAKsB,GACbV,EAAAA,IAACW,EAAA,CAEC,GAAID,EAAI,GACR,UAAW,CAAC,CAAE,SAAAE,CAAA,IACZH,EACE,uDACA,+BACAG,EACI,uCACA,sDAAA,EAIP,SAAA,EAAEF,EAAI,OAAO,CAAA,EAZTA,EAAI,EAAA,CAcZ,CAAA,CAAA,QAGF,OAAA,CAAK,UAAU,mCACd,SAAAV,EAAAA,IAACa,IAAO,CAAA,CACV,CAAA,EACF,CAEJ"}
@@ -1,7 +1,7 @@
1
- import{x as e}from"./index-B48azMO-.js";/**
1
+ import{x as e}from"./index-CITyis5g.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 r=e("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);export{r as L};
7
- //# sourceMappingURL=loader-circle-DBf4xFjo.js.map
7
+ //# sourceMappingURL=loader-circle-BFp6Oc8_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader-circle-DBf4xFjo.js","sources":["../../node_modules/lucide-react/dist/esm/icons/loader-circle.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 LoaderCircle = createLucideIcon(\"LoaderCircle\", [\n [\"path\", { d: \"M21 12a9 9 0 1 1-6.219-8.56\", key: \"13zald\" }]\n]);\n\nexport { LoaderCircle as default };\n//# sourceMappingURL=loader-circle.js.map\n"],"names":["LoaderCircle","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAeC,EAAiB,eAAgB,CACpD,CAAC,OAAQ,CAAE,EAAG,8BAA+B,IAAK,QAAQ,CAAE,CAC9D,CAAC","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"loader-circle-BFp6Oc8_.js","sources":["../../node_modules/lucide-react/dist/esm/icons/loader-circle.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 LoaderCircle = createLucideIcon(\"LoaderCircle\", [\n [\"path\", { d: \"M21 12a9 9 0 1 1-6.219-8.56\", key: \"13zald\" }]\n]);\n\nexport { LoaderCircle as default };\n//# sourceMappingURL=loader-circle.js.map\n"],"names":["LoaderCircle","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAeC,EAAiB,eAAgB,CACpD,CAAC,OAAQ,CAAE,EAAG,8BAA+B,IAAK,QAAQ,CAAE,CAC9D,CAAC","x_google_ignoreList":[0]}
@@ -1,7 +1,7 @@
1
- import{x as c}from"./index-B48azMO-.js";/**
1
+ import{x as c}from"./index-CITyis5g.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 r=c("MapPin",[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0",key:"1r0f0z"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}]]);export{r as M};
7
- //# sourceMappingURL=map-pin-BYR0Sf5r.js.map
7
+ //# sourceMappingURL=map-pin-DPqnsBIG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"map-pin-BYR0Sf5r.js","sources":["../../node_modules/lucide-react/dist/esm/icons/map-pin.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 MapPin = createLucideIcon(\"MapPin\", [\n [\n \"path\",\n {\n d: \"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0\",\n key: \"1r0f0z\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"10\", r: \"3\", key: \"ilqhr7\" }]\n]);\n\nexport { MapPin as default };\n//# sourceMappingURL=map-pin.js.map\n"],"names":["MapPin","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CACE,OACA,CACE,EAAG,uGACH,IAAK,QACX,CACA,EACE,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAQ,CAAE,CAC1D,CAAC","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"map-pin-DPqnsBIG.js","sources":["../../node_modules/lucide-react/dist/esm/icons/map-pin.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 MapPin = createLucideIcon(\"MapPin\", [\n [\n \"path\",\n {\n d: \"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0\",\n key: \"1r0f0z\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"10\", r: \"3\", key: \"ilqhr7\" }]\n]);\n\nexport { MapPin as default };\n//# sourceMappingURL=map-pin.js.map\n"],"names":["MapPin","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CACE,OACA,CACE,EAAG,uGACH,IAAK,QACX,CACA,EACE,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAQ,CAAE,CAC1D,CAAC","x_google_ignoreList":[0]}