@shawnstack/quickforge 1.7.2 → 1.7.4

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 (210) hide show
  1. package/README.md +1 -0
  2. package/bin/quickforge.mjs +2 -21
  3. package/dist/assets/AgentProfilesPage-B84w-r9S.js +1 -0
  4. package/dist/assets/{ChatPanelHost-Cld9HcVz.js → ChatPanelHost-CyO8fEoj.js} +98 -98
  5. package/dist/assets/{PluginsPage-DEMqwhOA.js → PluginsPage-D2862wul.js} +1 -1
  6. package/dist/assets/{ScheduledTasksPage-DTP_gedp.js → ScheduledTasksPage-GqbiJDPX.js} +2 -2
  7. package/dist/assets/SettingsWorkspacePage-B53_eDf2.js +1628 -0
  8. package/dist/assets/SharedConversationPage-Cw2QFnbC.js +1 -0
  9. package/dist/assets/TerminalDock-Dgg-_ZmU.js +2 -0
  10. package/dist/assets/WorkspaceInspector-3uLiu6Bv.js +13 -0
  11. package/dist/assets/{abnfDiagram-VRR7QNED-Dl_Fvlqg.js → abnfDiagram-VRR7QNED-Dy7veffk.js} +1 -1
  12. package/dist/assets/{arc-Bhg4c0ht.js → arc-Bo3p2lGy.js} +1 -1
  13. package/dist/assets/architecture-TIHT7OUA-7FQo5GbP.js +1 -0
  14. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BuojN27B.js → architectureDiagram-ZJ3FMSHR-Cwx8qOvu.js} +1 -1
  15. package/dist/assets/{blockDiagram-677ZJIJ3-alDLbrR1.js → blockDiagram-677ZJIJ3-BiuaAOFa.js} +1 -1
  16. package/dist/assets/{c4Diagram-LMCZKHZV-D1lKa4VT.js → c4Diagram-LMCZKHZV-BTcGfhK2.js} +1 -1
  17. package/dist/assets/channel-DToA8d-4.js +1 -0
  18. package/dist/assets/chunk-2Q5K7J3B-D_Wxpk-T.js +1 -0
  19. package/dist/assets/{chunk-32BRIVSS-CwWLNjMZ.js → chunk-32BRIVSS-B9PuF_RF.js} +1 -1
  20. package/dist/assets/{chunk-52WLFC77-Be569m7d.js → chunk-52WLFC77-3zGItfIM.js} +1 -1
  21. package/dist/assets/{chunk-5FCAYU7R-BSvoiWV1.js → chunk-5FCAYU7R-DQ66qlmt.js} +1 -1
  22. package/dist/assets/{chunk-5HE753X5-CRgsy8WS.js → chunk-5HE753X5-CLqTO-VF.js} +1 -1
  23. package/dist/assets/{chunk-5JV3BV7I-CdW477UR.js → chunk-5JV3BV7I-Csxv1jr6.js} +1 -1
  24. package/dist/assets/{chunk-5TONJI2A-CdtKzSmp.js → chunk-5TONJI2A-BCQttpBr.js} +1 -1
  25. package/dist/assets/{chunk-5VM5RSS4-BKjjssLZ.js → chunk-5VM5RSS4-DEWJGSTb.js} +1 -1
  26. package/dist/assets/{chunk-7BUUIJ7U-ByAXiXdn.js → chunk-7BUUIJ7U-DNorm9MM.js} +1 -1
  27. package/dist/assets/{chunk-BIQX33UG-DeG8SCT1.js → chunk-BIQX33UG-DgvY_BOT.js} +1 -1
  28. package/dist/assets/{chunk-C7G6YPKG-D_zHT68Q.js → chunk-C7G6YPKG-DMvn9IPs.js} +1 -1
  29. package/dist/assets/{chunk-CQNSW5MT-CHpHoUSu.js → chunk-CQNSW5MT-Dq2duUgr.js} +1 -1
  30. package/dist/assets/{chunk-CYSBUYHQ-CRP50Vov.js → chunk-CYSBUYHQ-BovYyc4Y.js} +1 -1
  31. package/dist/assets/{chunk-EMLP6XTP-DX6uQQ2v.js → chunk-EMLP6XTP-BMDOpuYN.js} +1 -1
  32. package/dist/assets/{chunk-EX3LRPZG-DAJRNkHR.js → chunk-EX3LRPZG-6eP2UPOf.js} +1 -1
  33. package/dist/assets/{chunk-FWX5IMBZ-XbM5yomC.js → chunk-FWX5IMBZ-D4q-wTSy.js} +2 -2
  34. package/dist/assets/{chunk-HOUHSVGY-Ci0ljnb1.js → chunk-HOUHSVGY-XjIcuzH7.js} +1 -1
  35. package/dist/assets/{chunk-ICXQ74PX-Dy2WJ71z.js → chunk-ICXQ74PX-YBXbanq9.js} +1 -1
  36. package/dist/assets/{chunk-JG7HCLWE-2PrkzHkt.js → chunk-JG7HCLWE-CcN3uNXF.js} +1 -1
  37. package/dist/assets/{chunk-JWPE2WC7-D32uacSM.js → chunk-JWPE2WC7-Cwg6AUAt.js} +1 -1
  38. package/dist/assets/{chunk-MOJQB5TN-DIAFLtiS.js → chunk-MOJQB5TN-NNspcGIU.js} +1 -1
  39. package/dist/assets/{chunk-MOZMSUNE-BbscOaEz.js → chunk-MOZMSUNE-pMJ_jMos.js} +1 -1
  40. package/dist/assets/{chunk-OGEWGWER-ivBVxzKe.js → chunk-OGEWGWER-CJy-3Ldv.js} +1 -1
  41. package/dist/assets/{chunk-OSBZ3O6U-frD0hkdx.js → chunk-OSBZ3O6U-D7cLHHOA.js} +1 -1
  42. package/dist/assets/{chunk-PUDLZKDR-B-37Rxae.js → chunk-PUDLZKDR-D5YBi1Pi.js} +1 -1
  43. package/dist/assets/{chunk-Q4XR5HBZ-Y90m2RTe.js → chunk-Q4XR5HBZ-DYmqQdIf.js} +1 -1
  44. package/dist/assets/{chunk-QBLGF6JB-DixBqslB.js → chunk-QBLGF6JB-Bfcx00X0.js} +1 -1
  45. package/dist/assets/{chunk-R7FJI6CG-DXS1UXn_.js → chunk-R7FJI6CG-oXzadpZY.js} +1 -1
  46. package/dist/assets/{chunk-RYQCIY6F-n4JxhNd0.js → chunk-RYQCIY6F-Dt3-8yUh.js} +1 -1
  47. package/dist/assets/{chunk-U6XO7XAA-yHlgxMmn.js → chunk-U6XO7XAA-B55eSvpx.js} +1 -1
  48. package/dist/assets/{chunk-V7JOEXUC-Z6kuZfbl.js → chunk-V7JOEXUC-Btk4JwCt.js} +1 -1
  49. package/dist/assets/chunk-VAUOI2AC-CIZseNLi.js +1 -0
  50. package/dist/assets/{chunk-VR4S4FIN-Cs_SIQma.js → chunk-VR4S4FIN-DgaSiVOY.js} +1 -1
  51. package/dist/assets/{chunk-WYO6CB5R-CGExYbV-.js → chunk-WYO6CB5R-rRDZ_Tta.js} +3 -3
  52. package/dist/assets/chunk-XXDRQBXY-Do-XYxat.js +1 -0
  53. package/dist/assets/{chunk-YOTPTUD7-DopPXWSl.js → chunk-YOTPTUD7-BnFxj5o4.js} +1 -1
  54. package/dist/assets/{chunk-ZGVPDNZ5-CJHOeqMb.js → chunk-ZGVPDNZ5-CrnlPlmJ.js} +1 -1
  55. package/dist/assets/{chunk-ZIRB5QZD-BBVc5mf6.js → chunk-ZIRB5QZD-BYTiATIH.js} +1 -1
  56. package/dist/assets/classDiagram-OUVF2IWQ-Bu24Sy-k.js +1 -0
  57. package/dist/assets/classDiagram-v2-EOCWNBFH-DKhh5HSW.js +1 -0
  58. package/dist/assets/{cose-bilkent-JH36ORCC-DLjV-dTm.js → cose-bilkent-JH36ORCC-BORKJRwF.js} +1 -1
  59. package/dist/assets/cynefin-VYW2F7L2-oyiAgYC4.js +1 -0
  60. package/dist/assets/{cynefinDiagram-TSTJHNR4-BfH3zVS-.js → cynefinDiagram-TSTJHNR4-BXKeaSl8.js} +1 -1
  61. package/dist/assets/{dagre-CY3D84Cl.js → dagre-CrA0XVwJ.js} +1 -1
  62. package/dist/assets/{dagre-VKFMJZFB-BIhwZCIZ.js → dagre-VKFMJZFB-ouq_lA1a.js} +1 -1
  63. package/dist/assets/{diagram-FQU43EPY-BHnzNM2w.js → diagram-FQU43EPY-BxR5kUiQ.js} +1 -1
  64. package/dist/assets/{diagram-G47NLZAW-C7-ZlmFT.js → diagram-G47NLZAW-DWonk1sL.js} +1 -1
  65. package/dist/assets/{diagram-NH7WQ7WH-C-P9Y7IR.js → diagram-NH7WQ7WH-fmV_wV5e.js} +1 -1
  66. package/dist/assets/{diagram-OA4YK3LP-Ci5lZ_T5.js → diagram-OA4YK3LP-DvOSRRiV.js} +1 -1
  67. package/dist/assets/{diagram-WEI45ONY-C1xuK2bs.js → diagram-WEI45ONY-Bp-xb3uS.js} +1 -1
  68. package/dist/assets/dist-DLKC_odd.js +1 -0
  69. package/dist/assets/{ebnfDiagram-CCIWWBDH-DKQ5NWMD.js → ebnfDiagram-CCIWWBDH-BTlnwneA.js} +1 -1
  70. package/dist/assets/{erDiagram-Q63AITRT-HAWySadP.js → erDiagram-Q63AITRT-C3fFWGCm.js} +1 -1
  71. package/dist/assets/esm-oa69b5u4.js +2 -0
  72. package/dist/assets/eventmodeling-45OFAUF4-YB_0MefX.js +1 -0
  73. package/dist/assets/flowDiagram-23GEKE2U-Bc73xdBd.js +1 -0
  74. package/dist/assets/{ganttDiagram-NO4QXBWP-C1hwIcOA.js → ganttDiagram-NO4QXBWP-DzAiojNi.js} +1 -1
  75. package/dist/assets/gitGraph-TEB2WS4Q-Bvp27L0n.js +1 -0
  76. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DYUYxs1R.js → gitGraphDiagram-IHSO6WYX-Cv57oNEA.js} +1 -1
  77. package/dist/assets/icons-cTkqD_Gi.js +1 -0
  78. package/dist/assets/index-B48prgLL.js +65 -0
  79. package/dist/assets/index-DQhwHHer.css +3 -0
  80. package/dist/assets/info-DKCQHKI2-BsLOivgq.js +1 -0
  81. package/dist/assets/{infoDiagram-FWYZ7A6U-CnIL6tv4.js → infoDiagram-FWYZ7A6U-B5Z_O3Ro.js} +1 -1
  82. package/dist/assets/{ishikawaDiagram-FXEZZL3T-B7WXNycQ.js → ishikawaDiagram-FXEZZL3T-CKTK_TKV.js} +1 -1
  83. package/dist/assets/{journeyDiagram-5HDEW3XC-D4HvioyY.js → journeyDiagram-5HDEW3XC-Cm9k4ywW.js} +1 -1
  84. package/dist/assets/{kanban-definition-HUTT4EX6-CDs763eS.js → kanban-definition-HUTT4EX6-DzNteLwx.js} +1 -1
  85. package/dist/assets/{line-ikKkQiFp.js → line-BG78c_WJ.js} +1 -1
  86. package/dist/assets/{linear-GqPyxQkN.js → linear-1X20_EGu.js} +1 -1
  87. package/dist/assets/{map-CJwjCvwr.js → map-CCI8WZkS.js} +1 -1
  88. package/dist/assets/{mcp-servers-dialog-Bp6kbIup.js → mcp-servers-dialog-GXXD9K0U.js} +2 -2
  89. package/dist/assets/{mermaid-parser.core-CoE8cccr.js → mermaid-parser.core-Bb8HI_OS.js} +2 -2
  90. package/dist/assets/{mermaid-renderer-BmbSAmpG.js → mermaid-renderer-PIaNiXAz.js} +2 -2
  91. package/dist/assets/{mermaid.core-DlFSL90j.js → mermaid.core-DUHOIwL5.js} +3 -3
  92. package/dist/assets/{mindmap-definition-LN4V7U3C-DLdq0K9C.js → mindmap-definition-LN4V7U3C-Tnbszo7b.js} +1 -1
  93. package/dist/assets/{monaco-BTsVCDWS.js → monaco-BVtCspYZ.js} +1 -1
  94. package/dist/assets/{ordinal-CibZU3PM.js → ordinal-v4prQcuq.js} +1 -1
  95. package/dist/assets/{otel-BNGM50C-.js → otel-D1LswXpM.js} +1 -1
  96. package/dist/assets/packet-7NZHBO7P-BKQkXRtk.js +1 -0
  97. package/dist/assets/{pegDiagram-2B236MQR-Cm1kPRC2.js → pegDiagram-2B236MQR-CX824Kd0.js} +1 -1
  98. package/dist/assets/{pi-ai-Cr3rpCXT.js → pi-ai-DAJlO9ZB.js} +2 -2
  99. package/dist/assets/{pi-web-ui-DoRGtREc.js → pi-web-ui-B-RJSxjR.js} +1 -1
  100. package/dist/assets/pie-RZYD4A2V-BTiTN5Ge.js +1 -0
  101. package/dist/assets/{pieDiagram-ENE6RG2P-Df05wjjo.js → pieDiagram-ENE6RG2P-BT04VWPQ.js} +1 -1
  102. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BjCwGxJC.js → quadrantDiagram-ABIIQ3AL-BvOZxkBr.js} +1 -1
  103. package/dist/assets/radar-I7S5WNFK-BU-oVjKM.js +1 -0
  104. package/dist/assets/railroad-3IZDKUUU-7H2hxNbL.js +1 -0
  105. package/dist/assets/railroad-abnf-AHOZXSZD-Mx6wf6XQ.js +1 -0
  106. package/dist/assets/railroad-ebnf-EBAXGLYW-BgXSKhYy.js +1 -0
  107. package/dist/assets/railroad-peg-LSFZ7HO6-DTEl2rAC.js +1 -0
  108. package/dist/assets/{railroadDiagram-RFXS5EU6-CTUvpKkv.js → railroadDiagram-RFXS5EU6-DnTU--BV.js} +1 -1
  109. package/dist/assets/{react-vendor-Dr5xvL-e.js → react-vendor-eWH0QhNU.js} +1 -1
  110. package/dist/assets/{requirementDiagram-TGXJPOKE-BU1kvEku.js → requirementDiagram-TGXJPOKE--MZX_yEv.js} +1 -1
  111. package/dist/assets/{sankeyDiagram-HTMAVEWB-BHmvxVqH.js → sankeyDiagram-HTMAVEWB-SEZkHhYn.js} +1 -1
  112. package/dist/assets/{sequenceDiagram-DBY2YBRQ-CTpwICgg.js → sequenceDiagram-DBY2YBRQ-BhC2OHiz.js} +1 -1
  113. package/dist/assets/{sizeCapture-X5ZJPWSS-BbLhGaa4.js → sizeCapture-X5ZJPWSS-Ci5LeToV.js} +1 -1
  114. package/dist/assets/{skills-dialog-DiJAUfvW.js → skills-dialog-DDh1h8eH.js} +3 -1
  115. package/dist/assets/{src-B4Tvg3gT.js → src-CovulcRC.js} +1 -1
  116. package/dist/assets/{stateDiagram-2N3HPSRC-BxuDwIgT.js → stateDiagram-2N3HPSRC-DKK5tFT4.js} +1 -1
  117. package/dist/assets/stateDiagram-v2-6OUMAXLB-D8x3mkni.js +1 -0
  118. package/dist/assets/{swimlanes-5IMT3BWC-EkZsiylV.js → swimlanes-5IMT3BWC-DxxGCk67.js} +2 -2
  119. package/dist/assets/swimlanesDiagram-G3AALYLV-C4nOBNp8.js +8 -0
  120. package/dist/assets/{timeline-definition-FHXFAJF6-C9ewOCTa.js → timeline-definition-FHXFAJF6-BPXB3Sue.js} +1 -1
  121. package/dist/assets/treeView-QDETBFTQ-DOE6NSdf.js +1 -0
  122. package/dist/assets/treemap-6X3UGDF4-Ca7QD_b4.js +1 -0
  123. package/dist/assets/{vennDiagram-L72KCM5P-CR49Y4Lp.js → vennDiagram-L72KCM5P-CxcBCKye.js} +1 -1
  124. package/dist/assets/wardley-OPB4EBWU-D9O4RKRm.js +1 -0
  125. package/dist/assets/{wardleyDiagram-EHGQE667-Cn0GgwWi.js → wardleyDiagram-EHGQE667-DEL6Yes5.js} +1 -1
  126. package/dist/assets/web-RgX7H-OH.js +1 -0
  127. package/dist/assets/{xychartDiagram-FW5EYKEG-DlmPNAvd.js → xychartDiagram-FW5EYKEG-N8cPbcjC.js} +1 -1
  128. package/dist/index.html +12 -11
  129. package/dist/mobile-error.html +24 -0
  130. package/package.json +7 -1
  131. package/server/acp/server.mjs +14 -9
  132. package/server/agent-manager.mjs +215 -20
  133. package/server/ai-http-logger.mjs +69 -12
  134. package/server/ai-provider-options.mjs +11 -0
  135. package/server/channels/channel-logs.mjs +13 -0
  136. package/server/channels/event-relay.mjs +33 -0
  137. package/server/channels/process-channel.mjs +29 -0
  138. package/server/channels/providers/wechat.mjs +9 -3
  139. package/server/channels/registry.mjs +5 -1
  140. package/server/index.mjs +43 -25
  141. package/server/lan-access-store.mjs +93 -3
  142. package/server/network-proxy.mjs +449 -0
  143. package/server/plugins/registry.mjs +64 -4
  144. package/server/public-api.mjs +10 -2
  145. package/server/routes/agent-profiles.mjs +4 -2
  146. package/server/routes/agent.mjs +28 -0
  147. package/server/routes/channels.mjs +47 -0
  148. package/server/routes/lan-access.mjs +246 -21
  149. package/server/routes/models.mjs +7 -3
  150. package/server/routes/project.mjs +19 -1
  151. package/server/routes/scheduled-tasks.mjs +4 -2
  152. package/server/routes/static.mjs +26 -1
  153. package/server/routes/system.mjs +37 -0
  154. package/server/routes/terminal.mjs +7 -2
  155. package/server/routes/workspace.mjs +62 -9
  156. package/server/session-utils.mjs +4 -2
  157. package/server/subagents.mjs +1 -1
  158. package/server/terminal/terminal-manager.mjs +2 -4
  159. package/server/tools/definitions.mjs +2 -2
  160. package/server/tools/index.mjs +5 -3
  161. package/server/utils/package-update.mjs +70 -47
  162. package/server/utils/process-env.mjs +7 -0
  163. package/server/utils/response.mjs +2 -2
  164. package/dist/assets/AgentProfilesPage-BFgyFa5e.js +0 -1
  165. package/dist/assets/SettingsWorkspacePage-Dr9zT5Bs.js +0 -1495
  166. package/dist/assets/SharedConversationPage-op3DRwcw.js +0 -1
  167. package/dist/assets/TerminalDock-BL_UovwU.js +0 -2
  168. package/dist/assets/WorkspaceInspector-73dhAime.js +0 -13
  169. package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +0 -1
  170. package/dist/assets/channel-Rz20dKTK.js +0 -1
  171. package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +0 -1
  172. package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +0 -1
  173. package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +0 -1
  174. package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +0 -1
  175. package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +0 -1
  176. package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +0 -1
  177. package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +0 -1
  178. package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +0 -1
  179. package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +0 -1
  180. package/dist/assets/icons-BP8YOS-Z.js +0 -1
  181. package/dist/assets/index-Cu2bBHLv.css +0 -3
  182. package/dist/assets/index-yV1wtqTr.js +0 -66
  183. package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +0 -1
  184. package/dist/assets/packet-7NZHBO7P-decrrxyX.js +0 -1
  185. package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +0 -1
  186. package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +0 -1
  187. package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +0 -1
  188. package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +0 -1
  189. package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +0 -1
  190. package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +0 -1
  191. package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +0 -1
  192. package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +0 -8
  193. package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +0 -1
  194. package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +0 -1
  195. package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +0 -1
  196. /package/dist/assets/{array-CYkMkqnU.js → array-DqLCdDFv.js} +0 -0
  197. /package/dist/assets/{chunk-KEIR6QF5-77hsebd7.js → chunk-KEIR6QF5-s7Qq-R3i.js} +0 -0
  198. /package/dist/assets/{chunk-Y2CYZVJY-CQltPwYk.js → chunk-Y2CYZVJY-BgISNSiw.js} +0 -0
  199. /package/dist/assets/{css-utils-rkE68RDy.js → css-utils-31qzCXsk.js} +0 -0
  200. /package/dist/assets/{cytoscape.esm-DwdZnGOm.js → cytoscape.esm-iekjSZ2n.js} +0 -0
  201. /package/dist/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-CrJzLgRD.js} +0 -0
  202. /package/dist/assets/{dist-DelZBZpK.js → dist-zzfqk5hx.js} +0 -0
  203. /package/dist/assets/{graphlib-CAISHZh4.js → graphlib-CIz_CfxW.js} +0 -0
  204. /package/dist/assets/{init-Rr1s_RiX.js → init-C0r9Gk5G.js} +0 -0
  205. /package/dist/assets/{lit-vendor-Dr3cpBGF.js → lit-vendor-DvaQX_UQ.js} +0 -0
  206. /package/dist/assets/{logger-B65Akg8A.js → logger-8REuLPGK.js} +0 -0
  207. /package/dist/assets/{path-BAQ3hXlG.js → path-DIKpVbHL.js} +0 -0
  208. /package/dist/assets/{plugin-api-BbUkYm3Y.js → plugin-api-C8hMT5W6.js} +0 -0
  209. /package/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-BMOtDaaS.js} +0 -0
  210. /package/dist/assets/{xterm-CGiQzDiR.js → xterm-BFzZ1FH4.js} +0 -0
@@ -1,1495 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AgentProfilesPage-BFgyFa5e.js","assets/rolldown-runtime-DWdDZTNf.js","assets/index-yV1wtqTr.js","assets/pi-ai-Cr3rpCXT.js","assets/pi-web-ui-DoRGtREc.js","assets/lit-vendor-Dr3cpBGF.js","assets/css-utils-rkE68RDy.js","assets/icons-BP8YOS-Z.js","assets/react-vendor-Dr5xvL-e.js","assets/skills-dialog-DiJAUfvW.js","assets/logger-B65Akg8A.js","assets/index-Cu2bBHLv.css","assets/mcp-servers-dialog-Bp6kbIup.js","assets/PluginsPage-DEMqwhOA.js","assets/plugin-api-BbUkYm3Y.js","assets/ScheduledTasksPage-DTP_gedp.js"])))=>i.map(i=>d[i]);
2
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{A as t,Dt as n,Et as r,H as i,Mt as a,Ot as o,Pt as s,Tt as c,_ as ee,f as l,ft as u,h as d,jt as te,l as f,ot as p,s as m,st as ne,x as re}from"./icons-BP8YOS-Z.js";import{n as ie,r as ae}from"./react-vendor-Dr5xvL-e.js";import{p as h}from"./lit-vendor-Dr3cpBGF.js";import{m as g,x as _}from"./pi-web-ui-DoRGtREc.js";import{J as v}from"./pi-ai-Cr3rpCXT.js";import{t as y}from"./logger-B65Akg8A.js";import{B as b,C as oe,D as se,E as ce,F as le,I as ue,O as x,P as de,R as fe,S as pe,T as me,V as S,_t as he,at as ge,ct as _e,ft as ve,gt as ye,ht as C,j as be,k as w,lt as T,mt as xe,nt as Se,rt as Ce,st as we,tt as E,ut as Te,vt as Ee,w as De,yt as D,z as Oe}from"./index-yV1wtqTr.js";var O=e(s(),1),ke={openai:{name:`OpenAI`,baseUrl:`https://api.openai.com/v1`,protocol:`openai-completions`,modelId:`gpt-4o`,supportsImages:!0},deepseek:{name:`DeepSeek`,baseUrl:`https://api.deepseek.com/v1`,protocol:`openai-completions`,modelId:`deepseek-chat`},glm:{name:`Zhipu GLM`,baseUrl:`https://open.bigmodel.cn/api/paas/v4`,protocol:`openai-completions`,modelId:`glm-4-plus`},ollama:{name:`Ollama`,baseUrl:`http://localhost:11434/v1`,protocol:`openai-completions`,modelId:`llama3.1`},litellm:{name:E.name,baseUrl:E.baseUrl,protocol:`openai-completions`,modelId:E.modelId,supportsImages:E.supportsImages}},Ae=[{key:`openai`,label:D(`presetOpenai`)},{key:`deepseek`,label:D(`presetDeepseek`)},{key:`glm`,label:D(`presetGlm`)},{key:`ollama`,label:D(`presetOllama`)},{key:`litellm`,label:D(`presetLitellm`)},{key:`custom`,label:D(`presetCustom`)}],k=()=>({modelId:``,contextWindow:E.contextWindow,maxTokens:E.maxTokens,reasoning:!0,supportsImages:!1,open:!1}),A=()=>({name:E.name,baseUrl:E.baseUrl,apiKey:``,headerRows:[],protocol:`openai-completions`,models:[k()]}),je=class extends g{providers=[];form=A();editingProviderId;formOpen=!1;loading=!0;apiKeyVisible=!1;advancedOpen=!1;activePreset=``;testing=!1;testResult=`idle`;testError=``;autoEditProviderName=null;async connectedCallback(){if(super.connectedCallback(),await this.loadProviders(),this.autoEditProviderName){let e=this.providers.find(e=>e.name===this.autoEditProviderName);e&&await this.openEditForm(e),this.autoEditProviderName=null}}getTabName(){return D(`customModels`)}async loadProviders(){this.loading=!0,this.requestUpdate();try{this.providers=await _().customProviders.getAll()}catch(e){y.error(`Failed to load custom providers:`,e),this.providers=[]}finally{this.loading=!1,this.requestUpdate()}}resetTestState(){this.testing=!1,this.testResult=`idle`,this.testError=``}openAddForm(){this.editingProviderId=void 0,this.form=A(),this.apiKeyVisible=!1,this.advancedOpen=!1,this.activePreset=``,this.resetTestState(),this.formOpen=!0,this.requestUpdate()}async openEditForm(e){let t=await _().providerKeys.get(e.name)??e.apiKey??``,n=e.models??[],r=n.length>0?n.map(e=>({modelId:e.id,contextWindow:e.contextWindow??E.contextWindow,maxTokens:e.maxTokens??E.maxTokens,reasoning:e.reasoning===!0,supportsImages:e.input?.includes(`image`)===!0,open:!1})):[k()],i=n[0]?.headers??{},a=Object.entries(i).map(([e,t])=>({key:e,value:String(t)}));this.editingProviderId=e.id,this.form={providerId:e.id,id:e.id,name:e.name,baseUrl:e.baseUrl,apiKey:t,headerRows:a,protocol:e.type===`anthropic-messages`?`anthropic-messages`:`openai-completions`,models:r},this.apiKeyVisible=!1,this.advancedOpen=a.length>0||e.type===`anthropic-messages`,this.activePreset=``,this.resetTestState(),this.formOpen=!0,this.requestUpdate()}closeForm(){this.formOpen=!1,this.editingProviderId=void 0,this.form=A(),this.apiKeyVisible=!1,this.advancedOpen=!1,this.activePreset=``,this.resetTestState(),this.requestUpdate()}toggleApiKeyVisibility(){this.apiKeyVisible=!this.apiKeyVisible,this.requestUpdate()}updateForm(e,t){this.form={...this.form,[e]:t},(e===`name`||e===`baseUrl`)&&(this.activePreset=``),this.resetTestState(),this.requestUpdate()}updateModelField(e,t,n){let r=this.form.models.map((r,i)=>i===e?{...r,[t]:n}:r);this.form={...this.form,models:r},t===`modelId`&&(this.activePreset=``,this.resetTestState()),this.requestUpdate()}addModelRow(){this.form={...this.form,models:[...this.form.models,k()]},this.requestUpdate()}removeModelRow(e){let t=this.form.models.filter((t,n)=>n!==e);this.form={...this.form,models:t},this.requestUpdate()}toggleModelExpanded(e){let t=this.form.models.map((t,n)=>n===e?{...t,open:!t.open}:t);this.form={...this.form,models:t},this.requestUpdate()}addHeaderRow(){this.form={...this.form,headerRows:[...this.form.headerRows,{key:``,value:``}]},this.requestUpdate()}updateHeaderRow(e,t,n){let r=this.form.headerRows.map((r,i)=>i===e?{...r,[t]:n}:r);this.form={...this.form,headerRows:r},this.requestUpdate()}removeHeaderRow(e){let t=this.form.headerRows.filter((t,n)=>n!==e);this.form={...this.form,headerRows:t},this.requestUpdate()}applyPreset(e){if(this.activePreset=e,e===`custom`)this.form={...this.form,name:``,baseUrl:``,protocol:`openai-completions`,models:[k()]};else{let t=ke[e];this.form={...this.form,name:t.name,baseUrl:t.baseUrl,protocol:t.protocol,models:[{...k(),modelId:t.modelId,supportsImages:t.supportsImages===!0,open:!1}]}}this.resetTestState(),this.requestUpdate()}buildHeadersFromRows(){let e={};for(let t of this.form.headerRows){let n=t.key.trim(),r=t.value.trim();n&&r&&(e[n]=r)}return e}buildModel(e,t){let n=this.form.name.trim(),r=this.form.baseUrl.trim(),i=e.reasoning===!0,a=e.supportsImages===!0?[`text`,`image`]:[`text`],o=r.includes(`api.deepseek.com`);return we({id:e.modelId,name:`${e.modelId} (${n})`,api:this.form.protocol,provider:n,baseUrl:r.replace(/\/$/,``),reasoning:i,input:a,cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:Number(e.contextWindow)||E.contextWindow,maxTokens:Number(e.maxTokens)||E.maxTokens,headers:Object.keys(t).length>0?t:void 0,thinkingLevelMap:o&&i?{low:`high`,medium:`high`,high:`high`,xhigh:`max`}:void 0,compat:this.form.protocol===`openai-completions`?{supportsStore:!1,supportsDeveloperRole:!1,supportsReasoningEffort:i,supportsUsageInStreaming:!1,supportsStrictMode:!1,maxTokensField:`max_tokens`,...o&&i?{requiresReasoningContentOnAssistantMessages:!0,thinkingFormat:`deepseek`}:{}}:void 0})}async testConnection(){let e=this.form.baseUrl.trim(),t=this.form.models.find(e=>e.modelId.trim());if(!e||!t){this.testing=!1,this.testResult=`fail`,this.testError=D(`testRequiresFields`),this.requestUpdate();return}let n=this.buildHeadersFromRows(),r=this.buildModel(t,n);this.testing=!0,this.testResult=`idle`,this.testError=``,this.requestUpdate();try{let e=await(await fetch(`/api/models/test-connection`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({model:r,apiKey:this.form.apiKey.trim()})})).json().catch(()=>({}));e?.ok?(this.testResult=`ok`,this.testError=``):(this.testResult=`fail`,this.testError=e?.error||D(`connectionFailed`))}catch(e){this.testResult=`fail`,this.testError=e?.message||D(`connectionFailed`)}finally{this.testing=!1,this.requestUpdate()}}async saveModel(){let e=this.form.name.trim(),t=this.form.baseUrl.trim();if(!e||!t){b(D(`fillProviderBaseUrlModel`));return}let n=this.form.models.filter(e=>e.modelId.trim());if(n.length===0){b(D(`atLeastOneModel`));return}let r=n.map(e=>e.modelId.trim());if(new Set(r).size!==r.length){b(D(`duplicateModelId`));return}let i=this.buildHeadersFromRows(),a=n.map(e=>this.buildModel(e,i)),o=this.form.apiKey.trim(),s=this.editingProviderId?this.providers.find(e=>e.id===this.editingProviderId):void 0,c={id:this.editingProviderId??Te(),name:e,type:this.form.protocol,baseUrl:a[0].baseUrl,apiKey:o||void 0,models:a};try{let e=_();await e.customProviders.set(c),s&&s.name!==c.name&&await e.providerKeys.delete(s.name),o?await e.providerKeys.set(c.name,o):await e.providerKeys.delete(c.name),this.closeForm(),await this.loadProviders()}catch(e){y.error(`Failed to save custom model:`,e),b(D(`saveCustomModelFailed`))}}async deleteProvider(e){if(await S({description:D(`confirmDeleteProvider`,{name:e.name}),confirmLabel:D(`confirmDelete`),cancelLabel:D(`cancel`),variant:`destructive`}))try{let t=_();await t.customProviders.delete(e.id),await t.providerKeys.delete(e.name),await this.loadProviders()}catch(e){y.error(`Failed to delete custom provider:`,e),b(D(`deleteFailed`))}}renderProvider(e){let t=e.models??[],n=t.length,r=e.type===`anthropic-messages`?`Anthropic Messages`:`OpenAI Compatible`;return h`
3
- <div class="quickforge-settings-list-item">
4
- <div class="quickforge-settings-list-item-main">
5
- <div class="flex min-w-0 items-start gap-3">
6
- <span class="quickforge-settings-avatar" aria-hidden="true">${e.name.trim().charAt(0).toUpperCase()||`M`}</span>
7
- <div class="min-w-0 flex-1">
8
- <div class="quickforge-settings-row-title">${e.name}</div>
9
- <div class="quickforge-settings-row-description break-all">${e.baseUrl}</div>
10
- <div class="quickforge-settings-meta">
11
- <span class="quickforge-settings-badge quickforge-settings-badge-muted">${D(`providerProtocol`)}: ${r}</span>
12
- <span class="quickforge-settings-badge quickforge-settings-badge-info">${D(`modelsCount`,{count:n})}</span>
13
- ${n===0?h`<span class="quickforge-settings-badge quickforge-settings-badge-warning">${D(`noModelAdded`)}</span>`:null}
14
- </div>
15
- ${n>0?h`
16
- <div class="quickforge-settings-meta">
17
- ${t.slice(0,6).map(e=>h`
18
- <code class="quickforge-settings-command-name">${e.id}</code>
19
- `)}
20
- ${t.length>6?h`<span class="quickforge-settings-badge quickforge-settings-badge-muted">+${t.length-6}</span>`:null}
21
- </div>
22
- `:null}
23
- </div>
24
- </div>
25
- </div>
26
- <div class="quickforge-settings-list-item-actions">
27
- <button
28
- class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
29
- type="button"
30
- @click=${()=>this.openEditForm(e)}
31
- >
32
- ${D(`editModel`)}
33
- </button>
34
- <button
35
- class="quickforge-settings-button quickforge-settings-button-danger quickforge-settings-button-compact"
36
- type="button"
37
- @click=${()=>this.deleteProvider(e)}
38
- >
39
- ${D(`delete`)}
40
- </button>
41
- </div>
42
- </div>
43
- `}renderPresetChips(){return h`
44
- <div class="quickforge-settings-form-row">
45
- <span class="quickforge-settings-form-label">${D(`presets`)}</span>
46
- <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap">
47
- ${Ae.map(e=>h`
48
- <button
49
- class="quickforge-settings-segmented-option ${this.activePreset===e.key?`quickforge-settings-segmented-option-active`:``}"
50
- type="button"
51
- @click=${()=>this.applyPreset(e.key)}
52
- >
53
- ${e.label}
54
- </button>
55
- `)}
56
- </div>
57
- </div>
58
- `}renderModelRow(e,t){let n=e.open===!0;return h`
59
- <div class="quickforge-settings-subrow quickforge-settings-row-align-start">
60
- <button
61
- class="quickforge-settings-icon-action"
62
- type="button"
63
- title=${D(`expandModel`)}
64
- aria-expanded=${n?`true`:`false`}
65
- @click=${()=>this.toggleModelExpanded(t)}
66
- >
67
- ${n?`▾`:`▸`}
68
- </button>
69
- <div class="quickforge-settings-list-item-main">
70
- <input
71
- class="quickforge-settings-input quickforge-settings-mono"
72
- .value=${e.modelId}
73
- @input=${e=>this.updateModelField(t,`modelId`,e.target.value)}
74
- placeholder=${D(`modelIdPlaceholder`)}
75
- />
76
- ${n?h`
77
- <div class="quickforge-settings-model-grid mt-3">
78
- <label class="quickforge-settings-form-row">
79
- <span class="quickforge-settings-form-label">${D(`contextWindow`)}</span>
80
- <input
81
- class="quickforge-settings-input"
82
- .value=${String(e.contextWindow)}
83
- type="number"
84
- @input=${e=>this.updateModelField(t,`contextWindow`,Number(e.target.value))}
85
- />
86
- </label>
87
- <label class="quickforge-settings-form-row">
88
- <span class="quickforge-settings-form-label">${D(`maxTokens`)}</span>
89
- <input
90
- class="quickforge-settings-input"
91
- .value=${String(e.maxTokens)}
92
- type="number"
93
- @input=${e=>this.updateModelField(t,`maxTokens`,Number(e.target.value))}
94
- />
95
- </label>
96
- </div>
97
- <label class="mt-3 flex items-center gap-2 text-sm text-muted-foreground">
98
- <input
99
- type="checkbox"
100
- .checked=${e.reasoning}
101
- @change=${e=>this.updateModelField(t,`reasoning`,e.target.checked)}
102
- />
103
- <span>${D(`reasoningModel`)}</span>
104
- </label>
105
- <label class="mt-2 flex items-center gap-2 text-sm text-muted-foreground">
106
- <input
107
- type="checkbox"
108
- .checked=${e.supportsImages}
109
- @change=${e=>this.updateModelField(t,`supportsImages`,e.target.checked)}
110
- />
111
- <span>
112
- ${D(`imageInputModel`)}
113
- <quickforge-info-tip .label=${D(`imageInputModelHelp`)}></quickforge-info-tip>
114
- </span>
115
- </label>
116
- `:null}
117
- </div>
118
- ${this.form.models.length>1?h`
119
- <button
120
- class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
121
- type="button"
122
- title=${D(`delete`)}
123
- @click=${()=>this.removeModelRow(t)}
124
- >
125
-
126
- </button>
127
- `:null}
128
- </div>
129
- `}renderHeadersEditor(){return h`
130
- <div class="quickforge-settings-form-row">
131
- <span class="quickforge-settings-form-label">
132
- ${D(`customHeaders`)}
133
- <quickforge-info-tip .label=${D(`customHeadersHelp`)}></quickforge-info-tip>
134
- </span>
135
- ${this.form.headerRows.length===0?h``:h`
136
- <div class="quickforge-settings-nested-list">
137
- ${this.form.headerRows.map((e,t)=>h`
138
- <div class="quickforge-settings-subrow">
139
- <input
140
- class="quickforge-settings-input"
141
- .value=${e.key}
142
- @input=${e=>this.updateHeaderRow(t,`key`,e.target.value)}
143
- placeholder=${D(`headerName`)}
144
- />
145
- <input
146
- class="quickforge-settings-input"
147
- .value=${e.value}
148
- @input=${e=>this.updateHeaderRow(t,`value`,e.target.value)}
149
- placeholder=${D(`headerValue`)}
150
- />
151
- <button
152
- class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
153
- type="button"
154
- title=${D(`removeHeader`)}
155
- @click=${()=>this.removeHeaderRow(t)}
156
- >
157
-
158
- </button>
159
- </div>
160
- `)}
161
- </div>
162
- `}
163
- <button
164
- class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
165
- type="button"
166
- @click=${()=>this.addHeaderRow()}
167
- >
168
- + ${D(`addHeader`)}
169
- </button>
170
- </div>
171
- `}renderTestStatus(){return this.testResult===`idle`&&!this.testing?h``:this.testing?h`<span class="quickforge-settings-status">${D(`testingConnection`)}</span>`:this.testResult===`ok`?h`<span class="quickforge-settings-badge quickforge-settings-badge-success">✓ ${D(`connectionOk`)}</span>`:h`<span class="quickforge-settings-alert">✗ ${this.testError||D(`connectionFailed`)}</span>`}renderForm(){return h`
172
- <section class="quickforge-settings-section" aria-label=${this.editingProviderId?D(`editCustomModel`):D(`addCustomModel`)}>
173
- <div class="quickforge-settings-toolbar">
174
- <button
175
- class="quickforge-settings-button quickforge-settings-button-secondary"
176
- type="button"
177
- @click=${()=>this.closeForm()}
178
- >
179
- <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
180
- ${D(`back`)}
181
- </button>
182
- <div class="quickforge-settings-row-main">
183
- <div class="quickforge-settings-row-title">${this.editingProviderId?D(`editCustomModel`):D(`addCustomModel`)}</div>
184
- <div class="quickforge-settings-row-description">${D(`customModelsDescription`)}</div>
185
- </div>
186
- </div>
187
-
188
- <div class="quickforge-settings-form-grid">
189
- ${this.renderPresetChips()}
190
-
191
- <label class="quickforge-settings-form-row">
192
- <span class="quickforge-settings-form-label">${D(`providerName`)}</span>
193
- <input
194
- class="quickforge-settings-input"
195
- .value=${this.form.name}
196
- @input=${e=>this.updateForm(`name`,e.target.value)}
197
- placeholder=${D(`providerNamePlaceholder`)}
198
- />
199
- </label>
200
-
201
- <label class="quickforge-settings-form-row">
202
- <span class="quickforge-settings-form-label">Base URL</span>
203
- <input
204
- class="quickforge-settings-input"
205
- .value=${this.form.baseUrl}
206
- @input=${e=>this.updateForm(`baseUrl`,e.target.value)}
207
- placeholder=${this.form.protocol===`anthropic-messages`?`e.g., https://api.anthropic.com`:`e.g., http://localhost:4000/v1`}
208
- />
209
- </label>
210
-
211
- <label class="quickforge-settings-form-row">
212
- <span class="quickforge-settings-form-label">${D(`apiKey`)}</span>
213
- <div class="quickforge-settings-inline-field">
214
- <input
215
- class="quickforge-settings-input pr-10"
216
- .value=${this.form.apiKey}
217
- type=${this.apiKeyVisible?`text`:`password`}
218
- @input=${e=>this.updateForm(`apiKey`,e.target.value)}
219
- placeholder=${D(`apiKeyPlaceholder`)}
220
- />
221
- <button
222
- class="quickforge-settings-inline-field-button"
223
- type="button"
224
- title=${this.apiKeyVisible?D(`hideApiKey`):D(`showApiKey`)}
225
- aria-label=${this.apiKeyVisible?D(`hideApiKey`):D(`showApiKey`)}
226
- aria-pressed=${this.apiKeyVisible?`true`:`false`}
227
- @click=${()=>this.toggleApiKeyVisibility()}
228
- >
229
- ${this.apiKeyVisible?h`<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" y1="2" x2="22" y2="22"/><path d="M8.53 8.53A5 5 0 0 0 12 17a5 5 0 0 0 3.47-8.53"/></svg>`:h`<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>`}
230
- </button>
231
- </div>
232
- </label>
233
-
234
- <div class="quickforge-settings-form-row">
235
- <div class="flex items-center justify-between gap-3">
236
- <span class="quickforge-settings-form-label">${D(`modelsList`)}</span>
237
- <button
238
- class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
239
- type="button"
240
- @click=${()=>this.addModelRow()}
241
- >
242
- + ${D(`addModel`)}
243
- </button>
244
- </div>
245
- <div class="quickforge-settings-nested-list">
246
- ${this.form.models.map((e,t)=>this.renderModelRow(e,t))}
247
- </div>
248
- </div>
249
-
250
- <button
251
- class="quickforge-settings-details-toggle"
252
- type="button"
253
- aria-expanded=${this.advancedOpen?`true`:`false`}
254
- @click=${()=>{this.advancedOpen=!this.advancedOpen,this.requestUpdate()}}
255
- >
256
- <span>${this.advancedOpen?`▾`:`▸`}</span>
257
- ${D(`providerAdvanced`)}
258
- </button>
259
-
260
- ${this.advancedOpen?h`
261
- <div class="quickforge-settings-form-grid rounded-lg border border-border p-3">
262
- <label class="quickforge-settings-form-row">
263
- <span class="quickforge-settings-form-label">
264
- ${D(`protocolType`)}
265
- <quickforge-info-tip .label=${D(`protocolHelp`)}></quickforge-info-tip>
266
- </span>
267
- <select
268
- class="quickforge-settings-select"
269
- .value=${this.form.protocol}
270
- @change=${e=>this.updateForm(`protocol`,e.target.value)}
271
- >
272
- <option value="openai-completions">OpenAI Compatible / Chat Completions</option>
273
- <option value="anthropic-messages">Anthropic Messages</option>
274
- </select>
275
- </label>
276
- ${this.renderHeadersEditor()}
277
- </div>
278
- `:null}
279
- </div>
280
-
281
- <div class="quickforge-settings-row">
282
- <div class="quickforge-settings-row-main">
283
- ${this.renderTestStatus()}
284
- </div>
285
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
286
- <button
287
- class="quickforge-settings-button quickforge-settings-button-secondary"
288
- type="button"
289
- @click=${()=>this.closeForm()}
290
- >
291
- ${D(`cancel`)}
292
- </button>
293
- <button
294
- class="quickforge-settings-button quickforge-settings-button-secondary"
295
- type="button"
296
- ?disabled=${this.testing}
297
- @click=${()=>this.testConnection()}
298
- >
299
- ${this.testing?D(`testingConnection`):D(`testConnection`)}
300
- </button>
301
- <button
302
- class="quickforge-settings-button quickforge-settings-button-primary"
303
- type="button"
304
- @click=${()=>this.saveModel()}
305
- >
306
- ${D(`save`)}
307
- </button>
308
- </div>
309
- </div>
310
- </section>
311
- `}render(){return this.formOpen?h`
312
- <div class="quickforge-settings-stack">
313
- <div class="quickforge-settings-heading">
314
- <h3 class="quickforge-settings-title">
315
- ${this.editingProviderId?D(`editCustomModel`):D(`addCustomModel`)}
316
- <quickforge-info-tip .label=${D(`customModelsDescription`)}></quickforge-info-tip>
317
- </h3>
318
- </div>
319
- ${this.renderForm()}
320
- </div>
321
- `:h`
322
- <div class="quickforge-settings-stack">
323
- <section class="quickforge-settings-section" aria-label=${D(`customModelsTitle`)}>
324
- <div class="quickforge-settings-toolbar">
325
- <div>
326
- <div class="quickforge-settings-row-title">${D(`customModelsTitle`)}</div>
327
- <div class="quickforge-settings-row-description">${D(`customModelsDescription`)}</div>
328
- </div>
329
- <button
330
- class="quickforge-settings-button quickforge-settings-button-primary"
331
- type="button"
332
- @click=${()=>this.openAddForm()}
333
- >
334
- ${D(`addModel`)}
335
- </button>
336
- </div>
337
-
338
- ${this.loading?h`<div class="quickforge-settings-empty-row">${D(`loading`)}</div>`:this.providers.length===0?h`<div class="quickforge-settings-empty-row">${D(`noCustomModels`)}</div>`:this.providers.map(e=>this.renderProvider(e))}
339
- </section>
340
- </div>
341
- `}},j=`quickforge-custom-providers-only-tab`;customElements.get(j)||customElements.define(j,je);function Me(e){let t=document.createElement(j);return e&&(t.autoEditProviderName=e),t}var Ne=[{value:`light`,label:()=>D(`lightTheme`)},{value:`dark`,label:()=>D(`darkTheme`)}],Pe=class extends g{theme=me();interfaceFontSizePx=x.interfaceFontSizePx;messageFontSizePx=x.messageFontSizePx;loading=!0;error=``;getTabName(){return D(`appearance`)}async connectedCallback(){super.connectedCallback(),await this.loadSettings()}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=_(),[t,n]=await Promise.all([ce(e),de(e)]);this.theme=t.theme,this.interfaceFontSizePx=n.interfaceFontSizePx,this.messageFontSizePx=n.messageFontSizePx}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async selectTheme(e){if(this.theme!==e){this.theme=e,this.requestUpdate();try{await se(_(),{theme:e}),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}}currentFontSizeSettings(){return le({interfaceFontSizePx:this.interfaceFontSizePx,messageFontSizePx:this.messageFontSizePx})}previewFontSize(e){let t=le(e);this.interfaceFontSizePx=t.interfaceFontSizePx,this.messageFontSizePx=t.messageFontSizePx,be(t),this.requestUpdate()}updateInterfaceFontSize(e){this.previewFontSize({...this.currentFontSizeSettings(),interfaceFontSizePx:Number(e)||x.interfaceFontSizePx})}updateMessageFontSize(e){this.previewFontSize({...this.currentFontSizeSettings(),messageFontSizePx:Number(e)||x.messageFontSizePx})}async saveFontSize(){try{await ue(_(),this.currentFontSizeSettings()),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}renderThemeOption(e){let t=this.theme===e.value;return h`
342
- <button
343
- type="button"
344
- class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
345
- aria-pressed=${t?`true`:`false`}
346
- @click=${()=>this.selectTheme(e.value)}
347
- >
348
- ${e.label()}
349
- </button>
350
- `}getFontSizeRangeProgress(e){return(e-w.min)/(w.max-w.min)*100}renderFontSizeSlider(e,t,n,r){return h`
351
- <div class="quickforge-settings-row">
352
- <div class="quickforge-settings-row-main">
353
- <div class="quickforge-settings-row-title">
354
- ${e}
355
- ${t?h`<quickforge-info-tip .label=${t}></quickforge-info-tip>`:null}
356
- </div>
357
- <div class="quickforge-settings-row-description">${D(`fontSizeRangeDescription`)}</div>
358
- </div>
359
- <div class="quickforge-settings-row-control quickforge-settings-slider-control">
360
- <span class="quickforge-settings-value-badge">${n}px</span>
361
- <input
362
- class="quickforge-font-size-slider"
363
- style=${`--quickforge-font-size-slider-progress: ${this.getFontSizeRangeProgress(n)}%`}
364
- type="range"
365
- min=${String(w.min)}
366
- max=${String(w.max)}
367
- step="1"
368
- .value=${String(n)}
369
- @input=${e=>r(e.target.value)}
370
- @change=${()=>this.saveFontSize()}
371
- />
372
- </div>
373
- </div>
374
- `}render(){return this.loading?h`<div class="text-sm text-muted-foreground">${D(`loading`)}</div>`:h`
375
- <div class="quickforge-settings-stack">
376
- <section class="quickforge-settings-section" aria-label=${D(`appearance`)}>
377
- <div class="quickforge-settings-row">
378
- <div class="quickforge-settings-row-main">
379
- <div class="quickforge-settings-row-title">
380
- ${D(`theme`)}
381
- <quickforge-info-tip .label=${D(`themeDescription`)}></quickforge-info-tip>
382
- </div>
383
- <div class="quickforge-settings-row-description">${D(`themeDescription`)}</div>
384
- </div>
385
- <div class="quickforge-settings-row-control">
386
- <div class="quickforge-settings-segmented" role="group" aria-label=${D(`theme`)}>
387
- ${Ne.map(e=>this.renderThemeOption(e))}
388
- </div>
389
- </div>
390
- </div>
391
-
392
- ${this.renderFontSizeSlider(D(`interfaceFontSize`),null,this.interfaceFontSizePx,e=>this.updateInterfaceFontSize(e))}
393
-
394
- ${this.renderFontSizeSlider(D(`messageFontSize`),D(`messageFontSizeNote`),this.messageFontSizePx,e=>this.updateMessageFontSize(e))}
395
- </section>
396
-
397
- ${this.error?h`<span class="quickforge-settings-error">${this.error}</span>`:null}
398
- </div>
399
- `}},M=`quickforge-appearance-settings-tab`;customElements.get(M)||customElements.define(M,Pe);function Fe(){return document.createElement(M)}var Ie=`auto-compact-settings`,N={enabled:!0,thresholdPercent:80,keepRecentTurns:3,minSourceChars:1600,requireConfirmation:!0};function P(e,t,n,r){let i=Number(e);return Number.isFinite(i)?Math.min(r,Math.max(n,Math.round(i))):t}function Le(e){if(!e||typeof e!=`object`)return{...N};let t=e;return{enabled:t.enabled!==!1,thresholdPercent:P(t.thresholdPercent,N.thresholdPercent,50,95),keepRecentTurns:P(t.keepRecentTurns,N.keepRecentTurns,1,20),minSourceChars:P(t.minSourceChars,N.minSourceChars,0,2e5),requireConfirmation:t.requireConfirmation!==!1}}async function Re(e){return Le(await e.settings.get(Ie))}async function ze(e,t){await e.settings.set(Ie,Le(t))}var Be=`auto-archive-settings`,Ve={enabled:!1};function He(e){return!e||typeof e!=`object`?{...Ve}:{enabled:e.enabled===!0}}async function Ue(e){return He(await e.settings.get(Be))}async function We(e,t){await e.settings.set(Be,He(t))}var Ge=[{value:`off`,label:()=>D(`thinkingOff`)},{value:`low`,label:()=>D(`thinkingLow`)},{value:`medium`,label:()=>D(`thinkingMedium`)},{value:`high`,label:()=>D(`thinkingHigh`)},{value:`xhigh`,label:()=>D(`thinkingXHigh`)}],Ke=[{value:`compact`,label:()=>D(`toolDisplayCompact`)},{value:`detailed`,label:()=>D(`toolDisplayDetailed`)}],F=`__custom__`;function qe(e){return(e??``).trim().replace(/\/$/,``)}function I(e){return JSON.stringify([e.provider,e.id,e.api,qe(e.baseUrl)])}function Je(e){return`${e.provider} / ${e.id}`}function Ye(){return`custom_${globalThis.crypto?.randomUUID?.().slice(0,8)||Date.now().toString(36)}`}function Xe(e){let t=e.trim(),n=t.split(/[\\/]/).pop()?.replace(/^"|"$/g,``)||t;return/^bash(\.exe)?$/i.test(n)?`Bash`:/^zsh$/i.test(n)?`Zsh`:/^fish$/i.test(n)?`Fish`:/^cmd(\.exe)?$/i.test(n)?`Command Prompt`:/^powershell(\.exe)?$/i.test(n)?`Windows PowerShell`:/^pwsh(\.exe)?$/i.test(n)?`PowerShell 7+`:n||`Custom Shell`}var Ze=h`
400
- <svg class="size-3.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
401
- <path d="M4.5 4.5 11.5 11.5M11.5 4.5 4.5 11.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" />
402
- </svg>
403
- `,Qe=class extends g{models=[];selectedModel;thinkingLevel=`off`;toolDisplayMode=`compact`;showContextUsage=!1;autoCompactEnabled=!0;autoCompactRequireConfirmation=!0;autoCompactThresholdPercent=80;autoCompactThresholdPercentInput=`80`;autoCompactKeepRecentTurns=3;autoArchiveEnabled=!1;selectedLanguage=he();loading=!0;saved=!1;savedMessage=``;error=``;terminalShellConfig={terminalShell:`auto`,defaultProfileId:`auto`,profiles:[]};customShellCommand=``;customShellEditorOpen=!1;getTabName(){return D(`defaultOptions`)}async connectedCallback(){super.connectedCallback(),await this.loadSettings()}updated(){this.syncSelectValues()}syncSelectValues(){let e=this.querySelector(`[data-default-model-select]`);e&&this.selectedModel&&(e.value=I(this.selectedModel));let t=this.querySelector(`[data-default-thinking-select]`);t&&(t.value=this.thinkingLevel);let n=this.querySelector(`[data-language-select]`);n&&(n.value=this.selectedLanguage);let r=this.querySelector(`[data-terminal-shell-select]`);r&&(r.value=this.customShellEditorOpen?F:this.selectedTerminalShellProfileId())}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=_(),[t,n,r,i,a]=await Promise.all([Ce(e),ge(e),fe(e),Re(e),Ue(e)]);this.models=t,this.selectedModel=n.model?t.find(e=>I(e)===I(n.model))??n.model:t[0],this.thinkingLevel=n.thinkingLevel??Se(this.selectedModel),this.toolDisplayMode=r.toolDisplayMode,this.showContextUsage=r.showContextUsage,this.autoCompactEnabled=i.enabled,this.autoCompactRequireConfirmation=i.requireConfirmation,this.autoCompactThresholdPercent=i.thresholdPercent,this.autoCompactThresholdPercentInput=String(i.thresholdPercent),this.autoCompactKeepRecentTurns=i.keepRecentTurns,this.autoArchiveEnabled=a.enabled,await this.loadTerminalShell()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}updateModel(e){let t=this.models.find(t=>I(t)===e);this.selectedModel=t,this.thinkingLevel=Se(t),this.saved=!1,this.requestUpdate(),this.saveDefaultModelOptions()}updateThinkingLevel(e){this.thinkingLevel=Ge.some(t=>t.value===e)?e:`off`,this.saved=!1,this.requestUpdate(),this.saveDefaultModelOptions()}updateLanguage(e){this.selectedLanguage=e===`zh`?`zh`:`en`,this.requestUpdate(),this.applyLanguage()}async applyLanguage(){await ye(_(),this.selectedLanguage)}updateToolDisplayMode(e){this.toolDisplayMode!==e&&(this.toolDisplayMode=e,this.saved=!1,this.requestUpdate(),this.saveToolDisplayOptions())}updateShowContextUsage(e){this.showContextUsage=e,this.saved=!1,this.requestUpdate(),this.saveToolDisplayOptions()}updateAutoCompactEnabled(e){this.autoCompactEnabled=e,this.saved=!1,this.requestUpdate(),this.saveAutoCompactOptions()}updateAutoCompactRequireConfirmation(e){this.autoCompactRequireConfirmation=e,this.saved=!1,this.requestUpdate(),this.saveAutoCompactOptions()}updateAutoArchiveEnabled(e){this.autoArchiveEnabled=e,this.saved=!1,this.requestUpdate(),this.saveAutoArchiveOptions()}updateAutoCompactThresholdPercent(e){this.autoCompactThresholdPercentInput=e;let t=Number(e);e!==``&&Number.isFinite(t)&&(this.autoCompactThresholdPercent=t),this.saved=!1,this.requestUpdate()}updateAutoCompactKeepRecentTurns(e){this.autoCompactKeepRecentTurns=Number(e)||3,this.saved=!1,this.requestUpdate()}commitAutoCompactThresholdPercent(){let e=Number(this.autoCompactThresholdPercentInput);if(!Number.isFinite(e)){this.autoCompactThresholdPercentInput=String(this.autoCompactThresholdPercent),this.requestUpdate();return}let t=Math.max(50,Math.min(95,Math.round(e)));this.autoCompactThresholdPercent=t,this.autoCompactThresholdPercentInput=String(t),this.requestUpdate(),this.saveAutoCompactOptions()}commitAutoCompactKeepRecentTurns(){let e=Math.max(1,Math.min(20,Math.round(this.autoCompactKeepRecentTurns||3)));this.autoCompactKeepRecentTurns=e,this.requestUpdate(),this.saveAutoCompactOptions()}markSaved(e=D(`defaultOptionsSaved`)){this.saved=!0,this.savedMessage=e,this.error=``,this.requestUpdate()}customShellProfiles(){return this.terminalShellConfig.profiles.filter(e=>!e.builtin)}selectedTerminalShellProfileId(){let e=this.terminalShellConfig.profiles;return e.some(e=>e.id===this.terminalShellConfig.defaultProfileId)?this.terminalShellConfig.defaultProfileId:e[0]?.id||F}updateTerminalShellSelection(e){if(e===F){this.customShellEditorOpen=!0,this.requestUpdate();return}this.customShellEditorOpen=!1,this.requestUpdate(),this.saveTerminalShellConfig(e)}async loadTerminalShell(){try{let e=await fetch(`/api/system/terminal-shell`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||D(`requestFailed`));this.terminalShellConfig={terminalShell:t?.terminalShell||`auto`,defaultProfileId:t?.defaultProfileId||`auto`,profiles:Array.isArray(t?.profiles)?t.profiles:[]}}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}}async saveTerminalShellConfig(e,t=this.customShellProfiles(),n=D(`terminalShellSaved`)){try{let r=await fetch(`/api/system/terminal-shell`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({defaultProfileId:e,profiles:t})}),i=await r.json().catch(()=>null);if(!r.ok)throw Error(i?.error||D(`requestFailed`));this.terminalShellConfig=i,this.markSaved(n)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}async addCustomTerminalShell(){let e=this.customShellCommand.trim();if(!e){this.error=D(`terminalShellProfileRequired`),this.requestUpdate();return}let t={id:Ye(),name:Xe(e),command:e,builtin:!1},n=[...this.customShellProfiles(),t];this.customShellCommand=``,this.customShellEditorOpen=!1,await this.saveTerminalShellConfig(t.id,n,D(`terminalShellProfilesSaved`))}async deleteCustomTerminalShell(e){let t=this.terminalShellConfig.profiles.find(t=>t.id===e);if(!t||t.builtin||!await S({description:D(`terminalShellDeleteConfirm`,{name:t.name}),confirmLabel:D(`confirmDelete`),cancelLabel:D(`cancel`),variant:`destructive`}))return;let n=this.customShellProfiles().filter(t=>t.id!==e),r=this.terminalShellConfig.defaultProfileId===e?`auto`:this.terminalShellConfig.defaultProfileId;await this.saveTerminalShellConfig(r,n,D(`terminalShellProfilesSaved`))}async saveDefaultModelOptions(){try{let e=this.selectedModel?.reasoning?this.thinkingLevel:`off`;await _e(_(),{model:this.selectedModel,thinkingLevel:e}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}async saveToolDisplayOptions(){try{await Oe(_(),{toolDisplayMode:this.toolDisplayMode,showContextUsage:this.showContextUsage}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}async saveAutoCompactOptions(){try{await ze(_(),{enabled:this.autoCompactEnabled,thresholdPercent:this.autoCompactThresholdPercent,keepRecentTurns:this.autoCompactKeepRecentTurns,minSourceChars:1600,requireConfirmation:this.autoCompactRequireConfirmation}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}async saveAutoArchiveOptions(){try{if(await We(_(),{enabled:this.autoArchiveEnabled}),this.markSaved(D(`autoArchiveSaved`)),typeof BroadcastChannel<`u`)try{let e=new BroadcastChannel(`quickforge-sync`);e.postMessage({type:`settings-changed`,sourceTabId:`default-options-settings-tab`,timestamp:Date.now()}),e.close()}catch{}}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}modelOptions(){if(!this.selectedModel)return this.models;let e=I(this.selectedModel);return this.models.some(t=>I(t)===e)?this.models:[this.selectedModel,...this.models]}terminalShellSettings(){let e=this.terminalShellConfig.profiles,t=this.selectedTerminalShellProfileId(),n=e.find(e=>e.id===t),r=this.customShellEditorOpen||e.length===0;return h`
404
- <section class="quickforge-settings-section" aria-label=${D(`terminalShell`)}>
405
- <div class="quickforge-settings-row">
406
- <div class="quickforge-settings-row-main">
407
- <div class="quickforge-settings-row-title">
408
- ${D(`terminalShellDefault`)}
409
- <quickforge-info-tip .label=${D(`terminalShellDescription`)}></quickforge-info-tip>
410
- </div>
411
- <div class="quickforge-settings-row-description quickforge-settings-mono">
412
- ${n?.command||D(`terminalShellNoDetected`)}
413
- </div>
414
- </div>
415
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
416
- <select
417
- data-terminal-shell-select
418
- class="quickforge-settings-select"
419
- .value=${r?F:t}
420
- aria-label=${D(`terminalShellDefault`)}
421
- @change=${e=>this.updateTerminalShellSelection(e.target.value)}
422
- >
423
- ${e.map(e=>h`<option value=${e.id}>${e.name}</option>`)}
424
- <option value=${F}>${D(`terminalShellCustomOption`)}</option>
425
- </select>
426
- ${n&&!n.builtin&&!r?h`
427
- <button
428
- class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
429
- type="button"
430
- title=${D(`delete`)}
431
- aria-label=${D(`delete`)}
432
- @click=${()=>this.deleteCustomTerminalShell(n.id)}
433
- >
434
- ${Ze}
435
- </button>
436
- `:null}
437
- </div>
438
- </div>
439
-
440
- ${r?h`
441
- <div class="quickforge-settings-row">
442
- <div class="quickforge-settings-row-main">
443
- <div class="quickforge-settings-row-title">${D(`terminalShellCommand`)}</div>
444
- <div class="quickforge-settings-row-description">${D(`terminalShellCustomDescription`)}</div>
445
- </div>
446
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-terminal-shell-command-control">
447
- <input
448
- class="quickforge-settings-input quickforge-settings-mono"
449
- type="text"
450
- .value=${this.customShellCommand}
451
- placeholder=${D(`terminalShellCommandPlaceholder`)}
452
- @input=${e=>{this.customShellCommand=e.target.value}}
453
- />
454
- <button
455
- class="quickforge-settings-button quickforge-settings-button-primary"
456
- type="button"
457
- title=${D(`terminalShellAdd`)}
458
- aria-label=${D(`terminalShellAdd`)}
459
- @click=${()=>this.addCustomTerminalShell()}
460
- >
461
- ${D(`terminalShellAdd`)}
462
- </button>
463
- </div>
464
- </div>
465
- `:null}
466
- </section>
467
- `}renderToolDisplayModeOption(e){let t=this.toolDisplayMode===e.value;return h`
468
- <button
469
- type="button"
470
- class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
471
- aria-pressed=${t?`true`:`false`}
472
- @click=${()=>this.updateToolDisplayMode(e.value)}
473
- >
474
- ${e.label()}
475
- </button>
476
- `}renderSwitch(e,t,n=!1){return h`
477
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
478
- <input
479
- type="checkbox"
480
- .checked=${e}
481
- ?disabled=${n}
482
- @change=${e=>t(e.target.checked)}
483
- />
484
- <span aria-hidden="true"></span>
485
- </label>
486
- `}render(){return this.loading?h`<div class="text-sm text-muted-foreground">${D(`loading`)}</div>`:h`
487
- <div class="quickforge-settings-stack">
488
- <section class="quickforge-settings-section" aria-label=${D(`defaultOptions`)}>
489
- <div class="quickforge-settings-row">
490
- <div class="quickforge-settings-row-main">
491
- <div class="quickforge-settings-row-title">
492
- ${D(`language`)}
493
- <quickforge-info-tip .label=${D(`languageDescription`)}></quickforge-info-tip>
494
- </div>
495
- <div class="quickforge-settings-row-description">${D(`displayLanguage`)}</div>
496
- </div>
497
- <div class="quickforge-settings-row-control">
498
- <select
499
- data-language-select
500
- class="quickforge-settings-select"
501
- .value=${this.selectedLanguage}
502
- @change=${e=>this.updateLanguage(e.target.value)}
503
- >
504
- <option value="zh">${D(`simplifiedChinese`)}</option>
505
- <option value="en">${D(`english`)}</option>
506
- </select>
507
- </div>
508
- </div>
509
-
510
- <div class="quickforge-settings-row">
511
- <div class="quickforge-settings-row-main">
512
- <div class="quickforge-settings-row-title">${D(`defaultModel`)}</div>
513
- <div class="quickforge-settings-row-description">${D(`defaultModelDescription`)}</div>
514
- </div>
515
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
516
- <select
517
- data-default-model-select
518
- class="quickforge-settings-select"
519
- .value=${this.selectedModel?I(this.selectedModel):``}
520
- @change=${e=>this.updateModel(e.target.value)}
521
- >
522
- ${this.modelOptions().length===0?h`<option value="">${D(`noModelAvailable`)}</option>`:this.modelOptions().map(e=>h`
523
- <option .value=${I(e)}>${Je(e)}</option>
524
- `)}
525
- </select>
526
- </div>
527
- </div>
528
-
529
- <div class="quickforge-settings-row">
530
- <div class="quickforge-settings-row-main">
531
- <div class="quickforge-settings-row-title">${D(`defaultThinkingLevel`)}</div>
532
- <div class="quickforge-settings-row-description">
533
- ${this.selectedModel?.reasoning?D(`defaultThinkingLevelDescription`):D(`thinkingRequiresReasoningModel`)}
534
- </div>
535
- </div>
536
- <div class="quickforge-settings-row-control">
537
- <select
538
- data-default-thinking-select
539
- class="quickforge-settings-select"
540
- .value=${this.thinkingLevel}
541
- ?disabled=${!this.selectedModel?.reasoning}
542
- @change=${e=>this.updateThinkingLevel(e.target.value)}
543
- >
544
- ${Ge.map(e=>h`
545
- <option .value=${e.value}>${e.label()}</option>
546
- `)}
547
- </select>
548
- </div>
549
- </div>
550
-
551
- <div class="quickforge-settings-row">
552
- <div class="quickforge-settings-row-main">
553
- <div class="quickforge-settings-row-title">
554
- ${D(`toolDisplay`)}
555
- <quickforge-info-tip .label=${D(`toolDisplayModeDescription`)}></quickforge-info-tip>
556
- </div>
557
- <div class="quickforge-settings-row-description">${D(`toolDisplayModeDescription`)}</div>
558
- </div>
559
- <div class="quickforge-settings-row-control">
560
- <div class="quickforge-settings-segmented" role="group" aria-label=${D(`toolDisplay`)}>
561
- ${Ke.map(e=>this.renderToolDisplayModeOption(e))}
562
- </div>
563
- </div>
564
- </div>
565
-
566
- <div class="quickforge-settings-row">
567
- <div class="quickforge-settings-row-main">
568
- <div class="quickforge-settings-row-title">
569
- ${D(`showContextUsage`)}
570
- <quickforge-info-tip .label=${D(`showContextUsageDescription`)}></quickforge-info-tip>
571
- </div>
572
- <div class="quickforge-settings-row-description">${D(`showContextUsageDescription`)}</div>
573
- </div>
574
- <div class="quickforge-settings-row-control">
575
- ${this.renderSwitch(this.showContextUsage,e=>this.updateShowContextUsage(e))}
576
- </div>
577
- </div>
578
-
579
- <div class="quickforge-settings-row">
580
- <div class="quickforge-settings-row-main">
581
- <div class="quickforge-settings-row-title">
582
- ${D(`autoArchiveEnabled`)}
583
- <quickforge-info-tip .label=${D(`autoArchiveDescription`)}></quickforge-info-tip>
584
- </div>
585
- <div class="quickforge-settings-row-description">${D(`autoArchiveTriggerNote`)}</div>
586
- </div>
587
- <div class="quickforge-settings-row-control">
588
- ${this.renderSwitch(this.autoArchiveEnabled,e=>this.updateAutoArchiveEnabled(e))}
589
- </div>
590
- </div>
591
-
592
- <div class="quickforge-settings-row">
593
- <div class="quickforge-settings-row-main">
594
- <div class="quickforge-settings-row-title">
595
- ${D(`autoCompactEnabled`)}
596
- <quickforge-info-tip .label=${D(`autoCompactDescription`)}></quickforge-info-tip>
597
- </div>
598
- <div class="quickforge-settings-row-description">${D(`autoCompactTriggerNote`)}</div>
599
- </div>
600
- <div class="quickforge-settings-row-control">
601
- ${this.renderSwitch(this.autoCompactEnabled,e=>this.updateAutoCompactEnabled(e))}
602
- </div>
603
- </div>
604
-
605
- <div class="quickforge-settings-row">
606
- <div class="quickforge-settings-row-main">
607
- <div class="quickforge-settings-row-title">${D(`autoCompactRequireConfirmation`)}</div>
608
- <div class="quickforge-settings-row-description">${D(`autoCompactRequireConfirmationDescription`)}</div>
609
- </div>
610
- <div class="quickforge-settings-row-control">
611
- ${this.renderSwitch(this.autoCompactRequireConfirmation,e=>this.updateAutoCompactRequireConfirmation(e),!this.autoCompactEnabled)}
612
- </div>
613
- </div>
614
-
615
- <div class="quickforge-settings-row">
616
- <div class="quickforge-settings-row-main">
617
- <div class="quickforge-settings-row-title">${D(`autoCompactThresholdPercent`)}</div>
618
- <div class="quickforge-settings-row-description">${D(`autoCompactThresholdDescription`)}</div>
619
- </div>
620
- <div class="quickforge-settings-row-control">
621
- <input
622
- class="quickforge-settings-input quickforge-settings-number-input"
623
- type="number"
624
- min="50"
625
- max="95"
626
- step="1"
627
- .value=${this.autoCompactThresholdPercentInput}
628
- ?disabled=${!this.autoCompactEnabled}
629
- @input=${e=>this.updateAutoCompactThresholdPercent(e.target.value)}
630
- @change=${()=>this.commitAutoCompactThresholdPercent()}
631
- @blur=${()=>this.commitAutoCompactThresholdPercent()}
632
- />
633
- </div>
634
- </div>
635
-
636
- <div class="quickforge-settings-row">
637
- <div class="quickforge-settings-row-main">
638
- <div class="quickforge-settings-row-title">
639
- ${D(`autoCompactKeepRecentTurns`)}
640
- <quickforge-info-tip .label=${D(`autoCompactHistoryPreserved`)}></quickforge-info-tip>
641
- </div>
642
- <div class="quickforge-settings-row-description">${D(`autoCompactKeepRecentTurnsDescription`)}</div>
643
- </div>
644
- <div class="quickforge-settings-row-control">
645
- <input
646
- class="quickforge-settings-input quickforge-settings-number-input"
647
- type="number"
648
- min="1"
649
- max="20"
650
- step="1"
651
- .value=${String(this.autoCompactKeepRecentTurns)}
652
- ?disabled=${!this.autoCompactEnabled}
653
- @input=${e=>this.updateAutoCompactKeepRecentTurns(e.target.value)}
654
- @change=${()=>this.commitAutoCompactKeepRecentTurns()}
655
- @blur=${()=>this.commitAutoCompactKeepRecentTurns()}
656
- />
657
- </div>
658
- </div>
659
- </section>
660
-
661
- ${this.terminalShellSettings()}
662
-
663
- ${this.saved?h`<div class="quickforge-settings-message">${this.savedMessage||D(`defaultOptionsSaved`)}</div>`:null}
664
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
665
- </div>
666
- `}},L=`quickforge-default-options-settings-tab`;customElements.get(L)||customElements.define(L,Qe);function $e(){return document.createElement(L)}var et=`memory-settings`,tt={enabled:!0};function nt(e){return!e||typeof e!=`object`?{...tt}:{enabled:e.enabled!==!1}}async function rt(e){return nt(await e.settings.get(et))}async function it(e,t){await e.settings.set(et,nt(t))}var at=class extends g{enabled=!0;loading=!0;saving=!1;loadingDocument=!1;markdown=``;savedMarkdown=``;memoryPath=`~/.quickforge/MEMORY.md`;message=``;error=``;getTabName(){return D(`memory`)}async connectedCallback(){super.connectedCallback(),await this.loadSettings()}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let[e]=await Promise.all([rt(_()),this.loadDocument()]);this.enabled=e.enabled}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadDocument(e=!1){this.loadingDocument=!0,this.error=``,e&&(this.message=``),this.requestUpdate();try{let t=await fetch(`/api/memory`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(!t.ok||!n)throw Error(n?.error||D(`requestFailed`));this.markdown=n.markdown,this.savedMarkdown=n.markdown,this.memoryPath=n.path||this.memoryPath,e&&(this.message=D(`memoryReloaded`))}finally{this.loadingDocument=!1,this.requestUpdate()}}async updateEnabled(e){let t=this.enabled;this.enabled=e,this.saving=!0,this.message=``,this.error=``,this.requestUpdate();try{await it(_(),{enabled:e}),this.message=D(e?`memoryEnabledSaved`:`memoryDisabledSaved`)}catch(e){this.enabled=t,this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}async reloadDocument(){try{await this.loadDocument(!0)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}async saveDocument(){if(!(!this.enabled||this.saving)){this.saving=!0,this.message=``,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/memory`,{method:`PUT`,headers:{"content-type":`application/json`},body:JSON.stringify({markdown:this.markdown})}),t=await e.json().catch(()=>null);if(!e.ok||!t)throw Error(t?.error||D(`requestFailed`));this.markdown=t.markdown,this.savedMarkdown=t.markdown,this.memoryPath=t.path||this.memoryPath,this.message=D(`memoryContentSaved`)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(){return h`
667
- <label class="quickforge-settings-switch" aria-disabled=${this.saving?`true`:`false`}>
668
- <input
669
- type="checkbox"
670
- .checked=${this.enabled}
671
- ?disabled=${this.saving}
672
- @change=${e=>void this.updateEnabled(e.target.checked)}
673
- />
674
- <span aria-hidden="true"></span>
675
- </label>
676
- `}render(){if(this.loading)return h`<div class="text-sm text-muted-foreground">${D(`loading`)}</div>`;let e=this.markdown!==this.savedMarkdown,t=this.saving||this.loadingDocument;return h`
677
- <div class="quickforge-settings-stack">
678
- <section class="quickforge-settings-section" aria-label=${D(`memory`)}>
679
- <div class="quickforge-settings-row">
680
- <div class="quickforge-settings-row-main">
681
- <div class="quickforge-settings-row-title">
682
- ${D(`globalMemory`)}
683
- <quickforge-info-tip .label=${D(`globalMemoryInfo`)}></quickforge-info-tip>
684
- </div>
685
- <div class="quickforge-settings-row-description">${D(`globalMemoryDescription`)}</div>
686
- </div>
687
- <div class="quickforge-settings-row-control">${this.renderSwitch()}</div>
688
- </div>
689
-
690
- <div class="quickforge-settings-row">
691
- <div class="quickforge-settings-row-main">
692
- <div class="quickforge-settings-row-title">${D(`memoryFile`)}</div>
693
- <div class="quickforge-settings-row-description">${D(`memoryFileDescription`)}</div>
694
- </div>
695
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
696
- <code class="text-xs text-muted-foreground">${this.memoryPath}</code>
697
- </div>
698
- </div>
699
-
700
- <div class="quickforge-settings-row quickforge-settings-row-align-start quickforge-memory-content-row">
701
- <div class="quickforge-settings-row-main">
702
- <div class="quickforge-settings-row-title">${D(`memoryContent`)}</div>
703
- <div class="quickforge-settings-row-description">${this.enabled?D(`memoryContentDescription`):D(`memoryContentDisabledDescription`)}</div>
704
- </div>
705
- <div class="quickforge-memory-editor-control">
706
- <textarea
707
- class="quickforge-settings-textarea quickforge-settings-mono quickforge-memory-editor"
708
- .value=${this.markdown}
709
- ?disabled=${!this.enabled||t}
710
- spellcheck="false"
711
- @input=${e=>{this.markdown=e.target.value,this.message=``,this.requestUpdate()}}
712
- ></textarea>
713
- <div class="quickforge-memory-editor-actions">
714
- <button
715
- class="quickforge-settings-button quickforge-settings-button-secondary"
716
- type="button"
717
- ?disabled=${t}
718
- @click=${()=>void this.reloadDocument()}
719
- >${this.loadingDocument?D(`loading`):D(`memoryReload`)}</button>
720
- <button
721
- class="quickforge-settings-button quickforge-settings-button-primary"
722
- type="button"
723
- ?disabled=${!this.enabled||t||!e}
724
- @click=${()=>void this.saveDocument()}
725
- >${this.saving?D(`saving`):D(`memorySave`)}</button>
726
- </div>
727
- </div>
728
- </div>
729
-
730
- <div class="quickforge-settings-note">${D(`memoryCurrentSessionNote`)}</div>
731
- </section>
732
-
733
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
734
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
735
- </div>
736
- `}},R=`quickforge-memory-settings-tab`;customElements.get(R)||customElements.define(R,at);function ot(){return document.createElement(R)}var st=`quickforge-backup`,z=[{id:`settings`,countKey:`settings`,label:()=>D(`restoreSettings`),description:()=>D(`restoreSettingsDescription`)},{id:`mcp`,countKey:`mcp`,label:()=>D(`restoreMcp`),description:()=>D(`restoreMcpDescription`)},{id:`providerKeys`,countKey:`providerKeys`,label:()=>D(`restoreProviderKeys`),description:()=>D(`restoreProviderKeysDescription`)},{id:`customProviders`,countKey:`customProviders`,label:()=>D(`restoreCustomProviders`),description:()=>D(`restoreCustomProvidersDescription`)},{id:`scheduledTasks`,countKey:`scheduledTasks`,label:()=>D(`restoreScheduledTasks`),description:()=>D(`restoreScheduledTasksDescription`)}],ct=z;function lt(e,t){let n=new Blob([`${JSON.stringify(t,null,2)}\n`],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=e,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(r)}function ut(){return new Date().toISOString().replace(/[:.]/g,`-`)}function dt(e){return e?Object.entries(e).map(([e,t])=>`${e}: ${t}`).join(`, `):``}function ft(e){return ct.filter(t=>(e.sections?.[t.countKey]??0)>0)}function pt(e){return ft(e).map(e=>e.id)}function mt(e){return e instanceof RangeError||e instanceof Error&&e.message.includes(`Invalid string length`)}function B(e,t){return mt(e)?D(`backupStringTooLarge`):e instanceof Error?e.message:t}var ht=class extends g{exportSections=new Set(z.map(e=>e.id));busy=!1;message=``;error=``;safetyBackupPath=``;pendingImport=null;getTabName(){return D(`backupRestore`)}toggleExportSection(e,t){t?this.exportSections.add(e):this.exportSections.delete(e),this.clearStatus(),this.requestUpdate()}clearStatus(){this.message=``,this.error=``,this.safetyBackupPath=``}async exportBackup(){if(this.exportSections.size===0){this.error=D(`selectAtLeastOneExportSection`),this.requestUpdate();return}this.busy=!0,this.clearStatus(),this.requestUpdate();try{let e=new URLSearchParams({sections:[...this.exportSections].join(`,`)}),t=await fetch(`/api/backup/export?${e.toString()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||D(`backupExportFailed`));lt(`${st}-settings-${ut()}.json`,n),this.message=D(`backupExported`)}catch(e){this.error=B(e,D(`backupExportFailed`))}finally{this.busy=!1,this.requestUpdate()}}async inspectBackupFile(e){let t=await fetch(`/api/backup/inspect-file`,{method:`POST`,headers:{"content-type":`application/json`},body:e}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||D(`backupInspectFailed`));if(!n)throw Error(D(`backupInspectFailed`));return n}async importBackupFromFile(e){this.busy=!0,this.clearStatus(),this.pendingImport=null,this.requestUpdate();try{let t=await this.inspectBackupFile(e),n=new Set(pt(t));this.pendingImport={backup:null,importToken:t.importToken,inspect:t,selectedSections:n,mode:`replace`},this.message=t.ignoredConversations?D(`backupInspectedWithIgnoredConversations`):D(`backupInspected`)}catch(e){this.error=B(e,D(`backupImportFailed`))}finally{this.busy=!1,this.requestUpdate()}}togglePendingSection(e,t){this.pendingImport&&(t?this.pendingImport.selectedSections.add(e):this.pendingImport.selectedSections.delete(e),this.message=``,this.error=``,this.requestUpdate())}setRestoreMode(e){this.pendingImport&&(this.pendingImport.mode=e,this.message=``,this.error=``,this.requestUpdate())}cancelPendingImport(){this.pendingImport=null,this.message=``,this.error=``,this.requestUpdate()}async confirmPendingImport(){if(this.pendingImport){if(this.pendingImport.selectedSections.size===0){this.error=D(`selectAtLeastOneRestoreSection`),this.requestUpdate();return}if(await S({description:this.pendingImport.mode===`replace`?D(`backupImportReplaceConfirm`):D(`backupImportMergeConfirm`),confirmLabel:D(`confirmImportSelected`),cancelLabel:D(`cancel`)})){this.busy=!0,this.clearStatus(),this.requestUpdate();try{let e=this.pendingImport.importToken?{importToken:this.pendingImport.importToken,sections:[...this.pendingImport.selectedSections],mode:this.pendingImport.mode}:{backup:this.pendingImport.backup,sections:[...this.pendingImport.selectedSections],mode:this.pendingImport.mode},t=await fetch(`/api/backup/import`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||D(`backupImportFailed`));let r=dt(n?.summary);this.safetyBackupPath=n?.safetyBackupPath||``,this.pendingImport=null,this.message=r?`${D(`backupImported`)} ${r}`:D(`backupImported`),window.setTimeout(()=>window.location.reload(),1500)}catch(e){this.error=B(e,D(`backupImportFailed`))}finally{this.busy=!1,this.requestUpdate()}}}}handleFileChange(e){let t=e.target,n=t.files?.[0];t.value=``,n&&this.importBackupFromFile(n)}renderSwitch(e,t,n=!1){return h`
737
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
738
- <input
739
- type="checkbox"
740
- .checked=${e}
741
- ?disabled=${n}
742
- @change=${e=>t(e.target.checked)}
743
- />
744
- <span aria-hidden="true"></span>
745
- </label>
746
- `}renderPendingImport(){if(!this.pendingImport)return null;let{inspect:e,selectedSections:t}=this.pendingImport,n=ft(e);return h`
747
- <section class="quickforge-settings-section" aria-label=${D(`backupInspectTitle`)}>
748
- <div class="quickforge-settings-row quickforge-settings-row-top">
749
- <div class="quickforge-settings-row-main">
750
- <div class="quickforge-settings-row-title">${D(`backupInspectTitle`)}</div>
751
- <div class="quickforge-settings-row-description">${D(`backupInspectDescription`)}</div>
752
- </div>
753
- </div>
754
-
755
- <div class="quickforge-settings-nested-list">
756
- <div class="quickforge-settings-subrow">
757
- <div class="quickforge-settings-row-main">
758
- <div class="quickforge-settings-row-title">${D(`backupInspectExportedAt`)}</div>
759
- </div>
760
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.exportedAt||`-`}</div>
761
- </div>
762
- <div class="quickforge-settings-subrow">
763
- <div class="quickforge-settings-row-main">
764
- <div class="quickforge-settings-row-title">${D(`backupInspectVersion`)}</div>
765
- </div>
766
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version??`-`}</div>
767
- </div>
768
- <div class="quickforge-settings-subrow">
769
- <div class="quickforge-settings-row-main">
770
- <div class="quickforge-settings-row-title">${D(`backupInspectScope`)}</div>
771
- </div>
772
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.scope||`-`}</div>
773
- </div>
774
- <div class="quickforge-settings-subrow">
775
- <div class="quickforge-settings-row-main">
776
- <div class="quickforge-settings-row-title">${D(`backupInspectSecrets`)}</div>
777
- </div>
778
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.includeSecrets?D(`yes`):D(`no`)}</div>
779
- </div>
780
- </div>
781
-
782
- ${e.warnings?.length?h`
783
- <div class="quickforge-settings-warning quickforge-settings-warning-attached">
784
- ${e.warnings.map(e=>h`<div>⚠ ${e}</div>`)}
785
- </div>
786
- `:null}
787
-
788
- ${e.invalidSections&&Object.keys(e.invalidSections).length?h`
789
- <div class="quickforge-settings-warning quickforge-settings-warning-attached">
790
- <div>${D(`backupInvalidSections`)}</div>
791
- ${Object.entries(e.invalidSections).map(([e,t])=>h`<div>${e}: ${t}</div>`)}
792
- </div>
793
- `:null}
794
-
795
- <div class="quickforge-settings-row">
796
- <div class="quickforge-settings-row-main">
797
- <div class="quickforge-settings-row-title">${D(`restoreMode`)}</div>
798
- <div class="quickforge-settings-row-description">${D(`restoreModeDescription`)}</div>
799
- </div>
800
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
801
- <div class="quickforge-settings-segmented" role="group" aria-label=${D(`restoreMode`)}>
802
- <button
803
- type="button"
804
- class="quickforge-settings-segmented-option ${this.pendingImport.mode===`replace`?`quickforge-settings-segmented-option-active`:``}"
805
- ?disabled=${this.busy}
806
- @click=${()=>this.setRestoreMode(`replace`)}
807
- >
808
- ${D(`restoreModeReplace`)}
809
- </button>
810
- <button
811
- type="button"
812
- class="quickforge-settings-segmented-option ${this.pendingImport.mode===`merge`?`quickforge-settings-segmented-option-active`:``}"
813
- ?disabled=${this.busy}
814
- @click=${()=>this.setRestoreMode(`merge`)}
815
- >
816
- ${D(`restoreModeMerge`)}
817
- </button>
818
- </div>
819
- </div>
820
- </div>
821
-
822
- <div class="quickforge-settings-row quickforge-settings-row-top">
823
- <div class="quickforge-settings-row-main">
824
- <div class="quickforge-settings-row-title">${D(`selectRestoreSections`)}</div>
825
- <div class="quickforge-settings-row-description">
826
- ${this.pendingImport.mode===`replace`?D(`restoreModeReplaceDescription`):D(`restoreModeMergeDescription`)}
827
- </div>
828
- </div>
829
- </div>
830
- <div class="quickforge-settings-nested-list">
831
- ${n.map(n=>h`
832
- <div class="quickforge-settings-subrow">
833
- <div class="quickforge-settings-row-main">
834
- <div class="quickforge-settings-row-title">${n.label()} (${e.sections?.[n.countKey]??0})</div>
835
- <div class="quickforge-settings-row-description">${n.description()}</div>
836
- </div>
837
- <div class="quickforge-settings-row-control">
838
- ${this.renderSwitch(t.has(n.id),e=>this.togglePendingSection(n.id,e),this.busy)}
839
- </div>
840
- </div>
841
- `)}
842
- </div>
843
-
844
- <div class="quickforge-settings-row">
845
- <div class="quickforge-settings-row-main">
846
- <div class="quickforge-settings-row-title">${D(`backupImportActions`)}</div>
847
- <div class="quickforge-settings-row-description">${D(`backupImportActionsDescription`)}</div>
848
- </div>
849
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
850
- <button
851
- class="quickforge-settings-button quickforge-settings-button-primary"
852
- type="button"
853
- ?disabled=${this.busy||t.size===0}
854
- @click=${()=>this.confirmPendingImport()}
855
- >
856
- ${this.busy?D(`loading`):D(`confirmImportSelected`)}
857
- </button>
858
- <button
859
- class="quickforge-settings-button quickforge-settings-button-secondary"
860
- type="button"
861
- ?disabled=${this.busy}
862
- @click=${()=>this.cancelPendingImport()}
863
- >
864
- ${D(`cancel`)}
865
- </button>
866
- </div>
867
- </div>
868
- </section>
869
- `}render(){return h`
870
- <div class="quickforge-settings-stack">
871
- <section class="quickforge-settings-section" aria-label=${D(`exportData`)}>
872
- <div class="quickforge-settings-row quickforge-settings-row-top">
873
- <div class="quickforge-settings-row-main">
874
- <div class="quickforge-settings-row-title">
875
- ${D(`selectExportSections`)}
876
- <quickforge-info-tip .label=${D(`exportDataDescription`)}></quickforge-info-tip>
877
- </div>
878
- <div class="quickforge-settings-row-description">${D(`selectExportSectionsDescription`)}</div>
879
- </div>
880
- </div>
881
-
882
- <div class="quickforge-settings-nested-list">
883
- ${z.map(e=>h`
884
- <div class="quickforge-settings-subrow">
885
- <div class="quickforge-settings-row-main">
886
- <div class="quickforge-settings-row-title">${e.label()}</div>
887
- <div class="quickforge-settings-row-description">${e.description()}</div>
888
- </div>
889
- <div class="quickforge-settings-row-control">
890
- ${this.renderSwitch(this.exportSections.has(e.id),t=>this.toggleExportSection(e.id,t),this.busy)}
891
- </div>
892
- </div>
893
- `)}
894
- </div>
895
-
896
- <div class="quickforge-settings-note">${D(`conversationBackupExcluded`)}</div>
897
-
898
- <div class="quickforge-settings-row">
899
- <div class="quickforge-settings-row-main">
900
- <div class="quickforge-settings-row-title">${D(`exportBackup`)}</div>
901
- <div class="quickforge-settings-row-description">${D(`exportBackupDescription`)}</div>
902
- </div>
903
- <div class="quickforge-settings-row-control">
904
- <button
905
- class="quickforge-settings-button quickforge-settings-button-primary"
906
- type="button"
907
- ?disabled=${this.busy||this.exportSections.size===0}
908
- @click=${()=>this.exportBackup()}
909
- >
910
- ${this.busy?D(`loading`):D(`exportBackup`)}
911
- </button>
912
- </div>
913
- </div>
914
- </section>
915
-
916
- <section class="quickforge-settings-section" aria-label=${D(`importData`)}>
917
- <div class="quickforge-settings-row">
918
- <div class="quickforge-settings-row-main">
919
- <div class="quickforge-settings-row-title">
920
- ${D(`importBackup`)}
921
- <quickforge-info-tip .label=${D(`importDataDescription`)}></quickforge-info-tip>
922
- </div>
923
- <div class="quickforge-settings-row-description">${D(`importDataDescription`)}</div>
924
- </div>
925
- <div class="quickforge-settings-row-control">
926
- <label class="quickforge-settings-button quickforge-settings-button-secondary ${this.busy?`pointer-events-none opacity-60`:``}">
927
- <input
928
- class="hidden"
929
- type="file"
930
- accept="application/json,.json"
931
- ?disabled=${this.busy}
932
- @change=${e=>this.handleFileChange(e)}
933
- />
934
- ${D(`importBackup`)}
935
- </label>
936
- </div>
937
- </div>
938
- </section>
939
-
940
- ${this.renderPendingImport()}
941
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
942
- ${this.safetyBackupPath?h`<div class="quickforge-settings-note">${D(`backupSafetyBackupPath`)}: <code>${this.safetyBackupPath}</code></div>`:null}
943
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
944
- </div>
945
- `}},V=`quickforge-backup-settings-tab`;customElements.get(V)||customElements.define(V,ht);function gt(){return document.createElement(V)}function _t(e){if(!e)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString(Ee())}function vt(e){let t={...e};return delete t.archivedAt,t}function H(){if(!(typeof BroadcastChannel>`u`))try{let e=new BroadcastChannel(`quickforge-sync`);e.postMessage({type:`sessions-changed`,sourceTabId:`archived-conversations-settings-tab`,timestamp:Date.now()}),e.close()}catch{}}var yt=class extends g{sessions=[];projects=[];loading=!0;busySessionId=``;query=``;projectFilter=`all`;message=``;error=``;getTabName(){return D(`archivedConversations`)}async connectedCallback(){super.connectedCallback(),await this.loadData()}get projectNameById(){return new Map(this.projects.map(e=>[e.id,e.name]))}async loadData(){this.loading=!0,this.error=``,this.requestUpdate();try{let[e,t]=await Promise.all([fetch(`/api/storage/sessions-metadata/index/lastModified?direction=desc&limit=1000&offset=0&archived=only`,{cache:`no-store`}),fetch(`/api/project`,{cache:`no-store`}).catch(()=>null)]),n=await e.json().catch(()=>null);if(!e.ok)throw Error(n?.error||D(`requestFailed`));if(this.sessions=Array.isArray(n?.values)?n.values:[],t?.ok){let e=await t.json().catch(()=>null);this.projects=Array.isArray(e?.projects)?e.projects:[]}}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}filteredSessions(){let e=this.query.trim().toLowerCase();return this.sessions.filter(t=>{if(this.projectFilter!==`all`&&(this.projectFilter===`global`&&t.scope===`project`||this.projectFilter!==`global`&&t.projectId!==this.projectFilter))return!1;if(!e)return!0;let n=t.projectId&&this.projectNameById.get(t.projectId)||``;return`${T(t.title)} ${n}`.toLowerCase().includes(e)})}groupedSessions(){let e=new Map;for(let t of this.filteredSessions()){let n=t.scope===`project`&&t.projectId?t.projectId:`global`;e.set(n,[...e.get(n)??[],t])}return[...e.entries()]}projectLabel(e){return e===`global`?D(`normalChat`):this.projectNameById.get(e)||D(`unknownProject`)}async restoreSession(e){if(!this.busySessionId){this.busySessionId=e,this.message=``,this.error=``,this.requestUpdate();try{let t=_(),n=await t.sessions.get(e),r=await t.sessions.getMetadata(e);if(!n||!r)throw Error(D(`sessionNotFound`));await t.sessions.save(vt(n),vt(r)),this.sessions=this.sessions.filter(t=>t.id!==e),this.message=D(`sessionRestored`),H()}catch(e){this.error=e instanceof Error?e.message:D(`restoreSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteSession(e){if(!this.busySessionId&&await S({description:D(`deleteArchivedSessionConfirm`,{title:T(e.title)}),confirmLabel:D(`confirmDelete`),cancelLabel:D(`cancel`),variant:`destructive`})){this.busySessionId=e.id,this.message=``,this.error=``,this.requestUpdate();try{await _().sessions.delete(e.id),this.sessions=this.sessions.filter(t=>t.id!==e.id),this.message=D(`archivedSessionDeleted`),H()}catch(e){this.error=e instanceof Error?e.message:D(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteAllArchivedSessions(){if(!(this.busySessionId||this.sessions.length===0)&&await S({description:D(`deleteAllArchivedSessionsConfirm`),confirmLabel:D(`confirmDelete`),cancelLabel:D(`cancel`),variant:`destructive`})){this.busySessionId=`__all__`,this.message=``,this.error=``,this.requestUpdate();try{let e=_();await Promise.all(this.sessions.map(t=>e.sessions.delete(t.id))),this.sessions=[],this.message=D(`archivedSessionsDeleted`),H()}catch(e){this.error=e instanceof Error?e.message:D(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}renderSession(e){let t=this.busySessionId===e.id||this.busySessionId===`__all__`;return h`
946
- <div class="border-t border-border px-4 py-3 first:border-t-0">
947
- <div class="flex items-start justify-between gap-3">
948
- <div class="min-w-0">
949
- <div class="truncate text-sm font-medium text-foreground">${T(e.title)}</div>
950
- <div class="mt-1 text-xs text-muted-foreground">
951
- ${D(`lastModified`)}: ${_t(e.lastModified)} · ${D(`archivedAt`)}: ${_t(e.archivedAt)}
952
- </div>
953
- </div>
954
- <div class="flex shrink-0 items-center gap-2">
955
- <button
956
- class="rounded-md border border-input px-2.5 py-1 text-xs hover:bg-muted/60 disabled:opacity-60"
957
- type="button"
958
- ?disabled=${t}
959
- @click=${()=>this.restoreSession(e.id)}
960
- >
961
- ${D(`restoreSession`)}
962
- </button>
963
- <button
964
- class="rounded-md px-2.5 py-1 text-xs text-destructive hover:bg-destructive/10 disabled:opacity-60"
965
- type="button"
966
- ?disabled=${t}
967
- @click=${()=>this.deleteSession(e)}
968
- >
969
- ${D(`deletePermanently`)}
970
- </button>
971
- </div>
972
- </div>
973
- </div>
974
- `}render(){let e=this.groupedSessions();return h`
975
- <div class="flex flex-col gap-4">
976
- <div class="flex justify-end">
977
- <button
978
- class="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive hover:bg-destructive/15 disabled:opacity-60"
979
- type="button"
980
- ?disabled=${!!this.busySessionId||this.sessions.length===0}
981
- @click=${()=>this.deleteAllArchivedSessions()}
982
- >
983
- ${D(`deleteAll`)}
984
- </button>
985
- </div>
986
-
987
- <section class="rounded-lg border border-border">
988
- <div class="grid gap-2 border-b border-border p-3 sm:grid-cols-[minmax(0,1fr)_12rem]">
989
- <input
990
- class="rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring/30"
991
- type="search"
992
- .value=${this.query}
993
- placeholder=${D(`searchArchivedConversations`)}
994
- @input=${e=>{this.query=e.target.value,this.requestUpdate()}}
995
- />
996
- <select
997
- class="rounded-md border border-input bg-background px-3 py-2 text-sm"
998
- .value=${this.projectFilter}
999
- @change=${e=>{this.projectFilter=e.target.value,this.requestUpdate()}}
1000
- >
1001
- <option value="all">${D(`allProjects`)}</option>
1002
- <option value="global">${D(`normalChat`)}</option>
1003
- ${this.projects.map(e=>h`<option value=${e.id}>${e.name}</option>`)}
1004
- </select>
1005
- </div>
1006
-
1007
- ${this.loading?h`
1008
- <div class="px-4 py-8 text-center text-sm text-muted-foreground">${D(`loading`)}</div>
1009
- `:e.length===0?h`
1010
- <div class="px-4 py-8 text-center text-sm text-muted-foreground">${D(`noArchivedConversations`)}</div>
1011
- `:h`
1012
- <div>
1013
- ${e.map(([e,t])=>h`
1014
- <div class="border-b border-border last:border-b-0">
1015
- <div class="flex items-center justify-between gap-3 bg-muted/20 px-4 py-2 text-sm text-muted-foreground">
1016
- <div class="truncate">${this.projectLabel(e)}</div>
1017
- <div class="shrink-0">${D(`conversationCount`,{count:t.length})}</div>
1018
- </div>
1019
- ${t.map(e=>this.renderSession(e))}
1020
- </div>
1021
- `)}
1022
- </div>
1023
- `}
1024
- </section>
1025
-
1026
- ${this.message?h`<div class="rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-300">${this.message}</div>`:null}
1027
- ${this.error?h`<div class="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">${this.error}</div>`:null}
1028
- </div>
1029
- `}},U=`quickforge-archived-conversations-settings-tab`;customElements.get(U)||customElements.define(U,yt);function bt(){return document.createElement(U)}function xt(){return Array.from({length:16},()=>`ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%^&*`[Math.floor(Math.random()*65)]).join(``)}var St=class extends g{loading=!0;saving=!1;enabled=!1;hasPassword=!1;password=``;sessionTtlHours=12;activeTokenCount=0;lanUrls=[];error=``;message=``;getTabName(){return D(`lanAccess`)}async connectedCallback(){super.connectedCallback(),await this.loadStatus()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.body?{"content-type":`application/json`}:void 0,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||D(`requestFailed`));return r}applyStatus(e){this.enabled=!!e.enabled,this.hasPassword=!!e.hasPassword,this.sessionTtlHours=Number(e.sessionTtlHours||12),this.activeTokenCount=Number(e.activeTokenCount||0),this.lanUrls=Array.isArray(e.lanUrls)?e.lanUrls:[]}async loadStatus(){this.loading=!0,this.error=``,this.requestUpdate();try{this.applyStatus(await this.request(`/api/lan-access/status`))}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}updatePassword(e){this.password=e,this.requestUpdate()}updateEnabled(e){this.enabled=e,this.requestUpdate()}updateTtl(e){this.sessionTtlHours=Number(e)||12,this.requestUpdate()}async saveSettings(){if(!this.saving){if(this.enabled&&!this.hasPassword&&!this.password.trim()){this.error=D(`lanAccessPasswordRequired`),this.requestUpdate();return}if(this.password.trim()&&this.password.trim().length<8){this.error=D(`lanAccessPasswordTooShort`),this.requestUpdate();return}if(!(this.enabled&&!await S({description:D(`lanAccessEnableConfirm`),confirmLabel:D(`enabled`),cancelLabel:D(`cancel`)}))){this.saving=!0,this.error=``,this.message=``,this.requestUpdate();try{let e=await this.request(`/api/lan-access/settings`,{method:`PUT`,body:JSON.stringify({enabled:this.enabled,password:this.password.trim()||void 0,sessionTtlHours:this.sessionTtlHours})});this.applyStatus(e),this.password=``,this.message=D(`lanAccessSaved`)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}}async revokeAll(){if(await S({description:D(`lanAccessRevokeAllConfirm`),confirmLabel:D(`lanAccessRevokeAll`),cancelLabel:D(`cancel`),variant:`destructive`})){this.saving=!0,this.error=``,this.message=``,this.requestUpdate();try{let e=await this.request(`/api/lan-access/revoke-all`,{method:`POST`});this.applyStatus(e),this.message=D(`lanAccessRevoked`)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(e,t,n=!1){return h`
1030
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
1031
- <input
1032
- type="checkbox"
1033
- .checked=${e}
1034
- ?disabled=${n}
1035
- @change=${e=>t(e.target.checked)}
1036
- />
1037
- <span aria-hidden="true"></span>
1038
- </label>
1039
- `}render(){return this.loading?h`<div class="text-sm text-muted-foreground">${D(`loading`)}</div>`:h`
1040
- <div class="quickforge-settings-stack">
1041
- <div class="quickforge-settings-warning">${D(`lanAccessRiskWarning`)}</div>
1042
-
1043
- <section class="quickforge-settings-section" aria-label=${D(`lanAccessStatus`)}>
1044
- <div class="quickforge-settings-row">
1045
- <div class="quickforge-settings-row-main">
1046
- <div class="quickforge-settings-row-title">${D(`lanAccessEnabled`)}</div>
1047
- <div class="quickforge-settings-row-description">${D(`lanAccessEnabledDescription`)}</div>
1048
- </div>
1049
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.enabled?D(`enabled`):D(`disabled`)}</div>
1050
- </div>
1051
- <div class="quickforge-settings-row">
1052
- <div class="quickforge-settings-row-main">
1053
- <div class="quickforge-settings-row-title">${D(`lanAccessPassword`)}</div>
1054
- <div class="quickforge-settings-row-description">${D(`lanAccessPasswordStatusDescription`)}</div>
1055
- </div>
1056
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.hasPassword?D(`configured`):D(`notConfigured`)}</div>
1057
- </div>
1058
- <div class="quickforge-settings-row">
1059
- <div class="quickforge-settings-row-main">
1060
- <div class="quickforge-settings-row-title">${D(`lanAccessActiveDevices`)}</div>
1061
- <div class="quickforge-settings-row-description">${D(`lanAccessActiveDevicesDescription`)}</div>
1062
- </div>
1063
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.activeTokenCount}</div>
1064
- </div>
1065
- <div class="quickforge-settings-row">
1066
- <div class="quickforge-settings-row-main">
1067
- <div class="quickforge-settings-row-title">${D(`lanAccessUrls`)}</div>
1068
- <div class="quickforge-settings-row-description">${D(`lanAccessUrlsDescription`)}</div>
1069
- </div>
1070
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1071
- ${this.lanUrls.length?this.lanUrls.map(e=>h`<div>${e}</div>`):`-`}
1072
- </div>
1073
- </div>
1074
- </section>
1075
-
1076
- <section class="quickforge-settings-section" aria-label=${D(`lanAccess`)}>
1077
- <div class="quickforge-settings-row">
1078
- <div class="quickforge-settings-row-main">
1079
- <div class="quickforge-settings-row-title">${D(`lanAccessAllowFull`)}</div>
1080
- <div class="quickforge-settings-row-description">${D(`lanAccessAllowFullDescription`)}</div>
1081
- </div>
1082
- <div class="quickforge-settings-row-control">
1083
- ${this.renderSwitch(this.enabled,e=>this.updateEnabled(e),this.saving)}
1084
- </div>
1085
- </div>
1086
-
1087
- <div class="quickforge-settings-row">
1088
- <div class="quickforge-settings-row-main">
1089
- <div class="quickforge-settings-row-title">${D(`lanAccessPassword`)}</div>
1090
- <div class="quickforge-settings-row-description">${this.hasPassword?D(`lanAccessPasswordPlaceholderConfigured`):D(`lanAccessPasswordPlaceholder`)}</div>
1091
- </div>
1092
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-lan-password-control">
1093
- <input
1094
- class="quickforge-settings-input"
1095
- type="password"
1096
- .value=${this.password}
1097
- @input=${e=>this.updatePassword(e.target.value)}
1098
- placeholder=${this.hasPassword?D(`lanAccessPasswordPlaceholderConfigured`):D(`lanAccessPasswordPlaceholder`)}
1099
- />
1100
- <button
1101
- class="quickforge-settings-button quickforge-settings-button-secondary"
1102
- type="button"
1103
- @click=${()=>this.updatePassword(xt())}
1104
- >
1105
- ${D(`generatePassword`)}
1106
- </button>
1107
- </div>
1108
- </div>
1109
-
1110
- <div class="quickforge-settings-row">
1111
- <div class="quickforge-settings-row-main">
1112
- <div class="quickforge-settings-row-title">${D(`lanAccessSessionTtl`)}</div>
1113
- <div class="quickforge-settings-row-description">${D(`lanAccessSessionTtlDescription`)}</div>
1114
- </div>
1115
- <div class="quickforge-settings-row-control">
1116
- <select class="quickforge-settings-select" .value=${String(this.sessionTtlHours)} @change=${e=>this.updateTtl(e.target.value)}>
1117
- <option value="1">1 ${D(`hour`)}</option>
1118
- <option value="12">12 ${D(`hours`)}</option>
1119
- <option value="24">24 ${D(`hours`)}</option>
1120
- <option value="168">7 ${D(`days`)}</option>
1121
- </select>
1122
- </div>
1123
- </div>
1124
-
1125
- <div class="quickforge-settings-row">
1126
- <div class="quickforge-settings-row-main">
1127
- <div class="quickforge-settings-row-title">${D(`lanAccessActions`)}</div>
1128
- <div class="quickforge-settings-row-description">${D(`lanAccessActionsDescription`)}</div>
1129
- </div>
1130
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1131
- <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${this.saving} @click=${()=>this.saveSettings()}>
1132
- ${this.saving?D(`saving`):D(`save`)}
1133
- </button>
1134
- <button class="quickforge-settings-button quickforge-settings-button-danger" type="button" ?disabled=${this.saving} @click=${()=>this.revokeAll()}>
1135
- ${D(`lanAccessRevokeAll`)}
1136
- </button>
1137
- </div>
1138
- </div>
1139
- </section>
1140
-
1141
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1142
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1143
- </div>
1144
- `}},W=`quickforge-lan-access-settings-tab`;customElements.get(W)||customElements.define(W,St);function Ct(){return document.createElement(W)}var wt=18e4,Tt=3e4,Et=1e3,Dt=`https://github.com/shawnstack/quickforge/releases/latest`;function G(){if(typeof document>`u`)return!1;let e=window;return document.body.classList.contains(`quickforge-desktop-app`)||e.__quickforgeDesktopApp===!0}function Ot(e){return new Promise(t=>window.setTimeout(t,e))}var kt=[{value:`startup`,label:()=>D(`frequencyStartup`)},{value:`daily`,label:()=>D(`frequencyDaily`)},{value:`weekly`,label:()=>D(`frequencyWeekly`)},{value:`off`,label:()=>D(`frequencyOff`)}],At=class extends g{about;updateInfo;loading=!0;checking=!1;updating=!1;restarting=!1;message=``;error=``;frequency=pe.frequency;lastCheckAt=null;serviceStatus;getTabName(){return D(`about`)}async connectedCallback(){super.connectedCallback(),await this.loadAbout()}async loadAbout(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/about`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||D(`requestFailed`));this.about=t;try{let[e,t]=await Promise.all([oe(_()),this.loadServiceStatus()]);this.frequency=e.frequency,this.lastCheckAt=e.lastCheckAt,this.serviceStatus=t}catch{}}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadServiceStatus(){let e=await fetch(`/api/health`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||D(`requestFailed`));return t}async checkUpdate(){if(!(this.checking||this.updating)){if(G()){window.open(Dt,`_blank`,`noopener,noreferrer`),this.message=D(`desktopUpdateHint`),this.error=``,this.requestUpdate();return}this.checking=!0,this.message=``,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/update/check`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||D(`updateCheckFailed`));this.updateInfo=t,this.about=t,this.updateInfo.updateAvailable?this.message=D(`updateAvailableMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.updateInfo.localVersionIsNewer?this.message=D(`localVersionNewerMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.message=D(`alreadyLatestVersion`,{version:this.updateInfo.currentVersion})}catch(e){this.error=e instanceof Error?e.message:D(`updateCheckFailed`)}finally{this.checking=!1,this.requestUpdate()}}}async pollUntilUpdated(e){let t=Date.now();for(;Date.now()-t<wt;){await Ot(Et);try{let t=await fetch(`/api/health?updatePoll=${Date.now()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(t.ok&&n?.ok&&n.bootId&&n.bootId!==e){this.message=D(`updateRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(D(`updateRestartTimeout`))}async pollUntilRestarted(e){let t=Date.now();for(;Date.now()-t<Tt;){await Ot(Et);try{let t=await fetch(`/api/health?restartPoll=${Date.now()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(t.ok&&n?.ok&&n.bootId&&n.bootId!==e){this.message=D(`backendRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(D(`backendRestartTimeout`))}async restartService(){if(!this.serviceStatus||this.restarting||!await S({description:D(`restartBackendConfirm`),confirmLabel:D(`restartBackendService`),cancelLabel:D(`cancel`)}))return;this.restarting=!0,this.message=D(`backendRestarting`),this.error=``,this.requestUpdate();let e=this.serviceStatus.bootId;try{let t=await fetch(`/api/system/restart`,{method:`POST`,headers:{"x-quickforge-action":`restart`}}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||D(`backendRestartFailed`));await this.pollUntilRestarted(e)}catch(e){this.error=e instanceof Error?e.message:D(`backendRestartFailed`),this.message=``,this.restarting=!1,this.requestUpdate()}}async updateQuickForge(){if(G()){window.open(Dt,`_blank`,`noopener,noreferrer`),this.message=D(`desktopUpdateHint`),this.error=``,this.requestUpdate();return}if(!(!this.updateInfo?.updateAvailable||this.updating)&&await S({description:D(`updateConfirm`,{command:this.updateInfo.installCommand}),confirmLabel:D(`updateRuntimeNow`),cancelLabel:D(`cancel`)})){this.updating=!0,this.message=D(`updatingQuickForge`),this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/update`,{method:`POST`,headers:{"x-quickforge-action":`update`}}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||D(`updateFailed`));if(this.updateInfo=t,t?.updateStarted){let e=t.logFile?` ${D(`updateLogFile`,{path:t.logFile})}`:``;this.message=`${D(`updateStarted`)}${e}`,this.requestUpdate(),await this.pollUntilUpdated(t.bootId)}else this.message=t?.updated?D(`updateCompleted`):D(`alreadyLatestVersion`,{version:t?.currentVersion||this.about?.version||`-`}),this.updating=!1}catch(e){this.error=e instanceof Error?e.message:D(`updateFailed`),this.message=``,this.updating=!1}finally{this.requestUpdate()}}}async selectFrequency(e){if(this.frequency!==e){this.frequency=e,this.requestUpdate();try{let t=_();await De(t,{...await oe(t),frequency:e}),this.error=``}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.requestUpdate()}}}renderFrequencyOption(e){let t=this.frequency===e.value;return h`
1145
- <button
1146
- type="button"
1147
- class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
1148
- aria-pressed=${t?`true`:`false`}
1149
- @click=${()=>this.selectFrequency(e.value)}
1150
- >
1151
- ${e.label()}
1152
- </button>
1153
- `}infoRows(){let e=this.about;return e?h`
1154
- <div class="quickforge-settings-row">
1155
- <div class="quickforge-settings-row-main">
1156
- <div class="quickforge-settings-row-title">${D(`packageName`)}</div>
1157
- <div class="quickforge-settings-row-description">${D(`packageNameDescription`)}</div>
1158
- </div>
1159
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.name}</div>
1160
- </div>
1161
- <div class="quickforge-settings-row">
1162
- <div class="quickforge-settings-row-main">
1163
- <div class="quickforge-settings-row-title">${D(`currentVersion`)}</div>
1164
- <div class="quickforge-settings-row-description">${D(`currentVersionDescription`)}</div>
1165
- </div>
1166
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version}</div>
1167
- </div>
1168
- <div class="quickforge-settings-row">
1169
- <div class="quickforge-settings-row-main">
1170
- <div class="quickforge-settings-row-title">${D(`github`)}</div>
1171
- <div class="quickforge-settings-row-description">${D(`githubDescription`)}</div>
1172
- </div>
1173
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1174
- <a class="quickforge-settings-link" href=${e.repositoryUrl} target="_blank" rel="noreferrer">
1175
- ${e.repositoryUrl}
1176
- </a>
1177
- </div>
1178
- </div>
1179
- `:null}updateStatus(){return this.updateInfo?h`
1180
- <div class="quickforge-settings-row">
1181
- <div class="quickforge-settings-row-main">
1182
- <div class="quickforge-settings-row-title">${D(`latestVersion`)}</div>
1183
- <div class="quickforge-settings-row-description">${D(`latestVersionDescription`)}</div>
1184
- </div>
1185
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.updateInfo.latestVersion}</div>
1186
- </div>
1187
- ${this.updateInfo.logFile?h`
1188
- <div class="quickforge-settings-row">
1189
- <div class="quickforge-settings-row-main">
1190
- <div class="quickforge-settings-row-title">${D(`updateLog`)}</div>
1191
- <div class="quickforge-settings-row-description">${D(`updateLogDescription`)}</div>
1192
- </div>
1193
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1194
- <code>${this.updateInfo.logFile}</code>
1195
- </div>
1196
- </div>
1197
- `:null}
1198
- `:null}restartSection(){let e=this.serviceStatus?.restartUnsupportedReason||D(`backendRestartUnsupported`),t=this.restarting||!this.serviceStatus?.restartSupported;return h`
1199
- <section class="quickforge-settings-section" aria-label=${D(`restartBackendService`)}>
1200
- <div class="quickforge-settings-row">
1201
- <div class="quickforge-settings-row-main">
1202
- <div class="quickforge-settings-row-title">
1203
- ${D(`restartBackendService`)}
1204
- <quickforge-info-tip .label=${D(`restartBackendServiceDescription`)}></quickforge-info-tip>
1205
- </div>
1206
- <div class="quickforge-settings-row-description">
1207
- ${this.serviceStatus?.restartSupported?D(`restartBackendServiceDescription`):e}
1208
- </div>
1209
- </div>
1210
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1211
- <button
1212
- class="quickforge-settings-button quickforge-settings-button-danger"
1213
- type="button"
1214
- ?disabled=${t}
1215
- @click=${()=>this.restartService()}
1216
- >
1217
- ${this.restarting?D(`backendRestarting`):D(`restartBackendService`)}
1218
- </button>
1219
- </div>
1220
- </div>
1221
- </section>
1222
- `}render(){if(this.loading)return h`<div class="text-sm text-muted-foreground">${D(`loading`)}</div>`;let e=this.checking||this.updating||!this.updateInfo?.updateAvailable,t=G();return h`
1223
- <div class="quickforge-settings-stack">
1224
- <section class="quickforge-settings-section" aria-label=${D(`projectInfo`)}>
1225
- ${this.infoRows()}
1226
- </section>
1227
-
1228
- <section class="quickforge-settings-section" aria-label=${D(t?`desktopUpdates`:`runtimeUpdates`)}>
1229
- ${t?null:h`
1230
- <div class="quickforge-settings-row">
1231
- <div class="quickforge-settings-row-main">
1232
- <div class="quickforge-settings-row-title">
1233
- ${D(`updateFrequencySection`)}
1234
- <quickforge-info-tip .label=${D(`updateFrequencyDescription`)}></quickforge-info-tip>
1235
- </div>
1236
- <div class="quickforge-settings-row-description">
1237
- ${this.lastCheckAt?D(`lastCheckedAt`,{time:new Date(this.lastCheckAt).toLocaleString(Ee())}):D(`lastCheckedNever`)}
1238
- </div>
1239
- </div>
1240
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1241
- <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap" role="group" aria-label=${D(`updateFrequencySection`)}>
1242
- ${kt.map(e=>this.renderFrequencyOption(e))}
1243
- </div>
1244
- </div>
1245
- </div>
1246
-
1247
- ${this.updateStatus()}
1248
- `}
1249
-
1250
- <div class="quickforge-settings-row">
1251
- <div class="quickforge-settings-row-main">
1252
- <div class="quickforge-settings-row-title">
1253
- ${D(t?`desktopUpdates`:`runtimeUpdates`)}
1254
- <quickforge-info-tip .label=${D(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}></quickforge-info-tip>
1255
- </div>
1256
- <div class="quickforge-settings-row-description">
1257
- ${D(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}
1258
- </div>
1259
- </div>
1260
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1261
- <button
1262
- class="quickforge-settings-button quickforge-settings-button-secondary"
1263
- type="button"
1264
- ?disabled=${this.checking||this.updating}
1265
- @click=${()=>this.checkUpdate()}
1266
- >
1267
- ${this.checking?D(`checkingUpdate`):D(t?`openDesktopReleases`:`checkRuntimeUpdate`)}
1268
- </button>
1269
- ${t?null:h`
1270
- <button
1271
- class="quickforge-settings-button quickforge-settings-button-primary"
1272
- type="button"
1273
- ?disabled=${e}
1274
- @click=${()=>this.updateQuickForge()}
1275
- >
1276
- ${this.updating?D(`updatingQuickForge`):D(`updateRuntimeNow`)}
1277
- </button>
1278
- `}
1279
- </div>
1280
- </div>
1281
- </section>
1282
-
1283
- ${this.restartSection()}
1284
-
1285
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1286
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1287
- </div>
1288
- `}},K=`quickforge-about-settings-tab`;customElements.get(K)||customElements.define(K,At);function jt(){return document.createElement(K)}var Mt=class extends g{loading=!0;saving=!1;saved=!1;error=``;message=``;projects=[];project;commandDir=``;commands=[];loadingCommands=!1;saveTimer=null;lastSavedCommandDir=``;pendingSaveAfterCurrent=!1;getTabName(){return D(`projectCommands`)}async connectedCallback(){super.connectedCallback(),await this.loadProjects()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.body?{"content-type":`application/json`}:void 0,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||D(`requestFailed`));return r}async loadProjects(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await this.request(`/api/project`);this.projects=e?.projects??(e?.project?[e.project]:[]),this.project=e?.project??this.projects[0],this.commandDir=typeof this.project?.commandDir==`string`?this.project.commandDir:``,this.lastSavedCommandDir=this.commandDir,await this.loadCommands()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}selectProject(e){this.clearSaveTimer();let t=e.target,n=this.projects.find(e=>e.id===t.value);n&&(this.project=n,this.commandDir=typeof n.commandDir==`string`?n.commandDir:``,this.lastSavedCommandDir=this.commandDir,this.saved=!1,this.message=``,this.error=``,this.commands=[],this.requestUpdate(),this.loadCommands())}async loadCommands(){if(this.project?.id){this.loadingCommands=!0,this.requestUpdate();try{let e=await this.request(`/api/project/commands?projectId=${encodeURIComponent(this.project.id)}`);this.commands=e?.commands??[]}catch{this.commands=[]}finally{this.loadingCommands=!1,this.requestUpdate()}}}updateCommandDir(e){this.commandDir=e,this.saved=!1,this.message=``,this.scheduleSave(),this.requestUpdate()}clearSaveTimer(){this.saveTimer!==null&&(window.clearTimeout(this.saveTimer),this.saveTimer=null)}scheduleSave(){this.clearSaveTimer(),this.saveTimer=window.setTimeout(()=>{this.saveTimer=null,this.save()},800)}disconnectedCallback(){this.clearSaveTimer(),super.disconnectedCallback()}async save(){if(!this.project)return;if(this.saving){this.pendingSaveAfterCurrent=!0;return}let e=this.project.id,t=this.commandDir;if(t!==this.lastSavedCommandDir){this.saving=!0,this.saved=!1,this.error=``,this.message=``,this.requestUpdate();try{let n=await this.request(`/api/project/${encodeURIComponent(e)}/command-dir`,{method:`PUT`,body:JSON.stringify({commandDir:t})});if(this.project?.id===e){if(this.project=n?.project??this.project,this.project){let e=this.projects.findIndex(e=>e.id===this.project.id);e>=0&&(this.projects[e]=this.project)}this.commandDir=typeof this.project?.commandDir==`string`?this.project.commandDir:t,this.lastSavedCommandDir=this.commandDir,this.saved=!0,await this.loadCommands()}}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.saving=!1,this.pendingSaveAfterCurrent&&(this.pendingSaveAfterCurrent=!1,this.scheduleSave()),this.requestUpdate()}}}async openCommandDir(){if(this.project){this.error=``,this.message=``,this.requestUpdate();try{await this.request(`/api/project/open-path`,{method:`POST`,body:JSON.stringify({path:`.ai/commands`,projectId:this.project.id})})}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`),this.requestUpdate()}}}async createCommand(){if(!this.project)return;let e=window.prompt(D(`newCommandPrompt`));if(e?.trim()){this.error=``,this.message=``,this.requestUpdate();try{let t=await this.request(`/api/project/command`,{method:`POST`,body:JSON.stringify({name:e.trim(),projectId:this.project.id})});t.ok?(this.message=D(`commandCreated`,{name:t.name??e.trim()}),await this.loadCommands()):t.reason===`exists`?this.error=D(`commandAlreadyExists`,{name:t.name??e.trim()}):this.error=D(`invalidCommandName`)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.requestUpdate()}}}render(){return this.loading?h`<div class="quickforge-settings-note">${D(`loading`)}</div>`:this.projects.length===0?h`
1289
- <div class="quickforge-settings-stack">
1290
- <div class="quickforge-settings-note">${D(`selectProjectForCommands`)}</div>
1291
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1292
- </div>
1293
- `:h`
1294
- <div class="quickforge-settings-stack">
1295
- <section class="quickforge-settings-section" aria-label=${D(`projectCommands`)}>
1296
- <div class="quickforge-settings-row">
1297
- <div class="quickforge-settings-row-main">
1298
- <div class="quickforge-settings-row-title">${D(`project`)}</div>
1299
- <div class="quickforge-settings-row-description">
1300
- ${this.project?.path?this.project.path:D(`projectCommandsProjectDescription`)}
1301
- </div>
1302
- </div>
1303
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1304
- <select
1305
- class="quickforge-settings-select"
1306
- .value=${this.project?.id??``}
1307
- @change=${e=>this.selectProject(e)}
1308
- >
1309
- ${this.projects.map(e=>h`<option value=${e.id} ?selected=${e.id===this.project?.id}>${e.name}</option>`)}
1310
- </select>
1311
- </div>
1312
- </div>
1313
-
1314
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1315
- <div class="quickforge-settings-row-main">
1316
- <div class="quickforge-settings-row-title">
1317
- ${D(`commandDirectories`)}
1318
- <quickforge-info-tip .label=${D(`commandDirectoryHelp`)}></quickforge-info-tip>
1319
- </div>
1320
- <div class="quickforge-settings-row-description">${D(`commandDirectoriesDescription`)}</div>
1321
- </div>
1322
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1323
- <textarea
1324
- class="quickforge-settings-textarea quickforge-settings-mono"
1325
- .value=${this.commandDir}
1326
- placeholder=${D(`commandDirectoryPlaceholder`)}
1327
- @input=${e=>this.updateCommandDir(e.target.value)}
1328
- ></textarea>
1329
- </div>
1330
- </div>
1331
-
1332
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1333
- <div class="quickforge-settings-row-main">
1334
- <div class="quickforge-settings-row-title">${D(`commandDirectoryExamples`)}</div>
1335
- <div class="quickforge-settings-row-description">${D(`commandDirectoryExamplesDescription`)}</div>
1336
- </div>
1337
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-code-list">
1338
- <code>.ai/commands</code>
1339
- <code>.claude/commands</code>
1340
- <code>.opencode/commands</code>
1341
- <code>D:\shared\ai-commands</code>
1342
- </div>
1343
- </div>
1344
- </section>
1345
-
1346
- <section class="quickforge-settings-section" aria-label=${D(`loadedCommands`,{count:this.commands.length})}>
1347
- <div class="quickforge-settings-row">
1348
- <div class="quickforge-settings-row-main">
1349
- <div class="quickforge-settings-row-title">${D(`loadedCommands`,{count:this.commands.length})}</div>
1350
- <div class="quickforge-settings-row-description">${D(`loadedCommandsDescription`)}</div>
1351
- </div>
1352
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1353
- <button
1354
- class="quickforge-settings-button quickforge-settings-button-secondary"
1355
- type="button"
1356
- ?disabled=${this.loadingCommands}
1357
- @click=${()=>this.openCommandDir()}
1358
- >
1359
- ${D(`openCommandDir`)}
1360
- </button>
1361
- <button
1362
- class="quickforge-settings-button quickforge-settings-button-primary"
1363
- type="button"
1364
- ?disabled=${this.loadingCommands}
1365
- @click=${()=>this.createCommand()}
1366
- >
1367
- ${D(`createCommand`)}
1368
- </button>
1369
- </div>
1370
- </div>
1371
-
1372
- <div class="quickforge-settings-nested-list">
1373
- ${this.loadingCommands?h`<div class="quickforge-settings-empty-row">${D(`loading`)}</div>`:this.commands.length===0?h`<div class="quickforge-settings-empty-row">${D(`noCommandsLoaded`)}</div>`:this.commands.map(e=>{let t=e.argumentHint?` ${e.argumentHint}`:``;return h`
1374
- <div class="quickforge-settings-subrow">
1375
- <div class="quickforge-settings-row-main">
1376
- <div class="quickforge-settings-row-title">
1377
- <code class="quickforge-settings-command-name">/${e.name}${t}</code>
1378
- </div>
1379
- <div class="quickforge-settings-row-description">
1380
- ${e.description||D(`noDescription`)}
1381
- </div>
1382
- </div>
1383
- ${e.relativePath?h`<div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">${e.relativePath}</div>`:null}
1384
- </div>
1385
- `})}
1386
- </div>
1387
- </section>
1388
-
1389
- ${this.saving?h`<div class="quickforge-settings-note">${D(`saving`)}</div>`:null}
1390
- ${this.saved?h`<div class="quickforge-settings-message">${D(`projectCommandsSaved`)}</div>`:null}
1391
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1392
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1393
- </div>
1394
- `}},q=`quickforge-project-commands-settings-tab`;customElements.get(q)||customElements.define(q,Mt);function Nt(){return document.createElement(q)}var J={"x-quickforge-action":`channel-action`};function Pt(e){switch(e){case`running`:return`quickforge-settings-badge-success`;case`waiting_scan`:case`starting`:case`stopping`:return`quickforge-settings-badge-warning`;case`error`:return`quickforge-settings-badge-danger`;default:return`quickforge-settings-badge-muted`}}function Ft(e){switch(e){case`starting`:return D(`channelStatusStarting`);case`waiting_scan`:return D(`channelStatusWaitingScan`);case`running`:return D(`channelStatusRunning`);case`stopping`:return D(`channelStatusStopping`);case`error`:return D(`channelStatusError`);default:return D(`channelStatusStopped`)}}function It(e){if(!e)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString()}var Lt=class extends g{loading=!0;channels=[];workspaces=[];selectedWorkspaceIdByChannel={};error=``;message=``;eventSource;busyChannelId=``;getTabName(){return D(`channels`)}async connectedCallback(){super.connectedCallback(),await Promise.all([this.loadChannels(),this.loadWorkspaces()]),this.connectEvents()}disconnectedCallback(){this.eventSource?.close(),this.eventSource=void 0,super.disconnectedCallback()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.headers||{}}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||D(`requestFailed`));return r}async loadChannels(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await this.request(`/api/channels`);this.channels=Array.isArray(e.channels)?e.channels:[],this.ensureWorkspaceSelections()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadWorkspaces(){try{let e=await this.request(`/api/project`),t=e.defaultWorkspaceRoot||``,n=t?[{id:`default`,name:D(`channelDefaultWorkspace`),path:t,kind:`default`}]:[],r=(Array.isArray(e.projects)?e.projects:[]).map(e=>({id:e.id,name:e.name,path:e.path,kind:`project`}));this.workspaces=[...n,...r],this.ensureWorkspaceSelections()}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.requestUpdate()}}ensureWorkspaceSelections(){let e=new Set(this.workspaces.map(e=>e.id)),t={...this.selectedWorkspaceIdByChannel};for(let n of this.channels){if(!n.supportsWorkspaceSelection)continue;let r=t[n.id],i=n.launchWorkspace?.id,a=n.status!==`stopped`&&n.status!==`error`;if(i&&e.has(i)&&a){t[n.id]=i;continue}r&&e.has(r)||(t[n.id]=i&&e.has(i)?i:`default`)}this.selectedWorkspaceIdByChannel=t}connectEvents(){this.eventSource?.close();let e=new EventSource(`/api/channels/events`);this.eventSource=e;let t=e=>{try{let t=JSON.parse(e.data);this.applyChannelEvent(t)}catch{}};e.addEventListener(`snapshot`,t),e.addEventListener(`status`,t),e.addEventListener(`log`,t),e.addEventListener(`qrcode`,t),e.addEventListener(`error`,()=>{})}applyChannelEvent(e){if(Array.isArray(e.channels)){this.channels=e.channels,this.ensureWorkspaceSelections(),this.requestUpdate();return}if(e.snapshot){this.upsertChannel(e.snapshot);return}if(e.channelId&&e.log){let t=this.channels.find(t=>t.id===e.channelId);t&&(t.logs=[...t.logs||[],e.log].slice(-300),this.requestUpdate())}}upsertChannel(e){let t=this.channels.findIndex(t=>t.id===e.id);t>=0?this.channels=[...this.channels.slice(0,t),e,...this.channels.slice(t+1)]:this.channels=[...this.channels,e],this.ensureWorkspaceSelections(),this.requestUpdate()}selectedWorkspaceId(e){return this.selectedWorkspaceIdByChannel[e.id]||e.launchWorkspace?.id||`default`}handleWorkspaceChange(e,t){let n=t.currentTarget;n&&(this.selectedWorkspaceIdByChannel={...this.selectedWorkspaceIdByChannel,[e.id]:n.value||`default`},this.requestUpdate())}startOptions(e){return e.supportsWorkspaceSelection?{projectId:this.selectedWorkspaceId(e)}:void 0}async invoke(e,t){if(!this.busyChannelId&&!(t===`restart`&&!await S({description:D(`channelRestartConfirm`,{name:e.name}),confirmLabel:D(`restart`),cancelLabel:D(`cancel`)}))){this.busyChannelId=e.id,this.error=``,this.message=``,this.requestUpdate();try{let n=t===`start`||t===`restart`?this.startOptions(e):void 0,r=await this.request(`/api/channels/${encodeURIComponent(e.id)}/${t}`,{method:`POST`,headers:n?{...J,"content-type":`application/json`}:J,body:n?JSON.stringify(n):void 0});this.upsertChannel(r)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}async invokeAction(e,t){if(!this.busyChannelId&&!((t.destructive||t.id===`relogin`)&&!await S({description:t.id===`relogin`?D(`channelReloginConfirm`,{name:e.name}):D(`channelLogoutConfirm`,{name:e.name}),confirmLabel:t.label,cancelLabel:D(`cancel`),variant:t.destructive?`destructive`:void 0}))){this.busyChannelId=e.id,this.error=``,this.message=``,this.requestUpdate();try{let n=t.id===`relogin`?this.startOptions(e):void 0,r=await this.request(`/api/channels/${encodeURIComponent(e.id)}/actions/${encodeURIComponent(t.id)}`,{method:`POST`,headers:n?{...J,"content-type":`application/json`}:J,body:n?JSON.stringify(n):void 0});this.upsertChannel(r)}catch(e){this.error=e instanceof Error?e.message:D(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}workspaceSection(e,t){if(!e.supportsWorkspaceSelection)return null;let n=this.selectedWorkspaceId(e),r=e.launchWorkspace;return h`
1395
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1396
- <div class="quickforge-settings-row-main">
1397
- <div class="quickforge-settings-row-title">
1398
- ${D(`channelWorkspace`)}
1399
- <quickforge-info-tip .label=${D(`channelWorkspaceDescription`)}></quickforge-info-tip>
1400
- </div>
1401
- ${r?h`<div class="quickforge-settings-row-description">${D(`channelCurrentWorkspace`)}: <span class="quickforge-settings-mono">${r.path}</span></div>`:h`<div class="quickforge-settings-row-description">${D(`channelWorkspaceDescription`)}</div>`}
1402
- </div>
1403
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1404
- <select
1405
- class="quickforge-settings-select"
1406
- ?disabled=${t}
1407
- .value=${n}
1408
- @change=${t=>this.handleWorkspaceChange(e,t)}
1409
- >
1410
- ${this.workspaces.map(e=>h`
1411
- <option value=${e.id}>${e.kind===`default`?D(`channelDefaultWorkspace`):e.name} — ${e.path}</option>
1412
- `)}
1413
- </select>
1414
- </div>
1415
- </div>
1416
- `}channelMeta(e){return h`
1417
- <div class="quickforge-settings-nested-list">
1418
- ${[[D(`channelProvider`),e.provider||`-`],[D(`channelCommand`),e.commandLabel||`-`],[D(`channelPid`),e.pid?String(e.pid):`-`],[D(`channelStartedAt`),It(e.startedAt)]].map(([e,t])=>h`
1419
- <div class="quickforge-settings-subrow">
1420
- <div class="quickforge-settings-row-main">
1421
- <div class="quickforge-settings-row-title">${e}</div>
1422
- </div>
1423
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value ${e===D(`channelCommand`)?`quickforge-settings-mono`:``}">${t}</div>
1424
- </div>
1425
- `)}
1426
- </div>
1427
- `}qrSection(e){return!e.qrCodeText&&!e.qrCodeUrl&&e.status!==`waiting_scan`?null:h`
1428
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1429
- <div class="quickforge-settings-row-main">
1430
- <div class="quickforge-settings-row-title">
1431
- ${D(`channelQrTitle`)}
1432
- <quickforge-info-tip .label=${D(`channelQrDescription`)}></quickforge-info-tip>
1433
- </div>
1434
- <div class="quickforge-settings-row-description">${D(`channelQrDescription`)}</div>
1435
- ${e.qrCodeUrl?h`
1436
- <div class="quickforge-settings-meta">
1437
- <a class="quickforge-settings-link quickforge-settings-mono" href=${e.qrCodeUrl} target="_blank" rel="noreferrer">${e.qrCodeUrl}</a>
1438
- </div>
1439
- `:null}
1440
- </div>
1441
- ${e.qrCodeText?h`<pre class="quickforge-channel-qr-text">${e.qrCodeText}</pre>`:null}
1442
- </div>
1443
- `}logsSection(e){let t=e.logs||[];return h`
1444
- <details class="quickforge-channel-logs" open>
1445
- <summary class="quickforge-channel-logs-summary">${D(`channelRecentLogs`)}</summary>
1446
- <div class="quickforge-channel-logs-body">
1447
- ${t.length?t.slice(-80).map(e=>h`
1448
- <div class="quickforge-channel-log-row">
1449
- <span class="text-muted-foreground/55">${new Date(e.time).toLocaleTimeString()}</span>
1450
- <span class=${e.stream===`stderr`?`text-destructive/80`:`text-muted-foreground/70`}>${e.stream}</span>
1451
- <span class="min-w-0 whitespace-pre-wrap break-words text-foreground/85">${e.text}</span>
1452
- </div>
1453
- `):h`<div class="quickforge-settings-empty-row">${D(`channelNoLogs`)}</div>`}
1454
- </div>
1455
- </details>
1456
- `}channelCard(e){let t=this.busyChannelId===e.id||!!e.activeAction,n=e.status===`running`||e.status===`waiting_scan`||e.status===`starting`,r=e.status===`stopping`,i=!!(e.supportsWorkspaceSelection&&this.workspaces.length===0);return h`
1457
- <section class="quickforge-settings-section" aria-label=${e.name}>
1458
- <div class="quickforge-settings-row quickforge-settings-row-top">
1459
- <div class="quickforge-settings-row-main">
1460
- <div class="quickforge-settings-row-title">
1461
- ${e.name}
1462
- <span class="quickforge-settings-badge ${Pt(e.status)}">${Ft(e.status)}</span>
1463
- </div>
1464
- <div class="quickforge-settings-row-description">${e.description}</div>
1465
- ${e.requirements?.length?h`<div class="quickforge-settings-row-description">${D(`channelRequirements`)}: ${e.requirements.join(` · `)}</div>`:null}
1466
- </div>
1467
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1468
- <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${t||n||r||i} @click=${()=>this.invoke(e,`start`)}>${D(`start`)}</button>
1469
- <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||!n||r} @click=${()=>this.invoke(e,`stop`)}>${D(`stop`)}</button>
1470
- <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||r||i} @click=${()=>this.invoke(e,`restart`)}>${D(`restart`)}</button>
1471
- ${e.actions?.map(n=>h`
1472
- <button class="quickforge-settings-button ${n.destructive?`quickforge-settings-button-danger`:`quickforge-settings-button-secondary`}" type="button" ?disabled=${t} @click=${()=>this.invokeAction(e,n)}>${n.label}</button>
1473
- `)}
1474
- </div>
1475
- </div>
1476
-
1477
- ${this.workspaceSection(e,t||n||r)}
1478
- ${i?h`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${D(`channelNoWorkspaces`)}</div>`:null}
1479
- ${this.channelMeta(e)}
1480
- ${e.error?h`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${e.error}</div>`:null}
1481
- ${this.qrSection(e)}
1482
- ${this.logsSection(e)}
1483
- </section>
1484
- `}render(){return this.loading?h`<div class="quickforge-settings-note">${D(`loading`)}</div>`:h`
1485
- <div class="quickforge-settings-stack">
1486
- <section class="quickforge-settings-warning">
1487
- ${D(`channelsSecurityWarning`)}
1488
- </section>
1489
-
1490
- ${this.channels.length?h`${this.channels.map(e=>this.channelCard(e))}`:h`<div class="quickforge-settings-note">${D(`channelsEmpty`)}</div>`}
1491
-
1492
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1493
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1494
- </div>
1495
- `}},Y=`quickforge-channels-settings-tab`;customElements.get(Y)||customElements.define(Y,Lt);function Rt(){return document.createElement(Y)}var zt=ae(),X=ie(),Bt=(0,O.lazy)(()=>v(()=>import(`./AgentProfilesPage-BFgyFa5e.js`).then(e=>({default:e.AgentProfilesPage})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11]))),Vt=(0,O.lazy)(()=>v(()=>import(`./mcp-servers-dialog-Bp6kbIup.js`).then(e=>({default:e.McpServersPanel})),__vite__mapDeps([12,1,2,3,4,5,6,7,8,9,10,11]))),Ht=(0,O.lazy)(()=>v(()=>import(`./skills-dialog-DiJAUfvW.js`).then(e=>e.n).then(e=>({default:e.SkillsManagerPanel})),__vite__mapDeps([9,1,2,3,4,5,6,7,8,10,11]))),Ut=(0,O.lazy)(()=>v(()=>import(`./PluginsPage-DEMqwhOA.js`).then(e=>({default:e.PluginsPage})),__vite__mapDeps([13,1,2,3,4,5,6,7,8,9,10,11,14]))),Wt=(0,O.lazy)(()=>v(()=>import(`./ScheduledTasksPage-DTP_gedp.js`).then(e=>({default:e.ScheduledTasksPage})),__vite__mapDeps([15,1,2,3,4,5,6,7,8,9,10,11]))),Gt=class extends g{tabName=``;renderReact;root;getTabName(){return this.tabName}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>this.mountReact())}disconnectedCallback(){this.root?.unmount(),this.root=void 0,super.disconnectedCallback()}render(){return h`<div class="quickforge-react-settings-tab h-full min-h-0"></div>`}mountReact(){let e=this.querySelector(`.quickforge-react-settings-tab`);!e||!this.renderReact||(this.root??=(0,zt.createRoot)(e),this.root.render(this.renderReact()))}},Z=`quickforge-react-settings-tab`;customElements.get(Z)||customElements.define(Z,Gt);function Q(e,t){let n=document.createElement(Z);return n.tabName=e,n.renderReact=t,n}function $({children:e}){return(0,X.jsx)(`div`,{className:`quickforge-settings-stack h-full min-h-[30rem]`,children:(0,X.jsx)(O.Suspense,{fallback:(0,X.jsx)(`div`,{className:`quickforge-settings-note`,children:D(`loading`)}),children:e})})}function Kt(e){window.dispatchEvent(new CustomEvent(`quickforge:open-session-from-settings`,{detail:{sessionId:e}}))}function qt(){return Q(D(`agentsTab`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Bt,{})}))}function Jt(){return Q(D(`skills`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Ht,{active:!0,scope:`global`,embedded:!0,onSaved:()=>void 0})}))}function Yt(){return Q(D(`mcpServers`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Vt,{active:!0})}))}function Xt(){return Q(D(`plugins`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Ut,{})}))}function Zt(){return Q(D(`scheduledTasks`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Wt,{onOpenSession:Kt})}))}function Qt(e){let t=[{key:`appearance`,tab:Fe(),getDescription:()=>D(`appearanceDescription`)},{key:`defaults`,tab:$e(),getDescription:()=>D(`defaultOptionsDescription`)},{key:`memory`,tab:ot(),getDescription:()=>D(`memoryDescription`)},{key:`customModels`,tab:Me(e),getDescription:()=>D(`customModelsDescription`)},{key:`agents`,tab:qt(),getDescription:()=>D(`agentsDescription`)},{key:`skills`,tab:Jt(),getDescription:()=>D(`globalSkillsDescription`)},{key:`mcp`,tab:Yt(),getDescription:void 0},{key:`plugins`,tab:Xt(),getDescription:()=>D(`pluginsDescription`)},{key:`scheduledTasks`,tab:Zt(),getDescription:()=>D(`scheduledTasksDescription`)},{key:`projectCommands`,tab:Nt(),getDescription:()=>D(`projectCommandsDescription`)},{key:`backup`,tab:gt(),getDescription:()=>D(`backupRestoreDescription`)},{key:`archivedConversations`,tab:bt(),getDescription:()=>D(`archivedConversationsDescription`)},{key:`channels`,tab:Rt(),getDescription:()=>D(`channelsDescription`)},{key:`lanAccess`,tab:Ct(),getDescription:()=>D(`lanAccessDescription`)},{key:`about`,tab:jt(),getDescription:()=>D(`aboutQuickForgeDescription`)}];return{items:[...t],tabs:t.map(e=>e.tab),indexOf:e=>t.findIndex(t=>t.key===e)}}var $t={appearance:t,defaults:f,memory:r,customModels:ne,agents:n,skills:o,mcp:d,plugins:re,scheduledTasks:c,projectCommands:m,backup:u,archivedConversations:a,channels:l,lanAccess:p,about:i};function en({tab:e}){let t=(0,O.useRef)(null);return(0,O.useEffect)(()=>{let n=t.current;if(n)return n.replaceChildren(e),()=>{e.parentNode===n&&n.removeChild(e)}},[e]),(0,X.jsx)(`div`,{ref:t,className:`quickforge-settings-tab-host min-h-0 flex-1`})}function tn({initialTab:e,customProvider:t,onBack:n}){let r=(0,O.useMemo)(()=>Qt(t),[t]),i=Math.max(0,r.indexOf(e)),[a,o]=(0,O.useState)(),s=a??i,c=r.items[s]??r.items[0],l=c?.getDescription?.(),u=c?$t[c.key]:void 0,[d,f]=(0,O.useState)(``),p=d.trim().toLowerCase(),m=(0,O.useMemo)(()=>r.items.map((e,t)=>({item:e,index:t})).filter(({item:e})=>p?[e.key,e.tab.getTabName(),e.getDescription?.()??``].join(` `).toLowerCase().includes(p):!0),[r.items,p]);return(0,X.jsxs)(`div`,{className:`flex h-screen min-h-0 bg-[var(--quickforge-sidebar-bg)] text-foreground`,children:[(0,X.jsxs)(`aside`,{className:`relative z-10 hidden w-80 shrink-0 overflow-hidden bg-[var(--quickforge-sidebar-bg)] md:flex md:min-h-0 md:flex-col`,children:[(0,X.jsxs)(`div`,{className:`shrink-0 px-3 pb-2 pt-3`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`group relative flex w-full items-center gap-2 overflow-hidden rounded-lg px-2 py-1.5 text-left text-muted-foreground/72 transition-[background-color,color,box-shadow] duration-160 ease-out hover:bg-[var(--quickforge-sidebar-hover-bg)] hover:text-foreground/86 hover:shadow-[0_8px_20px_-18px_rgb(15_23_42_/_0.35)]`,onClick:n,"aria-label":`返回工作区`,children:[(0,X.jsx)(`span`,{className:`inline-flex size-6 shrink-0 items-center justify-center rounded-full text-muted-foreground/55 transition-colors group-hover:text-foreground/70`,children:(0,X.jsx)(te,{className:`size-4`})}),(0,X.jsx)(`span`,{className:`truncate text-sm leading-5`,children:`返回工作区`})]}),(0,X.jsxs)(`div`,{className:`quickforge-settings-inline-field mt-3`,children:[(0,X.jsx)(ee,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground/60`,"aria-hidden":`true`}),(0,X.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),placeholder:D(`searchSettings`),"aria-label":D(`searchSettings`),className:`quickforge-settings-input`})]})]}),(0,X.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3 pb-4 pt-4`,children:(0,X.jsxs)(`nav`,{className:`space-y-1`,"aria-label":D(`settings`),children:[m.map(({item:e,index:t})=>{let n=t===s,r=$t[e.key];return(0,X.jsxs)(`button`,{type:`button`,className:C(`group relative flex w-full items-center gap-2.5 overflow-hidden rounded-lg px-2 py-1.5 text-left text-sm leading-5 transition-[background-color,color,box-shadow] duration-160 ease-out`,n?`bg-[var(--quickforge-sidebar-active-bg)] font-medium text-foreground/92 shadow-[0_8px_22px_-20px_rgb(15_23_42_/_0.32)]`:`text-muted-foreground/76 hover:bg-[var(--quickforge-sidebar-hover-bg)] hover:text-foreground/90 hover:shadow-[0_8px_20px_-18px_rgb(15_23_42_/_0.35)]`),onClick:()=>o(t),"aria-current":n?`page`:void 0,children:[(0,X.jsx)(`span`,{className:C(`inline-flex size-5 shrink-0 items-center justify-center transition-colors`,n?`text-foreground/72`:`text-muted-foreground/52 group-hover:text-foreground/68`),children:(0,X.jsx)(r,{className:`size-4`,"aria-hidden":`true`})}),(0,X.jsx)(`span`,{className:`truncate`,children:e.tab.getTabName()})]},e.key)}),m.length===0?(0,X.jsx)(`div`,{className:`px-2 py-3 text-sm text-muted-foreground/70`,children:D(`noSettingsResults`)}):null]})})]}),(0,X.jsxs)(`main`,{className:`flex min-w-0 flex-1 flex-col bg-[var(--quickforge-main-bg)] md:overflow-hidden md:rounded-tl-2xl`,children:[(0,X.jsxs)(`header`,{className:`flex h-14 shrink-0 items-center gap-2 border-b-[0.5px] border-[color-mix(in_oklab,var(--border)_34%,transparent)] px-3 pr-4 md:px-5`,children:[(0,X.jsx)(xe,{variant:`ghost`,size:`icon`,className:`md:hidden`,onClick:n,"aria-label":`返回工作区`,children:(0,X.jsx)(te,{className:`size-4`})}),(0,X.jsx)(`div`,{className:`min-w-0 flex-1`,children:(0,X.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[u?(0,X.jsx)(u,{className:`size-4 shrink-0 text-muted-foreground/65`,"aria-hidden":`true`}):null,(0,X.jsx)(`div`,{className:`min-w-0 truncate text-sm font-medium text-foreground/90`,children:c?.tab.getTabName()}),l?(0,X.jsx)(ve,{label:l}):null]})})]}),(0,X.jsx)(`div`,{className:`shrink-0 border-b-[0.5px] border-[color-mix(in_oklab,var(--border)_30%,transparent)] px-3 py-2 md:hidden`,children:(0,X.jsxs)(`div`,{className:`quickforge-settings-inline-field`,children:[(0,X.jsx)(ee,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground/60`,"aria-hidden":`true`}),(0,X.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),placeholder:D(`searchSettings`),"aria-label":D(`searchSettings`),className:`quickforge-settings-input`})]})}),(0,X.jsxs)(`div`,{className:`quickforge-settings-mobile-tabs flex shrink-0 gap-1 overflow-x-auto border-b-[0.5px] border-[color-mix(in_oklab,var(--border)_30%,transparent)] px-3 py-2 md:hidden`,children:[m.map(({item:e,index:t})=>{let n=t===s,r=$t[e.key];return(0,X.jsxs)(`button`,{type:`button`,className:C(`inline-flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1.5 text-sm transition-colors`,n?`bg-muted text-foreground`:`text-muted-foreground hover:bg-muted/55 hover:text-foreground`),onClick:()=>o(t),children:[(0,X.jsx)(r,{className:`size-3.5 shrink-0`,"aria-hidden":`true`}),(0,X.jsx)(`span`,{children:e.tab.getTabName()})]},e.key)}),m.length===0?(0,X.jsx)(`div`,{className:`shrink-0 px-1 py-1.5 text-sm text-muted-foreground/70`,children:D(`noSettingsResults`)}):null]}),(0,X.jsx)(`section`,{className:`min-h-0 flex-1 overflow-y-auto px-4 py-5 md:px-8 md:py-7`,children:(0,X.jsx)(`div`,{className:`mx-auto flex min-h-full w-full max-w-5xl flex-col`,children:c?(0,X.jsx)(en,{tab:c.tab}):null})})]})]})}export{tn as SettingsWorkspacePage};