@shawnstack/quickforge 1.7.3 → 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 -1
  2. package/bin/quickforge.mjs +0 -21
  3. package/dist/assets/AgentProfilesPage-B84w-r9S.js +1 -0
  4. package/dist/assets/{ChatPanelHost-wfGCaCzD.js → ChatPanelHost-CyO8fEoj.js} +91 -91
  5. package/dist/assets/{PluginsPage-NAGroBm3.js → PluginsPage-D2862wul.js} +1 -1
  6. package/dist/assets/{ScheduledTasksPage-Bp0MNfQD.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-BQbFywVL.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-REBeTFSH.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 +4 -1
  131. package/server/acp/server.mjs +14 -9
  132. package/server/agent-manager.mjs +215 -20
  133. package/server/ai-http-logger.mjs +68 -11
  134. package/server/ai-provider-options.mjs +3 -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 +36 -23
  141. package/server/lan-access-store.mjs +93 -3
  142. package/server/network-proxy.mjs +68 -3
  143. package/server/plugins/registry.mjs +64 -4
  144. package/server/public-api.mjs +6 -2
  145. package/server/routes/agent-profiles.mjs +2 -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 +6 -3
  150. package/server/routes/project.mjs +19 -1
  151. package/server/routes/scheduled-tasks.mjs +2 -2
  152. package/server/routes/static.mjs +26 -1
  153. package/server/routes/system.mjs +11 -0
  154. package/server/routes/terminal.mjs +7 -2
  155. package/server/routes/workspace.mjs +60 -9
  156. package/server/session-utils.mjs +2 -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-D7pbg8nm.js +0 -1
  165. package/dist/assets/SettingsWorkspacePage-BtJZMIsZ.js +0 -1576
  166. package/dist/assets/SharedConversationPage-Ce3KoVZt.js +0 -1
  167. package/dist/assets/TerminalDock-4eUuXP7X.js +0 -2
  168. package/dist/assets/WorkspaceInspector-kM3BRPjY.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-C6k5taeb.js +0 -66
  182. package/dist/assets/index-CzN8NSKC.css +0 -3
  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,1576 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AgentProfilesPage-D7pbg8nm.js","assets/rolldown-runtime-DWdDZTNf.js","assets/index-C6k5taeb.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-REBeTFSH.js","assets/logger-B65Akg8A.js","assets/index-CzN8NSKC.css","assets/mcp-servers-dialog-BQbFywVL.js","assets/PluginsPage-NAGroBm3.js","assets/plugin-api-BbUkYm3Y.js","assets/ScheduledTasksPage-Bp0MNfQD.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,Ct as se,D as ce,Dt as le,E as ue,Et as de,F as fe,I as pe,O as x,Ot as S,P as me,R as he,S as ge,T as _e,Tt as ve,V as C,_t as ye,dt as be,ft as xe,gt as Se,j as Ce,k as w,mt as we,ut as T,vt as E,w as Te,wt as D,xt as Ee,yt as De,z as Oe}from"./index-C6k5taeb.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:T.name,baseUrl:T.baseUrl,protocol:`openai-completions`,modelId:T.modelId,supportsImages:T.supportsImages}},Ae=[{key:`openai`,label:S(`presetOpenai`)},{key:`deepseek`,label:S(`presetDeepseek`)},{key:`glm`,label:S(`presetGlm`)},{key:`ollama`,label:S(`presetOllama`)},{key:`litellm`,label:S(`presetLitellm`)},{key:`custom`,label:S(`presetCustom`)}],k=()=>({modelId:``,contextWindow:T.contextWindow,maxTokens:T.maxTokens,reasoning:!0,supportsImages:!1,open:!1}),A=()=>({name:T.name,baseUrl:T.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 S(`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??T.contextWindow,maxTokens:e.maxTokens??T.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 Se({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)||T.contextWindow,maxTokens:Number(e.maxTokens)||T.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=S(`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||S(`connectionFailed`))}catch(e){this.testResult=`fail`,this.testError=e?.message||S(`connectionFailed`)}finally{this.testing=!1,this.requestUpdate()}}async saveModel(){let e=this.form.name.trim(),t=this.form.baseUrl.trim();if(!e||!t){b(S(`fillProviderBaseUrlModel`));return}let n=this.form.models.filter(e=>e.modelId.trim());if(n.length===0){b(S(`atLeastOneModel`));return}let r=n.map(e=>e.modelId.trim());if(new Set(r).size!==r.length){b(S(`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??De(),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(S(`saveCustomModelFailed`))}}async deleteProvider(e){if(await C({description:S(`confirmDeleteProvider`,{name:e.name}),confirmLabel:S(`confirmDelete`),cancelLabel:S(`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(S(`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">${S(`providerProtocol`)}: ${r}</span>
12
- <span class="quickforge-settings-badge quickforge-settings-badge-info">${S(`modelsCount`,{count:n})}</span>
13
- ${n===0?h`<span class="quickforge-settings-badge quickforge-settings-badge-warning">${S(`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
- ${S(`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
- ${S(`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">${S(`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=${S(`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=${S(`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">${S(`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">${S(`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>${S(`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
- ${S(`imageInputModel`)}
113
- <quickforge-info-tip .label=${S(`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=${S(`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
- ${S(`customHeaders`)}
133
- <quickforge-info-tip .label=${S(`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=${S(`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=${S(`headerValue`)}
150
- />
151
- <button
152
- class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
153
- type="button"
154
- title=${S(`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
- + ${S(`addHeader`)}
169
- </button>
170
- </div>
171
- `}renderTestStatus(){return this.testResult===`idle`&&!this.testing?h``:this.testing?h`<span class="quickforge-settings-status">${S(`testingConnection`)}</span>`:this.testResult===`ok`?h`<span class="quickforge-settings-badge quickforge-settings-badge-success">✓ ${S(`connectionOk`)}</span>`:h`<span class="quickforge-settings-alert">✗ ${this.testError||S(`connectionFailed`)}</span>`}renderForm(){return h`
172
- <section class="quickforge-settings-section" aria-label=${this.editingProviderId?S(`editCustomModel`):S(`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
- ${S(`back`)}
181
- </button>
182
- <div class="quickforge-settings-row-main">
183
- <div class="quickforge-settings-row-title">${this.editingProviderId?S(`editCustomModel`):S(`addCustomModel`)}</div>
184
- <div class="quickforge-settings-row-description">${S(`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">${S(`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=${S(`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">${S(`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=${S(`apiKeyPlaceholder`)}
220
- />
221
- <button
222
- class="quickforge-settings-inline-field-button"
223
- type="button"
224
- title=${this.apiKeyVisible?S(`hideApiKey`):S(`showApiKey`)}
225
- aria-label=${this.apiKeyVisible?S(`hideApiKey`):S(`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">${S(`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
- + ${S(`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
- ${S(`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
- ${S(`protocolType`)}
265
- <quickforge-info-tip .label=${S(`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
- ${S(`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?S(`testingConnection`):S(`testConnection`)}
300
- </button>
301
- <button
302
- class="quickforge-settings-button quickforge-settings-button-primary"
303
- type="button"
304
- @click=${()=>this.saveModel()}
305
- >
306
- ${S(`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?S(`editCustomModel`):S(`addCustomModel`)}
316
- <quickforge-info-tip .label=${S(`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=${S(`customModelsTitle`)}>
324
- <div class="quickforge-settings-toolbar">
325
- <div>
326
- <div class="quickforge-settings-row-title">${S(`customModelsTitle`)}</div>
327
- <div class="quickforge-settings-row-description">${S(`customModelsDescription`)}</div>
328
- </div>
329
- <button
330
- class="quickforge-settings-button quickforge-settings-button-primary"
331
- type="button"
332
- @click=${()=>this.openAddForm()}
333
- >
334
- ${S(`addModel`)}
335
- </button>
336
- </div>
337
-
338
- ${this.loading?h`<div class="quickforge-settings-empty-row">${S(`loading`)}</div>`:this.providers.length===0?h`<div class="quickforge-settings-empty-row">${S(`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:()=>S(`lightTheme`)},{value:`dark`,label:()=>S(`darkTheme`)}],Pe=class extends g{theme=_e();interfaceFontSizePx=x.interfaceFontSizePx;messageFontSizePx=x.messageFontSizePx;loading=!0;error=``;getTabName(){return S(`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([ue(e),me(e)]);this.theme=t.theme,this.interfaceFontSizePx=n.interfaceFontSizePx,this.messageFontSizePx=n.messageFontSizePx}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async selectTheme(e){if(this.theme!==e){this.theme=e,this.requestUpdate();try{await ce(_(),{theme:e}),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`),this.requestUpdate()}}}currentFontSizeSettings(){return fe({interfaceFontSizePx:this.interfaceFontSizePx,messageFontSizePx:this.messageFontSizePx})}previewFontSize(e){let t=fe(e);this.interfaceFontSizePx=t.interfaceFontSizePx,this.messageFontSizePx=t.messageFontSizePx,Ce(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 pe(_(),this.currentFontSizeSettings()),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:S(`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">${S(`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">${S(`loading`)}</div>`:h`
375
- <div class="quickforge-settings-stack">
376
- <section class="quickforge-settings-section" aria-label=${S(`appearance`)}>
377
- <div class="quickforge-settings-row">
378
- <div class="quickforge-settings-row-main">
379
- <div class="quickforge-settings-row-title">
380
- ${S(`theme`)}
381
- <quickforge-info-tip .label=${S(`themeDescription`)}></quickforge-info-tip>
382
- </div>
383
- <div class="quickforge-settings-row-description">${S(`themeDescription`)}</div>
384
- </div>
385
- <div class="quickforge-settings-row-control">
386
- <div class="quickforge-settings-segmented" role="group" aria-label=${S(`theme`)}>
387
- ${Ne.map(e=>this.renderThemeOption(e))}
388
- </div>
389
- </div>
390
- </div>
391
-
392
- ${this.renderFontSizeSlider(S(`interfaceFontSize`),null,this.interfaceFontSizePx,e=>this.updateInterfaceFontSize(e))}
393
-
394
- ${this.renderFontSizeSlider(S(`messageFontSize`),S(`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:()=>S(`thinkingOff`)},{value:`low`,label:()=>S(`thinkingLow`)},{value:`medium`,label:()=>S(`thinkingMedium`)},{value:`high`,label:()=>S(`thinkingHigh`)},{value:`xhigh`,label:()=>S(`thinkingXHigh`)}],Ke=[{value:`compact`,label:()=>S(`toolDisplayCompact`)},{value:`detailed`,label:()=>S(`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=de();loading=!0;saved=!1;savedMessage=``;error=``;terminalShellConfig={terminalShell:`auto`,defaultProfileId:`auto`,profiles:[]};customShellCommand=``;customShellEditorOpen=!1;networkProxyMode=`direct`;networkProxyUrl=``;savedNetworkProxyConfig={mode:`direct`,proxyUrl:``};networkProxyStatus=null;networkProxySaving=!1;networkProxyLoaded=!1;getTabName(){return S(`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([xe(e),we(e),he(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??be(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 Promise.all([this.loadTerminalShell(),this.loadNetworkProxy()])}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}updateModel(e){let t=this.models.find(t=>I(t)===e);this.selectedModel=t,this.thinkingLevel=be(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 ve(_(),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=S(`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 loadNetworkProxy(){try{let e=await fetch(`/api/system/network-proxy`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||S(`requestFailed`));this.networkProxyMode=t?.config?.mode||`direct`,this.networkProxyUrl=t?.config?.proxyUrl||``,this.savedNetworkProxyConfig={mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl},this.networkProxyStatus=t?.status||null,this.networkProxyLoaded=!0}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}}updateNetworkProxyMode(e){this.networkProxyMode===e||this.networkProxySaving||!this.networkProxyLoaded||(this.networkProxyMode=e,this.saved=!1,this.error=``,this.requestUpdate(),!(e===`manual`&&!this.networkProxyUrl.trim())&&this.saveNetworkProxy())}updateNetworkProxyUrl(e){this.networkProxyUrl=e,this.saved=!1,this.requestUpdate()}networkProxyValidationError(){let e=this.networkProxyUrl.trim();if(!e)return S(`networkProxyAddressRequired`);try{let t=new URL(e);return[`http:`,`https:`].includes(t.protocol)?!t.hostname||!t.port?S(`networkProxyAddressPortError`):t.username||t.password?S(`networkProxyAddressCredentialsError`):t.pathname&&t.pathname!==`/`||t.search||t.hash?S(`networkProxyAddressPathError`):``:S(`networkProxyAddressProtocolError`)}catch{return S(`networkProxyAddressInvalid`)}}async saveNetworkProxy(){if(!(this.networkProxySaving||!this.networkProxyLoaded)){if(this.networkProxyMode===`manual`){let e=this.networkProxyValidationError();if(e){this.saved=!1,this.error=e,this.requestUpdate();return}}this.networkProxySaving=!0,this.saved=!1,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/network-proxy`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl})}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||S(`requestFailed`));this.networkProxyMode=t?.config?.mode||this.networkProxyMode,this.networkProxyUrl=t?.config?.proxyUrl||``,this.savedNetworkProxyConfig={mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl},this.networkProxyStatus=t?.status||null,this.markSaved(S(`networkProxySaved`))}catch(e){this.networkProxyMode=this.savedNetworkProxyConfig.mode,this.networkProxyUrl=this.savedNetworkProxyConfig.proxyUrl,this.saved=!1,this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.networkProxySaving=!1,this.requestUpdate()}}}async refreshNetworkProxy(){if(!(this.networkProxySaving||!this.networkProxyLoaded)){this.networkProxySaving=!0,this.saved=!1,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/network-proxy/refresh`,{method:`POST`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||S(`requestFailed`));this.networkProxyStatus=t?.status||null,this.markSaved(S(`networkProxyRefreshed`))}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.networkProxySaving=!1,this.requestUpdate()}}}renderNetworkProxyModeOption(e,t){let n=this.networkProxyMode===e;return h`
404
- <button
405
- type="button"
406
- class="quickforge-settings-segmented-option ${n?`quickforge-settings-segmented-option-active`:``}"
407
- aria-pressed=${n?`true`:`false`}
408
- ?disabled=${this.networkProxySaving||!this.networkProxyLoaded}
409
- @click=${()=>this.updateNetworkProxyMode(e)}
410
- >
411
- ${t}
412
- </button>
413
- `}networkProxySettings(){let e=this.networkProxyMode===`manual`?this.networkProxyValidationError():``,t=this.networkProxyMode===`manual`&&(this.networkProxyUrl!==this.savedNetworkProxyConfig.proxyUrl||this.savedNetworkProxyConfig.mode!==`manual`),n=this.networkProxyLoaded?this.networkProxyStatus?.supported===!1?this.networkProxyStatus.error||S(`networkProxyUnsupported`):S(`networkProxyStatus`,{source:this.networkProxyStatus?.source||S(`unknown`),runtime:this.networkProxyStatus?.runtimeKind||S(`unknown`)}):S(`networkProxyLoadFailed`);return h`
414
- <section class="quickforge-settings-section" aria-label=${S(`networkConnection`)}>
415
- <div class="quickforge-settings-row">
416
- <div class="quickforge-settings-row-main">
417
- <div class="quickforge-settings-row-title">
418
- ${S(`networkProxyMode`)}
419
- <quickforge-info-tip .label=${S(`networkProxyDescription`)}></quickforge-info-tip>
420
- </div>
421
- <div class="quickforge-settings-row-description">${n}</div>
422
- </div>
423
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
424
- <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap" role="group" aria-label=${S(`networkProxyMode`)}>
425
- ${this.renderNetworkProxyModeOption(`direct`,S(`networkProxyDirect`))}
426
- ${this.renderNetworkProxyModeOption(`system`,S(`networkProxySystem`))}
427
- ${this.renderNetworkProxyModeOption(`manual`,S(`networkProxyManual`))}
428
- </div>
429
- </div>
430
- </div>
431
-
432
- ${this.networkProxyMode===`manual`?h`
433
- <div class="quickforge-settings-row">
434
- <div class="quickforge-settings-row-main">
435
- <div class="quickforge-settings-row-title">${S(`networkProxyAddress`)}</div>
436
- <div class="quickforge-settings-row-description">${S(`networkProxyAddressDescription`)}</div>
437
- </div>
438
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-network-proxy-control">
439
- <input
440
- id="quickforge-network-proxy-url"
441
- class="quickforge-settings-input quickforge-settings-mono"
442
- type="url"
443
- aria-label=${S(`networkProxyAddress`)}
444
- aria-invalid=${t&&e?`true`:`false`}
445
- .value=${this.networkProxyUrl}
446
- placeholder="http://127.0.0.1:7890"
447
- ?disabled=${this.networkProxySaving||!this.networkProxyLoaded}
448
- @input=${e=>this.updateNetworkProxyUrl(e.target.value)}
449
- @keydown=${e=>{e.key===`Enter`&&this.networkProxyUrl.trim()&&(e.preventDefault(),this.saveNetworkProxy())}}
450
- />
451
- <button
452
- class="quickforge-settings-button quickforge-settings-button-primary"
453
- type="button"
454
- ?disabled=${this.networkProxySaving||!this.networkProxyLoaded||!!e||!t}
455
- @click=${()=>this.saveNetworkProxy()}
456
- >
457
- ${this.networkProxySaving?S(`saving`):S(`networkProxySave`)}
458
- </button>
459
- </div>
460
- </div>
461
- `:null}
462
-
463
- ${this.networkProxyMode===`system`?h`
464
- <div class="quickforge-settings-row">
465
- <div class="quickforge-settings-row-main">
466
- <div class="quickforge-settings-row-title">${S(`networkProxyRefresh`)}</div>
467
- <div class="quickforge-settings-row-description">${S(`networkProxyRefreshDescription`)}</div>
468
- </div>
469
- <div class="quickforge-settings-row-control">
470
- <button
471
- class="quickforge-settings-button"
472
- type="button"
473
- ?disabled=${this.networkProxySaving||!this.networkProxyLoaded}
474
- @click=${()=>this.refreshNetworkProxy()}
475
- >
476
- ${this.networkProxySaving?S(`saving`):S(`networkProxyRefresh`)}
477
- </button>
478
- </div>
479
- </div>
480
- `:null}
481
- </section>
482
- `}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||S(`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:S(`requestFailed`)}}async saveTerminalShellConfig(e,t=this.customShellProfiles(),n=S(`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||S(`requestFailed`));this.terminalShellConfig=i,this.markSaved(n)}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`),this.requestUpdate()}}async addCustomTerminalShell(){let e=this.customShellCommand.trim();if(!e){this.error=S(`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,S(`terminalShellProfilesSaved`))}async deleteCustomTerminalShell(e){let t=this.terminalShellConfig.profiles.find(t=>t.id===e);if(!t||t.builtin||!await C({description:S(`terminalShellDeleteConfirm`,{name:t.name}),confirmLabel:S(`confirmDelete`),cancelLabel:S(`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,S(`terminalShellProfilesSaved`))}async saveDefaultModelOptions(){try{let e=this.selectedModel?.reasoning?this.thinkingLevel:`off`;await ye(_(),{model:this.selectedModel,thinkingLevel:e}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:S(`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:S(`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:S(`requestFailed`),this.requestUpdate()}}async saveAutoArchiveOptions(){try{if(await We(_(),{enabled:this.autoArchiveEnabled}),this.markSaved(S(`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:S(`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`
483
- <section class="quickforge-settings-section" aria-label=${S(`terminalShell`)}>
484
- <div class="quickforge-settings-row">
485
- <div class="quickforge-settings-row-main">
486
- <div class="quickforge-settings-row-title">
487
- ${S(`terminalShellDefault`)}
488
- <quickforge-info-tip .label=${S(`terminalShellDescription`)}></quickforge-info-tip>
489
- </div>
490
- <div class="quickforge-settings-row-description quickforge-settings-mono">
491
- ${n?.command||S(`terminalShellNoDetected`)}
492
- </div>
493
- </div>
494
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
495
- <select
496
- data-terminal-shell-select
497
- class="quickforge-settings-select"
498
- .value=${r?F:t}
499
- aria-label=${S(`terminalShellDefault`)}
500
- @change=${e=>this.updateTerminalShellSelection(e.target.value)}
501
- >
502
- ${e.map(e=>h`<option value=${e.id}>${e.name}</option>`)}
503
- <option value=${F}>${S(`terminalShellCustomOption`)}</option>
504
- </select>
505
- ${n&&!n.builtin&&!r?h`
506
- <button
507
- class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
508
- type="button"
509
- title=${S(`delete`)}
510
- aria-label=${S(`delete`)}
511
- @click=${()=>this.deleteCustomTerminalShell(n.id)}
512
- >
513
- ${Ze}
514
- </button>
515
- `:null}
516
- </div>
517
- </div>
518
-
519
- ${r?h`
520
- <div class="quickforge-settings-row">
521
- <div class="quickforge-settings-row-main">
522
- <div class="quickforge-settings-row-title">${S(`terminalShellCommand`)}</div>
523
- <div class="quickforge-settings-row-description">${S(`terminalShellCustomDescription`)}</div>
524
- </div>
525
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-terminal-shell-command-control">
526
- <input
527
- class="quickforge-settings-input quickforge-settings-mono"
528
- type="text"
529
- .value=${this.customShellCommand}
530
- placeholder=${S(`terminalShellCommandPlaceholder`)}
531
- @input=${e=>{this.customShellCommand=e.target.value}}
532
- />
533
- <button
534
- class="quickforge-settings-button quickforge-settings-button-primary"
535
- type="button"
536
- title=${S(`terminalShellAdd`)}
537
- aria-label=${S(`terminalShellAdd`)}
538
- @click=${()=>this.addCustomTerminalShell()}
539
- >
540
- ${S(`terminalShellAdd`)}
541
- </button>
542
- </div>
543
- </div>
544
- `:null}
545
- </section>
546
- `}renderToolDisplayModeOption(e){let t=this.toolDisplayMode===e.value;return h`
547
- <button
548
- type="button"
549
- class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
550
- aria-pressed=${t?`true`:`false`}
551
- @click=${()=>this.updateToolDisplayMode(e.value)}
552
- >
553
- ${e.label()}
554
- </button>
555
- `}renderSwitch(e,t,n=!1){return h`
556
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
557
- <input
558
- type="checkbox"
559
- .checked=${e}
560
- ?disabled=${n}
561
- @change=${e=>t(e.target.checked)}
562
- />
563
- <span aria-hidden="true"></span>
564
- </label>
565
- `}render(){return this.loading?h`<div class="text-sm text-muted-foreground">${S(`loading`)}</div>`:h`
566
- <div class="quickforge-settings-stack">
567
- <section class="quickforge-settings-section" aria-label=${S(`defaultOptions`)}>
568
- <div class="quickforge-settings-row">
569
- <div class="quickforge-settings-row-main">
570
- <div class="quickforge-settings-row-title">
571
- ${S(`language`)}
572
- <quickforge-info-tip .label=${S(`languageDescription`)}></quickforge-info-tip>
573
- </div>
574
- <div class="quickforge-settings-row-description">${S(`displayLanguage`)}</div>
575
- </div>
576
- <div class="quickforge-settings-row-control">
577
- <select
578
- data-language-select
579
- class="quickforge-settings-select"
580
- .value=${this.selectedLanguage}
581
- @change=${e=>this.updateLanguage(e.target.value)}
582
- >
583
- <option value="zh">${S(`simplifiedChinese`)}</option>
584
- <option value="en">${S(`english`)}</option>
585
- </select>
586
- </div>
587
- </div>
588
-
589
- <div class="quickforge-settings-row">
590
- <div class="quickforge-settings-row-main">
591
- <div class="quickforge-settings-row-title">${S(`defaultModel`)}</div>
592
- <div class="quickforge-settings-row-description">${S(`defaultModelDescription`)}</div>
593
- </div>
594
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
595
- <select
596
- data-default-model-select
597
- class="quickforge-settings-select"
598
- .value=${this.selectedModel?I(this.selectedModel):``}
599
- @change=${e=>this.updateModel(e.target.value)}
600
- >
601
- ${this.modelOptions().length===0?h`<option value="">${S(`noModelAvailable`)}</option>`:this.modelOptions().map(e=>h`
602
- <option .value=${I(e)}>${Je(e)}</option>
603
- `)}
604
- </select>
605
- </div>
606
- </div>
607
-
608
- <div class="quickforge-settings-row">
609
- <div class="quickforge-settings-row-main">
610
- <div class="quickforge-settings-row-title">${S(`defaultThinkingLevel`)}</div>
611
- <div class="quickforge-settings-row-description">
612
- ${this.selectedModel?.reasoning?S(`defaultThinkingLevelDescription`):S(`thinkingRequiresReasoningModel`)}
613
- </div>
614
- </div>
615
- <div class="quickforge-settings-row-control">
616
- <select
617
- data-default-thinking-select
618
- class="quickforge-settings-select"
619
- .value=${this.thinkingLevel}
620
- ?disabled=${!this.selectedModel?.reasoning}
621
- @change=${e=>this.updateThinkingLevel(e.target.value)}
622
- >
623
- ${Ge.map(e=>h`
624
- <option .value=${e.value}>${e.label()}</option>
625
- `)}
626
- </select>
627
- </div>
628
- </div>
629
-
630
- <div class="quickforge-settings-row">
631
- <div class="quickforge-settings-row-main">
632
- <div class="quickforge-settings-row-title">
633
- ${S(`toolDisplay`)}
634
- <quickforge-info-tip .label=${S(`toolDisplayModeDescription`)}></quickforge-info-tip>
635
- </div>
636
- <div class="quickforge-settings-row-description">${S(`toolDisplayModeDescription`)}</div>
637
- </div>
638
- <div class="quickforge-settings-row-control">
639
- <div class="quickforge-settings-segmented" role="group" aria-label=${S(`toolDisplay`)}>
640
- ${Ke.map(e=>this.renderToolDisplayModeOption(e))}
641
- </div>
642
- </div>
643
- </div>
644
-
645
- <div class="quickforge-settings-row">
646
- <div class="quickforge-settings-row-main">
647
- <div class="quickforge-settings-row-title">
648
- ${S(`showContextUsage`)}
649
- <quickforge-info-tip .label=${S(`showContextUsageDescription`)}></quickforge-info-tip>
650
- </div>
651
- <div class="quickforge-settings-row-description">${S(`showContextUsageDescription`)}</div>
652
- </div>
653
- <div class="quickforge-settings-row-control">
654
- ${this.renderSwitch(this.showContextUsage,e=>this.updateShowContextUsage(e))}
655
- </div>
656
- </div>
657
-
658
- <div class="quickforge-settings-row">
659
- <div class="quickforge-settings-row-main">
660
- <div class="quickforge-settings-row-title">
661
- ${S(`autoArchiveEnabled`)}
662
- <quickforge-info-tip .label=${S(`autoArchiveDescription`)}></quickforge-info-tip>
663
- </div>
664
- <div class="quickforge-settings-row-description">${S(`autoArchiveTriggerNote`)}</div>
665
- </div>
666
- <div class="quickforge-settings-row-control">
667
- ${this.renderSwitch(this.autoArchiveEnabled,e=>this.updateAutoArchiveEnabled(e))}
668
- </div>
669
- </div>
670
-
671
- <div class="quickforge-settings-row">
672
- <div class="quickforge-settings-row-main">
673
- <div class="quickforge-settings-row-title">
674
- ${S(`autoCompactEnabled`)}
675
- <quickforge-info-tip .label=${S(`autoCompactDescription`)}></quickforge-info-tip>
676
- </div>
677
- <div class="quickforge-settings-row-description">${S(`autoCompactTriggerNote`)}</div>
678
- </div>
679
- <div class="quickforge-settings-row-control">
680
- ${this.renderSwitch(this.autoCompactEnabled,e=>this.updateAutoCompactEnabled(e))}
681
- </div>
682
- </div>
683
-
684
- <div class="quickforge-settings-row">
685
- <div class="quickforge-settings-row-main">
686
- <div class="quickforge-settings-row-title">${S(`autoCompactRequireConfirmation`)}</div>
687
- <div class="quickforge-settings-row-description">${S(`autoCompactRequireConfirmationDescription`)}</div>
688
- </div>
689
- <div class="quickforge-settings-row-control">
690
- ${this.renderSwitch(this.autoCompactRequireConfirmation,e=>this.updateAutoCompactRequireConfirmation(e),!this.autoCompactEnabled)}
691
- </div>
692
- </div>
693
-
694
- <div class="quickforge-settings-row">
695
- <div class="quickforge-settings-row-main">
696
- <div class="quickforge-settings-row-title">${S(`autoCompactThresholdPercent`)}</div>
697
- <div class="quickforge-settings-row-description">${S(`autoCompactThresholdDescription`)}</div>
698
- </div>
699
- <div class="quickforge-settings-row-control">
700
- <input
701
- class="quickforge-settings-input quickforge-settings-number-input"
702
- type="number"
703
- min="50"
704
- max="95"
705
- step="1"
706
- .value=${this.autoCompactThresholdPercentInput}
707
- ?disabled=${!this.autoCompactEnabled}
708
- @input=${e=>this.updateAutoCompactThresholdPercent(e.target.value)}
709
- @change=${()=>this.commitAutoCompactThresholdPercent()}
710
- @blur=${()=>this.commitAutoCompactThresholdPercent()}
711
- />
712
- </div>
713
- </div>
714
-
715
- <div class="quickforge-settings-row">
716
- <div class="quickforge-settings-row-main">
717
- <div class="quickforge-settings-row-title">
718
- ${S(`autoCompactKeepRecentTurns`)}
719
- <quickforge-info-tip .label=${S(`autoCompactHistoryPreserved`)}></quickforge-info-tip>
720
- </div>
721
- <div class="quickforge-settings-row-description">${S(`autoCompactKeepRecentTurnsDescription`)}</div>
722
- </div>
723
- <div class="quickforge-settings-row-control">
724
- <input
725
- class="quickforge-settings-input quickforge-settings-number-input"
726
- type="number"
727
- min="1"
728
- max="20"
729
- step="1"
730
- .value=${String(this.autoCompactKeepRecentTurns)}
731
- ?disabled=${!this.autoCompactEnabled}
732
- @input=${e=>this.updateAutoCompactKeepRecentTurns(e.target.value)}
733
- @change=${()=>this.commitAutoCompactKeepRecentTurns()}
734
- @blur=${()=>this.commitAutoCompactKeepRecentTurns()}
735
- />
736
- </div>
737
- </div>
738
- </section>
739
-
740
- ${this.networkProxySettings()}
741
-
742
- ${this.terminalShellSettings()}
743
-
744
- ${this.saved?h`<div class="quickforge-settings-message" role="status">${this.savedMessage||S(`defaultOptionsSaved`)}</div>`:null}
745
- ${this.error?h`<div class="quickforge-settings-alert" role="alert">${this.error}</div>`:null}
746
- </div>
747
- `}},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 S(`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:S(`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||S(`requestFailed`));this.markdown=n.markdown,this.savedMarkdown=n.markdown,this.memoryPath=n.path||this.memoryPath,e&&(this.message=S(`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=S(e?`memoryEnabledSaved`:`memoryDisabledSaved`)}catch(e){this.enabled=t,this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}async reloadDocument(){try{await this.loadDocument(!0)}catch(e){this.error=e instanceof Error?e.message:S(`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||S(`requestFailed`));this.markdown=t.markdown,this.savedMarkdown=t.markdown,this.memoryPath=t.path||this.memoryPath,this.message=S(`memoryContentSaved`)}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(){return h`
748
- <label class="quickforge-settings-switch" aria-disabled=${this.saving?`true`:`false`}>
749
- <input
750
- type="checkbox"
751
- .checked=${this.enabled}
752
- ?disabled=${this.saving}
753
- @change=${e=>void this.updateEnabled(e.target.checked)}
754
- />
755
- <span aria-hidden="true"></span>
756
- </label>
757
- `}render(){if(this.loading)return h`<div class="text-sm text-muted-foreground">${S(`loading`)}</div>`;let e=this.markdown!==this.savedMarkdown,t=this.saving||this.loadingDocument;return h`
758
- <div class="quickforge-settings-stack">
759
- <section class="quickforge-settings-section" aria-label=${S(`memory`)}>
760
- <div class="quickforge-settings-row">
761
- <div class="quickforge-settings-row-main">
762
- <div class="quickforge-settings-row-title">
763
- ${S(`globalMemory`)}
764
- <quickforge-info-tip .label=${S(`globalMemoryInfo`)}></quickforge-info-tip>
765
- </div>
766
- <div class="quickforge-settings-row-description">${S(`globalMemoryDescription`)}</div>
767
- </div>
768
- <div class="quickforge-settings-row-control">${this.renderSwitch()}</div>
769
- </div>
770
-
771
- <div class="quickforge-settings-row">
772
- <div class="quickforge-settings-row-main">
773
- <div class="quickforge-settings-row-title">${S(`memoryFile`)}</div>
774
- <div class="quickforge-settings-row-description">${S(`memoryFileDescription`)}</div>
775
- </div>
776
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
777
- <code class="text-xs text-muted-foreground">${this.memoryPath}</code>
778
- </div>
779
- </div>
780
-
781
- <div class="quickforge-settings-row quickforge-settings-row-align-start quickforge-memory-content-row">
782
- <div class="quickforge-settings-row-main">
783
- <div class="quickforge-settings-row-title">${S(`memoryContent`)}</div>
784
- <div class="quickforge-settings-row-description">${this.enabled?S(`memoryContentDescription`):S(`memoryContentDisabledDescription`)}</div>
785
- </div>
786
- <div class="quickforge-memory-editor-control">
787
- <textarea
788
- class="quickforge-settings-textarea quickforge-settings-mono quickforge-memory-editor"
789
- .value=${this.markdown}
790
- ?disabled=${!this.enabled||t}
791
- spellcheck="false"
792
- @input=${e=>{this.markdown=e.target.value,this.message=``,this.requestUpdate()}}
793
- ></textarea>
794
- <div class="quickforge-memory-editor-actions">
795
- <button
796
- class="quickforge-settings-button quickforge-settings-button-secondary"
797
- type="button"
798
- ?disabled=${t}
799
- @click=${()=>void this.reloadDocument()}
800
- >${this.loadingDocument?S(`loading`):S(`memoryReload`)}</button>
801
- <button
802
- class="quickforge-settings-button quickforge-settings-button-primary"
803
- type="button"
804
- ?disabled=${!this.enabled||t||!e}
805
- @click=${()=>void this.saveDocument()}
806
- >${this.saving?S(`saving`):S(`memorySave`)}</button>
807
- </div>
808
- </div>
809
- </div>
810
-
811
- <div class="quickforge-settings-note">${S(`memoryCurrentSessionNote`)}</div>
812
- </section>
813
-
814
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
815
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
816
- </div>
817
- `}},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:()=>S(`restoreSettings`),description:()=>S(`restoreSettingsDescription`)},{id:`mcp`,countKey:`mcp`,label:()=>S(`restoreMcp`),description:()=>S(`restoreMcpDescription`)},{id:`providerKeys`,countKey:`providerKeys`,label:()=>S(`restoreProviderKeys`),description:()=>S(`restoreProviderKeysDescription`)},{id:`customProviders`,countKey:`customProviders`,label:()=>S(`restoreCustomProviders`),description:()=>S(`restoreCustomProvidersDescription`)},{id:`scheduledTasks`,countKey:`scheduledTasks`,label:()=>S(`restoreScheduledTasks`),description:()=>S(`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)?S(`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 S(`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=S(`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||S(`backupExportFailed`));lt(`${st}-settings-${ut()}.json`,n),this.message=S(`backupExported`)}catch(e){this.error=B(e,S(`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||S(`backupInspectFailed`));if(!n)throw Error(S(`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?S(`backupInspectedWithIgnoredConversations`):S(`backupInspected`)}catch(e){this.error=B(e,S(`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=S(`selectAtLeastOneRestoreSection`),this.requestUpdate();return}if(await C({description:this.pendingImport.mode===`replace`?S(`backupImportReplaceConfirm`):S(`backupImportMergeConfirm`),confirmLabel:S(`confirmImportSelected`),cancelLabel:S(`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||S(`backupImportFailed`));let r=dt(n?.summary);this.safetyBackupPath=n?.safetyBackupPath||``,this.pendingImport=null,this.message=r?`${S(`backupImported`)} ${r}`:S(`backupImported`),window.setTimeout(()=>window.location.reload(),1500)}catch(e){this.error=B(e,S(`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`
818
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
819
- <input
820
- type="checkbox"
821
- .checked=${e}
822
- ?disabled=${n}
823
- @change=${e=>t(e.target.checked)}
824
- />
825
- <span aria-hidden="true"></span>
826
- </label>
827
- `}renderPendingImport(){if(!this.pendingImport)return null;let{inspect:e,selectedSections:t}=this.pendingImport,n=ft(e);return h`
828
- <section class="quickforge-settings-section" aria-label=${S(`backupInspectTitle`)}>
829
- <div class="quickforge-settings-row quickforge-settings-row-top">
830
- <div class="quickforge-settings-row-main">
831
- <div class="quickforge-settings-row-title">${S(`backupInspectTitle`)}</div>
832
- <div class="quickforge-settings-row-description">${S(`backupInspectDescription`)}</div>
833
- </div>
834
- </div>
835
-
836
- <div class="quickforge-settings-nested-list">
837
- <div class="quickforge-settings-subrow">
838
- <div class="quickforge-settings-row-main">
839
- <div class="quickforge-settings-row-title">${S(`backupInspectExportedAt`)}</div>
840
- </div>
841
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.exportedAt||`-`}</div>
842
- </div>
843
- <div class="quickforge-settings-subrow">
844
- <div class="quickforge-settings-row-main">
845
- <div class="quickforge-settings-row-title">${S(`backupInspectVersion`)}</div>
846
- </div>
847
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version??`-`}</div>
848
- </div>
849
- <div class="quickforge-settings-subrow">
850
- <div class="quickforge-settings-row-main">
851
- <div class="quickforge-settings-row-title">${S(`backupInspectScope`)}</div>
852
- </div>
853
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.scope||`-`}</div>
854
- </div>
855
- <div class="quickforge-settings-subrow">
856
- <div class="quickforge-settings-row-main">
857
- <div class="quickforge-settings-row-title">${S(`backupInspectSecrets`)}</div>
858
- </div>
859
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.includeSecrets?S(`yes`):S(`no`)}</div>
860
- </div>
861
- </div>
862
-
863
- ${e.warnings?.length?h`
864
- <div class="quickforge-settings-warning quickforge-settings-warning-attached">
865
- ${e.warnings.map(e=>h`<div>⚠ ${e}</div>`)}
866
- </div>
867
- `:null}
868
-
869
- ${e.invalidSections&&Object.keys(e.invalidSections).length?h`
870
- <div class="quickforge-settings-warning quickforge-settings-warning-attached">
871
- <div>${S(`backupInvalidSections`)}</div>
872
- ${Object.entries(e.invalidSections).map(([e,t])=>h`<div>${e}: ${t}</div>`)}
873
- </div>
874
- `:null}
875
-
876
- <div class="quickforge-settings-row">
877
- <div class="quickforge-settings-row-main">
878
- <div class="quickforge-settings-row-title">${S(`restoreMode`)}</div>
879
- <div class="quickforge-settings-row-description">${S(`restoreModeDescription`)}</div>
880
- </div>
881
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
882
- <div class="quickforge-settings-segmented" role="group" aria-label=${S(`restoreMode`)}>
883
- <button
884
- type="button"
885
- class="quickforge-settings-segmented-option ${this.pendingImport.mode===`replace`?`quickforge-settings-segmented-option-active`:``}"
886
- ?disabled=${this.busy}
887
- @click=${()=>this.setRestoreMode(`replace`)}
888
- >
889
- ${S(`restoreModeReplace`)}
890
- </button>
891
- <button
892
- type="button"
893
- class="quickforge-settings-segmented-option ${this.pendingImport.mode===`merge`?`quickforge-settings-segmented-option-active`:``}"
894
- ?disabled=${this.busy}
895
- @click=${()=>this.setRestoreMode(`merge`)}
896
- >
897
- ${S(`restoreModeMerge`)}
898
- </button>
899
- </div>
900
- </div>
901
- </div>
902
-
903
- <div class="quickforge-settings-row quickforge-settings-row-top">
904
- <div class="quickforge-settings-row-main">
905
- <div class="quickforge-settings-row-title">${S(`selectRestoreSections`)}</div>
906
- <div class="quickforge-settings-row-description">
907
- ${this.pendingImport.mode===`replace`?S(`restoreModeReplaceDescription`):S(`restoreModeMergeDescription`)}
908
- </div>
909
- </div>
910
- </div>
911
- <div class="quickforge-settings-nested-list">
912
- ${n.map(n=>h`
913
- <div class="quickforge-settings-subrow">
914
- <div class="quickforge-settings-row-main">
915
- <div class="quickforge-settings-row-title">${n.label()} (${e.sections?.[n.countKey]??0})</div>
916
- <div class="quickforge-settings-row-description">${n.description()}</div>
917
- </div>
918
- <div class="quickforge-settings-row-control">
919
- ${this.renderSwitch(t.has(n.id),e=>this.togglePendingSection(n.id,e),this.busy)}
920
- </div>
921
- </div>
922
- `)}
923
- </div>
924
-
925
- <div class="quickforge-settings-row">
926
- <div class="quickforge-settings-row-main">
927
- <div class="quickforge-settings-row-title">${S(`backupImportActions`)}</div>
928
- <div class="quickforge-settings-row-description">${S(`backupImportActionsDescription`)}</div>
929
- </div>
930
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
931
- <button
932
- class="quickforge-settings-button quickforge-settings-button-primary"
933
- type="button"
934
- ?disabled=${this.busy||t.size===0}
935
- @click=${()=>this.confirmPendingImport()}
936
- >
937
- ${this.busy?S(`loading`):S(`confirmImportSelected`)}
938
- </button>
939
- <button
940
- class="quickforge-settings-button quickforge-settings-button-secondary"
941
- type="button"
942
- ?disabled=${this.busy}
943
- @click=${()=>this.cancelPendingImport()}
944
- >
945
- ${S(`cancel`)}
946
- </button>
947
- </div>
948
- </div>
949
- </section>
950
- `}render(){return h`
951
- <div class="quickforge-settings-stack">
952
- <section class="quickforge-settings-section" aria-label=${S(`exportData`)}>
953
- <div class="quickforge-settings-row quickforge-settings-row-top">
954
- <div class="quickforge-settings-row-main">
955
- <div class="quickforge-settings-row-title">
956
- ${S(`selectExportSections`)}
957
- <quickforge-info-tip .label=${S(`exportDataDescription`)}></quickforge-info-tip>
958
- </div>
959
- <div class="quickforge-settings-row-description">${S(`selectExportSectionsDescription`)}</div>
960
- </div>
961
- </div>
962
-
963
- <div class="quickforge-settings-nested-list">
964
- ${z.map(e=>h`
965
- <div class="quickforge-settings-subrow">
966
- <div class="quickforge-settings-row-main">
967
- <div class="quickforge-settings-row-title">${e.label()}</div>
968
- <div class="quickforge-settings-row-description">${e.description()}</div>
969
- </div>
970
- <div class="quickforge-settings-row-control">
971
- ${this.renderSwitch(this.exportSections.has(e.id),t=>this.toggleExportSection(e.id,t),this.busy)}
972
- </div>
973
- </div>
974
- `)}
975
- </div>
976
-
977
- <div class="quickforge-settings-note">${S(`conversationBackupExcluded`)}</div>
978
-
979
- <div class="quickforge-settings-row">
980
- <div class="quickforge-settings-row-main">
981
- <div class="quickforge-settings-row-title">${S(`exportBackup`)}</div>
982
- <div class="quickforge-settings-row-description">${S(`exportBackupDescription`)}</div>
983
- </div>
984
- <div class="quickforge-settings-row-control">
985
- <button
986
- class="quickforge-settings-button quickforge-settings-button-primary"
987
- type="button"
988
- ?disabled=${this.busy||this.exportSections.size===0}
989
- @click=${()=>this.exportBackup()}
990
- >
991
- ${this.busy?S(`loading`):S(`exportBackup`)}
992
- </button>
993
- </div>
994
- </div>
995
- </section>
996
-
997
- <section class="quickforge-settings-section" aria-label=${S(`importData`)}>
998
- <div class="quickforge-settings-row">
999
- <div class="quickforge-settings-row-main">
1000
- <div class="quickforge-settings-row-title">
1001
- ${S(`importBackup`)}
1002
- <quickforge-info-tip .label=${S(`importDataDescription`)}></quickforge-info-tip>
1003
- </div>
1004
- <div class="quickforge-settings-row-description">${S(`importDataDescription`)}</div>
1005
- </div>
1006
- <div class="quickforge-settings-row-control">
1007
- <label class="quickforge-settings-button quickforge-settings-button-secondary ${this.busy?`pointer-events-none opacity-60`:``}">
1008
- <input
1009
- class="hidden"
1010
- type="file"
1011
- accept="application/json,.json"
1012
- ?disabled=${this.busy}
1013
- @change=${e=>this.handleFileChange(e)}
1014
- />
1015
- ${S(`importBackup`)}
1016
- </label>
1017
- </div>
1018
- </div>
1019
- </section>
1020
-
1021
- ${this.renderPendingImport()}
1022
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1023
- ${this.safetyBackupPath?h`<div class="quickforge-settings-note">${S(`backupSafetyBackupPath`)}: <code>${this.safetyBackupPath}</code></div>`:null}
1024
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1025
- </div>
1026
- `}},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(le())}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 S(`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||S(`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:S(`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`${E(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`?S(`normalChat`):this.projectNameById.get(e)||S(`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(S(`sessionNotFound`));await t.sessions.save(vt(n),vt(r)),this.sessions=this.sessions.filter(t=>t.id!==e),this.message=S(`sessionRestored`),H()}catch(e){this.error=e instanceof Error?e.message:S(`restoreSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteSession(e){if(!this.busySessionId&&await C({description:S(`deleteArchivedSessionConfirm`,{title:E(e.title)}),confirmLabel:S(`confirmDelete`),cancelLabel:S(`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=S(`archivedSessionDeleted`),H()}catch(e){this.error=e instanceof Error?e.message:S(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteAllArchivedSessions(){if(!(this.busySessionId||this.sessions.length===0)&&await C({description:S(`deleteAllArchivedSessionsConfirm`),confirmLabel:S(`confirmDelete`),cancelLabel:S(`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=S(`archivedSessionsDeleted`),H()}catch(e){this.error=e instanceof Error?e.message:S(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}renderSession(e){let t=this.busySessionId===e.id||this.busySessionId===`__all__`;return h`
1027
- <div class="border-t border-border px-4 py-3 first:border-t-0">
1028
- <div class="flex items-start justify-between gap-3">
1029
- <div class="min-w-0">
1030
- <div class="truncate text-sm font-medium text-foreground">${E(e.title)}</div>
1031
- <div class="mt-1 text-xs text-muted-foreground">
1032
- ${S(`lastModified`)}: ${_t(e.lastModified)} · ${S(`archivedAt`)}: ${_t(e.archivedAt)}
1033
- </div>
1034
- </div>
1035
- <div class="flex shrink-0 items-center gap-2">
1036
- <button
1037
- class="rounded-md border border-input px-2.5 py-1 text-xs hover:bg-muted/60 disabled:opacity-60"
1038
- type="button"
1039
- ?disabled=${t}
1040
- @click=${()=>this.restoreSession(e.id)}
1041
- >
1042
- ${S(`restoreSession`)}
1043
- </button>
1044
- <button
1045
- class="rounded-md px-2.5 py-1 text-xs text-destructive hover:bg-destructive/10 disabled:opacity-60"
1046
- type="button"
1047
- ?disabled=${t}
1048
- @click=${()=>this.deleteSession(e)}
1049
- >
1050
- ${S(`deletePermanently`)}
1051
- </button>
1052
- </div>
1053
- </div>
1054
- </div>
1055
- `}render(){let e=this.groupedSessions();return h`
1056
- <div class="flex flex-col gap-4">
1057
- <div class="flex justify-end">
1058
- <button
1059
- class="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive hover:bg-destructive/15 disabled:opacity-60"
1060
- type="button"
1061
- ?disabled=${!!this.busySessionId||this.sessions.length===0}
1062
- @click=${()=>this.deleteAllArchivedSessions()}
1063
- >
1064
- ${S(`deleteAll`)}
1065
- </button>
1066
- </div>
1067
-
1068
- <section class="rounded-lg border border-border">
1069
- <div class="grid gap-2 border-b border-border p-3 sm:grid-cols-[minmax(0,1fr)_12rem]">
1070
- <input
1071
- class="rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring/30"
1072
- type="search"
1073
- .value=${this.query}
1074
- placeholder=${S(`searchArchivedConversations`)}
1075
- @input=${e=>{this.query=e.target.value,this.requestUpdate()}}
1076
- />
1077
- <select
1078
- class="rounded-md border border-input bg-background px-3 py-2 text-sm"
1079
- .value=${this.projectFilter}
1080
- @change=${e=>{this.projectFilter=e.target.value,this.requestUpdate()}}
1081
- >
1082
- <option value="all">${S(`allProjects`)}</option>
1083
- <option value="global">${S(`normalChat`)}</option>
1084
- ${this.projects.map(e=>h`<option value=${e.id}>${e.name}</option>`)}
1085
- </select>
1086
- </div>
1087
-
1088
- ${this.loading?h`
1089
- <div class="px-4 py-8 text-center text-sm text-muted-foreground">${S(`loading`)}</div>
1090
- `:e.length===0?h`
1091
- <div class="px-4 py-8 text-center text-sm text-muted-foreground">${S(`noArchivedConversations`)}</div>
1092
- `:h`
1093
- <div>
1094
- ${e.map(([e,t])=>h`
1095
- <div class="border-b border-border last:border-b-0">
1096
- <div class="flex items-center justify-between gap-3 bg-muted/20 px-4 py-2 text-sm text-muted-foreground">
1097
- <div class="truncate">${this.projectLabel(e)}</div>
1098
- <div class="shrink-0">${S(`conversationCount`,{count:t.length})}</div>
1099
- </div>
1100
- ${t.map(e=>this.renderSession(e))}
1101
- </div>
1102
- `)}
1103
- </div>
1104
- `}
1105
- </section>
1106
-
1107
- ${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}
1108
- ${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}
1109
- </div>
1110
- `}},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 S(`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||S(`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:S(`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=S(`lanAccessPasswordRequired`),this.requestUpdate();return}if(this.password.trim()&&this.password.trim().length<8){this.error=S(`lanAccessPasswordTooShort`),this.requestUpdate();return}if(!(this.enabled&&!await C({description:S(`lanAccessEnableConfirm`),confirmLabel:S(`enabled`),cancelLabel:S(`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=S(`lanAccessSaved`)}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}}async revokeAll(){if(await C({description:S(`lanAccessRevokeAllConfirm`),confirmLabel:S(`lanAccessRevokeAll`),cancelLabel:S(`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=S(`lanAccessRevoked`)}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(e,t,n=!1){return h`
1111
- <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
1112
- <input
1113
- type="checkbox"
1114
- .checked=${e}
1115
- ?disabled=${n}
1116
- @change=${e=>t(e.target.checked)}
1117
- />
1118
- <span aria-hidden="true"></span>
1119
- </label>
1120
- `}render(){return this.loading?h`<div class="text-sm text-muted-foreground">${S(`loading`)}</div>`:h`
1121
- <div class="quickforge-settings-stack">
1122
- <div class="quickforge-settings-warning">${S(`lanAccessRiskWarning`)}</div>
1123
-
1124
- <section class="quickforge-settings-section" aria-label=${S(`lanAccessStatus`)}>
1125
- <div class="quickforge-settings-row">
1126
- <div class="quickforge-settings-row-main">
1127
- <div class="quickforge-settings-row-title">${S(`lanAccessEnabled`)}</div>
1128
- <div class="quickforge-settings-row-description">${S(`lanAccessEnabledDescription`)}</div>
1129
- </div>
1130
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.enabled?S(`enabled`):S(`disabled`)}</div>
1131
- </div>
1132
- <div class="quickforge-settings-row">
1133
- <div class="quickforge-settings-row-main">
1134
- <div class="quickforge-settings-row-title">${S(`lanAccessPassword`)}</div>
1135
- <div class="quickforge-settings-row-description">${S(`lanAccessPasswordStatusDescription`)}</div>
1136
- </div>
1137
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.hasPassword?S(`configured`):S(`notConfigured`)}</div>
1138
- </div>
1139
- <div class="quickforge-settings-row">
1140
- <div class="quickforge-settings-row-main">
1141
- <div class="quickforge-settings-row-title">${S(`lanAccessActiveDevices`)}</div>
1142
- <div class="quickforge-settings-row-description">${S(`lanAccessActiveDevicesDescription`)}</div>
1143
- </div>
1144
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.activeTokenCount}</div>
1145
- </div>
1146
- <div class="quickforge-settings-row">
1147
- <div class="quickforge-settings-row-main">
1148
- <div class="quickforge-settings-row-title">${S(`lanAccessUrls`)}</div>
1149
- <div class="quickforge-settings-row-description">${S(`lanAccessUrlsDescription`)}</div>
1150
- </div>
1151
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1152
- ${this.lanUrls.length?this.lanUrls.map(e=>h`<div>${e}</div>`):`-`}
1153
- </div>
1154
- </div>
1155
- </section>
1156
-
1157
- <section class="quickforge-settings-section" aria-label=${S(`lanAccess`)}>
1158
- <div class="quickforge-settings-row">
1159
- <div class="quickforge-settings-row-main">
1160
- <div class="quickforge-settings-row-title">${S(`lanAccessAllowFull`)}</div>
1161
- <div class="quickforge-settings-row-description">${S(`lanAccessAllowFullDescription`)}</div>
1162
- </div>
1163
- <div class="quickforge-settings-row-control">
1164
- ${this.renderSwitch(this.enabled,e=>this.updateEnabled(e),this.saving)}
1165
- </div>
1166
- </div>
1167
-
1168
- <div class="quickforge-settings-row">
1169
- <div class="quickforge-settings-row-main">
1170
- <div class="quickforge-settings-row-title">${S(`lanAccessPassword`)}</div>
1171
- <div class="quickforge-settings-row-description">${this.hasPassword?S(`lanAccessPasswordPlaceholderConfigured`):S(`lanAccessPasswordPlaceholder`)}</div>
1172
- </div>
1173
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-lan-password-control">
1174
- <input
1175
- class="quickforge-settings-input"
1176
- type="password"
1177
- .value=${this.password}
1178
- @input=${e=>this.updatePassword(e.target.value)}
1179
- placeholder=${this.hasPassword?S(`lanAccessPasswordPlaceholderConfigured`):S(`lanAccessPasswordPlaceholder`)}
1180
- />
1181
- <button
1182
- class="quickforge-settings-button quickforge-settings-button-secondary"
1183
- type="button"
1184
- @click=${()=>this.updatePassword(xt())}
1185
- >
1186
- ${S(`generatePassword`)}
1187
- </button>
1188
- </div>
1189
- </div>
1190
-
1191
- <div class="quickforge-settings-row">
1192
- <div class="quickforge-settings-row-main">
1193
- <div class="quickforge-settings-row-title">${S(`lanAccessSessionTtl`)}</div>
1194
- <div class="quickforge-settings-row-description">${S(`lanAccessSessionTtlDescription`)}</div>
1195
- </div>
1196
- <div class="quickforge-settings-row-control">
1197
- <select class="quickforge-settings-select" .value=${String(this.sessionTtlHours)} @change=${e=>this.updateTtl(e.target.value)}>
1198
- <option value="1">1 ${S(`hour`)}</option>
1199
- <option value="12">12 ${S(`hours`)}</option>
1200
- <option value="24">24 ${S(`hours`)}</option>
1201
- <option value="168">7 ${S(`days`)}</option>
1202
- </select>
1203
- </div>
1204
- </div>
1205
-
1206
- <div class="quickforge-settings-row">
1207
- <div class="quickforge-settings-row-main">
1208
- <div class="quickforge-settings-row-title">${S(`lanAccessActions`)}</div>
1209
- <div class="quickforge-settings-row-description">${S(`lanAccessActionsDescription`)}</div>
1210
- </div>
1211
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1212
- <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${this.saving} @click=${()=>this.saveSettings()}>
1213
- ${this.saving?S(`saving`):S(`save`)}
1214
- </button>
1215
- <button class="quickforge-settings-button quickforge-settings-button-danger" type="button" ?disabled=${this.saving} @click=${()=>this.revokeAll()}>
1216
- ${S(`lanAccessRevokeAll`)}
1217
- </button>
1218
- </div>
1219
- </div>
1220
- </section>
1221
-
1222
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1223
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1224
- </div>
1225
- `}},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:()=>S(`frequencyStartup`)},{value:`daily`,label:()=>S(`frequencyDaily`)},{value:`weekly`,label:()=>S(`frequencyWeekly`)},{value:`off`,label:()=>S(`frequencyOff`)}],At=class extends g{about;updateInfo;loading=!0;checking=!1;updating=!1;restarting=!1;message=``;error=``;frequency=ge.frequency;lastCheckAt=null;serviceStatus;getTabName(){return S(`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||S(`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:S(`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||S(`requestFailed`));return t}async checkUpdate(){if(!(this.checking||this.updating)){if(G()){window.open(Dt,`_blank`,`noopener,noreferrer`),this.message=S(`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||S(`updateCheckFailed`));this.updateInfo=t,this.about=t,this.updateInfo.updateAvailable?this.message=S(`updateAvailableMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.updateInfo.localVersionIsNewer?this.message=S(`localVersionNewerMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.message=S(`alreadyLatestVersion`,{version:this.updateInfo.currentVersion})}catch(e){this.error=e instanceof Error?e.message:S(`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=S(`updateRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(S(`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=S(`backendRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(S(`backendRestartTimeout`))}async restartService(){if(!this.serviceStatus||this.restarting||!await C({description:S(`restartBackendConfirm`),confirmLabel:S(`restartBackendService`),cancelLabel:S(`cancel`)}))return;this.restarting=!0,this.message=S(`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||S(`backendRestartFailed`));await this.pollUntilRestarted(e)}catch(e){this.error=e instanceof Error?e.message:S(`backendRestartFailed`),this.message=``,this.restarting=!1,this.requestUpdate()}}async updateQuickForge(){if(G()){window.open(Dt,`_blank`,`noopener,noreferrer`),this.message=S(`desktopUpdateHint`),this.error=``,this.requestUpdate();return}if(!(!this.updateInfo?.updateAvailable||this.updating)&&await C({description:S(`updateConfirm`,{command:this.updateInfo.installCommand}),confirmLabel:S(`updateRuntimeNow`),cancelLabel:S(`cancel`)})){this.updating=!0,this.message=S(`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||S(`updateFailed`));if(this.updateInfo=t,t?.updateStarted){let e=t.logFile?` ${S(`updateLogFile`,{path:t.logFile})}`:``;this.message=`${S(`updateStarted`)}${e}`,this.requestUpdate(),await this.pollUntilUpdated(t.bootId)}else this.message=t?.updated?S(`updateCompleted`):S(`alreadyLatestVersion`,{version:t?.currentVersion||this.about?.version||`-`}),this.updating=!1}catch(e){this.error=e instanceof Error?e.message:S(`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 Te(t,{...await oe(t),frequency:e}),this.error=``}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.requestUpdate()}}}renderFrequencyOption(e){let t=this.frequency===e.value;return h`
1226
- <button
1227
- type="button"
1228
- class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
1229
- aria-pressed=${t?`true`:`false`}
1230
- @click=${()=>this.selectFrequency(e.value)}
1231
- >
1232
- ${e.label()}
1233
- </button>
1234
- `}infoRows(){let e=this.about;return e?h`
1235
- <div class="quickforge-settings-row">
1236
- <div class="quickforge-settings-row-main">
1237
- <div class="quickforge-settings-row-title">${S(`packageName`)}</div>
1238
- <div class="quickforge-settings-row-description">${S(`packageNameDescription`)}</div>
1239
- </div>
1240
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.name}</div>
1241
- </div>
1242
- <div class="quickforge-settings-row">
1243
- <div class="quickforge-settings-row-main">
1244
- <div class="quickforge-settings-row-title">${S(`currentVersion`)}</div>
1245
- <div class="quickforge-settings-row-description">${S(`currentVersionDescription`)}</div>
1246
- </div>
1247
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version}</div>
1248
- </div>
1249
- <div class="quickforge-settings-row">
1250
- <div class="quickforge-settings-row-main">
1251
- <div class="quickforge-settings-row-title">${S(`github`)}</div>
1252
- <div class="quickforge-settings-row-description">${S(`githubDescription`)}</div>
1253
- </div>
1254
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1255
- <a class="quickforge-settings-link" href=${e.repositoryUrl} target="_blank" rel="noreferrer">
1256
- ${e.repositoryUrl}
1257
- </a>
1258
- </div>
1259
- </div>
1260
- `:null}updateStatus(){return this.updateInfo?h`
1261
- <div class="quickforge-settings-row">
1262
- <div class="quickforge-settings-row-main">
1263
- <div class="quickforge-settings-row-title">${S(`latestVersion`)}</div>
1264
- <div class="quickforge-settings-row-description">${S(`latestVersionDescription`)}</div>
1265
- </div>
1266
- <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.updateInfo.latestVersion}</div>
1267
- </div>
1268
- ${this.updateInfo.logFile?h`
1269
- <div class="quickforge-settings-row">
1270
- <div class="quickforge-settings-row-main">
1271
- <div class="quickforge-settings-row-title">${S(`updateLog`)}</div>
1272
- <div class="quickforge-settings-row-description">${S(`updateLogDescription`)}</div>
1273
- </div>
1274
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1275
- <code>${this.updateInfo.logFile}</code>
1276
- </div>
1277
- </div>
1278
- `:null}
1279
- `:null}restartSection(){let e=this.serviceStatus?.restartUnsupportedReason||S(`backendRestartUnsupported`),t=this.restarting||!this.serviceStatus?.restartSupported;return h`
1280
- <section class="quickforge-settings-section" aria-label=${S(`restartBackendService`)}>
1281
- <div class="quickforge-settings-row">
1282
- <div class="quickforge-settings-row-main">
1283
- <div class="quickforge-settings-row-title">
1284
- ${S(`restartBackendService`)}
1285
- <quickforge-info-tip .label=${S(`restartBackendServiceDescription`)}></quickforge-info-tip>
1286
- </div>
1287
- <div class="quickforge-settings-row-description">
1288
- ${this.serviceStatus?.restartSupported?S(`restartBackendServiceDescription`):e}
1289
- </div>
1290
- </div>
1291
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1292
- <button
1293
- class="quickforge-settings-button quickforge-settings-button-danger"
1294
- type="button"
1295
- ?disabled=${t}
1296
- @click=${()=>this.restartService()}
1297
- >
1298
- ${this.restarting?S(`backendRestarting`):S(`restartBackendService`)}
1299
- </button>
1300
- </div>
1301
- </div>
1302
- </section>
1303
- `}render(){if(this.loading)return h`<div class="text-sm text-muted-foreground">${S(`loading`)}</div>`;let e=this.checking||this.updating||!this.updateInfo?.updateAvailable,t=G();return h`
1304
- <div class="quickforge-settings-stack">
1305
- <section class="quickforge-settings-section" aria-label=${S(`projectInfo`)}>
1306
- ${this.infoRows()}
1307
- </section>
1308
-
1309
- <section class="quickforge-settings-section" aria-label=${S(t?`desktopUpdates`:`runtimeUpdates`)}>
1310
- ${t?null:h`
1311
- <div class="quickforge-settings-row">
1312
- <div class="quickforge-settings-row-main">
1313
- <div class="quickforge-settings-row-title">
1314
- ${S(`updateFrequencySection`)}
1315
- <quickforge-info-tip .label=${S(`updateFrequencyDescription`)}></quickforge-info-tip>
1316
- </div>
1317
- <div class="quickforge-settings-row-description">
1318
- ${this.lastCheckAt?S(`lastCheckedAt`,{time:new Date(this.lastCheckAt).toLocaleString(le())}):S(`lastCheckedNever`)}
1319
- </div>
1320
- </div>
1321
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1322
- <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap" role="group" aria-label=${S(`updateFrequencySection`)}>
1323
- ${kt.map(e=>this.renderFrequencyOption(e))}
1324
- </div>
1325
- </div>
1326
- </div>
1327
-
1328
- ${this.updateStatus()}
1329
- `}
1330
-
1331
- <div class="quickforge-settings-row">
1332
- <div class="quickforge-settings-row-main">
1333
- <div class="quickforge-settings-row-title">
1334
- ${S(t?`desktopUpdates`:`runtimeUpdates`)}
1335
- <quickforge-info-tip .label=${S(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}></quickforge-info-tip>
1336
- </div>
1337
- <div class="quickforge-settings-row-description">
1338
- ${S(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}
1339
- </div>
1340
- </div>
1341
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1342
- <button
1343
- class="quickforge-settings-button quickforge-settings-button-secondary"
1344
- type="button"
1345
- ?disabled=${this.checking||this.updating}
1346
- @click=${()=>this.checkUpdate()}
1347
- >
1348
- ${this.checking?S(`checkingUpdate`):S(t?`openDesktopReleases`:`checkRuntimeUpdate`)}
1349
- </button>
1350
- ${t?null:h`
1351
- <button
1352
- class="quickforge-settings-button quickforge-settings-button-primary"
1353
- type="button"
1354
- ?disabled=${e}
1355
- @click=${()=>this.updateQuickForge()}
1356
- >
1357
- ${this.updating?S(`updatingQuickForge`):S(`updateRuntimeNow`)}
1358
- </button>
1359
- `}
1360
- </div>
1361
- </div>
1362
- </section>
1363
-
1364
- ${this.restartSection()}
1365
-
1366
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1367
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1368
- </div>
1369
- `}},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 S(`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||S(`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:S(`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:S(`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:S(`requestFailed`),this.requestUpdate()}}}async createCommand(){if(!this.project)return;let e=window.prompt(S(`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=S(`commandCreated`,{name:t.name??e.trim()}),await this.loadCommands()):t.reason===`exists`?this.error=S(`commandAlreadyExists`,{name:t.name??e.trim()}):this.error=S(`invalidCommandName`)}catch(e){this.error=e instanceof Error?e.message:S(`requestFailed`)}finally{this.requestUpdate()}}}render(){return this.loading?h`<div class="quickforge-settings-note">${S(`loading`)}</div>`:this.projects.length===0?h`
1370
- <div class="quickforge-settings-stack">
1371
- <div class="quickforge-settings-note">${S(`selectProjectForCommands`)}</div>
1372
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1373
- </div>
1374
- `:h`
1375
- <div class="quickforge-settings-stack">
1376
- <section class="quickforge-settings-section" aria-label=${S(`projectCommands`)}>
1377
- <div class="quickforge-settings-row">
1378
- <div class="quickforge-settings-row-main">
1379
- <div class="quickforge-settings-row-title">${S(`project`)}</div>
1380
- <div class="quickforge-settings-row-description">
1381
- ${this.project?.path?this.project.path:S(`projectCommandsProjectDescription`)}
1382
- </div>
1383
- </div>
1384
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1385
- <select
1386
- class="quickforge-settings-select"
1387
- .value=${this.project?.id??``}
1388
- @change=${e=>this.selectProject(e)}
1389
- >
1390
- ${this.projects.map(e=>h`<option value=${e.id} ?selected=${e.id===this.project?.id}>${e.name}</option>`)}
1391
- </select>
1392
- </div>
1393
- </div>
1394
-
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
- ${S(`commandDirectories`)}
1399
- <quickforge-info-tip .label=${S(`commandDirectoryHelp`)}></quickforge-info-tip>
1400
- </div>
1401
- <div class="quickforge-settings-row-description">${S(`commandDirectoriesDescription`)}</div>
1402
- </div>
1403
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1404
- <textarea
1405
- class="quickforge-settings-textarea quickforge-settings-mono"
1406
- .value=${this.commandDir}
1407
- placeholder=${S(`commandDirectoryPlaceholder`)}
1408
- @input=${e=>this.updateCommandDir(e.target.value)}
1409
- ></textarea>
1410
- </div>
1411
- </div>
1412
-
1413
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1414
- <div class="quickforge-settings-row-main">
1415
- <div class="quickforge-settings-row-title">${S(`commandDirectoryExamples`)}</div>
1416
- <div class="quickforge-settings-row-description">${S(`commandDirectoryExamplesDescription`)}</div>
1417
- </div>
1418
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-code-list">
1419
- <code>.ai/commands</code>
1420
- <code>.claude/commands</code>
1421
- <code>.opencode/commands</code>
1422
- <code>D:\shared\ai-commands</code>
1423
- </div>
1424
- </div>
1425
- </section>
1426
-
1427
- <section class="quickforge-settings-section" aria-label=${S(`loadedCommands`,{count:this.commands.length})}>
1428
- <div class="quickforge-settings-row">
1429
- <div class="quickforge-settings-row-main">
1430
- <div class="quickforge-settings-row-title">${S(`loadedCommands`,{count:this.commands.length})}</div>
1431
- <div class="quickforge-settings-row-description">${S(`loadedCommandsDescription`)}</div>
1432
- </div>
1433
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1434
- <button
1435
- class="quickforge-settings-button quickforge-settings-button-secondary"
1436
- type="button"
1437
- ?disabled=${this.loadingCommands}
1438
- @click=${()=>this.openCommandDir()}
1439
- >
1440
- ${S(`openCommandDir`)}
1441
- </button>
1442
- <button
1443
- class="quickforge-settings-button quickforge-settings-button-primary"
1444
- type="button"
1445
- ?disabled=${this.loadingCommands}
1446
- @click=${()=>this.createCommand()}
1447
- >
1448
- ${S(`createCommand`)}
1449
- </button>
1450
- </div>
1451
- </div>
1452
-
1453
- <div class="quickforge-settings-nested-list">
1454
- ${this.loadingCommands?h`<div class="quickforge-settings-empty-row">${S(`loading`)}</div>`:this.commands.length===0?h`<div class="quickforge-settings-empty-row">${S(`noCommandsLoaded`)}</div>`:this.commands.map(e=>{let t=e.argumentHint?` ${e.argumentHint}`:``;return h`
1455
- <div class="quickforge-settings-subrow">
1456
- <div class="quickforge-settings-row-main">
1457
- <div class="quickforge-settings-row-title">
1458
- <code class="quickforge-settings-command-name">/${e.name}${t}</code>
1459
- </div>
1460
- <div class="quickforge-settings-row-description">
1461
- ${e.description||S(`noDescription`)}
1462
- </div>
1463
- </div>
1464
- ${e.relativePath?h`<div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">${e.relativePath}</div>`:null}
1465
- </div>
1466
- `})}
1467
- </div>
1468
- </section>
1469
-
1470
- ${this.saving?h`<div class="quickforge-settings-note">${S(`saving`)}</div>`:null}
1471
- ${this.saved?h`<div class="quickforge-settings-message">${S(`projectCommandsSaved`)}</div>`:null}
1472
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1473
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1474
- </div>
1475
- `}},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 S(`channelStatusStarting`);case`waiting_scan`:return S(`channelStatusWaitingScan`);case`running`:return S(`channelStatusRunning`);case`stopping`:return S(`channelStatusStopping`);case`error`:return S(`channelStatusError`);default:return S(`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 S(`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||S(`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:S(`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:S(`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:S(`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 C({description:S(`channelRestartConfirm`,{name:e.name}),confirmLabel:S(`restart`),cancelLabel:S(`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:S(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}async invokeAction(e,t){if(!this.busyChannelId&&!((t.destructive||t.id===`relogin`)&&!await C({description:t.id===`relogin`?S(`channelReloginConfirm`,{name:e.name}):S(`channelLogoutConfirm`,{name:e.name}),confirmLabel:t.label,cancelLabel:S(`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:S(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}workspaceSection(e,t){if(!e.supportsWorkspaceSelection)return null;let n=this.selectedWorkspaceId(e),r=e.launchWorkspace;return h`
1476
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1477
- <div class="quickforge-settings-row-main">
1478
- <div class="quickforge-settings-row-title">
1479
- ${S(`channelWorkspace`)}
1480
- <quickforge-info-tip .label=${S(`channelWorkspaceDescription`)}></quickforge-info-tip>
1481
- </div>
1482
- ${r?h`<div class="quickforge-settings-row-description">${S(`channelCurrentWorkspace`)}: <span class="quickforge-settings-mono">${r.path}</span></div>`:h`<div class="quickforge-settings-row-description">${S(`channelWorkspaceDescription`)}</div>`}
1483
- </div>
1484
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1485
- <select
1486
- class="quickforge-settings-select"
1487
- ?disabled=${t}
1488
- .value=${n}
1489
- @change=${t=>this.handleWorkspaceChange(e,t)}
1490
- >
1491
- ${this.workspaces.map(e=>h`
1492
- <option value=${e.id}>${e.kind===`default`?S(`channelDefaultWorkspace`):e.name} — ${e.path}</option>
1493
- `)}
1494
- </select>
1495
- </div>
1496
- </div>
1497
- `}channelMeta(e){return h`
1498
- <div class="quickforge-settings-nested-list">
1499
- ${[[S(`channelProvider`),e.provider||`-`],[S(`channelCommand`),e.commandLabel||`-`],[S(`channelPid`),e.pid?String(e.pid):`-`],[S(`channelStartedAt`),It(e.startedAt)]].map(([e,t])=>h`
1500
- <div class="quickforge-settings-subrow">
1501
- <div class="quickforge-settings-row-main">
1502
- <div class="quickforge-settings-row-title">${e}</div>
1503
- </div>
1504
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value ${e===S(`channelCommand`)?`quickforge-settings-mono`:``}">${t}</div>
1505
- </div>
1506
- `)}
1507
- </div>
1508
- `}qrSection(e){return!e.qrCodeText&&!e.qrCodeUrl&&e.status!==`waiting_scan`?null:h`
1509
- <div class="quickforge-settings-row quickforge-settings-row-align-start">
1510
- <div class="quickforge-settings-row-main">
1511
- <div class="quickforge-settings-row-title">
1512
- ${S(`channelQrTitle`)}
1513
- <quickforge-info-tip .label=${S(`channelQrDescription`)}></quickforge-info-tip>
1514
- </div>
1515
- <div class="quickforge-settings-row-description">${S(`channelQrDescription`)}</div>
1516
- ${e.qrCodeUrl?h`
1517
- <div class="quickforge-settings-meta">
1518
- <a class="quickforge-settings-link quickforge-settings-mono" href=${e.qrCodeUrl} target="_blank" rel="noreferrer">${e.qrCodeUrl}</a>
1519
- </div>
1520
- `:null}
1521
- </div>
1522
- ${e.qrCodeText?h`<pre class="quickforge-channel-qr-text">${e.qrCodeText}</pre>`:null}
1523
- </div>
1524
- `}logsSection(e){let t=e.logs||[];return h`
1525
- <details class="quickforge-channel-logs" open>
1526
- <summary class="quickforge-channel-logs-summary">${S(`channelRecentLogs`)}</summary>
1527
- <div class="quickforge-channel-logs-body">
1528
- ${t.length?t.slice(-80).map(e=>h`
1529
- <div class="quickforge-channel-log-row">
1530
- <span class="text-muted-foreground/55">${new Date(e.time).toLocaleTimeString()}</span>
1531
- <span class=${e.stream===`stderr`?`text-destructive/80`:`text-muted-foreground/70`}>${e.stream}</span>
1532
- <span class="min-w-0 whitespace-pre-wrap break-words text-foreground/85">${e.text}</span>
1533
- </div>
1534
- `):h`<div class="quickforge-settings-empty-row">${S(`channelNoLogs`)}</div>`}
1535
- </div>
1536
- </details>
1537
- `}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`
1538
- <section class="quickforge-settings-section" aria-label=${e.name}>
1539
- <div class="quickforge-settings-row quickforge-settings-row-top">
1540
- <div class="quickforge-settings-row-main">
1541
- <div class="quickforge-settings-row-title">
1542
- ${e.name}
1543
- <span class="quickforge-settings-badge ${Pt(e.status)}">${Ft(e.status)}</span>
1544
- </div>
1545
- <div class="quickforge-settings-row-description">${e.description}</div>
1546
- ${e.requirements?.length?h`<div class="quickforge-settings-row-description">${S(`channelRequirements`)}: ${e.requirements.join(` · `)}</div>`:null}
1547
- </div>
1548
- <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1549
- <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${t||n||r||i} @click=${()=>this.invoke(e,`start`)}>${S(`start`)}</button>
1550
- <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||!n||r} @click=${()=>this.invoke(e,`stop`)}>${S(`stop`)}</button>
1551
- <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||r||i} @click=${()=>this.invoke(e,`restart`)}>${S(`restart`)}</button>
1552
- ${e.actions?.map(n=>h`
1553
- <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>
1554
- `)}
1555
- </div>
1556
- </div>
1557
-
1558
- ${this.workspaceSection(e,t||n||r)}
1559
- ${i?h`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${S(`channelNoWorkspaces`)}</div>`:null}
1560
- ${this.channelMeta(e)}
1561
- ${e.error?h`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${e.error}</div>`:null}
1562
- ${this.qrSection(e)}
1563
- ${this.logsSection(e)}
1564
- </section>
1565
- `}render(){return this.loading?h`<div class="quickforge-settings-note">${S(`loading`)}</div>`:h`
1566
- <div class="quickforge-settings-stack">
1567
- <section class="quickforge-settings-warning">
1568
- ${S(`channelsSecurityWarning`)}
1569
- </section>
1570
-
1571
- ${this.channels.length?h`${this.channels.map(e=>this.channelCard(e))}`:h`<div class="quickforge-settings-note">${S(`channelsEmpty`)}</div>`}
1572
-
1573
- ${this.message?h`<div class="quickforge-settings-message">${this.message}</div>`:null}
1574
- ${this.error?h`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1575
- </div>
1576
- `}},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-D7pbg8nm.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-BQbFywVL.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-REBeTFSH.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-NAGroBm3.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-Bp0MNfQD.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:S(`loading`)}),children:e})})}function Kt(e){window.dispatchEvent(new CustomEvent(`quickforge:open-session-from-settings`,{detail:{sessionId:e}}))}function qt(){return Q(S(`agentsTab`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Bt,{})}))}function Jt(){return Q(S(`skills`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Ht,{active:!0,scope:`global`,embedded:!0,onSaved:()=>void 0})}))}function Yt(){return Q(S(`mcpServers`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Vt,{active:!0})}))}function Xt(){return Q(S(`plugins`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Ut,{})}))}function Zt(){return Q(S(`scheduledTasks`),()=>(0,X.jsx)($,{children:(0,X.jsx)(Wt,{onOpenSession:Kt})}))}function Qt(e){let t=[{key:`appearance`,tab:Fe(),getDescription:()=>S(`appearanceDescription`)},{key:`defaults`,tab:$e(),getDescription:()=>S(`defaultOptionsDescription`)},{key:`memory`,tab:ot(),getDescription:()=>S(`memoryDescription`)},{key:`customModels`,tab:Me(e),getDescription:()=>S(`customModelsDescription`)},{key:`agents`,tab:qt(),getDescription:()=>S(`agentsDescription`)},{key:`skills`,tab:Jt(),getDescription:()=>S(`globalSkillsDescription`)},{key:`mcp`,tab:Yt(),getDescription:void 0},{key:`plugins`,tab:Xt(),getDescription:()=>S(`pluginsDescription`)},{key:`scheduledTasks`,tab:Zt(),getDescription:()=>S(`scheduledTasksDescription`)},{key:`projectCommands`,tab:Nt(),getDescription:()=>S(`projectCommandsDescription`)},{key:`backup`,tab:gt(),getDescription:()=>S(`backupRestoreDescription`)},{key:`archivedConversations`,tab:bt(),getDescription:()=>S(`archivedConversationsDescription`)},{key:`channels`,tab:Rt(),getDescription:()=>S(`channelsDescription`)},{key:`lanAccess`,tab:Ct(),getDescription:()=>S(`lanAccessDescription`)},{key:`about`,tab:jt(),getDescription:()=>S(`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:S(`searchSettings`),"aria-label":S(`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":S(`settings`),children:[m.map(({item:e,index:t})=>{let n=t===s,r=$t[e.key];return(0,X.jsxs)(`button`,{type:`button`,className:D(`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:D(`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:S(`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)(se,{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)(Ee,{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:S(`searchSettings`),"aria-label":S(`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:D(`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:S(`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};