agim-cli 1.2.138 → 1.2.140

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 (191) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/dist/core/a2a.d.ts +6 -0
  3. package/dist/core/a2a.d.ts.map +1 -1
  4. package/dist/core/a2a.js +5 -0
  5. package/dist/core/a2a.js.map +1 -1
  6. package/dist/core/live-config.d.ts.map +1 -1
  7. package/dist/core/live-config.js +8 -0
  8. package/dist/core/live-config.js.map +1 -1
  9. package/dist/core/llm/imhub-dispatcher.d.ts +22 -0
  10. package/dist/core/llm/imhub-dispatcher.d.ts.map +1 -1
  11. package/dist/core/llm/imhub-dispatcher.js +154 -24
  12. package/dist/core/llm/imhub-dispatcher.js.map +1 -1
  13. package/dist/plugins/agents/native/index.d.ts.map +1 -1
  14. package/dist/plugins/agents/native/index.js +9 -1
  15. package/dist/plugins/agents/native/index.js.map +1 -1
  16. package/dist/web/public/assets/{a2a-COCxV3Cc.js → a2a-DczMMkbl.js} +2 -2
  17. package/dist/web/public/assets/{a2a-COCxV3Cc.js.map → a2a-DczMMkbl.js.map} +1 -1
  18. package/dist/web/public/assets/{activity-lssr7XnJ.js → activity-cbLHkzca.js} +2 -2
  19. package/dist/web/public/assets/{activity-lssr7XnJ.js.map → activity-cbLHkzca.js.map} +1 -1
  20. package/dist/web/public/assets/{admins-DSW8IUl9.js → admins-C-YsGMj7.js} +2 -2
  21. package/dist/web/public/assets/{admins-DSW8IUl9.js.map → admins-C-YsGMj7.js.map} +1 -1
  22. package/dist/web/public/assets/agents-BWfov_1-.js +7 -0
  23. package/dist/web/public/assets/agents-BWfov_1-.js.map +1 -0
  24. package/dist/web/public/assets/{approvals-ClzA_Sl1.js → approvals-HSssmXKS.js} +2 -2
  25. package/dist/web/public/assets/{approvals-ClzA_Sl1.js.map → approvals-HSssmXKS.js.map} +1 -1
  26. package/dist/web/public/assets/{arrow-down-B6b32UC0.js → arrow-down-BXvC8Al2.js} +2 -2
  27. package/dist/web/public/assets/{arrow-down-B6b32UC0.js.map → arrow-down-BXvC8Al2.js.map} +1 -1
  28. package/dist/web/public/assets/{arrow-up-B-nP5kYY.js → arrow-up-63xELY5Q.js} +2 -2
  29. package/dist/web/public/assets/{arrow-up-B-nP5kYY.js.map → arrow-up-63xELY5Q.js.map} +1 -1
  30. package/dist/web/public/assets/{asks-DyXhQTTK.js → asks-COLEFOvK.js} +2 -2
  31. package/dist/web/public/assets/{asks-DyXhQTTK.js.map → asks-COLEFOvK.js.map} +1 -1
  32. package/dist/web/public/assets/{audit-BI7SCouF.js → audit-D4ZEiZub.js} +2 -2
  33. package/dist/web/public/assets/{audit-BI7SCouF.js.map → audit-D4ZEiZub.js.map} +1 -1
  34. package/dist/web/public/assets/{bell-B3N02OLo.js → bell-Cg2Bvv06.js} +2 -2
  35. package/dist/web/public/assets/{bell-B3N02OLo.js.map → bell-Cg2Bvv06.js.map} +1 -1
  36. package/dist/web/public/assets/{bgjobs-CicZiNip.js → bgjobs-CEjCzwtd.js} +2 -2
  37. package/dist/web/public/assets/{bgjobs-CicZiNip.js.map → bgjobs-CEjCzwtd.js.map} +1 -1
  38. package/dist/web/public/assets/{brain-C6ukvGuf.js → brain-euvl6F6C.js} +2 -2
  39. package/dist/web/public/assets/{brain-C6ukvGuf.js.map → brain-euvl6F6C.js.map} +1 -1
  40. package/dist/web/public/assets/{briefcase-hn1X3B5h.js → briefcase-DPWLbCnA.js} +2 -2
  41. package/dist/web/public/assets/{briefcase-hn1X3B5h.js.map → briefcase-DPWLbCnA.js.map} +1 -1
  42. package/dist/web/public/assets/{chat-BDQj16Vz.js → chat-Dz9kfaxH.js} +2 -2
  43. package/dist/web/public/assets/{chat-BDQj16Vz.js.map → chat-Dz9kfaxH.js.map} +1 -1
  44. package/dist/web/public/assets/{chevron-left-RxNVjq1Y.js → chevron-left-BeIh5thq.js} +2 -2
  45. package/dist/web/public/assets/{chevron-left-RxNVjq1Y.js.map → chevron-left-BeIh5thq.js.map} +1 -1
  46. package/dist/web/public/assets/{chevron-right-BUp0DVnl.js → chevron-right-uP_l9MMb.js} +2 -2
  47. package/dist/web/public/assets/{chevron-right-BUp0DVnl.js.map → chevron-right-uP_l9MMb.js.map} +1 -1
  48. package/dist/web/public/assets/{circle-check-Bm4WZ21u.js → circle-check-CewnjFgv.js} +2 -2
  49. package/dist/web/public/assets/{circle-check-Bm4WZ21u.js.map → circle-check-CewnjFgv.js.map} +1 -1
  50. package/dist/web/public/assets/{circle-check-big-BIrgy-jJ.js → circle-check-big-C2RTc48c.js} +2 -2
  51. package/dist/web/public/assets/{circle-check-big-BIrgy-jJ.js.map → circle-check-big-C2RTc48c.js.map} +1 -1
  52. package/dist/web/public/assets/{circle-x-D3x0-M6I.js → circle-x-Ccg1HyV-.js} +2 -2
  53. package/dist/web/public/assets/{circle-x-D3x0-M6I.js.map → circle-x-Ccg1HyV-.js.map} +1 -1
  54. package/dist/web/public/assets/{clock-DS7DQ1w2.js → clock-qxbYSynv.js} +2 -2
  55. package/dist/web/public/assets/{clock-DS7DQ1w2.js.map → clock-qxbYSynv.js.map} +1 -1
  56. package/dist/web/public/assets/{confirm-dialog-C68_XUSd.js → confirm-dialog-DmJq4Td9.js} +2 -2
  57. package/dist/web/public/assets/{confirm-dialog-C68_XUSd.js.map → confirm-dialog-DmJq4Td9.js.map} +1 -1
  58. package/dist/web/public/assets/{copy-Bzjhsg1p.js → copy-DxSHRdbc.js} +2 -2
  59. package/dist/web/public/assets/{copy-Bzjhsg1p.js.map → copy-DxSHRdbc.js.map} +1 -1
  60. package/dist/web/public/assets/{data-table-DEXi61Ud.js → data-table-S7rIjwdO.js} +2 -2
  61. package/dist/web/public/assets/{data-table-DEXi61Ud.js.map → data-table-S7rIjwdO.js.map} +1 -1
  62. package/dist/web/public/assets/{dialog-gd2B3olO.js → dialog-bAIDaO-6.js} +2 -2
  63. package/dist/web/public/assets/{dialog-gd2B3olO.js.map → dialog-bAIDaO-6.js.map} +1 -1
  64. package/dist/web/public/assets/{download-DCcaTdc6.js → download-OhsGtnO-.js} +2 -2
  65. package/dist/web/public/assets/{download-DCcaTdc6.js.map → download-OhsGtnO-.js.map} +1 -1
  66. package/dist/web/public/assets/{email-Bp4hAFCc.js → email-C1-HxWLF.js} +2 -2
  67. package/dist/web/public/assets/{email-Bp4hAFCc.js.map → email-C1-HxWLF.js.map} +1 -1
  68. package/dist/web/public/assets/{empty-state-Bzkp-ADK.js → empty-state-C-qjOHyu.js} +2 -2
  69. package/dist/web/public/assets/{empty-state-Bzkp-ADK.js.map → empty-state-C-qjOHyu.js.map} +1 -1
  70. package/dist/web/public/assets/{external-link-Deer-Lh8.js → external-link-DRVp9-lb.js} +2 -2
  71. package/dist/web/public/assets/{external-link-Deer-Lh8.js.map → external-link-DRVp9-lb.js.map} +1 -1
  72. package/dist/web/public/assets/{eye-aJAK4qYW.js → eye-CFhg5BTa.js} +2 -2
  73. package/dist/web/public/assets/{eye-aJAK4qYW.js.map → eye-CFhg5BTa.js.map} +1 -1
  74. package/dist/web/public/assets/{facts-CX8Slc_J.js → facts-CGaLWhzi.js} +2 -2
  75. package/dist/web/public/assets/{facts-CX8Slc_J.js.map → facts-CGaLWhzi.js.map} +1 -1
  76. package/dist/web/public/assets/{goals-CoO5qR3l.js → goals-C-dJANmn.js} +2 -2
  77. package/dist/web/public/assets/{goals-CoO5qR3l.js.map → goals-C-dJANmn.js.map} +1 -1
  78. package/dist/web/public/assets/{health-QRL6KLLZ.js → health-CWcti5h3.js} +2 -2
  79. package/dist/web/public/assets/{health-QRL6KLLZ.js.map → health-CWcti5h3.js.map} +1 -1
  80. package/dist/web/public/assets/{heart-pulse-DTuy4Tma.js → heart-pulse-DmGhKR2W.js} +2 -2
  81. package/dist/web/public/assets/{heart-pulse-DTuy4Tma.js.map → heart-pulse-DmGhKR2W.js.map} +1 -1
  82. package/dist/web/public/assets/{heartbeat-BLutWB3t.js → heartbeat-kLoGBNCo.js} +2 -2
  83. package/dist/web/public/assets/{heartbeat-BLutWB3t.js.map → heartbeat-kLoGBNCo.js.map} +1 -1
  84. package/dist/web/public/assets/{hot-B96_1Z-G.js → hot-BITDoax1.js} +2 -2
  85. package/dist/web/public/assets/{hot-B96_1Z-G.js.map → hot-BITDoax1.js.map} +1 -1
  86. package/dist/web/public/assets/{index-wVLKSZpI.js → index-O0BQoyzo.js} +22 -22
  87. package/dist/web/public/assets/{index-wVLKSZpI.js.map → index-O0BQoyzo.js.map} +1 -1
  88. package/dist/web/public/assets/{installed-5bpbtNzF.js → installed-Co9WrtQ7.js} +2 -2
  89. package/dist/web/public/assets/{installed-5bpbtNzF.js.map → installed-Co9WrtQ7.js.map} +1 -1
  90. package/dist/web/public/assets/{jobs-CPLjCaI5.js → jobs-hdHhBEvi.js} +2 -2
  91. package/dist/web/public/assets/{jobs-CPLjCaI5.js.map → jobs-hdHhBEvi.js.map} +1 -1
  92. package/dist/web/public/assets/{layout-Cdi09rZy.js → layout-BMXC1Uh1.js} +2 -2
  93. package/dist/web/public/assets/{layout-Cdi09rZy.js.map → layout-BMXC1Uh1.js.map} +1 -1
  94. package/dist/web/public/assets/{layout-CN-hNeUX.js → layout-CQtbOBag.js} +2 -2
  95. package/dist/web/public/assets/{layout-CN-hNeUX.js.map → layout-CQtbOBag.js.map} +1 -1
  96. package/dist/web/public/assets/{layout-nYKX1VY4.js → layout-CyBGneZ9.js} +2 -2
  97. package/dist/web/public/assets/{layout-nYKX1VY4.js.map → layout-CyBGneZ9.js.map} +1 -1
  98. package/dist/web/public/assets/{layout-DOvroJYL.js → layout-CysVsySh.js} +2 -2
  99. package/dist/web/public/assets/{layout-DOvroJYL.js.map → layout-CysVsySh.js.map} +1 -1
  100. package/dist/web/public/assets/{layout-K_oWND8q.js → layout-bDMXIKIR.js} +2 -2
  101. package/dist/web/public/assets/{layout-K_oWND8q.js.map → layout-bDMXIKIR.js.map} +1 -1
  102. package/dist/web/public/assets/{llm-BDW-Xpvk.js → llm-CPIRNQU2.js} +2 -2
  103. package/dist/web/public/assets/{llm-BDW-Xpvk.js.map → llm-CPIRNQU2.js.map} +1 -1
  104. package/dist/web/public/assets/{loader-circle-DPc6botW.js → loader-circle-9VUMGitw.js} +2 -2
  105. package/dist/web/public/assets/{loader-circle-DPc6botW.js.map → loader-circle-9VUMGitw.js.map} +1 -1
  106. package/dist/web/public/assets/{map-pin-ECh-25H3.js → map-pin-BXYvvHry.js} +2 -2
  107. package/dist/web/public/assets/{map-pin-ECh-25H3.js.map → map-pin-BXYvvHry.js.map} +1 -1
  108. package/dist/web/public/assets/{mcp-BV-k5VFD.js → mcp-BgLdlwSn.js} +2 -2
  109. package/dist/web/public/assets/{mcp-BV-k5VFD.js.map → mcp-BgLdlwSn.js.map} +1 -1
  110. package/dist/web/public/assets/{memos-BTsHVXah.js → memos-CfneX9DH.js} +2 -2
  111. package/dist/web/public/assets/{memos-BTsHVXah.js.map → memos-CfneX9DH.js.map} +1 -1
  112. package/dist/web/public/assets/{messengers-fyjNQwDX.js → messengers-7Phqea62.js} +2 -2
  113. package/dist/web/public/assets/{messengers-fyjNQwDX.js.map → messengers-7Phqea62.js.map} +1 -1
  114. package/dist/web/public/assets/{mobile-CeRuQZ-W.js → mobile-CV5b6D2W.js} +2 -2
  115. package/dist/web/public/assets/{mobile-CeRuQZ-W.js.map → mobile-CV5b6D2W.js.map} +1 -1
  116. package/dist/web/public/assets/{native-agent-Cg-Sab4t.js → native-agent-QvIa6LjE.js} +2 -2
  117. package/dist/web/public/assets/{native-agent-Cg-Sab4t.js.map → native-agent-QvIa6LjE.js.map} +1 -1
  118. package/dist/web/public/assets/{network-FK3KNI1a.js → network-BXhEjGhE.js} +2 -2
  119. package/dist/web/public/assets/{network-FK3KNI1a.js.map → network-BXhEjGhE.js.map} +1 -1
  120. package/dist/web/public/assets/{outbox-DhlxB8l0.js → outbox-DHQL7TQb.js} +2 -2
  121. package/dist/web/public/assets/{outbox-DhlxB8l0.js.map → outbox-DHQL7TQb.js.map} +1 -1
  122. package/dist/web/public/assets/{pagination-DzNnFLlI.js → pagination-VKuPb1Ot.js} +2 -2
  123. package/dist/web/public/assets/{pagination-DzNnFLlI.js.map → pagination-VKuPb1Ot.js.map} +1 -1
  124. package/dist/web/public/assets/{persona-B_EkauQB.js → persona-CWug2GLR.js} +2 -2
  125. package/dist/web/public/assets/{persona-B_EkauQB.js.map → persona-CWug2GLR.js.map} +1 -1
  126. package/dist/web/public/assets/{plans-D4aczyt4.js → plans-CZoEs5SY.js} +2 -2
  127. package/dist/web/public/assets/{plans-D4aczyt4.js.map → plans-CZoEs5SY.js.map} +1 -1
  128. package/dist/web/public/assets/{play-BdTIFReu.js → play-CfSn5Vdl.js} +2 -2
  129. package/dist/web/public/assets/{play-BdTIFReu.js.map → play-CfSn5Vdl.js.map} +1 -1
  130. package/dist/web/public/assets/{plus-CjK8x5Ta.js → plus-Z8l4CiqJ.js} +2 -2
  131. package/dist/web/public/assets/{plus-CjK8x5Ta.js.map → plus-Z8l4CiqJ.js.map} +1 -1
  132. package/dist/web/public/assets/{policy-D7vnZQeQ.js → policy-CutDSEPW.js} +2 -2
  133. package/dist/web/public/assets/{policy-D7vnZQeQ.js.map → policy-CutDSEPW.js.map} +1 -1
  134. package/dist/web/public/assets/{qr-code-CpmzdYE3.js → qr-code-DgU5aiM6.js} +2 -2
  135. package/dist/web/public/assets/{qr-code-CpmzdYE3.js.map → qr-code-DgU5aiM6.js.map} +1 -1
  136. package/dist/web/public/assets/{refresh-ccw-C2Q06LWj.js → refresh-ccw-D2CWiyU_.js} +2 -2
  137. package/dist/web/public/assets/{refresh-ccw-C2Q06LWj.js.map → refresh-ccw-D2CWiyU_.js.map} +1 -1
  138. package/dist/web/public/assets/{reminders-BW3VfYCD.js → reminders-Cb6Izedg.js} +2 -2
  139. package/dist/web/public/assets/{reminders-BW3VfYCD.js.map → reminders-Cb6Izedg.js.map} +1 -1
  140. package/dist/web/public/assets/{save-3XXDgdfD.js → save-DB0BDYTs.js} +2 -2
  141. package/dist/web/public/assets/{save-3XXDgdfD.js.map → save-DB0BDYTs.js.map} +1 -1
  142. package/dist/web/public/assets/{schedules-Bg5oBl6i.js → schedules-8mSjE14D.js} +2 -2
  143. package/dist/web/public/assets/{schedules-Bg5oBl6i.js.map → schedules-8mSjE14D.js.map} +1 -1
  144. package/dist/web/public/assets/{search-oQrctQNZ.js → search-B4fHilZ0.js} +2 -2
  145. package/dist/web/public/assets/{search-oQrctQNZ.js.map → search-B4fHilZ0.js.map} +1 -1
  146. package/dist/web/public/assets/{search-BjW_KGfJ.js → search-Con69NhG.js} +2 -2
  147. package/dist/web/public/assets/{search-BjW_KGfJ.js.map → search-Con69NhG.js.map} +1 -1
  148. package/dist/web/public/assets/{security-ongR2bsH.js → security-BTe3zUg8.js} +2 -2
  149. package/dist/web/public/assets/{security-ongR2bsH.js.map → security-BTe3zUg8.js.map} +1 -1
  150. package/dist/web/public/assets/{service-CNTTFkAV.js → service-C7SqcwfL.js} +2 -2
  151. package/dist/web/public/assets/{service-CNTTFkAV.js.map → service-C7SqcwfL.js.map} +1 -1
  152. package/dist/web/public/assets/{shield-alert-DW0flso9.js → shield-alert-CKFVsGgI.js} +2 -2
  153. package/dist/web/public/assets/{shield-alert-DW0flso9.js.map → shield-alert-CKFVsGgI.js.map} +1 -1
  154. package/dist/web/public/assets/{status-badge-CnqMu-7R.js → status-badge-BSkpyN4D.js} +2 -2
  155. package/dist/web/public/assets/{status-badge-CnqMu-7R.js.map → status-badge-BSkpyN4D.js.map} +1 -1
  156. package/dist/web/public/assets/{subtasks-ChtI7jpP.js → subtasks-Bel-I1Sk.js} +2 -2
  157. package/dist/web/public/assets/{subtasks-ChtI7jpP.js.map → subtasks-Bel-I1Sk.js.map} +1 -1
  158. package/dist/web/public/assets/{table-DVQPjWsi.js → table-CPn1MRcy.js} +2 -2
  159. package/dist/web/public/assets/{table-DVQPjWsi.js.map → table-CPn1MRcy.js.map} +1 -1
  160. package/dist/web/public/assets/{topn-Cs-wP6Tf.js → topn-Ba3RjcK1.js} +2 -2
  161. package/dist/web/public/assets/{topn-Cs-wP6Tf.js.map → topn-Ba3RjcK1.js.map} +1 -1
  162. package/dist/web/public/assets/{trash-2-BXhBc6TL.js → trash-2-Dfov8aHD.js} +2 -2
  163. package/dist/web/public/assets/{trash-2-BXhBc6TL.js.map → trash-2-Dfov8aHD.js.map} +1 -1
  164. package/dist/web/public/assets/{use-background-tasks-DWe_iYgk.js → use-background-tasks-BQrEeUwY.js} +2 -2
  165. package/dist/web/public/assets/{use-background-tasks-DWe_iYgk.js.map → use-background-tasks-BQrEeUwY.js.map} +1 -1
  166. package/dist/web/public/assets/{use-llm-admin-SE-2iSIe.js → use-llm-admin-DYekqogG.js} +2 -2
  167. package/dist/web/public/assets/{use-llm-admin-SE-2iSIe.js.map → use-llm-admin-DYekqogG.js.map} +1 -1
  168. package/dist/web/public/assets/{use-memory-tCgpqYla.js → use-memory-DbJ4pP2Z.js} +2 -2
  169. package/dist/web/public/assets/{use-memory-tCgpqYla.js.map → use-memory-DbJ4pP2Z.js.map} +1 -1
  170. package/dist/web/public/assets/{use-observability-CMUSiy5z.js → use-observability-C2M6WZ9W.js} +2 -2
  171. package/dist/web/public/assets/{use-observability-CMUSiy5z.js.map → use-observability-C2M6WZ9W.js.map} +1 -1
  172. package/dist/web/public/assets/{use-settings-BUM0BmtA.js → use-settings-DMdaoWsB.js} +2 -2
  173. package/dist/web/public/assets/{use-settings-BUM0BmtA.js.map → use-settings-DMdaoWsB.js.map} +1 -1
  174. package/dist/web/public/assets/{use-workspace-BOUxEbaB.js → use-workspace-BHG7h3jQ.js} +2 -2
  175. package/dist/web/public/assets/{use-workspace-BOUxEbaB.js.map → use-workspace-BHG7h3jQ.js.map} +1 -1
  176. package/dist/web/public/assets/{useQuery-II-YwTUw.js → useQuery-PdiC7-sY.js} +2 -2
  177. package/dist/web/public/assets/{useQuery-II-YwTUw.js.map → useQuery-PdiC7-sY.js.map} +1 -1
  178. package/dist/web/public/assets/{vector-C83bHtEW.js → vector-DnZM3OXU.js} +2 -2
  179. package/dist/web/public/assets/{vector-C83bHtEW.js.map → vector-DnZM3OXU.js.map} +1 -1
  180. package/dist/web/public/assets/{viewer-rMDo9pIb.js → viewer-Dz6k0YKp.js} +2 -2
  181. package/dist/web/public/assets/{viewer-rMDo9pIb.js.map → viewer-Dz6k0YKp.js.map} +1 -1
  182. package/dist/web/public/assets/{workspace-tVixiuWJ.js → workspace-BnXrWS3j.js} +2 -2
  183. package/dist/web/public/assets/{workspace-tVixiuWJ.js.map → workspace-BnXrWS3j.js.map} +1 -1
  184. package/dist/web/public/assets/{workspaces-CsLKDstm.js → workspaces-CSS_UBEi.js} +2 -2
  185. package/dist/web/public/assets/{workspaces-CsLKDstm.js.map → workspaces-CSS_UBEi.js.map} +1 -1
  186. package/dist/web/public/assets/{x-B0lqsHGi.js → x-DG-JKVw_.js} +2 -2
  187. package/dist/web/public/assets/{x-B0lqsHGi.js.map → x-DG-JKVw_.js.map} +1 -1
  188. package/dist/web/public/index.html +1 -1
  189. package/package.json +1 -1
  190. package/dist/web/public/assets/agents-0IMyFVui.js +0 -7
  191. package/dist/web/public/assets/agents-0IMyFVui.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{b as S,d as O,a as V,u as L,j as e,k,B as p,S as Q,f as G,g as X,h as Z,i as H,I as y,l as c,m as j,L as J,e as $,c as Y}from"./index-wVLKSZpI.js";import{r as g}from"./react-Cb2sDjhD.js";import{u as ee,a as te}from"./use-settings-BUM0BmtA.js";import{u as se}from"./useQuery-II-YwTUw.js";import{m as ae}from"./use-memory-tCgpqYla.js";import{L as M}from"./loader-circle-DPc6botW.js";import{S as re}from"./save-3XXDgdfD.js";import{E as oe,a as ne}from"./eye-aJAK4qYW.js";import{C as ie}from"./confirm-dialog-C68_XUSd.js";import{E as ce}from"./empty-state-Bzkp-ADK.js";import{C as F}from"./circle-x-D3x0-M6I.js";import{R as le}from"./refresh-ccw-C2Q06LWj.js";import{P}from"./play-BdTIFReu.js";import{D as de}from"./download-DCcaTdc6.js";import{T as me}from"./trash-2-BXhBc6TL.js";import{C as ue}from"./circle-check-big-BIrgy-jJ.js";import"./dialog-gd2B3olO.js";import"./x-B0lqsHGi.js";const D={all:["memory","vector"],status:t=>["memory","vector","status",t??null]};function xe(t={}){return se({queryKey:D.status(t.user_key),queryFn:()=>O.getMemoryVectorStatus(t),refetchInterval:3e3,refetchIntervalInBackground:!1})}function z(t){return()=>{t.invalidateQueries({queryKey:D.all}),t.invalidateQueries({queryKey:ae.all})}}function he(){const t=V(),s=z(t);return S({mutationFn:n=>O.backfillMemoryVector({user_key:n?.user_key},n?.max_rows?{max_rows:n.max_rows}:void 0),onSuccess:s})}function fe(){const t=V();return S({mutationFn:()=>O.downloadMemoryVector(),onSuccess:z(t)})}function pe(){const t=V();return S({mutationFn:s=>O.clearMemoryVector({user_key:s?.user_key}),onSuccess:z(t)})}function W(){return S({mutationFn:()=>O.testMemoryVector()})}const m={backend:"off",localModel:"Xenova/bge-base-zh-v1.5",openaiBaseUrl:"https://api.openai.com/v1",openaiModel:"text-embedding-3-small",openaiApiKey:"",batchSize:"32",hybridWeight:"0.5"};function ve(t){const s=(t.IMHUB_MEMORY_VECTOR_BACKEND||"").toLowerCase();return{backend:s==="local"||s==="openai"?s:"off",localModel:t.IMHUB_MEMORY_VECTOR_LOCAL_MODEL||"",openaiBaseUrl:t.IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL||"",openaiModel:t.IMHUB_MEMORY_VECTOR_OPENAI_MODEL||"",openaiApiKey:"",batchSize:t.IMHUB_MEMORY_VECTOR_BATCH_SIZE||"",hybridWeight:t.IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT||""}}function ge({onSaved:t}){const{t:s}=L(["memory","common"]),n=ee({reveal:!1}),l=g.useMemo(()=>n.data?.env??{},[n.data?.env]),[r,h]=g.useState(m),[N,_]=g.useState(!1),f=g.useRef(m);g.useEffect(()=>{if(!n.data)return;const a=ve(l);h(a),f.current=a},[l,n.data]);const b=te(),E=W(),w=g.useMemo(()=>{const a=f.current;return Object.keys(r).some(d=>r[d]!==a[d])},[r]);function x(a,d){h(o=>({...o,[a]:d}))}function B(a){h(d=>{const o={...d,backend:a};return a==="local"&&!o.localModel.trim()&&(o.localModel=m.localModel),a==="openai"&&(o.openaiBaseUrl.trim()||(o.openaiBaseUrl=m.openaiBaseUrl),o.openaiModel.trim()||(o.openaiModel=m.openaiModel)),a!=="off"&&(o.batchSize.trim()||(o.batchSize=m.batchSize),o.hybridWeight.trim()||(o.hybridWeight=m.hybridWeight)),o})}function A(){if(r.backend==="openai"){if(!r.openaiBaseUrl.trim())return s("vector.form.errors.baseUrlRequired");try{new URL(r.openaiBaseUrl.trim())}catch{return s("vector.form.errors.baseUrlInvalid")}if(!r.openaiModel.trim())return s("vector.form.errors.openaiModelRequired");if(!!!l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY&&!r.openaiApiKey.trim())return s("vector.form.errors.apiKeyRequired")}if(r.backend==="local"&&!r.localModel.trim())return s("vector.form.errors.localModelRequired");if(r.batchSize.trim()){const a=Number(r.batchSize);if(!Number.isFinite(a)||a<1||a>1024)return s("vector.form.errors.batchSizeRange")}if(r.hybridWeight.trim()){const a=Number(r.hybridWeight);if(!Number.isFinite(a)||a<0||a>1)return s("vector.form.errors.hybridWeightRange")}return null}function I(){const a=f.current,d={},o=(C,U,q)=>{U!==q&&(d[C]=U.trim()===""?null:U.trim())};return o("IMHUB_MEMORY_VECTOR_BACKEND",r.backend,a.backend),o("IMHUB_MEMORY_VECTOR_LOCAL_MODEL",r.localModel,a.localModel),o("IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL",r.openaiBaseUrl,a.openaiBaseUrl),o("IMHUB_MEMORY_VECTOR_OPENAI_MODEL",r.openaiModel,a.openaiModel),o("IMHUB_MEMORY_VECTOR_BATCH_SIZE",r.batchSize,a.batchSize),o("IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT",r.hybridWeight,a.hybridWeight),r.openaiApiKey.trim()&&(d.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY=r.openaiApiKey.trim()),d}async function T(){const a=A();if(a){c.error(a);return}const d=I();if(Object.keys(d).length===0){c.info(s("vector.form.nothingChanged"));return}try{await b.mutateAsync({updates:d}),c.success(s("vector.form.savedToast"));try{const o=await E.mutateAsync();o.ok?c.success(s("vector.actions.testOk")):c.error(s("vector.actions.testFail",{detail:o.error??""}))}catch(o){const{message:C}=j(o,s);c.error(s("vector.actions.testFail",{detail:C}))}t?.()}catch(o){const{message:C}=j(o,s);c.error(C)}}function R(){h(f.current)}const i=b.isPending||E.isPending,u=!!l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY;return e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("h2",{className:"text-sm font-medium",children:s("vector.form.title")}),w&&e.jsx(k,{variant:"info",children:s("vector.form.dirty")}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[w&&e.jsx(p,{type:"button",variant:"secondary",size:"sm",onClick:R,disabled:i,children:s("vector.form.discard")}),e.jsxs(p,{type:"button",size:"sm",onClick:()=>void T(),disabled:!w||i,children:[i?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(re,{className:"h-4 w-4"}),s("vector.form.saveAndTest")]})]})]}),e.jsx("p",{className:"mt-1 text-xs text-text-dim",children:s("vector.form.hint")}),n.isLoading?e.jsx("div",{className:"mt-3 h-32 w-full rounded-md bg-surface-2 animate-pulse"}):e.jsxs("div",{className:"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2",children:[e.jsx(v,{label:s("vector.form.backend"),help:s("vector.form.backendHelp"),children:e.jsxs(Q,{value:r.backend,onValueChange:a=>B(a),children:[e.jsx(G,{children:e.jsx(X,{})}),e.jsxs(Z,{children:[e.jsx(H,{value:"off",children:s("vector.form.backendOff")}),e.jsx(H,{value:"local",children:s("vector.form.backendLocal")}),e.jsx(H,{value:"openai",children:s("vector.form.backendOpenai")})]})]})}),r.backend==="local"&&e.jsx(v,{label:s("vector.form.localModel"),help:s("vector.form.localModelHelp"),children:e.jsx(y,{value:r.localModel,placeholder:m.localModel,onChange:a=>x("localModel",a.target.value),className:"font-mono text-xs"})}),r.backend==="openai"&&e.jsxs(e.Fragment,{children:[e.jsx(v,{label:s("vector.form.openaiBaseUrl"),help:s("vector.form.openaiBaseUrlHelp"),children:e.jsx(y,{value:r.openaiBaseUrl,placeholder:m.openaiBaseUrl,onChange:a=>x("openaiBaseUrl",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.openaiModel"),help:s("vector.form.openaiModelHelp"),children:e.jsx(y,{value:r.openaiModel,placeholder:m.openaiModel,onChange:a=>x("openaiModel",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.openaiApiKey"),help:s(u?"vector.form.apiKeyKeepHint":"vector.form.apiKeyNewHint"),children:e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(y,{type:N?"text":"password",value:r.openaiApiKey,placeholder:u?l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY||"••••":"sk-…",onChange:a=>x("openaiApiKey",a.target.value),className:"font-mono text-xs",autoComplete:"off"}),e.jsx(p,{type:"button",variant:"ghost",size:"icon",onClick:()=>_(a=>!a),"aria-label":s("vector.form.toggleApiKey"),children:N?e.jsx(oe,{className:"h-4 w-4"}):e.jsx(ne,{className:"h-4 w-4"})})]})})]}),r.backend!=="off"&&e.jsxs(e.Fragment,{children:[e.jsx(v,{label:s("vector.form.batchSize"),help:s("vector.form.batchSizeHelp"),children:e.jsx(y,{type:"number",min:1,max:1024,step:1,value:r.batchSize,placeholder:m.batchSize,onChange:a=>x("batchSize",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.hybridWeight"),help:s("vector.form.hybridWeightHelp"),children:e.jsx(y,{type:"number",min:0,max:1,step:.05,value:r.hybridWeight,placeholder:m.hybridWeight,onChange:a=>x("hybridWeight",a.target.value),className:"font-mono text-xs"})})]})]})]})}function v({label:t,help:s,children:n}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(J,{className:"text-xs text-text-dim",children:t}),n,s&&e.jsx("p",{className:"text-[11px] text-text-muted",children:s})]})}function Le(){const{t}=L(["memory","common"]),[s]=$(),n=s.get("user")??"",l=xe(n?{user_key:n}:{}),r=l.data?.status,h=l.data?.coverage,N=l.data?.jobs??[],_=W(),f=fe(),b=he(),E=pe(),[w,x]=g.useState(!1);async function B(){try{const i=await _.mutateAsync();i.ok?c.success(t("vector.actions.testOk")):c.error(t("vector.actions.testFail",{detail:i.error??""}))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function A(){try{const i=await f.mutateAsync();c.success(i.alreadyReady?t("vector.actions.downloadAlreadyReady"):t("vector.actions.downloadStarted"))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function I(){try{await b.mutateAsync(n?{user_key:n}:void 0),c.success(t("vector.actions.backfillStarted"))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function T(){try{const i=await E.mutateAsync(n?{user_key:n}:void 0);c.success(t("vector.actions.clearStarted",{count:i.cleared}))}catch(i){const{message:u}=j(i,t);throw c.error(u),i}}if(l.isLoading)return e.jsx("div",{className:"h-48 w-full rounded-md bg-surface-2 animate-pulse"});if(!r)return e.jsx(ce,{icon:e.jsx(F,{}),title:t("common:states.error"),description:l.error?.message??""});const R=n?t("vector.coverage.scope_user",{userKey:n}):t("vector.coverage.scope_global");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("vector.title")}),e.jsxs(p,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>l.refetch(),disabled:l.isFetching,"aria-label":t("actions.refresh",{ns:"common"}),children:[l.isFetching?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(le,{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("vector.subtitle")})]}),e.jsx(ge,{onSaved:()=>void l.refetch()}),e.jsxs("div",{className:"grid gap-3 md:grid-cols-2",children:[e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-sm",children:[e.jsx("span",{className:"font-medium",children:t("vector.backendLabel")}),e.jsx(k,{variant:"secondary",children:r.backend}),e.jsx(k,{variant:r.ready?"success":"warning",children:r.ready?t("vector.readyTrue"):t("vector.readyFalse")}),r.downloaded&&e.jsx(k,{variant:"info",children:t("vector.downloadedTrue")})]}),e.jsxs("dl",{className:"mt-3 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm",children:[e.jsx("dt",{className:"text-text-dim",children:t("vector.modelLabel")}),e.jsx("dd",{className:"font-mono text-text",children:r.modelId||"—"}),e.jsx("dt",{className:"text-text-dim",children:t("vector.dimsLabel")}),e.jsx("dd",{className:"tabular-nums text-text",children:r.dims||"—"}),r.lastError&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:t("vector.lastError")}),e.jsx("dd",{className:"text-danger text-xs break-all",children:r.lastError})]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.coverage.title")}),e.jsx("div",{className:"mt-1 text-xs text-text-dim",children:R}),e.jsxs("div",{className:"mt-3 grid grid-cols-3 gap-2",children:[e.jsx(K,{label:t("vector.coverage.total"),value:h?.total??0,tone:"info"}),e.jsx(K,{label:t("vector.coverage.withEmbedding"),value:h?.withEmbedding??0,tone:"success"}),e.jsx(K,{label:t("vector.coverage.withDifferentModel"),value:h?.withDifferentModel??0,tone:"warning"})]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.actions.title")}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsxs(p,{variant:"secondary",size:"sm",onClick:()=>void B(),disabled:_.isPending,children:[_.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),t("vector.actions.test")]}),r.backend==="local"&&e.jsxs(p,{variant:"secondary",size:"sm",onClick:()=>void A(),disabled:f.isPending,children:[f.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(de,{className:"h-4 w-4"}),t("vector.actions.download")]}),e.jsxs(p,{variant:"default",size:"sm",onClick:()=>void I(),disabled:b.isPending||!r.ready,children:[b.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),t("vector.actions.backfill")]}),e.jsxs(p,{variant:"destructive",size:"sm",onClick:()=>x(!0),disabled:E.isPending,children:[e.jsx(me,{className:"h-4 w-4"}),t("vector.actions.clear")]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.jobs.title")}),N.length===0?e.jsx("div",{className:"mt-2 text-sm text-text-muted",children:t("vector.jobs.empty")}):e.jsx("ul",{className:"mt-2 flex flex-col gap-1.5 text-sm",children:N.map(i=>e.jsx("li",{children:e.jsx(ye,{job:i})},i.id))})]}),e.jsx(ie,{open:w,onOpenChange:x,title:t("vector.actions.confirmClear"),description:t("vector.actions.confirmClearDesc",{scope:R}),intent:"danger",confirmLabel:t("vector.actions.clear"),onConfirm:T})]})}const be={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 K({label:t,value:s,tone:n}){return e.jsxs("div",{className:Y("rounded-md border bg-bg px-2 py-2",be[n]),children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-text-dim",children:t}),e.jsx("div",{className:"text-xl font-semibold tabular-nums",children:s})]})}function ye({job:t}){const{t:s}=L("memory"),n=t.phase==="done"?"text-success":t.phase==="failed"?"text-danger":"text-info";return e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-xs",children:[e.jsx(k,{variant:"outline",children:t.kind}),e.jsxs("span",{className:Y("font-medium",n),children:[t.phase==="running"&&e.jsx(M,{className:"inline h-3 w-3 animate-spin"}),t.phase==="done"&&e.jsx(ue,{className:"inline h-3 w-3"}),t.phase==="failed"&&e.jsx(F,{className:"inline h-3 w-3"})," ",s(`vector.jobs.phase.${t.phase}`)]}),e.jsx("span",{className:"text-text-dim",children:t.message}),e.jsx("span",{className:"ml-auto text-text-muted tabular-nums",children:je(t.finishedAt??t.startedAt)})]})}function je(t){try{const s=new Date(t);return Number.isNaN(s.getTime())?String(t):s.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return String(t)}}export{Le as default};
2
- //# sourceMappingURL=vector-C83bHtEW.js.map
1
+ import{b as S,d as O,a as V,u as L,j as e,k,B as p,S as Q,f as G,g as X,h as Z,i as H,I as y,l as c,m as j,L as J,e as $,c as Y}from"./index-O0BQoyzo.js";import{r as g}from"./react-Cb2sDjhD.js";import{u as ee,a as te}from"./use-settings-DMdaoWsB.js";import{u as se}from"./useQuery-PdiC7-sY.js";import{m as ae}from"./use-memory-DbJ4pP2Z.js";import{L as M}from"./loader-circle-9VUMGitw.js";import{S as re}from"./save-DB0BDYTs.js";import{E as oe,a as ne}from"./eye-CFhg5BTa.js";import{C as ie}from"./confirm-dialog-DmJq4Td9.js";import{E as ce}from"./empty-state-C-qjOHyu.js";import{C as F}from"./circle-x-Ccg1HyV-.js";import{R as le}from"./refresh-ccw-D2CWiyU_.js";import{P}from"./play-CfSn5Vdl.js";import{D as de}from"./download-OhsGtnO-.js";import{T as me}from"./trash-2-Dfov8aHD.js";import{C as ue}from"./circle-check-big-C2RTc48c.js";import"./dialog-bAIDaO-6.js";import"./x-DG-JKVw_.js";const D={all:["memory","vector"],status:t=>["memory","vector","status",t??null]};function xe(t={}){return se({queryKey:D.status(t.user_key),queryFn:()=>O.getMemoryVectorStatus(t),refetchInterval:3e3,refetchIntervalInBackground:!1})}function z(t){return()=>{t.invalidateQueries({queryKey:D.all}),t.invalidateQueries({queryKey:ae.all})}}function he(){const t=V(),s=z(t);return S({mutationFn:n=>O.backfillMemoryVector({user_key:n?.user_key},n?.max_rows?{max_rows:n.max_rows}:void 0),onSuccess:s})}function fe(){const t=V();return S({mutationFn:()=>O.downloadMemoryVector(),onSuccess:z(t)})}function pe(){const t=V();return S({mutationFn:s=>O.clearMemoryVector({user_key:s?.user_key}),onSuccess:z(t)})}function W(){return S({mutationFn:()=>O.testMemoryVector()})}const m={backend:"off",localModel:"Xenova/bge-base-zh-v1.5",openaiBaseUrl:"https://api.openai.com/v1",openaiModel:"text-embedding-3-small",openaiApiKey:"",batchSize:"32",hybridWeight:"0.5"};function ve(t){const s=(t.IMHUB_MEMORY_VECTOR_BACKEND||"").toLowerCase();return{backend:s==="local"||s==="openai"?s:"off",localModel:t.IMHUB_MEMORY_VECTOR_LOCAL_MODEL||"",openaiBaseUrl:t.IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL||"",openaiModel:t.IMHUB_MEMORY_VECTOR_OPENAI_MODEL||"",openaiApiKey:"",batchSize:t.IMHUB_MEMORY_VECTOR_BATCH_SIZE||"",hybridWeight:t.IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT||""}}function ge({onSaved:t}){const{t:s}=L(["memory","common"]),n=ee({reveal:!1}),l=g.useMemo(()=>n.data?.env??{},[n.data?.env]),[r,h]=g.useState(m),[N,_]=g.useState(!1),f=g.useRef(m);g.useEffect(()=>{if(!n.data)return;const a=ve(l);h(a),f.current=a},[l,n.data]);const b=te(),E=W(),w=g.useMemo(()=>{const a=f.current;return Object.keys(r).some(d=>r[d]!==a[d])},[r]);function x(a,d){h(o=>({...o,[a]:d}))}function B(a){h(d=>{const o={...d,backend:a};return a==="local"&&!o.localModel.trim()&&(o.localModel=m.localModel),a==="openai"&&(o.openaiBaseUrl.trim()||(o.openaiBaseUrl=m.openaiBaseUrl),o.openaiModel.trim()||(o.openaiModel=m.openaiModel)),a!=="off"&&(o.batchSize.trim()||(o.batchSize=m.batchSize),o.hybridWeight.trim()||(o.hybridWeight=m.hybridWeight)),o})}function A(){if(r.backend==="openai"){if(!r.openaiBaseUrl.trim())return s("vector.form.errors.baseUrlRequired");try{new URL(r.openaiBaseUrl.trim())}catch{return s("vector.form.errors.baseUrlInvalid")}if(!r.openaiModel.trim())return s("vector.form.errors.openaiModelRequired");if(!!!l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY&&!r.openaiApiKey.trim())return s("vector.form.errors.apiKeyRequired")}if(r.backend==="local"&&!r.localModel.trim())return s("vector.form.errors.localModelRequired");if(r.batchSize.trim()){const a=Number(r.batchSize);if(!Number.isFinite(a)||a<1||a>1024)return s("vector.form.errors.batchSizeRange")}if(r.hybridWeight.trim()){const a=Number(r.hybridWeight);if(!Number.isFinite(a)||a<0||a>1)return s("vector.form.errors.hybridWeightRange")}return null}function I(){const a=f.current,d={},o=(C,U,q)=>{U!==q&&(d[C]=U.trim()===""?null:U.trim())};return o("IMHUB_MEMORY_VECTOR_BACKEND",r.backend,a.backend),o("IMHUB_MEMORY_VECTOR_LOCAL_MODEL",r.localModel,a.localModel),o("IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL",r.openaiBaseUrl,a.openaiBaseUrl),o("IMHUB_MEMORY_VECTOR_OPENAI_MODEL",r.openaiModel,a.openaiModel),o("IMHUB_MEMORY_VECTOR_BATCH_SIZE",r.batchSize,a.batchSize),o("IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT",r.hybridWeight,a.hybridWeight),r.openaiApiKey.trim()&&(d.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY=r.openaiApiKey.trim()),d}async function T(){const a=A();if(a){c.error(a);return}const d=I();if(Object.keys(d).length===0){c.info(s("vector.form.nothingChanged"));return}try{await b.mutateAsync({updates:d}),c.success(s("vector.form.savedToast"));try{const o=await E.mutateAsync();o.ok?c.success(s("vector.actions.testOk")):c.error(s("vector.actions.testFail",{detail:o.error??""}))}catch(o){const{message:C}=j(o,s);c.error(s("vector.actions.testFail",{detail:C}))}t?.()}catch(o){const{message:C}=j(o,s);c.error(C)}}function R(){h(f.current)}const i=b.isPending||E.isPending,u=!!l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY;return e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("h2",{className:"text-sm font-medium",children:s("vector.form.title")}),w&&e.jsx(k,{variant:"info",children:s("vector.form.dirty")}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[w&&e.jsx(p,{type:"button",variant:"secondary",size:"sm",onClick:R,disabled:i,children:s("vector.form.discard")}),e.jsxs(p,{type:"button",size:"sm",onClick:()=>void T(),disabled:!w||i,children:[i?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(re,{className:"h-4 w-4"}),s("vector.form.saveAndTest")]})]})]}),e.jsx("p",{className:"mt-1 text-xs text-text-dim",children:s("vector.form.hint")}),n.isLoading?e.jsx("div",{className:"mt-3 h-32 w-full rounded-md bg-surface-2 animate-pulse"}):e.jsxs("div",{className:"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2",children:[e.jsx(v,{label:s("vector.form.backend"),help:s("vector.form.backendHelp"),children:e.jsxs(Q,{value:r.backend,onValueChange:a=>B(a),children:[e.jsx(G,{children:e.jsx(X,{})}),e.jsxs(Z,{children:[e.jsx(H,{value:"off",children:s("vector.form.backendOff")}),e.jsx(H,{value:"local",children:s("vector.form.backendLocal")}),e.jsx(H,{value:"openai",children:s("vector.form.backendOpenai")})]})]})}),r.backend==="local"&&e.jsx(v,{label:s("vector.form.localModel"),help:s("vector.form.localModelHelp"),children:e.jsx(y,{value:r.localModel,placeholder:m.localModel,onChange:a=>x("localModel",a.target.value),className:"font-mono text-xs"})}),r.backend==="openai"&&e.jsxs(e.Fragment,{children:[e.jsx(v,{label:s("vector.form.openaiBaseUrl"),help:s("vector.form.openaiBaseUrlHelp"),children:e.jsx(y,{value:r.openaiBaseUrl,placeholder:m.openaiBaseUrl,onChange:a=>x("openaiBaseUrl",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.openaiModel"),help:s("vector.form.openaiModelHelp"),children:e.jsx(y,{value:r.openaiModel,placeholder:m.openaiModel,onChange:a=>x("openaiModel",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.openaiApiKey"),help:s(u?"vector.form.apiKeyKeepHint":"vector.form.apiKeyNewHint"),children:e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(y,{type:N?"text":"password",value:r.openaiApiKey,placeholder:u?l.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY||"••••":"sk-…",onChange:a=>x("openaiApiKey",a.target.value),className:"font-mono text-xs",autoComplete:"off"}),e.jsx(p,{type:"button",variant:"ghost",size:"icon",onClick:()=>_(a=>!a),"aria-label":s("vector.form.toggleApiKey"),children:N?e.jsx(oe,{className:"h-4 w-4"}):e.jsx(ne,{className:"h-4 w-4"})})]})})]}),r.backend!=="off"&&e.jsxs(e.Fragment,{children:[e.jsx(v,{label:s("vector.form.batchSize"),help:s("vector.form.batchSizeHelp"),children:e.jsx(y,{type:"number",min:1,max:1024,step:1,value:r.batchSize,placeholder:m.batchSize,onChange:a=>x("batchSize",a.target.value),className:"font-mono text-xs"})}),e.jsx(v,{label:s("vector.form.hybridWeight"),help:s("vector.form.hybridWeightHelp"),children:e.jsx(y,{type:"number",min:0,max:1,step:.05,value:r.hybridWeight,placeholder:m.hybridWeight,onChange:a=>x("hybridWeight",a.target.value),className:"font-mono text-xs"})})]})]})]})}function v({label:t,help:s,children:n}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(J,{className:"text-xs text-text-dim",children:t}),n,s&&e.jsx("p",{className:"text-[11px] text-text-muted",children:s})]})}function Le(){const{t}=L(["memory","common"]),[s]=$(),n=s.get("user")??"",l=xe(n?{user_key:n}:{}),r=l.data?.status,h=l.data?.coverage,N=l.data?.jobs??[],_=W(),f=fe(),b=he(),E=pe(),[w,x]=g.useState(!1);async function B(){try{const i=await _.mutateAsync();i.ok?c.success(t("vector.actions.testOk")):c.error(t("vector.actions.testFail",{detail:i.error??""}))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function A(){try{const i=await f.mutateAsync();c.success(i.alreadyReady?t("vector.actions.downloadAlreadyReady"):t("vector.actions.downloadStarted"))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function I(){try{await b.mutateAsync(n?{user_key:n}:void 0),c.success(t("vector.actions.backfillStarted"))}catch(i){const{message:u}=j(i,t);c.error(u)}}async function T(){try{const i=await E.mutateAsync(n?{user_key:n}:void 0);c.success(t("vector.actions.clearStarted",{count:i.cleared}))}catch(i){const{message:u}=j(i,t);throw c.error(u),i}}if(l.isLoading)return e.jsx("div",{className:"h-48 w-full rounded-md bg-surface-2 animate-pulse"});if(!r)return e.jsx(ce,{icon:e.jsx(F,{}),title:t("common:states.error"),description:l.error?.message??""});const R=n?t("vector.coverage.scope_user",{userKey:n}):t("vector.coverage.scope_global");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("vector.title")}),e.jsxs(p,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>l.refetch(),disabled:l.isFetching,"aria-label":t("actions.refresh",{ns:"common"}),children:[l.isFetching?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(le,{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("vector.subtitle")})]}),e.jsx(ge,{onSaved:()=>void l.refetch()}),e.jsxs("div",{className:"grid gap-3 md:grid-cols-2",children:[e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-sm",children:[e.jsx("span",{className:"font-medium",children:t("vector.backendLabel")}),e.jsx(k,{variant:"secondary",children:r.backend}),e.jsx(k,{variant:r.ready?"success":"warning",children:r.ready?t("vector.readyTrue"):t("vector.readyFalse")}),r.downloaded&&e.jsx(k,{variant:"info",children:t("vector.downloadedTrue")})]}),e.jsxs("dl",{className:"mt-3 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm",children:[e.jsx("dt",{className:"text-text-dim",children:t("vector.modelLabel")}),e.jsx("dd",{className:"font-mono text-text",children:r.modelId||"—"}),e.jsx("dt",{className:"text-text-dim",children:t("vector.dimsLabel")}),e.jsx("dd",{className:"tabular-nums text-text",children:r.dims||"—"}),r.lastError&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:t("vector.lastError")}),e.jsx("dd",{className:"text-danger text-xs break-all",children:r.lastError})]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.coverage.title")}),e.jsx("div",{className:"mt-1 text-xs text-text-dim",children:R}),e.jsxs("div",{className:"mt-3 grid grid-cols-3 gap-2",children:[e.jsx(K,{label:t("vector.coverage.total"),value:h?.total??0,tone:"info"}),e.jsx(K,{label:t("vector.coverage.withEmbedding"),value:h?.withEmbedding??0,tone:"success"}),e.jsx(K,{label:t("vector.coverage.withDifferentModel"),value:h?.withDifferentModel??0,tone:"warning"})]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.actions.title")}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsxs(p,{variant:"secondary",size:"sm",onClick:()=>void B(),disabled:_.isPending,children:[_.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),t("vector.actions.test")]}),r.backend==="local"&&e.jsxs(p,{variant:"secondary",size:"sm",onClick:()=>void A(),disabled:f.isPending,children:[f.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(de,{className:"h-4 w-4"}),t("vector.actions.download")]}),e.jsxs(p,{variant:"default",size:"sm",onClick:()=>void I(),disabled:b.isPending||!r.ready,children:[b.isPending?e.jsx(M,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),t("vector.actions.backfill")]}),e.jsxs(p,{variant:"destructive",size:"sm",onClick:()=>x(!0),disabled:E.isPending,children:[e.jsx(me,{className:"h-4 w-4"}),t("vector.actions.clear")]})]})]}),e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsx("div",{className:"text-sm font-medium",children:t("vector.jobs.title")}),N.length===0?e.jsx("div",{className:"mt-2 text-sm text-text-muted",children:t("vector.jobs.empty")}):e.jsx("ul",{className:"mt-2 flex flex-col gap-1.5 text-sm",children:N.map(i=>e.jsx("li",{children:e.jsx(ye,{job:i})},i.id))})]}),e.jsx(ie,{open:w,onOpenChange:x,title:t("vector.actions.confirmClear"),description:t("vector.actions.confirmClearDesc",{scope:R}),intent:"danger",confirmLabel:t("vector.actions.clear"),onConfirm:T})]})}const be={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 K({label:t,value:s,tone:n}){return e.jsxs("div",{className:Y("rounded-md border bg-bg px-2 py-2",be[n]),children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-text-dim",children:t}),e.jsx("div",{className:"text-xl font-semibold tabular-nums",children:s})]})}function ye({job:t}){const{t:s}=L("memory"),n=t.phase==="done"?"text-success":t.phase==="failed"?"text-danger":"text-info";return e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-xs",children:[e.jsx(k,{variant:"outline",children:t.kind}),e.jsxs("span",{className:Y("font-medium",n),children:[t.phase==="running"&&e.jsx(M,{className:"inline h-3 w-3 animate-spin"}),t.phase==="done"&&e.jsx(ue,{className:"inline h-3 w-3"}),t.phase==="failed"&&e.jsx(F,{className:"inline h-3 w-3"})," ",s(`vector.jobs.phase.${t.phase}`)]}),e.jsx("span",{className:"text-text-dim",children:t.message}),e.jsx("span",{className:"ml-auto text-text-muted tabular-nums",children:je(t.finishedAt??t.startedAt)})]})}function je(t){try{const s=new Date(t);return Number.isNaN(s.getTime())?String(t):s.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return String(t)}}export{Le as default};
2
+ //# sourceMappingURL=vector-DnZM3OXU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vector-C83bHtEW.js","sources":["../../src/hooks/use-vector.ts","../../src/routes/memory/vector-backend-form.tsx","../../src/routes/memory/vector.tsx"],"sourcesContent":["/**\n * useVector — react-query wrappers for the embedding/vector backend.\n *\n * useVectorStatus({ user_key }) — status + coverage + jobs\n * (polls every 3s; jobs\n * are async)\n * useTriggerVectorBackfill()\n * useTriggerVectorDownload()\n * useTriggerVectorClear() — DESTRUCTIVE; route gates\n * with ConfirmDialog\n * useTriggerVectorTest()\n *\n * Status polling stops backgrounded (refetchIntervalInBackground:\n * false) so an idle tab on a phone doesn't drain battery.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport { memoryKeys } from './use-memory'\nimport type {\n VectorActionResponse,\n VectorClearResponse,\n VectorStatus,\n VectorTestResponse,\n} from '@/types/api'\n\nexport const vectorKeys = {\n all: ['memory', 'vector'] as const,\n status: (user_key?: string) => ['memory', 'vector', 'status', user_key ?? null] as const,\n}\n\nexport function useVectorStatus(query: { user_key?: string } = {}) {\n return useQuery<VectorStatus>({\n queryKey: vectorKeys.status(query.user_key),\n queryFn: () => api.getMemoryVectorStatus(query),\n refetchInterval: 3000,\n refetchIntervalInBackground: false,\n })\n}\n\nfunction invalidateAfter(qc: ReturnType<typeof useQueryClient>): () => void {\n return () => {\n qc.invalidateQueries({ queryKey: vectorKeys.all })\n qc.invalidateQueries({ queryKey: memoryKeys.all })\n }\n}\n\nexport function useTriggerVectorBackfill() {\n const qc = useQueryClient()\n const invalidate = invalidateAfter(qc)\n return useMutation<\n VectorActionResponse,\n Error,\n { user_key?: string; max_rows?: number } | undefined\n >({\n mutationFn: (vars) => api.backfillMemoryVector(\n { user_key: vars?.user_key },\n vars?.max_rows ? { max_rows: vars.max_rows } : undefined,\n ),\n onSuccess: invalidate,\n })\n}\n\nexport function useTriggerVectorDownload() {\n const qc = useQueryClient()\n return useMutation<VectorActionResponse, Error, void>({\n mutationFn: () => api.downloadMemoryVector(),\n onSuccess: invalidateAfter(qc),\n })\n}\n\nexport function useTriggerVectorClear() {\n const qc = useQueryClient()\n return useMutation<VectorClearResponse, Error, { user_key?: string } | undefined>({\n mutationFn: (vars) => api.clearMemoryVector({ user_key: vars?.user_key }),\n onSuccess: invalidateAfter(qc),\n })\n}\n\nexport function useTriggerVectorTest() {\n return useMutation<VectorTestResponse, Error, void>({\n mutationFn: () => api.testMemoryVector(),\n })\n}\n","/**\n * VectorBackendForm — inline, form-based editor for the seven\n * IMHUB_MEMORY_VECTOR_* keys that drive the embedding backend.\n *\n * Replaces the older \"Edit in env\" link path: operators no longer\n * have to know which env var maps to which knob, and the on-the-wire\n * format (numbers, OpenAI URL shape) is enforced by the widget.\n *\n * Save flow: PUT /api/env (backend-side allowlist filters to just\n * these keys), then auto-trigger a single `vector/test` so the user\n * sees in toast whether the new config produces an embedding.\n *\n * API key: the GET /api/env response returns secrets masked (e.g.\n * \"sk-1***...abcd\"). The widget shows the mask as placeholder and\n * leaves the input empty by default — typing a value sends it as a\n * real update, leaving it empty sends nothing for that key (the\n * server already rejects masked echoes as \"no change\").\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Eye, EyeOff, Loader2, Save } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { useTriggerVectorTest } from '@/hooks/use-vector'\nimport { describeError } from '@/lib/api/errors'\n\ntype BackendName = 'off' | 'local' | 'openai'\n\ninterface FormState {\n backend: BackendName\n localModel: string\n openaiBaseUrl: string\n openaiModel: string\n openaiApiKey: string // empty = \"keep existing\"\n batchSize: string\n hybridWeight: string\n}\n\nconst DEFAULTS: FormState = {\n backend: 'off',\n localModel: 'Xenova/bge-base-zh-v1.5',\n openaiBaseUrl: 'https://api.openai.com/v1',\n openaiModel: 'text-embedding-3-small',\n openaiApiKey: '',\n batchSize: '32',\n hybridWeight: '0.5',\n}\n\nfunction readFormFromEnv(env: Record<string, string>): FormState {\n const raw = (env.IMHUB_MEMORY_VECTOR_BACKEND || '').toLowerCase()\n const backend: BackendName = raw === 'local' || raw === 'openai' ? raw : 'off'\n return {\n backend,\n localModel: env.IMHUB_MEMORY_VECTOR_LOCAL_MODEL || '',\n openaiBaseUrl: env.IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL || '',\n openaiModel: env.IMHUB_MEMORY_VECTOR_OPENAI_MODEL || '',\n openaiApiKey: '',\n batchSize: env.IMHUB_MEMORY_VECTOR_BATCH_SIZE || '',\n hybridWeight: env.IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT || '',\n }\n}\n\nexport interface VectorBackendFormProps {\n /** Called after a successful save + test cycle so the parent page can\n * refresh status + coverage. */\n onSaved?: () => void\n}\n\nexport function VectorBackendForm({ onSaved }: VectorBackendFormProps): JSX.Element {\n const { t } = useTranslation(['memory', 'common'])\n\n // Pull masked env (we never need real key on the client; the masked\n // value is shown as placeholder so the operator can tell a value is\n // configured without revealing it).\n const envQuery = useEnv({ reveal: false })\n const env = useMemo(() => envQuery.data?.env ?? {}, [envQuery.data?.env])\n\n const [form, setForm] = useState<FormState>(DEFAULTS)\n const [showApiKey, setShowApiKey] = useState(false)\n const initialRef = useRef<FormState>(DEFAULTS)\n\n // Reset from server only when the underlying env object identity\n // changes (after refetch). useEffect avoids stomping on local edits\n // mid-typing.\n useEffect(() => {\n if (!envQuery.data) return\n const next = readFormFromEnv(env)\n setForm(next)\n initialRef.current = next\n }, [env, envQuery.data])\n\n const updateEnv = useUpdateEnv()\n const test = useTriggerVectorTest()\n\n const dirty = useMemo(() => {\n const initial = initialRef.current\n return (Object.keys(form) as Array<keyof FormState>).some((k) => form[k] !== initial[k])\n }, [form])\n\n function set<K extends keyof FormState>(k: K, v: FormState[K]): void {\n setForm((prev) => ({ ...prev, [k]: v }))\n }\n\n // v1.2.75 — when the operator switches backend off→local / off→openai\n // and the per-backend fields are still empty, auto-prefill defaults so\n // they don't have to look up the BGE model id by hand. They can still\n // edit afterwards. Also fills shared numeric knobs (batch / weight)\n // since those have well-known sensible defaults too.\n function selectBackend(next: BackendName): void {\n setForm((prev) => {\n const out = { ...prev, backend: next }\n if (next === 'local' && !out.localModel.trim()) {\n out.localModel = DEFAULTS.localModel\n }\n if (next === 'openai') {\n if (!out.openaiBaseUrl.trim()) out.openaiBaseUrl = DEFAULTS.openaiBaseUrl\n if (!out.openaiModel.trim()) out.openaiModel = DEFAULTS.openaiModel\n }\n if (next !== 'off') {\n if (!out.batchSize.trim()) out.batchSize = DEFAULTS.batchSize\n if (!out.hybridWeight.trim()) out.hybridWeight = DEFAULTS.hybridWeight\n }\n return out\n })\n }\n\n function validate(): string | null {\n if (form.backend === 'openai') {\n if (!form.openaiBaseUrl.trim()) return t('vector.form.errors.baseUrlRequired')\n try { new URL(form.openaiBaseUrl.trim()) } catch { return t('vector.form.errors.baseUrlInvalid') }\n if (!form.openaiModel.trim()) return t('vector.form.errors.openaiModelRequired')\n const hasExistingKey = !!env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY\n if (!hasExistingKey && !form.openaiApiKey.trim()) {\n return t('vector.form.errors.apiKeyRequired')\n }\n }\n if (form.backend === 'local' && !form.localModel.trim()) {\n return t('vector.form.errors.localModelRequired')\n }\n if (form.batchSize.trim()) {\n const n = Number(form.batchSize)\n if (!Number.isFinite(n) || n < 1 || n > 1024) return t('vector.form.errors.batchSizeRange')\n }\n if (form.hybridWeight.trim()) {\n const n = Number(form.hybridWeight)\n if (!Number.isFinite(n) || n < 0 || n > 1) return t('vector.form.errors.hybridWeightRange')\n }\n return null\n }\n\n function buildUpdates(): Record<string, string | null> {\n const initial = initialRef.current\n const out: Record<string, string | null> = {}\n const diff = (key: string, current: string, prev: string): void => {\n if (current === prev) return\n out[key] = current.trim() === '' ? null : current.trim()\n }\n diff('IMHUB_MEMORY_VECTOR_BACKEND', form.backend, initial.backend)\n diff('IMHUB_MEMORY_VECTOR_LOCAL_MODEL', form.localModel, initial.localModel)\n diff('IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL', form.openaiBaseUrl, initial.openaiBaseUrl)\n diff('IMHUB_MEMORY_VECTOR_OPENAI_MODEL', form.openaiModel, initial.openaiModel)\n diff('IMHUB_MEMORY_VECTOR_BATCH_SIZE', form.batchSize, initial.batchSize)\n diff('IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT', form.hybridWeight, initial.hybridWeight)\n // API key: only send if the user actually typed a non-empty new value.\n // Empty input = keep current (which is the whole point of the placeholder UX).\n if (form.openaiApiKey.trim()) {\n out.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY = form.openaiApiKey.trim()\n }\n return out\n }\n\n async function onSave(): Promise<void> {\n const err = validate()\n if (err) {\n toast.error(err)\n return\n }\n const updates = buildUpdates()\n if (Object.keys(updates).length === 0) {\n toast.info(t('vector.form.nothingChanged'))\n return\n }\n try {\n await updateEnv.mutateAsync({ updates })\n toast.success(t('vector.form.savedToast'))\n // Auto-test against the freshly applied config. We don't await\n // the parent's refresh — the toast is the source of truth for the\n // operator; the status card behind it picks up dims / lastError\n // on its next 3s poll anyway.\n try {\n const r = await test.mutateAsync()\n if (r.ok) toast.success(t('vector.actions.testOk'))\n else toast.error(t('vector.actions.testFail', { detail: r.error ?? '' }))\n } catch (terr) {\n const { message } = describeError(terr, t)\n toast.error(t('vector.actions.testFail', { detail: message }))\n }\n onSaved?.()\n } catch (serr) {\n const { message } = describeError(serr, t)\n toast.error(message)\n }\n }\n\n function onDiscard(): void {\n setForm(initialRef.current)\n }\n\n const busy = updateEnv.isPending || test.isPending\n const hasExistingKey = !!env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY\n\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-sm font-medium\">{t('vector.form.title')}</h2>\n {dirty && <Badge variant=\"info\">{t('vector.form.dirty')}</Badge>}\n <div className=\"ml-auto flex items-center gap-2\">\n {dirty && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onDiscard}\n disabled={busy}\n >\n {t('vector.form.discard')}\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={!dirty || busy}\n >\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('vector.form.saveAndTest')}\n </Button>\n </div>\n </div>\n <p className=\"mt-1 text-xs text-text-dim\">{t('vector.form.hint')}</p>\n\n {envQuery.isLoading ? (\n <div className=\"mt-3 h-32 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <div className=\"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {/* Backend selector — full row on mobile, half on desktop */}\n <Field label={t('vector.form.backend')} help={t('vector.form.backendHelp')}>\n <Select value={form.backend} onValueChange={(v) => selectBackend(v as BackendName)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"off\">{t('vector.form.backendOff')}</SelectItem>\n <SelectItem value=\"local\">{t('vector.form.backendLocal')}</SelectItem>\n <SelectItem value=\"openai\">{t('vector.form.backendOpenai')}</SelectItem>\n </SelectContent>\n </Select>\n </Field>\n\n {/* Local-only field */}\n {form.backend === 'local' && (\n <Field label={t('vector.form.localModel')} help={t('vector.form.localModelHelp')}>\n <Input\n value={form.localModel}\n placeholder={DEFAULTS.localModel}\n onChange={(e) => set('localModel', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n )}\n\n {/* OpenAI-only fields */}\n {form.backend === 'openai' && (\n <>\n <Field label={t('vector.form.openaiBaseUrl')} help={t('vector.form.openaiBaseUrlHelp')}>\n <Input\n value={form.openaiBaseUrl}\n placeholder={DEFAULTS.openaiBaseUrl}\n onChange={(e) => set('openaiBaseUrl', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('vector.form.openaiModel')} help={t('vector.form.openaiModelHelp')}>\n <Input\n value={form.openaiModel}\n placeholder={DEFAULTS.openaiModel}\n onChange={(e) => set('openaiModel', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field\n label={t('vector.form.openaiApiKey')}\n help={hasExistingKey ? t('vector.form.apiKeyKeepHint') : t('vector.form.apiKeyNewHint')}\n >\n <div className=\"flex items-center gap-1\">\n <Input\n type={showApiKey ? 'text' : 'password'}\n value={form.openaiApiKey}\n placeholder={hasExistingKey\n ? (env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY || '••••')\n : 'sk-…'}\n onChange={(e) => set('openaiApiKey', e.target.value)}\n className=\"font-mono text-xs\"\n autoComplete=\"off\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setShowApiKey((v) => !v)}\n aria-label={t('vector.form.toggleApiKey')}\n >\n {showApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n </Button>\n </div>\n </Field>\n </>\n )}\n\n {/* Shared numeric knobs (hidden when off so they don't visually\n suggest the operator can tune anything without picking a\n backend first). */}\n {form.backend !== 'off' && (\n <>\n <Field label={t('vector.form.batchSize')} help={t('vector.form.batchSizeHelp')}>\n <Input\n type=\"number\"\n min={1}\n max={1024}\n step={1}\n value={form.batchSize}\n placeholder={DEFAULTS.batchSize}\n onChange={(e) => set('batchSize', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('vector.form.hybridWeight')} help={t('vector.form.hybridWeightHelp')}>\n <Input\n type=\"number\"\n min={0}\n max={1}\n step={0.05}\n value={form.hybridWeight}\n placeholder={DEFAULTS.hybridWeight}\n onChange={(e) => set('hybridWeight', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction Field({\n label,\n help,\n children,\n}: {\n label: string\n help?: string\n children: React.ReactNode\n}): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label className=\"text-xs text-text-dim\">{label}</Label>\n {children}\n {help && <p className=\"text-[11px] text-text-muted\">{help}</p>}\n </div>\n )\n}\n","/**\n * /memory/vector — backend status + maintenance actions.\n *\n * Status (left): backend name + ready badge + model + dims + last\n * error. Coverage (right): three KPI tiles (total / embedded /\n * mismatched). Actions (bottom): test, download, backfill, clear\n * (destructive — ConfirmDialog). Recent-job strip pulls from the\n * shared in-memory tracker via the status endpoint.\n *\n * The clear action picks up the ?user= filter from the layout, so\n * clearing scoped to a single user is a one-click operation.\n */\n\nimport { useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { CheckCircle, Download, Loader2, Play, RefreshCcw, Trash2, XCircle } from 'lucide-react'\n\nimport { VectorBackendForm } from './vector-backend-form'\n\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n useVectorStatus,\n useTriggerVectorBackfill,\n useTriggerVectorClear,\n useTriggerVectorDownload,\n useTriggerVectorTest,\n} from '@/hooks/use-vector'\nimport { describeError } from '@/lib/api/errors'\nimport type { VectorJob } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function MemoryVectorRoute(): JSX.Element {\n const { t } = useTranslation(['memory', 'common'])\n const [params] = useSearchParams()\n const userKey = params.get('user') ?? ''\n\n const statusQuery = useVectorStatus(userKey ? { user_key: userKey } : {})\n const status = statusQuery.data?.status\n const coverage = statusQuery.data?.coverage\n const jobs = statusQuery.data?.jobs ?? []\n\n const test = useTriggerVectorTest()\n const download = useTriggerVectorDownload()\n const backfill = useTriggerVectorBackfill()\n const clear = useTriggerVectorClear()\n const [confirmClear, setConfirmClear] = useState(false)\n\n async function onTest(): Promise<void> {\n try {\n const r = await test.mutateAsync()\n if (r.ok) toast.success(t('vector.actions.testOk'))\n else toast.error(t('vector.actions.testFail', { detail: r.error ?? '' }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onDownload(): Promise<void> {\n try {\n const r = await download.mutateAsync()\n toast.success(r.alreadyReady ? t('vector.actions.downloadAlreadyReady') : t('vector.actions.downloadStarted'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onBackfill(): Promise<void> {\n try {\n await backfill.mutateAsync(userKey ? { user_key: userKey } : undefined)\n toast.success(t('vector.actions.backfillStarted'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onConfirmClear(): Promise<void> {\n try {\n const r = await clear.mutateAsync(userKey ? { user_key: userKey } : undefined)\n toast.success(t('vector.actions.clearStarted', { count: r.cleared }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n if (statusQuery.isLoading) {\n return <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n }\n if (!status) {\n return (\n <EmptyState\n icon={<XCircle />}\n title={t('common:states.error')}\n description={statusQuery.error?.message ?? ''}\n />\n )\n }\n\n const coverageScope = userKey\n ? t('vector.coverage.scope_user', { userKey })\n : t('vector.coverage.scope_global')\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('vector.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => statusQuery.refetch()}\n disabled={statusQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {statusQuery.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('vector.subtitle')}</p>\n </header>\n\n {/* Backend configuration — inline form replaces the older\n \"Edit in env\" link. Saving runs PUT /api/env + an auto\n vector test against the freshly applied config. */}\n <VectorBackendForm onSaved={() => void statusQuery.refetch()} />\n\n {/* Status + Coverage */}\n <div className=\"grid gap-3 md:grid-cols-2\">\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <span className=\"font-medium\">{t('vector.backendLabel')}</span>\n <Badge variant=\"secondary\">{status.backend}</Badge>\n <Badge variant={status.ready ? 'success' : 'warning'}>\n {status.ready ? t('vector.readyTrue') : t('vector.readyFalse')}\n </Badge>\n {status.downloaded && <Badge variant=\"info\">{t('vector.downloadedTrue')}</Badge>}\n </div>\n <dl className=\"mt-3 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"text-text-dim\">{t('vector.modelLabel')}</dt>\n <dd className=\"font-mono text-text\">{status.modelId || '—'}</dd>\n <dt className=\"text-text-dim\">{t('vector.dimsLabel')}</dt>\n <dd className=\"tabular-nums text-text\">{status.dims || '—'}</dd>\n {status.lastError && (\n <>\n <dt className=\"text-text-dim\">{t('vector.lastError')}</dt>\n <dd className=\"text-danger text-xs break-all\">{status.lastError}</dd>\n </>\n )}\n </dl>\n </div>\n\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.coverage.title')}</div>\n <div className=\"mt-1 text-xs text-text-dim\">{coverageScope}</div>\n <div className=\"mt-3 grid grid-cols-3 gap-2\">\n <CoverageTile label={t('vector.coverage.total')} value={coverage?.total ?? 0} tone=\"info\" />\n <CoverageTile label={t('vector.coverage.withEmbedding')} value={coverage?.withEmbedding ?? 0} tone=\"success\" />\n <CoverageTile label={t('vector.coverage.withDifferentModel')} value={coverage?.withDifferentModel ?? 0} tone=\"warning\" />\n </div>\n </div>\n </div>\n\n {/* Maintenance actions */}\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.actions.title')}</div>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onTest()}\n disabled={test.isPending}\n >\n {test.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Play className=\"h-4 w-4\" />}\n {t('vector.actions.test')}\n </Button>\n {status.backend === 'local' && (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onDownload()}\n disabled={download.isPending}\n >\n {download.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Download className=\"h-4 w-4\" />}\n {t('vector.actions.download')}\n </Button>\n )}\n <Button\n variant=\"default\"\n size=\"sm\"\n onClick={() => void onBackfill()}\n disabled={backfill.isPending || !status.ready}\n >\n {backfill.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Play className=\"h-4 w-4\" />}\n {t('vector.actions.backfill')}\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setConfirmClear(true)}\n disabled={clear.isPending}\n >\n <Trash2 className=\"h-4 w-4\" />\n {t('vector.actions.clear')}\n </Button>\n </div>\n </div>\n\n {/* Jobs strip */}\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.jobs.title')}</div>\n {jobs.length === 0 ? (\n <div className=\"mt-2 text-sm text-text-muted\">{t('vector.jobs.empty')}</div>\n ) : (\n <ul className=\"mt-2 flex flex-col gap-1.5 text-sm\">\n {jobs.map((j) => <li key={j.id}><JobRow job={j} /></li>)}\n </ul>\n )}\n </div>\n\n <ConfirmDialog\n open={confirmClear}\n onOpenChange={setConfirmClear}\n title={t('vector.actions.confirmClear')}\n description={t('vector.actions.confirmClearDesc', { scope: coverageScope })}\n intent=\"danger\"\n confirmLabel={t('vector.actions.clear')}\n onConfirm={onConfirmClear}\n />\n </div>\n )\n}\n\ninterface CoverageTileProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<CoverageTileProps['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 CoverageTile({ label, value, tone }: CoverageTileProps): JSX.Element {\n return (\n <div className={cn('rounded-md border bg-bg px-2 py-2', TONE_STYLES[tone])}>\n <div className=\"text-[10px] uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-xl font-semibold tabular-nums\">{value}</div>\n </div>\n )\n}\n\nfunction JobRow({ job }: { job: VectorJob }): JSX.Element {\n const { t } = useTranslation('memory')\n const phaseClass =\n job.phase === 'done' ? 'text-success' :\n job.phase === 'failed' ? 'text-danger' :\n 'text-info'\n return (\n <div className=\"flex flex-wrap items-center gap-2 text-xs\">\n <Badge variant=\"outline\">{job.kind}</Badge>\n <span className={cn('font-medium', phaseClass)}>\n {job.phase === 'running' && <Loader2 className=\"inline h-3 w-3 animate-spin\" />}\n {job.phase === 'done' && <CheckCircle className=\"inline h-3 w-3\" />}\n {job.phase === 'failed' && <XCircle className=\"inline h-3 w-3\" />}\n {' '}\n {t(`vector.jobs.phase.${job.phase}`)}\n </span>\n <span className=\"text-text-dim\">{job.message}</span>\n <span className=\"ml-auto text-text-muted tabular-nums\">{formatEpoch(job.finishedAt ?? job.startedAt)}</span>\n </div>\n )\n}\n\nfunction formatEpoch(ms: number): string {\n try {\n const d = new Date(ms)\n if (Number.isNaN(d.getTime())) return String(ms)\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return String(ms)\n }\n}\n"],"names":["vectorKeys","user_key","useVectorStatus","query","useQuery","api","invalidateAfter","qc","memoryKeys","useTriggerVectorBackfill","useQueryClient","invalidate","useMutation","vars","useTriggerVectorDownload","useTriggerVectorClear","useTriggerVectorTest","DEFAULTS","readFormFromEnv","env","raw","VectorBackendForm","onSaved","t","useTranslation","envQuery","useEnv","useMemo","form","setForm","useState","showApiKey","setShowApiKey","initialRef","useRef","useEffect","next","updateEnv","useUpdateEnv","test","dirty","initial","k","set","v","prev","selectBackend","out","validate","n","buildUpdates","diff","key","current","onSave","err","toast","updates","r","terr","message","describeError","serr","onDiscard","busy","hasExistingKey","jsxs","jsx","Badge","Button","Loader2","Save","Field","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Input","e","Fragment","EyeOff","Eye","label","help","children","Label","MemoryVectorRoute","params","useSearchParams","userKey","statusQuery","status","coverage","jobs","download","backfill","clear","confirmClear","setConfirmClear","onTest","onDownload","onBackfill","onConfirmClear","EmptyState","XCircle","coverageScope","RefreshCcw","CoverageTile","Play","Download","Trash2","j","JobRow","ConfirmDialog","TONE_STYLES","value","tone","cn","job","phaseClass","CheckCircle","formatEpoch","ms","d"],"mappings":"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"}
1
+ {"version":3,"file":"vector-DnZM3OXU.js","sources":["../../src/hooks/use-vector.ts","../../src/routes/memory/vector-backend-form.tsx","../../src/routes/memory/vector.tsx"],"sourcesContent":["/**\n * useVector — react-query wrappers for the embedding/vector backend.\n *\n * useVectorStatus({ user_key }) — status + coverage + jobs\n * (polls every 3s; jobs\n * are async)\n * useTriggerVectorBackfill()\n * useTriggerVectorDownload()\n * useTriggerVectorClear() — DESTRUCTIVE; route gates\n * with ConfirmDialog\n * useTriggerVectorTest()\n *\n * Status polling stops backgrounded (refetchIntervalInBackground:\n * false) so an idle tab on a phone doesn't drain battery.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport { memoryKeys } from './use-memory'\nimport type {\n VectorActionResponse,\n VectorClearResponse,\n VectorStatus,\n VectorTestResponse,\n} from '@/types/api'\n\nexport const vectorKeys = {\n all: ['memory', 'vector'] as const,\n status: (user_key?: string) => ['memory', 'vector', 'status', user_key ?? null] as const,\n}\n\nexport function useVectorStatus(query: { user_key?: string } = {}) {\n return useQuery<VectorStatus>({\n queryKey: vectorKeys.status(query.user_key),\n queryFn: () => api.getMemoryVectorStatus(query),\n refetchInterval: 3000,\n refetchIntervalInBackground: false,\n })\n}\n\nfunction invalidateAfter(qc: ReturnType<typeof useQueryClient>): () => void {\n return () => {\n qc.invalidateQueries({ queryKey: vectorKeys.all })\n qc.invalidateQueries({ queryKey: memoryKeys.all })\n }\n}\n\nexport function useTriggerVectorBackfill() {\n const qc = useQueryClient()\n const invalidate = invalidateAfter(qc)\n return useMutation<\n VectorActionResponse,\n Error,\n { user_key?: string; max_rows?: number } | undefined\n >({\n mutationFn: (vars) => api.backfillMemoryVector(\n { user_key: vars?.user_key },\n vars?.max_rows ? { max_rows: vars.max_rows } : undefined,\n ),\n onSuccess: invalidate,\n })\n}\n\nexport function useTriggerVectorDownload() {\n const qc = useQueryClient()\n return useMutation<VectorActionResponse, Error, void>({\n mutationFn: () => api.downloadMemoryVector(),\n onSuccess: invalidateAfter(qc),\n })\n}\n\nexport function useTriggerVectorClear() {\n const qc = useQueryClient()\n return useMutation<VectorClearResponse, Error, { user_key?: string } | undefined>({\n mutationFn: (vars) => api.clearMemoryVector({ user_key: vars?.user_key }),\n onSuccess: invalidateAfter(qc),\n })\n}\n\nexport function useTriggerVectorTest() {\n return useMutation<VectorTestResponse, Error, void>({\n mutationFn: () => api.testMemoryVector(),\n })\n}\n","/**\n * VectorBackendForm — inline, form-based editor for the seven\n * IMHUB_MEMORY_VECTOR_* keys that drive the embedding backend.\n *\n * Replaces the older \"Edit in env\" link path: operators no longer\n * have to know which env var maps to which knob, and the on-the-wire\n * format (numbers, OpenAI URL shape) is enforced by the widget.\n *\n * Save flow: PUT /api/env (backend-side allowlist filters to just\n * these keys), then auto-trigger a single `vector/test` so the user\n * sees in toast whether the new config produces an embedding.\n *\n * API key: the GET /api/env response returns secrets masked (e.g.\n * \"sk-1***...abcd\"). The widget shows the mask as placeholder and\n * leaves the input empty by default — typing a value sends it as a\n * real update, leaving it empty sends nothing for that key (the\n * server already rejects masked echoes as \"no change\").\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Eye, EyeOff, Loader2, Save } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { useTriggerVectorTest } from '@/hooks/use-vector'\nimport { describeError } from '@/lib/api/errors'\n\ntype BackendName = 'off' | 'local' | 'openai'\n\ninterface FormState {\n backend: BackendName\n localModel: string\n openaiBaseUrl: string\n openaiModel: string\n openaiApiKey: string // empty = \"keep existing\"\n batchSize: string\n hybridWeight: string\n}\n\nconst DEFAULTS: FormState = {\n backend: 'off',\n localModel: 'Xenova/bge-base-zh-v1.5',\n openaiBaseUrl: 'https://api.openai.com/v1',\n openaiModel: 'text-embedding-3-small',\n openaiApiKey: '',\n batchSize: '32',\n hybridWeight: '0.5',\n}\n\nfunction readFormFromEnv(env: Record<string, string>): FormState {\n const raw = (env.IMHUB_MEMORY_VECTOR_BACKEND || '').toLowerCase()\n const backend: BackendName = raw === 'local' || raw === 'openai' ? raw : 'off'\n return {\n backend,\n localModel: env.IMHUB_MEMORY_VECTOR_LOCAL_MODEL || '',\n openaiBaseUrl: env.IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL || '',\n openaiModel: env.IMHUB_MEMORY_VECTOR_OPENAI_MODEL || '',\n openaiApiKey: '',\n batchSize: env.IMHUB_MEMORY_VECTOR_BATCH_SIZE || '',\n hybridWeight: env.IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT || '',\n }\n}\n\nexport interface VectorBackendFormProps {\n /** Called after a successful save + test cycle so the parent page can\n * refresh status + coverage. */\n onSaved?: () => void\n}\n\nexport function VectorBackendForm({ onSaved }: VectorBackendFormProps): JSX.Element {\n const { t } = useTranslation(['memory', 'common'])\n\n // Pull masked env (we never need real key on the client; the masked\n // value is shown as placeholder so the operator can tell a value is\n // configured without revealing it).\n const envQuery = useEnv({ reveal: false })\n const env = useMemo(() => envQuery.data?.env ?? {}, [envQuery.data?.env])\n\n const [form, setForm] = useState<FormState>(DEFAULTS)\n const [showApiKey, setShowApiKey] = useState(false)\n const initialRef = useRef<FormState>(DEFAULTS)\n\n // Reset from server only when the underlying env object identity\n // changes (after refetch). useEffect avoids stomping on local edits\n // mid-typing.\n useEffect(() => {\n if (!envQuery.data) return\n const next = readFormFromEnv(env)\n setForm(next)\n initialRef.current = next\n }, [env, envQuery.data])\n\n const updateEnv = useUpdateEnv()\n const test = useTriggerVectorTest()\n\n const dirty = useMemo(() => {\n const initial = initialRef.current\n return (Object.keys(form) as Array<keyof FormState>).some((k) => form[k] !== initial[k])\n }, [form])\n\n function set<K extends keyof FormState>(k: K, v: FormState[K]): void {\n setForm((prev) => ({ ...prev, [k]: v }))\n }\n\n // v1.2.75 — when the operator switches backend off→local / off→openai\n // and the per-backend fields are still empty, auto-prefill defaults so\n // they don't have to look up the BGE model id by hand. They can still\n // edit afterwards. Also fills shared numeric knobs (batch / weight)\n // since those have well-known sensible defaults too.\n function selectBackend(next: BackendName): void {\n setForm((prev) => {\n const out = { ...prev, backend: next }\n if (next === 'local' && !out.localModel.trim()) {\n out.localModel = DEFAULTS.localModel\n }\n if (next === 'openai') {\n if (!out.openaiBaseUrl.trim()) out.openaiBaseUrl = DEFAULTS.openaiBaseUrl\n if (!out.openaiModel.trim()) out.openaiModel = DEFAULTS.openaiModel\n }\n if (next !== 'off') {\n if (!out.batchSize.trim()) out.batchSize = DEFAULTS.batchSize\n if (!out.hybridWeight.trim()) out.hybridWeight = DEFAULTS.hybridWeight\n }\n return out\n })\n }\n\n function validate(): string | null {\n if (form.backend === 'openai') {\n if (!form.openaiBaseUrl.trim()) return t('vector.form.errors.baseUrlRequired')\n try { new URL(form.openaiBaseUrl.trim()) } catch { return t('vector.form.errors.baseUrlInvalid') }\n if (!form.openaiModel.trim()) return t('vector.form.errors.openaiModelRequired')\n const hasExistingKey = !!env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY\n if (!hasExistingKey && !form.openaiApiKey.trim()) {\n return t('vector.form.errors.apiKeyRequired')\n }\n }\n if (form.backend === 'local' && !form.localModel.trim()) {\n return t('vector.form.errors.localModelRequired')\n }\n if (form.batchSize.trim()) {\n const n = Number(form.batchSize)\n if (!Number.isFinite(n) || n < 1 || n > 1024) return t('vector.form.errors.batchSizeRange')\n }\n if (form.hybridWeight.trim()) {\n const n = Number(form.hybridWeight)\n if (!Number.isFinite(n) || n < 0 || n > 1) return t('vector.form.errors.hybridWeightRange')\n }\n return null\n }\n\n function buildUpdates(): Record<string, string | null> {\n const initial = initialRef.current\n const out: Record<string, string | null> = {}\n const diff = (key: string, current: string, prev: string): void => {\n if (current === prev) return\n out[key] = current.trim() === '' ? null : current.trim()\n }\n diff('IMHUB_MEMORY_VECTOR_BACKEND', form.backend, initial.backend)\n diff('IMHUB_MEMORY_VECTOR_LOCAL_MODEL', form.localModel, initial.localModel)\n diff('IMHUB_MEMORY_VECTOR_OPENAI_BASE_URL', form.openaiBaseUrl, initial.openaiBaseUrl)\n diff('IMHUB_MEMORY_VECTOR_OPENAI_MODEL', form.openaiModel, initial.openaiModel)\n diff('IMHUB_MEMORY_VECTOR_BATCH_SIZE', form.batchSize, initial.batchSize)\n diff('IMHUB_MEMORY_VECTOR_HYBRID_WEIGHT', form.hybridWeight, initial.hybridWeight)\n // API key: only send if the user actually typed a non-empty new value.\n // Empty input = keep current (which is the whole point of the placeholder UX).\n if (form.openaiApiKey.trim()) {\n out.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY = form.openaiApiKey.trim()\n }\n return out\n }\n\n async function onSave(): Promise<void> {\n const err = validate()\n if (err) {\n toast.error(err)\n return\n }\n const updates = buildUpdates()\n if (Object.keys(updates).length === 0) {\n toast.info(t('vector.form.nothingChanged'))\n return\n }\n try {\n await updateEnv.mutateAsync({ updates })\n toast.success(t('vector.form.savedToast'))\n // Auto-test against the freshly applied config. We don't await\n // the parent's refresh — the toast is the source of truth for the\n // operator; the status card behind it picks up dims / lastError\n // on its next 3s poll anyway.\n try {\n const r = await test.mutateAsync()\n if (r.ok) toast.success(t('vector.actions.testOk'))\n else toast.error(t('vector.actions.testFail', { detail: r.error ?? '' }))\n } catch (terr) {\n const { message } = describeError(terr, t)\n toast.error(t('vector.actions.testFail', { detail: message }))\n }\n onSaved?.()\n } catch (serr) {\n const { message } = describeError(serr, t)\n toast.error(message)\n }\n }\n\n function onDiscard(): void {\n setForm(initialRef.current)\n }\n\n const busy = updateEnv.isPending || test.isPending\n const hasExistingKey = !!env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY\n\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-sm font-medium\">{t('vector.form.title')}</h2>\n {dirty && <Badge variant=\"info\">{t('vector.form.dirty')}</Badge>}\n <div className=\"ml-auto flex items-center gap-2\">\n {dirty && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onDiscard}\n disabled={busy}\n >\n {t('vector.form.discard')}\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={!dirty || busy}\n >\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('vector.form.saveAndTest')}\n </Button>\n </div>\n </div>\n <p className=\"mt-1 text-xs text-text-dim\">{t('vector.form.hint')}</p>\n\n {envQuery.isLoading ? (\n <div className=\"mt-3 h-32 w-full rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <div className=\"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {/* Backend selector — full row on mobile, half on desktop */}\n <Field label={t('vector.form.backend')} help={t('vector.form.backendHelp')}>\n <Select value={form.backend} onValueChange={(v) => selectBackend(v as BackendName)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"off\">{t('vector.form.backendOff')}</SelectItem>\n <SelectItem value=\"local\">{t('vector.form.backendLocal')}</SelectItem>\n <SelectItem value=\"openai\">{t('vector.form.backendOpenai')}</SelectItem>\n </SelectContent>\n </Select>\n </Field>\n\n {/* Local-only field */}\n {form.backend === 'local' && (\n <Field label={t('vector.form.localModel')} help={t('vector.form.localModelHelp')}>\n <Input\n value={form.localModel}\n placeholder={DEFAULTS.localModel}\n onChange={(e) => set('localModel', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n )}\n\n {/* OpenAI-only fields */}\n {form.backend === 'openai' && (\n <>\n <Field label={t('vector.form.openaiBaseUrl')} help={t('vector.form.openaiBaseUrlHelp')}>\n <Input\n value={form.openaiBaseUrl}\n placeholder={DEFAULTS.openaiBaseUrl}\n onChange={(e) => set('openaiBaseUrl', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('vector.form.openaiModel')} help={t('vector.form.openaiModelHelp')}>\n <Input\n value={form.openaiModel}\n placeholder={DEFAULTS.openaiModel}\n onChange={(e) => set('openaiModel', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field\n label={t('vector.form.openaiApiKey')}\n help={hasExistingKey ? t('vector.form.apiKeyKeepHint') : t('vector.form.apiKeyNewHint')}\n >\n <div className=\"flex items-center gap-1\">\n <Input\n type={showApiKey ? 'text' : 'password'}\n value={form.openaiApiKey}\n placeholder={hasExistingKey\n ? (env.IMHUB_MEMORY_VECTOR_OPENAI_API_KEY || '••••')\n : 'sk-…'}\n onChange={(e) => set('openaiApiKey', e.target.value)}\n className=\"font-mono text-xs\"\n autoComplete=\"off\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setShowApiKey((v) => !v)}\n aria-label={t('vector.form.toggleApiKey')}\n >\n {showApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n </Button>\n </div>\n </Field>\n </>\n )}\n\n {/* Shared numeric knobs (hidden when off so they don't visually\n suggest the operator can tune anything without picking a\n backend first). */}\n {form.backend !== 'off' && (\n <>\n <Field label={t('vector.form.batchSize')} help={t('vector.form.batchSizeHelp')}>\n <Input\n type=\"number\"\n min={1}\n max={1024}\n step={1}\n value={form.batchSize}\n placeholder={DEFAULTS.batchSize}\n onChange={(e) => set('batchSize', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('vector.form.hybridWeight')} help={t('vector.form.hybridWeightHelp')}>\n <Input\n type=\"number\"\n min={0}\n max={1}\n step={0.05}\n value={form.hybridWeight}\n placeholder={DEFAULTS.hybridWeight}\n onChange={(e) => set('hybridWeight', e.target.value)}\n className=\"font-mono text-xs\"\n />\n </Field>\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction Field({\n label,\n help,\n children,\n}: {\n label: string\n help?: string\n children: React.ReactNode\n}): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label className=\"text-xs text-text-dim\">{label}</Label>\n {children}\n {help && <p className=\"text-[11px] text-text-muted\">{help}</p>}\n </div>\n )\n}\n","/**\n * /memory/vector — backend status + maintenance actions.\n *\n * Status (left): backend name + ready badge + model + dims + last\n * error. Coverage (right): three KPI tiles (total / embedded /\n * mismatched). Actions (bottom): test, download, backfill, clear\n * (destructive — ConfirmDialog). Recent-job strip pulls from the\n * shared in-memory tracker via the status endpoint.\n *\n * The clear action picks up the ?user= filter from the layout, so\n * clearing scoped to a single user is a one-click operation.\n */\n\nimport { useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { CheckCircle, Download, Loader2, Play, RefreshCcw, Trash2, XCircle } from 'lucide-react'\n\nimport { VectorBackendForm } from './vector-backend-form'\n\nimport { ConfirmDialog } from '@/components/common/confirm-dialog'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n useVectorStatus,\n useTriggerVectorBackfill,\n useTriggerVectorClear,\n useTriggerVectorDownload,\n useTriggerVectorTest,\n} from '@/hooks/use-vector'\nimport { describeError } from '@/lib/api/errors'\nimport type { VectorJob } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function MemoryVectorRoute(): JSX.Element {\n const { t } = useTranslation(['memory', 'common'])\n const [params] = useSearchParams()\n const userKey = params.get('user') ?? ''\n\n const statusQuery = useVectorStatus(userKey ? { user_key: userKey } : {})\n const status = statusQuery.data?.status\n const coverage = statusQuery.data?.coverage\n const jobs = statusQuery.data?.jobs ?? []\n\n const test = useTriggerVectorTest()\n const download = useTriggerVectorDownload()\n const backfill = useTriggerVectorBackfill()\n const clear = useTriggerVectorClear()\n const [confirmClear, setConfirmClear] = useState(false)\n\n async function onTest(): Promise<void> {\n try {\n const r = await test.mutateAsync()\n if (r.ok) toast.success(t('vector.actions.testOk'))\n else toast.error(t('vector.actions.testFail', { detail: r.error ?? '' }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onDownload(): Promise<void> {\n try {\n const r = await download.mutateAsync()\n toast.success(r.alreadyReady ? t('vector.actions.downloadAlreadyReady') : t('vector.actions.downloadStarted'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onBackfill(): Promise<void> {\n try {\n await backfill.mutateAsync(userKey ? { user_key: userKey } : undefined)\n toast.success(t('vector.actions.backfillStarted'))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n async function onConfirmClear(): Promise<void> {\n try {\n const r = await clear.mutateAsync(userKey ? { user_key: userKey } : undefined)\n toast.success(t('vector.actions.clearStarted', { count: r.cleared }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n throw err\n }\n }\n\n if (statusQuery.isLoading) {\n return <div className=\"h-48 w-full rounded-md bg-surface-2 animate-pulse\" />\n }\n if (!status) {\n return (\n <EmptyState\n icon={<XCircle />}\n title={t('common:states.error')}\n description={statusQuery.error?.message ?? ''}\n />\n )\n }\n\n const coverageScope = userKey\n ? t('vector.coverage.scope_user', { userKey })\n : t('vector.coverage.scope_global')\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('vector.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => statusQuery.refetch()}\n disabled={statusQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {statusQuery.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('vector.subtitle')}</p>\n </header>\n\n {/* Backend configuration — inline form replaces the older\n \"Edit in env\" link. Saving runs PUT /api/env + an auto\n vector test against the freshly applied config. */}\n <VectorBackendForm onSaved={() => void statusQuery.refetch()} />\n\n {/* Status + Coverage */}\n <div className=\"grid gap-3 md:grid-cols-2\">\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <span className=\"font-medium\">{t('vector.backendLabel')}</span>\n <Badge variant=\"secondary\">{status.backend}</Badge>\n <Badge variant={status.ready ? 'success' : 'warning'}>\n {status.ready ? t('vector.readyTrue') : t('vector.readyFalse')}\n </Badge>\n {status.downloaded && <Badge variant=\"info\">{t('vector.downloadedTrue')}</Badge>}\n </div>\n <dl className=\"mt-3 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"text-text-dim\">{t('vector.modelLabel')}</dt>\n <dd className=\"font-mono text-text\">{status.modelId || '—'}</dd>\n <dt className=\"text-text-dim\">{t('vector.dimsLabel')}</dt>\n <dd className=\"tabular-nums text-text\">{status.dims || '—'}</dd>\n {status.lastError && (\n <>\n <dt className=\"text-text-dim\">{t('vector.lastError')}</dt>\n <dd className=\"text-danger text-xs break-all\">{status.lastError}</dd>\n </>\n )}\n </dl>\n </div>\n\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.coverage.title')}</div>\n <div className=\"mt-1 text-xs text-text-dim\">{coverageScope}</div>\n <div className=\"mt-3 grid grid-cols-3 gap-2\">\n <CoverageTile label={t('vector.coverage.total')} value={coverage?.total ?? 0} tone=\"info\" />\n <CoverageTile label={t('vector.coverage.withEmbedding')} value={coverage?.withEmbedding ?? 0} tone=\"success\" />\n <CoverageTile label={t('vector.coverage.withDifferentModel')} value={coverage?.withDifferentModel ?? 0} tone=\"warning\" />\n </div>\n </div>\n </div>\n\n {/* Maintenance actions */}\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.actions.title')}</div>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onTest()}\n disabled={test.isPending}\n >\n {test.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Play className=\"h-4 w-4\" />}\n {t('vector.actions.test')}\n </Button>\n {status.backend === 'local' && (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => void onDownload()}\n disabled={download.isPending}\n >\n {download.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Download className=\"h-4 w-4\" />}\n {t('vector.actions.download')}\n </Button>\n )}\n <Button\n variant=\"default\"\n size=\"sm\"\n onClick={() => void onBackfill()}\n disabled={backfill.isPending || !status.ready}\n >\n {backfill.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Play className=\"h-4 w-4\" />}\n {t('vector.actions.backfill')}\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setConfirmClear(true)}\n disabled={clear.isPending}\n >\n <Trash2 className=\"h-4 w-4\" />\n {t('vector.actions.clear')}\n </Button>\n </div>\n </div>\n\n {/* Jobs strip */}\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"text-sm font-medium\">{t('vector.jobs.title')}</div>\n {jobs.length === 0 ? (\n <div className=\"mt-2 text-sm text-text-muted\">{t('vector.jobs.empty')}</div>\n ) : (\n <ul className=\"mt-2 flex flex-col gap-1.5 text-sm\">\n {jobs.map((j) => <li key={j.id}><JobRow job={j} /></li>)}\n </ul>\n )}\n </div>\n\n <ConfirmDialog\n open={confirmClear}\n onOpenChange={setConfirmClear}\n title={t('vector.actions.confirmClear')}\n description={t('vector.actions.confirmClearDesc', { scope: coverageScope })}\n intent=\"danger\"\n confirmLabel={t('vector.actions.clear')}\n onConfirm={onConfirmClear}\n />\n </div>\n )\n}\n\ninterface CoverageTileProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<CoverageTileProps['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 CoverageTile({ label, value, tone }: CoverageTileProps): JSX.Element {\n return (\n <div className={cn('rounded-md border bg-bg px-2 py-2', TONE_STYLES[tone])}>\n <div className=\"text-[10px] uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-xl font-semibold tabular-nums\">{value}</div>\n </div>\n )\n}\n\nfunction JobRow({ job }: { job: VectorJob }): JSX.Element {\n const { t } = useTranslation('memory')\n const phaseClass =\n job.phase === 'done' ? 'text-success' :\n job.phase === 'failed' ? 'text-danger' :\n 'text-info'\n return (\n <div className=\"flex flex-wrap items-center gap-2 text-xs\">\n <Badge variant=\"outline\">{job.kind}</Badge>\n <span className={cn('font-medium', phaseClass)}>\n {job.phase === 'running' && <Loader2 className=\"inline h-3 w-3 animate-spin\" />}\n {job.phase === 'done' && <CheckCircle className=\"inline h-3 w-3\" />}\n {job.phase === 'failed' && <XCircle className=\"inline h-3 w-3\" />}\n {' '}\n {t(`vector.jobs.phase.${job.phase}`)}\n </span>\n <span className=\"text-text-dim\">{job.message}</span>\n <span className=\"ml-auto text-text-muted tabular-nums\">{formatEpoch(job.finishedAt ?? job.startedAt)}</span>\n </div>\n )\n}\n\nfunction formatEpoch(ms: number): string {\n try {\n const d = new Date(ms)\n if (Number.isNaN(d.getTime())) return String(ms)\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return String(ms)\n }\n}\n"],"names":["vectorKeys","user_key","useVectorStatus","query","useQuery","api","invalidateAfter","qc","memoryKeys","useTriggerVectorBackfill","useQueryClient","invalidate","useMutation","vars","useTriggerVectorDownload","useTriggerVectorClear","useTriggerVectorTest","DEFAULTS","readFormFromEnv","env","raw","VectorBackendForm","onSaved","t","useTranslation","envQuery","useEnv","useMemo","form","setForm","useState","showApiKey","setShowApiKey","initialRef","useRef","useEffect","next","updateEnv","useUpdateEnv","test","dirty","initial","k","set","v","prev","selectBackend","out","validate","n","buildUpdates","diff","key","current","onSave","err","toast","updates","r","terr","message","describeError","serr","onDiscard","busy","hasExistingKey","jsxs","jsx","Badge","Button","Loader2","Save","Field","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Input","e","Fragment","EyeOff","Eye","label","help","children","Label","MemoryVectorRoute","params","useSearchParams","userKey","statusQuery","status","coverage","jobs","download","backfill","clear","confirmClear","setConfirmClear","onTest","onDownload","onBackfill","onConfirmClear","EmptyState","XCircle","coverageScope","RefreshCcw","CoverageTile","Play","Download","Trash2","j","JobRow","ConfirmDialog","TONE_STYLES","value","tone","cn","job","phaseClass","CheckCircle","formatEpoch","ms","d"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{n as k,u as R,j as e,B as h,k as f,I as y,S as W,f as T,g as O,h as F,i as B,c as L,L as C,l as g,m as U}from"./index-wVLKSZpI.js";import{r as x}from"./react-Cb2sDjhD.js";import{u as z,a as A,g as q}from"./use-settings-BUM0BmtA.js";import{E as K,a as X}from"./eye-aJAK4qYW.js";import{L as N}from"./loader-circle-DPc6botW.js";import{R as D}from"./refresh-ccw-C2Q06LWj.js";import{C as J}from"./circle-check-Bm4WZ21u.js";import{E as Q}from"./external-link-Deer-Lh8.js";import{X as V}from"./x-B0lqsHGi.js";import{S as P}from"./save-3XXDgdfD.js";import{M as Z}from"./map-pin-ECh-25H3.js";import"./useQuery-II-YwTUw.js";/**
1
+ import{n as k,u as R,j as e,B as h,k as f,I as y,S as W,f as T,g as O,h as F,i as B,c as L,L as C,l as g,m as U}from"./index-O0BQoyzo.js";import{r as x}from"./react-Cb2sDjhD.js";import{u as z,a as A,g as q}from"./use-settings-DMdaoWsB.js";import{E as K,a as X}from"./eye-CFhg5BTa.js";import{L as N}from"./loader-circle-9VUMGitw.js";import{R as D}from"./refresh-ccw-D2CWiyU_.js";import{C as J}from"./circle-check-CewnjFgv.js";import{E as Q}from"./external-link-DRVp9-lb.js";import{X as V}from"./x-DG-JKVw_.js";import{S as P}from"./save-DB0BDYTs.js";import{M as Z}from"./map-pin-BXYvvHry.js";import"./useQuery-PdiC7-sY.js";/**
2
2
  * @license lucide-react v0.469.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -9,4 +9,4 @@ import{n as k,u as R,j as e,B as h,k as f,I as y,S as W,f as T,g as O,h as F,i a
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
11
  */const Y=k("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);function ue(){const{t:i}=R(["settings","common"]),[s,r]=x.useState(!1),a=z({reveal:s});return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-6",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:i("viewerSettings.title")}),e.jsxs(h,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>r(t=>!t),disabled:a.isFetching,"aria-label":i(s?"viewerSettings.hide":"viewerSettings.reveal"),children:[s?e.jsx(K,{className:"h-4 w-4"}):e.jsx(X,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:i(s?"viewerSettings.hide":"viewerSettings.reveal")})]}),e.jsxs(h,{variant:"ghost",size:"sm",onClick:()=>void a.refetch(),disabled:a.isFetching,"aria-label":i("actions.refresh",{ns:"common"}),children:[a.isFetching?e.jsx(N,{className:"h-4 w-4 animate-spin"}):e.jsx(D,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:i("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:i("viewerSettings.subtitle")})]}),a.isLoading?e.jsx("div",{className:"h-64 rounded-md bg-surface-2 animate-pulse"}):e.jsxs(e.Fragment,{children:[e.jsx($,{env:a.data?.env??{}}),e.jsx(ee,{env:a.data?.env??{}})]})]})}function M(i){return{enabled:i.IMHUB_VIEWER_ENABLED==="1"||i.IMHUB_VIEWER_ENABLED?.toLowerCase()==="true",publicUrl:i.IMHUB_VIEWER_PUBLIC_BASE_URL??"",tunnelMode:i.IMHUB_VIEWER_TUNNEL_MODE==="quick"?"quick":"off",noDefaultRemote:i.IMHUB_VIEWER_NO_DEFAULT_REMOTE==="1"||i.IMHUB_VIEWER_NO_DEFAULT_REMOTE?.toLowerCase()==="true",chars:i.IMHUB_VIEWER_CHARS??"",lines:i.IMHUB_VIEWER_LINES??"",codeLines:i.IMHUB_VIEWER_CODE_LINES??"",maxPastes:i.IMHUB_VIEWER_MAX_PASTES??""}}function $({env:i}){const{t:s}=R(["settings","common"]),r=A(),a=q(),[t,d]=x.useState(()=>M(i)),[w,S]=x.useState("");x.useEffect(()=>{const n=M(i),m=JSON.stringify(n);m!==w&&(d(n),S(m))},[i.IMHUB_VIEWER_ENABLED,i.IMHUB_VIEWER_PUBLIC_BASE_URL,i.IMHUB_VIEWER_TUNNEL_MODE,i.IMHUB_VIEWER_CHARS,i.IMHUB_VIEWER_LINES,i.IMHUB_VIEWER_CODE_LINES,i.IMHUB_VIEWER_MAX_PASTES,i.IMHUB_VIEWER_NO_DEFAULT_REMOTE]);const c=x.useMemo(()=>M(i),[i]),b=x.useMemo(()=>JSON.stringify(t)!==JSON.stringify(c),[t,c]);function o(n,m){d(j=>({...j,[n]:m}))}async function _(){const n={};if(t.enabled!==c.enabled&&(n.IMHUB_VIEWER_ENABLED=t.enabled?"1":"0"),t.publicUrl!==c.publicUrl&&(n.IMHUB_VIEWER_PUBLIC_BASE_URL=t.publicUrl.trim()||null),t.tunnelMode!==c.tunnelMode&&(n.IMHUB_VIEWER_TUNNEL_MODE=t.tunnelMode),t.chars!==c.chars&&(n.IMHUB_VIEWER_CHARS=t.chars.trim()||null),t.lines!==c.lines&&(n.IMHUB_VIEWER_LINES=t.lines.trim()||null),t.codeLines!==c.codeLines&&(n.IMHUB_VIEWER_CODE_LINES=t.codeLines.trim()||null),t.maxPastes!==c.maxPastes&&(n.IMHUB_VIEWER_MAX_PASTES=t.maxPastes.trim()||null),t.noDefaultRemote!==c.noDefaultRemote&&(n.IMHUB_VIEWER_NO_DEFAULT_REMOTE=t.noDefaultRemote?"1":null),Object.keys(n).length!==0)try{await r.mutateAsync({updates:n}),g.success(s("viewerSettings.toast.saved"))}catch(m){g.error(U(m,s).message)}}function I(){d(c)}const l=a.data,H=l?.effectivePublicUrl;return e.jsxs("section",{className:"rounded-md border border-border bg-surface",children:[e.jsxs("header",{className:"flex items-center gap-2 border-b border-border px-4 py-3",children:[e.jsx(Y,{className:"h-4 w-4 text-text-dim"}),e.jsx("h2",{className:"text-sm font-semibold",children:s("viewerSettings.viewer.title")}),t.enabled?e.jsx(f,{variant:"success",children:s("viewerSettings.viewer.on")}):e.jsx(f,{variant:"outline",children:s("viewerSettings.viewer.off")})]}),e.jsx("p",{className:"px-4 pt-2 text-xs text-text-dim",children:s("viewerSettings.viewer.subtitle")}),e.jsx("div",{className:"flex items-center gap-3 px-4 py-3",children:e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:t.enabled,onChange:n=>{const m=n.target.checked;d(j=>({...j,enabled:m,noDefaultRemote:m?j.noDefaultRemote:!1}))},className:"h-4 w-4 accent-accent cursor-pointer"}),e.jsx("span",{className:"font-medium",children:s("viewerSettings.viewer.enableLabel")})]})}),e.jsxs("div",{className:"grid grid-cols-1 gap-3 px-4 pb-3 sm:grid-cols-2",children:[e.jsx(u,{label:s("viewerSettings.viewer.publicUrl"),hint:s("viewerSettings.viewer.publicUrlHint"),children:e.jsx(y,{value:t.publicUrl,onChange:n=>o("publicUrl",n.target.value),placeholder:"https://agim.example.com",className:"font-mono text-xs"})}),e.jsx(u,{label:s("viewerSettings.viewer.tunnelMode"),hint:s("viewerSettings.viewer.tunnelHint"),children:e.jsxs(W,{value:t.tunnelMode,onValueChange:n=>o("tunnelMode",n),children:[e.jsx(T,{children:e.jsx(O,{})}),e.jsxs(F,{children:[e.jsx(B,{value:"off",children:s("viewerSettings.viewer.tunnelOff")}),e.jsx(B,{value:"quick",children:s("viewerSettings.viewer.tunnelQuick")})]})]})}),e.jsx(u,{label:s("viewerSettings.viewer.noDefaultRemote"),hint:s("viewerSettings.viewer.noDefaultRemoteHint"),children:e.jsxs("label",{className:L("inline-flex items-center gap-2 text-xs",!t.enabled&&"opacity-50 cursor-not-allowed"),children:[e.jsx("input",{type:"checkbox",checked:t.noDefaultRemote,disabled:!t.enabled,onChange:n=>o("noDefaultRemote",n.target.checked),className:"h-4 w-4 accent-accent cursor-pointer disabled:cursor-not-allowed"}),e.jsx("span",{children:s("viewerSettings.viewer.noDefaultRemoteLabel")})]})})]}),t.tunnelMode==="quick"&&e.jsxs("div",{className:"mx-4 mb-3 rounded-md border border-border bg-surface-2 p-3",children:[e.jsxs("div",{className:"mb-1 flex items-center gap-2",children:[e.jsx(G,{className:"h-4 w-4 text-text-dim"}),e.jsx("span",{className:"text-xs font-medium uppercase tracking-wide text-text-dim",children:s("viewerSettings.viewer.tunnelStatus")}),e.jsxs(h,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>void a.refetch(),disabled:a.isFetching,"aria-label":s("viewerSettings.viewer.refreshTunnel"),children:[a.isFetching?e.jsx(N,{className:"h-4 w-4 animate-spin"}):e.jsx(D,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("viewerSettings.viewer.refreshTunnel")})]})]}),l?e.jsxs("div",{className:"flex flex-col gap-1 text-xs",children:[e.jsx(p,{label:s("viewerSettings.viewer.tunnelRunning"),children:l.tunnel.running?e.jsxs("span",{className:"inline-flex items-center gap-1 text-success",children:[e.jsx(J,{className:"h-3 w-3"}),s("viewerSettings.viewer.yes")]}):e.jsx("span",{className:"text-text-dim",children:s("viewerSettings.viewer.no")})}),e.jsx(p,{label:s("viewerSettings.viewer.tunnelUrl"),children:l.tunnel.url?e.jsxs("a",{href:l.tunnel.url,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 break-all font-mono text-info hover:underline",children:[l.tunnel.url,e.jsx(Q,{className:"h-3 w-3 shrink-0"})]}):e.jsx("span",{className:"text-text-muted",children:"—"})}),e.jsx(p,{label:s("viewerSettings.viewer.effectiveUrl"),children:H?e.jsx("span",{className:"break-all font-mono text-text",children:H}):e.jsx("span",{className:"text-text-muted",children:"—"})}),e.jsx(p,{label:s("viewerSettings.viewer.binary"),children:l.tunnel.binaryFound?e.jsx("span",{className:"font-mono text-text",children:l.tunnel.binaryPath}):e.jsx("span",{className:"text-warning",children:s("viewerSettings.viewer.binaryMissing")})}),l.tunnel.lastError&&e.jsx(p,{label:s("viewerSettings.viewer.lastError"),children:e.jsx("span",{className:"break-words text-danger",children:l.tunnel.lastError})})]}):e.jsx("p",{className:"text-xs text-text-dim",children:s("viewerSettings.viewer.tunnelLoading")})]}),e.jsx("h3",{className:"border-t border-border px-4 pt-3 text-xs font-semibold uppercase tracking-wide text-text-dim",children:s("viewerSettings.viewer.thresholds")}),e.jsxs("div",{className:"grid grid-cols-2 gap-3 px-4 py-3 sm:grid-cols-4",children:[e.jsx(u,{label:s("viewerSettings.viewer.chars"),hint:s("viewerSettings.viewer.charsHint"),children:e.jsx(E,{value:t.chars,onChange:n=>o("chars",n),min:100,max:1e4,placeholder:"500"})}),e.jsx(u,{label:s("viewerSettings.viewer.lines"),hint:s("viewerSettings.viewer.linesHint"),children:e.jsx(E,{value:t.lines,onChange:n=>o("lines",n),min:5,max:200,placeholder:"12"})}),e.jsx(u,{label:s("viewerSettings.viewer.codeLines"),hint:s("viewerSettings.viewer.codeLinesHint"),children:e.jsx(E,{value:t.codeLines,onChange:n=>o("codeLines",n),min:5,max:200,placeholder:"10"})}),e.jsx(u,{label:s("viewerSettings.viewer.maxPastes"),hint:s("viewerSettings.viewer.maxPastesHint"),children:e.jsx(E,{value:t.maxPastes,onChange:n=>o("maxPastes",n),min:100,max:1e6,placeholder:"10000"})})]}),b&&e.jsxs("div",{className:"flex items-center gap-2 border-t border-border px-4 py-3",children:[e.jsx(f,{variant:"info",children:s("viewerSettings.dirtyHint")}),e.jsxs(h,{variant:"ghost",size:"sm",className:"ml-auto",onClick:I,disabled:r.isPending,children:[e.jsx(V,{className:"h-4 w-4"}),s("viewerSettings.discard")]}),e.jsxs(h,{size:"sm",onClick:()=>void _(),disabled:r.isPending,children:[r.isPending?e.jsx(N,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),r.isPending?s("viewerSettings.saving"):s("viewerSettings.save")]})]})]})}function v(i){return/\*{3,}/.test(i)}function ee({env:i}){const{t:s}=R(["settings","common"]),r=A(),a=i.IMHUB_BAIDU_MAP_AK??"",[t,d]=x.useState(a),[w,S]=x.useState("");x.useEffect(()=>{a!==w&&(d(a),S(a))},[a,w]);const c=!!a,b=t!==a&&!(v(t)&&t===a),o=t.length>0&&!v(t)&&t.length!==32;async function _(){if(b&&!v(t))try{await r.mutateAsync({updates:{IMHUB_BAIDU_MAP_AK:t.trim()||null}}),g.success(s("viewerSettings.baidu.toast.saved"))}catch(l){g.error(U(l,s).message)}}async function I(){try{await r.mutateAsync({updates:{IMHUB_BAIDU_MAP_AK:null}}),d(""),g.success(s("viewerSettings.baidu.toast.cleared"))}catch(l){g.error(U(l,s).message)}}return e.jsxs("section",{className:"rounded-md border border-border bg-surface",children:[e.jsxs("header",{className:"flex items-center gap-2 border-b border-border px-4 py-3",children:[e.jsx(Z,{className:"h-4 w-4 text-text-dim"}),e.jsx("h2",{className:"text-sm font-semibold",children:s("viewerSettings.baidu.title")}),c?e.jsx(f,{variant:"success",children:s("viewerSettings.baidu.configured")}):e.jsx(f,{variant:"outline",children:s("viewerSettings.baidu.empty")})]}),e.jsx("p",{className:"px-4 pt-2 text-xs text-text-dim",children:s("viewerSettings.baidu.subtitle")}),e.jsxs("div",{className:"px-4 py-3",children:[e.jsx(C,{htmlFor:"baidu-ak",className:"text-xs font-medium",children:"IMHUB_BAIDU_MAP_AK"}),e.jsx(y,{id:"baidu-ak",value:t,onChange:l=>d(l.target.value),placeholder:s("viewerSettings.baidu.placeholder"),autoComplete:"off",className:L("mt-1 font-mono text-xs",v(t)&&"text-text-muted")}),o&&e.jsx("p",{className:"mt-1 text-[11px] text-warning",children:s("viewerSettings.baidu.suspicious",{length:t.length})}),e.jsx("p",{className:"mt-1 text-[11px] text-text-dim",children:s("viewerSettings.baidu.hint")})]}),e.jsxs("div",{className:"flex items-center gap-2 border-t border-border px-4 py-3",children:[e.jsxs(h,{variant:"ghost",size:"sm",onClick:()=>void I(),disabled:!c||r.isPending,className:"text-danger hover:text-danger",children:[e.jsx(V,{className:"h-4 w-4"}),s("viewerSettings.baidu.clear")]}),e.jsx("span",{className:"ml-auto"}),b&&e.jsxs(h,{size:"sm",onClick:()=>void _(),disabled:r.isPending||v(t),children:[r.isPending?e.jsx(N,{className:"h-4 w-4 animate-spin"}):e.jsx(P,{className:"h-4 w-4"}),r.isPending?s("viewerSettings.saving"):s("viewerSettings.save")]})]})]})}function u({label:i,hint:s,children:r}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(C,{className:"text-xs font-medium",children:i}),r,s&&e.jsx("p",{className:"text-[11px] text-text-dim",children:s})]})}function p({label:i,children:s}){return e.jsxs("div",{className:"flex flex-wrap items-baseline gap-2",children:[e.jsxs("span",{className:"shrink-0 font-medium text-text-dim",children:[i,":"]}),e.jsx("span",{className:"min-w-0 flex-1",children:s})]})}function E({value:i,onChange:s,min:r,max:a,placeholder:t}){return e.jsx(y,{type:"number",inputMode:"numeric",min:r,max:a,value:i,onChange:d=>s(d.target.value),placeholder:t})}export{ue as default};
12
- //# sourceMappingURL=viewer-rMDo9pIb.js.map
12
+ //# sourceMappingURL=viewer-Dz6k0YKp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewer-rMDo9pIb.js","sources":["../../node_modules/lucide-react/dist/esm/icons/earth.js","../../node_modules/lucide-react/dist/esm/icons/file-text.js","../../src/routes/settings/viewer.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 Earth = createLucideIcon(\"Earth\", [\n [\"path\", { d: \"M21.54 15H17a2 2 0 0 0-2 2v4.54\", key: \"1djwo0\" }],\n [\n \"path\",\n {\n d: \"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17\",\n key: \"1tzkfa\"\n }\n ],\n [\"path\", { d: \"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05\", key: \"14pb5j\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }]\n]);\n\nexport { Earth as default };\n//# sourceMappingURL=earth.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FileText = createLucideIcon(\"FileText\", [\n [\"path\", { d: \"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\", key: \"1rqfz7\" }],\n [\"path\", { d: \"M14 2v4a2 2 0 0 0 2 2h4\", key: \"tnqrlb\" }],\n [\"path\", { d: \"M10 9H8\", key: \"b1mrlr\" }],\n [\"path\", { d: \"M16 13H8\", key: \"t4e002\" }],\n [\"path\", { d: \"M16 17H8\", key: \"z1uh3a\" }]\n]);\n\nexport { FileText as default };\n//# sourceMappingURL=file-text.js.map\n","/**\n * /settings/viewer — long-message Viewer card + Baidu Maps AK card.\n *\n * Both cards are env-driven (~/.agim/.env), each with its own dirty /\n * Save / Discard state so a change to one doesn't force re-save of the\n * other. The Viewer card additionally surfaces cloudflared tunnel\n * status via `GET /api/viewer/tunnel`, with a manual Refresh button\n * since tunnel state is essentially static between operator actions.\n *\n * Env keys covered:\n * * Viewer: IMHUB_VIEWER_ENABLED / _PUBLIC_BASE_URL / _TUNNEL_MODE /\n * _CHARS / _LINES / _CODE_LINES / _MAX_PASTES\n * * Baidu: IMHUB_BAIDU_MAP_AK (in SECRET_KEYS — masked unless\n * reveal=1 is passed)\n *\n * AK validation: v1 mentioned \"32-char AK starting with non-digit\" as\n * a hint, but isn't strictly enforced (Baidu has refreshed AK formats\n * over the years). We surface a `pattern-looks-suspicious` warning\n * rather than blocking save.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n CheckCircle2, Eye, EyeOff, ExternalLink, FileText, Globe2, Loader2,\n MapPin, RefreshCcw, Save, X,\n} from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv, useViewerTunnel } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\n/* ─────────────── Page wrapper ─────────────── */\n\nexport default function SettingsViewerRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const [reveal, setReveal] = useState(false)\n const envQuery = useEnv({ reveal })\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\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('viewerSettings.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => setReveal((v) => !v)}\n disabled={envQuery.isFetching}\n aria-label={reveal ? t('viewerSettings.hide') : t('viewerSettings.reveal')}\n >\n {reveal ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">\n {reveal ? t('viewerSettings.hide') : t('viewerSettings.reveal')}\n </span>\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void envQuery.refetch()}\n disabled={envQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('viewerSettings.subtitle')}</p>\n </header>\n\n {envQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <>\n <ViewerCard env={envQuery.data?.env ?? {}} />\n <BaiduCard env={envQuery.data?.env ?? {}} />\n </>\n )}\n </div>\n )\n}\n\n/* ─────────────── Viewer card ─────────────── */\n\ninterface ViewerDraft {\n enabled: boolean\n publicUrl: string\n tunnelMode: 'off' | 'quick'\n // v1.2.45 — remote-paste URL/Token are env-only advanced knobs.\n // Only the opt-out toggle stays in the UI.\n noDefaultRemote: boolean\n chars: string\n lines: string\n codeLines: string\n maxPastes: string\n}\n\nfunction viewerDraftFromEnv(env: Record<string, string>): ViewerDraft {\n return {\n enabled: env.IMHUB_VIEWER_ENABLED === '1' || env.IMHUB_VIEWER_ENABLED?.toLowerCase() === 'true',\n publicUrl: env.IMHUB_VIEWER_PUBLIC_BASE_URL ?? '',\n tunnelMode: env.IMHUB_VIEWER_TUNNEL_MODE === 'quick' ? 'quick' : 'off',\n noDefaultRemote: env.IMHUB_VIEWER_NO_DEFAULT_REMOTE === '1' || env.IMHUB_VIEWER_NO_DEFAULT_REMOTE?.toLowerCase() === 'true',\n chars: env.IMHUB_VIEWER_CHARS ?? '',\n lines: env.IMHUB_VIEWER_LINES ?? '',\n codeLines: env.IMHUB_VIEWER_CODE_LINES ?? '',\n maxPastes: env.IMHUB_VIEWER_MAX_PASTES ?? '',\n }\n}\n\nfunction ViewerCard({ env }: { env: Record<string, string> }): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const updateEnv = useUpdateEnv()\n const tunnelQuery = useViewerTunnel()\n\n const [draft, setDraft] = useState<ViewerDraft>(() => viewerDraftFromEnv(env))\n const [syncedHash, setSyncedHash] = useState('')\n\n // Sync draft when env changes (initial load + post-save refetch).\n useEffect(() => {\n const next = viewerDraftFromEnv(env)\n const hash = JSON.stringify(next)\n if (hash !== syncedHash) {\n setDraft(next)\n setSyncedHash(hash)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [env.IMHUB_VIEWER_ENABLED, env.IMHUB_VIEWER_PUBLIC_BASE_URL,\n env.IMHUB_VIEWER_TUNNEL_MODE, env.IMHUB_VIEWER_CHARS,\n env.IMHUB_VIEWER_LINES, env.IMHUB_VIEWER_CODE_LINES,\n env.IMHUB_VIEWER_MAX_PASTES, env.IMHUB_VIEWER_NO_DEFAULT_REMOTE])\n\n const current = useMemo(() => viewerDraftFromEnv(env), [env])\n const isDirty = useMemo(\n () => JSON.stringify(draft) !== JSON.stringify(current),\n [draft, current],\n )\n\n function setField<K extends keyof ViewerDraft>(k: K, v: ViewerDraft[K]): void {\n setDraft((d) => ({ ...d, [k]: v }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n if (draft.enabled !== current.enabled) {\n updates.IMHUB_VIEWER_ENABLED = draft.enabled ? '1' : '0'\n }\n if (draft.publicUrl !== current.publicUrl) {\n updates.IMHUB_VIEWER_PUBLIC_BASE_URL = draft.publicUrl.trim() || null\n }\n if (draft.tunnelMode !== current.tunnelMode) {\n updates.IMHUB_VIEWER_TUNNEL_MODE = draft.tunnelMode\n }\n if (draft.chars !== current.chars) {\n updates.IMHUB_VIEWER_CHARS = draft.chars.trim() || null\n }\n if (draft.lines !== current.lines) {\n updates.IMHUB_VIEWER_LINES = draft.lines.trim() || null\n }\n if (draft.codeLines !== current.codeLines) {\n updates.IMHUB_VIEWER_CODE_LINES = draft.codeLines.trim() || null\n }\n if (draft.maxPastes !== current.maxPastes) {\n updates.IMHUB_VIEWER_MAX_PASTES = draft.maxPastes.trim() || null\n }\n if (draft.noDefaultRemote !== current.noDefaultRemote) {\n updates.IMHUB_VIEWER_NO_DEFAULT_REMOTE = draft.noDefaultRemote ? '1' : null\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n toast.success(t('viewerSettings.toast.saved'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n function onDiscard(): void {\n setDraft(current)\n }\n\n const tunnel = tunnelQuery.data\n const effectivePublicUrl = tunnel?.effectivePublicUrl\n\n return (\n <section className=\"rounded-md border border-border bg-surface\">\n <header className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <FileText className=\"h-4 w-4 text-text-dim\" />\n <h2 className=\"text-sm font-semibold\">{t('viewerSettings.viewer.title')}</h2>\n {draft.enabled\n ? <Badge variant=\"success\">{t('viewerSettings.viewer.on')}</Badge>\n : <Badge variant=\"outline\">{t('viewerSettings.viewer.off')}</Badge>}\n </header>\n <p className=\"px-4 pt-2 text-xs text-text-dim\">{t('viewerSettings.viewer.subtitle')}</p>\n\n {/* Enable toggle */}\n <div className=\"flex items-center gap-3 px-4 py-3\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={draft.enabled}\n onChange={(e) => {\n const on = e.target.checked\n setDraft((d) => ({\n ...d,\n enabled: on,\n // Opt-out only applies when viewer is on; clear when disabling.\n noDefaultRemote: on ? d.noDefaultRemote : false,\n }))\n }}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-medium\">{t('viewerSettings.viewer.enableLabel')}</span>\n </label>\n </div>\n\n {/* Public URL + tunnel mode */}\n <div className=\"grid grid-cols-1 gap-3 px-4 pb-3 sm:grid-cols-2\">\n <Field label={t('viewerSettings.viewer.publicUrl')} hint={t('viewerSettings.viewer.publicUrlHint')}>\n <Input\n value={draft.publicUrl}\n onChange={(e) => setField('publicUrl', e.target.value)}\n placeholder=\"https://agim.example.com\"\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('viewerSettings.viewer.tunnelMode')} hint={t('viewerSettings.viewer.tunnelHint')}>\n <Select\n value={draft.tunnelMode}\n onValueChange={(v) => setField('tunnelMode', v as 'off' | 'quick')}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"off\">{t('viewerSettings.viewer.tunnelOff')}</SelectItem>\n <SelectItem value=\"quick\">{t('viewerSettings.viewer.tunnelQuick')}</SelectItem>\n </SelectContent>\n </Select>\n </Field>\n\n {/* v1.2.45 — only the opt-out toggle is in the UI. The\n community default endpoint (viewer.iclaw.host) is the\n implicit fallback when neither public base URL nor tunnel\n is set; operators who need to point at a different paste\n server use the IMHUB_VIEWER_REMOTE_PASTE_URL env var\n (documented in docs/viewer-modes.md). */}\n <Field\n label={t('viewerSettings.viewer.noDefaultRemote')}\n hint={t('viewerSettings.viewer.noDefaultRemoteHint')}\n >\n <label className={cn(\n 'inline-flex items-center gap-2 text-xs',\n !draft.enabled && 'opacity-50 cursor-not-allowed',\n )}>\n <input\n type=\"checkbox\"\n checked={draft.noDefaultRemote}\n disabled={!draft.enabled}\n onChange={(e) => setField('noDefaultRemote', e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer disabled:cursor-not-allowed\"\n />\n <span>{t('viewerSettings.viewer.noDefaultRemoteLabel')}</span>\n </label>\n </Field>\n </div>\n\n {/* Tunnel status panel (only when quick mode is selected) */}\n {draft.tunnelMode === 'quick' && (\n <div className=\"mx-4 mb-3 rounded-md border border-border bg-surface-2 p-3\">\n <div className=\"mb-1 flex items-center gap-2\">\n <Globe2 className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-dim\">\n {t('viewerSettings.viewer.tunnelStatus')}\n </span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => void tunnelQuery.refetch()}\n disabled={tunnelQuery.isFetching}\n aria-label={t('viewerSettings.viewer.refreshTunnel')}\n >\n {tunnelQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('viewerSettings.viewer.refreshTunnel')}</span>\n </Button>\n </div>\n {tunnel ? (\n <div className=\"flex flex-col gap-1 text-xs\">\n <Row label={t('viewerSettings.viewer.tunnelRunning')}>\n {tunnel.tunnel.running ? (\n <span className=\"inline-flex items-center gap-1 text-success\">\n <CheckCircle2 className=\"h-3 w-3\" />\n {t('viewerSettings.viewer.yes')}\n </span>\n ) : (\n <span className=\"text-text-dim\">{t('viewerSettings.viewer.no')}</span>\n )}\n </Row>\n <Row label={t('viewerSettings.viewer.tunnelUrl')}>\n {tunnel.tunnel.url ? (\n <a\n href={tunnel.tunnel.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 break-all font-mono text-info hover:underline\"\n >\n {tunnel.tunnel.url}\n <ExternalLink className=\"h-3 w-3 shrink-0\" />\n </a>\n ) : <span className=\"text-text-muted\">—</span>}\n </Row>\n <Row label={t('viewerSettings.viewer.effectiveUrl')}>\n {effectivePublicUrl ? (\n <span className=\"break-all font-mono text-text\">{effectivePublicUrl}</span>\n ) : <span className=\"text-text-muted\">—</span>}\n </Row>\n <Row label={t('viewerSettings.viewer.binary')}>\n {tunnel.tunnel.binaryFound ? (\n <span className=\"font-mono text-text\">{tunnel.tunnel.binaryPath}</span>\n ) : (\n <span className=\"text-warning\">{t('viewerSettings.viewer.binaryMissing')}</span>\n )}\n </Row>\n {tunnel.tunnel.lastError && (\n <Row label={t('viewerSettings.viewer.lastError')}>\n <span className=\"break-words text-danger\">{tunnel.tunnel.lastError}</span>\n </Row>\n )}\n </div>\n ) : (\n <p className=\"text-xs text-text-dim\">{t('viewerSettings.viewer.tunnelLoading')}</p>\n )}\n </div>\n )}\n\n {/* Thresholds */}\n <h3 className=\"border-t border-border px-4 pt-3 text-xs font-semibold uppercase tracking-wide text-text-dim\">\n {t('viewerSettings.viewer.thresholds')}\n </h3>\n <div className=\"grid grid-cols-2 gap-3 px-4 py-3 sm:grid-cols-4\">\n <Field label={t('viewerSettings.viewer.chars')} hint={t('viewerSettings.viewer.charsHint')}>\n <NumberInput value={draft.chars} onChange={(v) => setField('chars', v)} min={100} max={10000} placeholder=\"500\" />\n </Field>\n <Field label={t('viewerSettings.viewer.lines')} hint={t('viewerSettings.viewer.linesHint')}>\n <NumberInput value={draft.lines} onChange={(v) => setField('lines', v)} min={5} max={200} placeholder=\"12\" />\n </Field>\n <Field label={t('viewerSettings.viewer.codeLines')} hint={t('viewerSettings.viewer.codeLinesHint')}>\n <NumberInput value={draft.codeLines} onChange={(v) => setField('codeLines', v)} min={5} max={200} placeholder=\"10\" />\n </Field>\n <Field label={t('viewerSettings.viewer.maxPastes')} hint={t('viewerSettings.viewer.maxPastesHint')}>\n <NumberInput value={draft.maxPastes} onChange={(v) => setField('maxPastes', v)} min={100} max={1000000} placeholder=\"10000\" />\n </Field>\n </div>\n\n {/* Save bar */}\n {isDirty && (\n <div className=\"flex items-center gap-2 border-t border-border px-4 py-3\">\n <Badge variant=\"info\">{t('viewerSettings.dirtyHint')}</Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" />\n {t('viewerSettings.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {updateEnv.isPending ? t('viewerSettings.saving') : t('viewerSettings.save')}\n </Button>\n </div>\n )}\n </section>\n )\n}\n\n/* ─────────────── Baidu Maps card ─────────────── */\n\nfunction isMasked(s: string): boolean {\n return /\\*{3,}/.test(s)\n}\n\nfunction BaiduCard({ env }: { env: Record<string, string> }): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const updateEnv = useUpdateEnv()\n\n const currentAk = env.IMHUB_BAIDU_MAP_AK ?? ''\n const [draftAk, setDraftAk] = useState(currentAk)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n if (currentAk !== syncedHash) {\n setDraftAk(currentAk)\n setSyncedHash(currentAk)\n }\n }, [currentAk, syncedHash])\n\n const isConfigured = !!currentAk\n // Treat masked echo as no-change. Operator either typed something\n // new, or cleared. AK validity = looks-suspicious warning.\n const isDirty = draftAk !== currentAk && !(isMasked(draftAk) && draftAk === currentAk)\n const suspicious = draftAk.length > 0 && !isMasked(draftAk) && draftAk.length !== 32\n\n async function onSave(): Promise<void> {\n if (!isDirty) return\n if (isMasked(draftAk)) return // server would skip anyway; be explicit\n try {\n await updateEnv.mutateAsync({\n updates: { IMHUB_BAIDU_MAP_AK: draftAk.trim() || null },\n })\n toast.success(t('viewerSettings.baidu.toast.saved'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n async function onClear(): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { IMHUB_BAIDU_MAP_AK: null } })\n setDraftAk('')\n toast.success(t('viewerSettings.baidu.toast.cleared'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n return (\n <section className=\"rounded-md border border-border bg-surface\">\n <header className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <MapPin className=\"h-4 w-4 text-text-dim\" />\n <h2 className=\"text-sm font-semibold\">{t('viewerSettings.baidu.title')}</h2>\n {isConfigured\n ? <Badge variant=\"success\">{t('viewerSettings.baidu.configured')}</Badge>\n : <Badge variant=\"outline\">{t('viewerSettings.baidu.empty')}</Badge>}\n </header>\n <p className=\"px-4 pt-2 text-xs text-text-dim\">{t('viewerSettings.baidu.subtitle')}</p>\n\n <div className=\"px-4 py-3\">\n <Label htmlFor=\"baidu-ak\" className=\"text-xs font-medium\">IMHUB_BAIDU_MAP_AK</Label>\n <Input\n id=\"baidu-ak\"\n value={draftAk}\n onChange={(e) => setDraftAk(e.target.value)}\n placeholder={t('viewerSettings.baidu.placeholder')}\n autoComplete=\"off\"\n className={cn('mt-1 font-mono text-xs', isMasked(draftAk) && 'text-text-muted')}\n />\n {suspicious && (\n <p className=\"mt-1 text-[11px] text-warning\">\n {t('viewerSettings.baidu.suspicious', { length: draftAk.length })}\n </p>\n )}\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('viewerSettings.baidu.hint')}</p>\n </div>\n\n <div className=\"flex items-center gap-2 border-t border-border px-4 py-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void onClear()}\n disabled={!isConfigured || updateEnv.isPending}\n className=\"text-danger hover:text-danger\"\n >\n <X className=\"h-4 w-4\" />\n {t('viewerSettings.baidu.clear')}\n </Button>\n <span className=\"ml-auto\" />\n {isDirty && (\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending || isMasked(draftAk)}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {updateEnv.isPending ? t('viewerSettings.saving') : t('viewerSettings.save')}\n </Button>\n )}\n </div>\n </section>\n )\n}\n\n/* ─────────────── Helpers ─────────────── */\n\ninterface FieldProps {\n label: string\n hint?: string\n children: React.ReactNode\n}\n\nfunction Field({ label, hint, children }: FieldProps): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">{label}</Label>\n {children}\n {hint && <p className=\"text-[11px] text-text-dim\">{hint}</p>}\n </div>\n )\n}\n\ninterface RowProps {\n label: string\n children: React.ReactNode\n}\n\nfunction Row({ label, children }: RowProps): JSX.Element {\n return (\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span className=\"shrink-0 font-medium text-text-dim\">{label}:</span>\n <span className=\"min-w-0 flex-1\">{children}</span>\n </div>\n )\n}\n\ninterface NumberInputProps {\n value: string\n onChange: (v: string) => void\n min?: number\n max?: number\n placeholder?: string\n}\n\nfunction NumberInput({ value, onChange, min, max, placeholder }: NumberInputProps): JSX.Element {\n return (\n <Input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n max={max}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n />\n )\n}\n"],"names":["Earth","createLucideIcon","FileText","SettingsViewerRoute","t","useTranslation","reveal","setReveal","useState","envQuery","useEnv","jsxs","jsx","Button","v","EyeOff","Eye","Loader2","RefreshCcw","Fragment","ViewerCard","BaiduCard","viewerDraftFromEnv","env","updateEnv","useUpdateEnv","tunnelQuery","useViewerTunnel","draft","setDraft","syncedHash","setSyncedHash","useEffect","next","hash","current","useMemo","isDirty","setField","k","d","onSave","updates","toast","err","describeError","onDiscard","tunnel","effectivePublicUrl","Badge","e","on","Field","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","cn","Globe2","Row","CheckCircle2","ExternalLink","NumberInput","X","Save","isMasked","s","currentAk","draftAk","setDraftAk","isConfigured","suspicious","onClear","MapPin","Label","label","hint","children","value","onChange","min","max","placeholder"],"mappings":"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","x_google_ignoreList":[0,1]}
1
+ {"version":3,"file":"viewer-Dz6k0YKp.js","sources":["../../node_modules/lucide-react/dist/esm/icons/earth.js","../../node_modules/lucide-react/dist/esm/icons/file-text.js","../../src/routes/settings/viewer.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 Earth = createLucideIcon(\"Earth\", [\n [\"path\", { d: \"M21.54 15H17a2 2 0 0 0-2 2v4.54\", key: \"1djwo0\" }],\n [\n \"path\",\n {\n d: \"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17\",\n key: \"1tzkfa\"\n }\n ],\n [\"path\", { d: \"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05\", key: \"14pb5j\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }]\n]);\n\nexport { Earth as default };\n//# sourceMappingURL=earth.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FileText = createLucideIcon(\"FileText\", [\n [\"path\", { d: \"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\", key: \"1rqfz7\" }],\n [\"path\", { d: \"M14 2v4a2 2 0 0 0 2 2h4\", key: \"tnqrlb\" }],\n [\"path\", { d: \"M10 9H8\", key: \"b1mrlr\" }],\n [\"path\", { d: \"M16 13H8\", key: \"t4e002\" }],\n [\"path\", { d: \"M16 17H8\", key: \"z1uh3a\" }]\n]);\n\nexport { FileText as default };\n//# sourceMappingURL=file-text.js.map\n","/**\n * /settings/viewer — long-message Viewer card + Baidu Maps AK card.\n *\n * Both cards are env-driven (~/.agim/.env), each with its own dirty /\n * Save / Discard state so a change to one doesn't force re-save of the\n * other. The Viewer card additionally surfaces cloudflared tunnel\n * status via `GET /api/viewer/tunnel`, with a manual Refresh button\n * since tunnel state is essentially static between operator actions.\n *\n * Env keys covered:\n * * Viewer: IMHUB_VIEWER_ENABLED / _PUBLIC_BASE_URL / _TUNNEL_MODE /\n * _CHARS / _LINES / _CODE_LINES / _MAX_PASTES\n * * Baidu: IMHUB_BAIDU_MAP_AK (in SECRET_KEYS — masked unless\n * reveal=1 is passed)\n *\n * AK validation: v1 mentioned \"32-char AK starting with non-digit\" as\n * a hint, but isn't strictly enforced (Baidu has refreshed AK formats\n * over the years). We surface a `pattern-looks-suspicious` warning\n * rather than blocking save.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n CheckCircle2, Eye, EyeOff, ExternalLink, FileText, Globe2, Loader2,\n MapPin, RefreshCcw, Save, X,\n} from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv, useViewerTunnel } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\n/* ─────────────── Page wrapper ─────────────── */\n\nexport default function SettingsViewerRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const [reveal, setReveal] = useState(false)\n const envQuery = useEnv({ reveal })\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\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('viewerSettings.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => setReveal((v) => !v)}\n disabled={envQuery.isFetching}\n aria-label={reveal ? t('viewerSettings.hide') : t('viewerSettings.reveal')}\n >\n {reveal ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">\n {reveal ? t('viewerSettings.hide') : t('viewerSettings.reveal')}\n </span>\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void envQuery.refetch()}\n disabled={envQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('viewerSettings.subtitle')}</p>\n </header>\n\n {envQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <>\n <ViewerCard env={envQuery.data?.env ?? {}} />\n <BaiduCard env={envQuery.data?.env ?? {}} />\n </>\n )}\n </div>\n )\n}\n\n/* ─────────────── Viewer card ─────────────── */\n\ninterface ViewerDraft {\n enabled: boolean\n publicUrl: string\n tunnelMode: 'off' | 'quick'\n // v1.2.45 — remote-paste URL/Token are env-only advanced knobs.\n // Only the opt-out toggle stays in the UI.\n noDefaultRemote: boolean\n chars: string\n lines: string\n codeLines: string\n maxPastes: string\n}\n\nfunction viewerDraftFromEnv(env: Record<string, string>): ViewerDraft {\n return {\n enabled: env.IMHUB_VIEWER_ENABLED === '1' || env.IMHUB_VIEWER_ENABLED?.toLowerCase() === 'true',\n publicUrl: env.IMHUB_VIEWER_PUBLIC_BASE_URL ?? '',\n tunnelMode: env.IMHUB_VIEWER_TUNNEL_MODE === 'quick' ? 'quick' : 'off',\n noDefaultRemote: env.IMHUB_VIEWER_NO_DEFAULT_REMOTE === '1' || env.IMHUB_VIEWER_NO_DEFAULT_REMOTE?.toLowerCase() === 'true',\n chars: env.IMHUB_VIEWER_CHARS ?? '',\n lines: env.IMHUB_VIEWER_LINES ?? '',\n codeLines: env.IMHUB_VIEWER_CODE_LINES ?? '',\n maxPastes: env.IMHUB_VIEWER_MAX_PASTES ?? '',\n }\n}\n\nfunction ViewerCard({ env }: { env: Record<string, string> }): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const updateEnv = useUpdateEnv()\n const tunnelQuery = useViewerTunnel()\n\n const [draft, setDraft] = useState<ViewerDraft>(() => viewerDraftFromEnv(env))\n const [syncedHash, setSyncedHash] = useState('')\n\n // Sync draft when env changes (initial load + post-save refetch).\n useEffect(() => {\n const next = viewerDraftFromEnv(env)\n const hash = JSON.stringify(next)\n if (hash !== syncedHash) {\n setDraft(next)\n setSyncedHash(hash)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [env.IMHUB_VIEWER_ENABLED, env.IMHUB_VIEWER_PUBLIC_BASE_URL,\n env.IMHUB_VIEWER_TUNNEL_MODE, env.IMHUB_VIEWER_CHARS,\n env.IMHUB_VIEWER_LINES, env.IMHUB_VIEWER_CODE_LINES,\n env.IMHUB_VIEWER_MAX_PASTES, env.IMHUB_VIEWER_NO_DEFAULT_REMOTE])\n\n const current = useMemo(() => viewerDraftFromEnv(env), [env])\n const isDirty = useMemo(\n () => JSON.stringify(draft) !== JSON.stringify(current),\n [draft, current],\n )\n\n function setField<K extends keyof ViewerDraft>(k: K, v: ViewerDraft[K]): void {\n setDraft((d) => ({ ...d, [k]: v }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n if (draft.enabled !== current.enabled) {\n updates.IMHUB_VIEWER_ENABLED = draft.enabled ? '1' : '0'\n }\n if (draft.publicUrl !== current.publicUrl) {\n updates.IMHUB_VIEWER_PUBLIC_BASE_URL = draft.publicUrl.trim() || null\n }\n if (draft.tunnelMode !== current.tunnelMode) {\n updates.IMHUB_VIEWER_TUNNEL_MODE = draft.tunnelMode\n }\n if (draft.chars !== current.chars) {\n updates.IMHUB_VIEWER_CHARS = draft.chars.trim() || null\n }\n if (draft.lines !== current.lines) {\n updates.IMHUB_VIEWER_LINES = draft.lines.trim() || null\n }\n if (draft.codeLines !== current.codeLines) {\n updates.IMHUB_VIEWER_CODE_LINES = draft.codeLines.trim() || null\n }\n if (draft.maxPastes !== current.maxPastes) {\n updates.IMHUB_VIEWER_MAX_PASTES = draft.maxPastes.trim() || null\n }\n if (draft.noDefaultRemote !== current.noDefaultRemote) {\n updates.IMHUB_VIEWER_NO_DEFAULT_REMOTE = draft.noDefaultRemote ? '1' : null\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n toast.success(t('viewerSettings.toast.saved'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n function onDiscard(): void {\n setDraft(current)\n }\n\n const tunnel = tunnelQuery.data\n const effectivePublicUrl = tunnel?.effectivePublicUrl\n\n return (\n <section className=\"rounded-md border border-border bg-surface\">\n <header className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <FileText className=\"h-4 w-4 text-text-dim\" />\n <h2 className=\"text-sm font-semibold\">{t('viewerSettings.viewer.title')}</h2>\n {draft.enabled\n ? <Badge variant=\"success\">{t('viewerSettings.viewer.on')}</Badge>\n : <Badge variant=\"outline\">{t('viewerSettings.viewer.off')}</Badge>}\n </header>\n <p className=\"px-4 pt-2 text-xs text-text-dim\">{t('viewerSettings.viewer.subtitle')}</p>\n\n {/* Enable toggle */}\n <div className=\"flex items-center gap-3 px-4 py-3\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={draft.enabled}\n onChange={(e) => {\n const on = e.target.checked\n setDraft((d) => ({\n ...d,\n enabled: on,\n // Opt-out only applies when viewer is on; clear when disabling.\n noDefaultRemote: on ? d.noDefaultRemote : false,\n }))\n }}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-medium\">{t('viewerSettings.viewer.enableLabel')}</span>\n </label>\n </div>\n\n {/* Public URL + tunnel mode */}\n <div className=\"grid grid-cols-1 gap-3 px-4 pb-3 sm:grid-cols-2\">\n <Field label={t('viewerSettings.viewer.publicUrl')} hint={t('viewerSettings.viewer.publicUrlHint')}>\n <Input\n value={draft.publicUrl}\n onChange={(e) => setField('publicUrl', e.target.value)}\n placeholder=\"https://agim.example.com\"\n className=\"font-mono text-xs\"\n />\n </Field>\n <Field label={t('viewerSettings.viewer.tunnelMode')} hint={t('viewerSettings.viewer.tunnelHint')}>\n <Select\n value={draft.tunnelMode}\n onValueChange={(v) => setField('tunnelMode', v as 'off' | 'quick')}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"off\">{t('viewerSettings.viewer.tunnelOff')}</SelectItem>\n <SelectItem value=\"quick\">{t('viewerSettings.viewer.tunnelQuick')}</SelectItem>\n </SelectContent>\n </Select>\n </Field>\n\n {/* v1.2.45 — only the opt-out toggle is in the UI. The\n community default endpoint (viewer.iclaw.host) is the\n implicit fallback when neither public base URL nor tunnel\n is set; operators who need to point at a different paste\n server use the IMHUB_VIEWER_REMOTE_PASTE_URL env var\n (documented in docs/viewer-modes.md). */}\n <Field\n label={t('viewerSettings.viewer.noDefaultRemote')}\n hint={t('viewerSettings.viewer.noDefaultRemoteHint')}\n >\n <label className={cn(\n 'inline-flex items-center gap-2 text-xs',\n !draft.enabled && 'opacity-50 cursor-not-allowed',\n )}>\n <input\n type=\"checkbox\"\n checked={draft.noDefaultRemote}\n disabled={!draft.enabled}\n onChange={(e) => setField('noDefaultRemote', e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer disabled:cursor-not-allowed\"\n />\n <span>{t('viewerSettings.viewer.noDefaultRemoteLabel')}</span>\n </label>\n </Field>\n </div>\n\n {/* Tunnel status panel (only when quick mode is selected) */}\n {draft.tunnelMode === 'quick' && (\n <div className=\"mx-4 mb-3 rounded-md border border-border bg-surface-2 p-3\">\n <div className=\"mb-1 flex items-center gap-2\">\n <Globe2 className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-dim\">\n {t('viewerSettings.viewer.tunnelStatus')}\n </span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => void tunnelQuery.refetch()}\n disabled={tunnelQuery.isFetching}\n aria-label={t('viewerSettings.viewer.refreshTunnel')}\n >\n {tunnelQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('viewerSettings.viewer.refreshTunnel')}</span>\n </Button>\n </div>\n {tunnel ? (\n <div className=\"flex flex-col gap-1 text-xs\">\n <Row label={t('viewerSettings.viewer.tunnelRunning')}>\n {tunnel.tunnel.running ? (\n <span className=\"inline-flex items-center gap-1 text-success\">\n <CheckCircle2 className=\"h-3 w-3\" />\n {t('viewerSettings.viewer.yes')}\n </span>\n ) : (\n <span className=\"text-text-dim\">{t('viewerSettings.viewer.no')}</span>\n )}\n </Row>\n <Row label={t('viewerSettings.viewer.tunnelUrl')}>\n {tunnel.tunnel.url ? (\n <a\n href={tunnel.tunnel.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 break-all font-mono text-info hover:underline\"\n >\n {tunnel.tunnel.url}\n <ExternalLink className=\"h-3 w-3 shrink-0\" />\n </a>\n ) : <span className=\"text-text-muted\">—</span>}\n </Row>\n <Row label={t('viewerSettings.viewer.effectiveUrl')}>\n {effectivePublicUrl ? (\n <span className=\"break-all font-mono text-text\">{effectivePublicUrl}</span>\n ) : <span className=\"text-text-muted\">—</span>}\n </Row>\n <Row label={t('viewerSettings.viewer.binary')}>\n {tunnel.tunnel.binaryFound ? (\n <span className=\"font-mono text-text\">{tunnel.tunnel.binaryPath}</span>\n ) : (\n <span className=\"text-warning\">{t('viewerSettings.viewer.binaryMissing')}</span>\n )}\n </Row>\n {tunnel.tunnel.lastError && (\n <Row label={t('viewerSettings.viewer.lastError')}>\n <span className=\"break-words text-danger\">{tunnel.tunnel.lastError}</span>\n </Row>\n )}\n </div>\n ) : (\n <p className=\"text-xs text-text-dim\">{t('viewerSettings.viewer.tunnelLoading')}</p>\n )}\n </div>\n )}\n\n {/* Thresholds */}\n <h3 className=\"border-t border-border px-4 pt-3 text-xs font-semibold uppercase tracking-wide text-text-dim\">\n {t('viewerSettings.viewer.thresholds')}\n </h3>\n <div className=\"grid grid-cols-2 gap-3 px-4 py-3 sm:grid-cols-4\">\n <Field label={t('viewerSettings.viewer.chars')} hint={t('viewerSettings.viewer.charsHint')}>\n <NumberInput value={draft.chars} onChange={(v) => setField('chars', v)} min={100} max={10000} placeholder=\"500\" />\n </Field>\n <Field label={t('viewerSettings.viewer.lines')} hint={t('viewerSettings.viewer.linesHint')}>\n <NumberInput value={draft.lines} onChange={(v) => setField('lines', v)} min={5} max={200} placeholder=\"12\" />\n </Field>\n <Field label={t('viewerSettings.viewer.codeLines')} hint={t('viewerSettings.viewer.codeLinesHint')}>\n <NumberInput value={draft.codeLines} onChange={(v) => setField('codeLines', v)} min={5} max={200} placeholder=\"10\" />\n </Field>\n <Field label={t('viewerSettings.viewer.maxPastes')} hint={t('viewerSettings.viewer.maxPastesHint')}>\n <NumberInput value={draft.maxPastes} onChange={(v) => setField('maxPastes', v)} min={100} max={1000000} placeholder=\"10000\" />\n </Field>\n </div>\n\n {/* Save bar */}\n {isDirty && (\n <div className=\"flex items-center gap-2 border-t border-border px-4 py-3\">\n <Badge variant=\"info\">{t('viewerSettings.dirtyHint')}</Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" />\n {t('viewerSettings.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {updateEnv.isPending ? t('viewerSettings.saving') : t('viewerSettings.save')}\n </Button>\n </div>\n )}\n </section>\n )\n}\n\n/* ─────────────── Baidu Maps card ─────────────── */\n\nfunction isMasked(s: string): boolean {\n return /\\*{3,}/.test(s)\n}\n\nfunction BaiduCard({ env }: { env: Record<string, string> }): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const updateEnv = useUpdateEnv()\n\n const currentAk = env.IMHUB_BAIDU_MAP_AK ?? ''\n const [draftAk, setDraftAk] = useState(currentAk)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n if (currentAk !== syncedHash) {\n setDraftAk(currentAk)\n setSyncedHash(currentAk)\n }\n }, [currentAk, syncedHash])\n\n const isConfigured = !!currentAk\n // Treat masked echo as no-change. Operator either typed something\n // new, or cleared. AK validity = looks-suspicious warning.\n const isDirty = draftAk !== currentAk && !(isMasked(draftAk) && draftAk === currentAk)\n const suspicious = draftAk.length > 0 && !isMasked(draftAk) && draftAk.length !== 32\n\n async function onSave(): Promise<void> {\n if (!isDirty) return\n if (isMasked(draftAk)) return // server would skip anyway; be explicit\n try {\n await updateEnv.mutateAsync({\n updates: { IMHUB_BAIDU_MAP_AK: draftAk.trim() || null },\n })\n toast.success(t('viewerSettings.baidu.toast.saved'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n async function onClear(): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { IMHUB_BAIDU_MAP_AK: null } })\n setDraftAk('')\n toast.success(t('viewerSettings.baidu.toast.cleared'))\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n return (\n <section className=\"rounded-md border border-border bg-surface\">\n <header className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <MapPin className=\"h-4 w-4 text-text-dim\" />\n <h2 className=\"text-sm font-semibold\">{t('viewerSettings.baidu.title')}</h2>\n {isConfigured\n ? <Badge variant=\"success\">{t('viewerSettings.baidu.configured')}</Badge>\n : <Badge variant=\"outline\">{t('viewerSettings.baidu.empty')}</Badge>}\n </header>\n <p className=\"px-4 pt-2 text-xs text-text-dim\">{t('viewerSettings.baidu.subtitle')}</p>\n\n <div className=\"px-4 py-3\">\n <Label htmlFor=\"baidu-ak\" className=\"text-xs font-medium\">IMHUB_BAIDU_MAP_AK</Label>\n <Input\n id=\"baidu-ak\"\n value={draftAk}\n onChange={(e) => setDraftAk(e.target.value)}\n placeholder={t('viewerSettings.baidu.placeholder')}\n autoComplete=\"off\"\n className={cn('mt-1 font-mono text-xs', isMasked(draftAk) && 'text-text-muted')}\n />\n {suspicious && (\n <p className=\"mt-1 text-[11px] text-warning\">\n {t('viewerSettings.baidu.suspicious', { length: draftAk.length })}\n </p>\n )}\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('viewerSettings.baidu.hint')}</p>\n </div>\n\n <div className=\"flex items-center gap-2 border-t border-border px-4 py-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void onClear()}\n disabled={!isConfigured || updateEnv.isPending}\n className=\"text-danger hover:text-danger\"\n >\n <X className=\"h-4 w-4\" />\n {t('viewerSettings.baidu.clear')}\n </Button>\n <span className=\"ml-auto\" />\n {isDirty && (\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending || isMasked(draftAk)}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {updateEnv.isPending ? t('viewerSettings.saving') : t('viewerSettings.save')}\n </Button>\n )}\n </div>\n </section>\n )\n}\n\n/* ─────────────── Helpers ─────────────── */\n\ninterface FieldProps {\n label: string\n hint?: string\n children: React.ReactNode\n}\n\nfunction Field({ label, hint, children }: FieldProps): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">{label}</Label>\n {children}\n {hint && <p className=\"text-[11px] text-text-dim\">{hint}</p>}\n </div>\n )\n}\n\ninterface RowProps {\n label: string\n children: React.ReactNode\n}\n\nfunction Row({ label, children }: RowProps): JSX.Element {\n return (\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span className=\"shrink-0 font-medium text-text-dim\">{label}:</span>\n <span className=\"min-w-0 flex-1\">{children}</span>\n </div>\n )\n}\n\ninterface NumberInputProps {\n value: string\n onChange: (v: string) => void\n min?: number\n max?: number\n placeholder?: string\n}\n\nfunction NumberInput({ value, onChange, min, max, placeholder }: NumberInputProps): JSX.Element {\n return (\n <Input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n max={max}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n />\n )\n}\n"],"names":["Earth","createLucideIcon","FileText","SettingsViewerRoute","t","useTranslation","reveal","setReveal","useState","envQuery","useEnv","jsxs","jsx","Button","v","EyeOff","Eye","Loader2","RefreshCcw","Fragment","ViewerCard","BaiduCard","viewerDraftFromEnv","env","updateEnv","useUpdateEnv","tunnelQuery","useViewerTunnel","draft","setDraft","syncedHash","setSyncedHash","useEffect","next","hash","current","useMemo","isDirty","setField","k","d","onSave","updates","toast","err","describeError","onDiscard","tunnel","effectivePublicUrl","Badge","e","on","Field","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","cn","Globe2","Row","CheckCircle2","ExternalLink","NumberInput","X","Save","isMasked","s","currentAk","draftAk","setDraftAk","isConfigured","suspicious","onClear","MapPin","Label","label","hint","children","value","onChange","min","max","placeholder"],"mappings":"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","x_google_ignoreList":[0,1]}