@zythum02/fello-server 0.1.55 → 0.1.57

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 (396) hide show
  1. package/out/renderer/assets/PopoverPopup-BcHY-oda.js +591 -0
  2. package/out/renderer/assets/PopoverPopup-BcHY-oda.js.br +0 -0
  3. package/out/renderer/assets/PopoverPopup-BcHY-oda.js.gz +0 -0
  4. package/out/renderer/assets/{_baseUniq-BdLqGxOi.js → _baseUniq-KD2XGr9F.js} +1 -1
  5. package/out/renderer/assets/_baseUniq-KD2XGr9F.js.br +0 -0
  6. package/out/renderer/assets/_baseUniq-KD2XGr9F.js.gz +0 -0
  7. package/out/renderer/assets/{arc-UpL8po5R.js → arc-CR7wDdln.js} +1 -1
  8. package/out/renderer/assets/arc-CR7wDdln.js.br +0 -0
  9. package/out/renderer/assets/arc-CR7wDdln.js.gz +0 -0
  10. package/out/renderer/assets/{architectureDiagram-Q4EWVU46-ManEkEbl.js → architectureDiagram-Q4EWVU46-ob7rXg_N.js} +6 -6
  11. package/out/renderer/assets/architectureDiagram-Q4EWVU46-ob7rXg_N.js.br +0 -0
  12. package/out/renderer/assets/architectureDiagram-Q4EWVU46-ob7rXg_N.js.gz +0 -0
  13. package/out/renderer/assets/{arrow-left-BnuWerui.js → arrow-left-Ck_ZVJr5.js} +1 -1
  14. package/out/renderer/assets/{arrow-up-quD46bYg.js → arrow-up-D7cOrlYE.js} +4 -15
  15. package/out/renderer/assets/{automation-cVl19jbl.js → automation-W30eYgU8.js} +36 -13
  16. package/out/renderer/assets/automation-W30eYgU8.js.br +0 -0
  17. package/out/renderer/assets/automation-W30eYgU8.js.gz +0 -0
  18. package/out/renderer/assets/{blockDiagram-DXYQGD6D-B7n8X8F6.js → blockDiagram-DXYQGD6D-DH0dgWHF.js} +7 -7
  19. package/out/renderer/assets/blockDiagram-DXYQGD6D-DH0dgWHF.js.br +0 -0
  20. package/out/renderer/assets/blockDiagram-DXYQGD6D-DH0dgWHF.js.gz +0 -0
  21. package/out/renderer/assets/{c4Diagram-AHTNJAMY-BoP_JNY0.js → c4Diagram-AHTNJAMY--55RlH_-.js} +3 -3
  22. package/out/renderer/assets/c4Diagram-AHTNJAMY--55RlH_-.js.br +0 -0
  23. package/out/renderer/assets/c4Diagram-AHTNJAMY--55RlH_-.js.gz +0 -0
  24. package/out/renderer/assets/{channel-BTAU8ahK.js → channel-Bz679Wzq.js} +1 -1
  25. package/out/renderer/assets/{chunk-4BX2VUAB-BCKz1EgZ.js → chunk-4BX2VUAB-0jn0O1rt.js} +1 -1
  26. package/out/renderer/assets/{chunk-4TB4RGXK-B2HhnMqB.js → chunk-4TB4RGXK-BI1TetiG.js} +5 -5
  27. package/out/renderer/assets/chunk-4TB4RGXK-BI1TetiG.js.br +0 -0
  28. package/out/renderer/assets/chunk-4TB4RGXK-BI1TetiG.js.gz +0 -0
  29. package/out/renderer/assets/{chunk-55IACEB6-Drurz_4k.js → chunk-55IACEB6-9LfmpuHC.js} +1 -1
  30. package/out/renderer/assets/{chunk-EDXVE4YY-Xfghcg7b.js → chunk-EDXVE4YY-CtHVLcvG.js} +1 -1
  31. package/out/renderer/assets/chunk-EDXVE4YY-CtHVLcvG.js.br +0 -0
  32. package/out/renderer/assets/chunk-EDXVE4YY-CtHVLcvG.js.gz +0 -0
  33. package/out/renderer/assets/{chunk-FMBD7UC4-Birgz8kz.js → chunk-FMBD7UC4-eUJaG2CL.js} +1 -1
  34. package/out/renderer/assets/{chunk-OYMX7WX6-Cv4jgShd.js → chunk-OYMX7WX6-D-sPmxYW.js} +3 -3
  35. package/out/renderer/assets/chunk-OYMX7WX6-D-sPmxYW.js.br +0 -0
  36. package/out/renderer/assets/chunk-OYMX7WX6-D-sPmxYW.js.gz +0 -0
  37. package/out/renderer/assets/{chunk-QZHKN3VN-DkqKjktH.js → chunk-QZHKN3VN-Pdv-mSqK.js} +1 -1
  38. package/out/renderer/assets/{chunk-YZCP3GAM-CJK_tBTq.js → chunk-YZCP3GAM-DLpllNY2.js} +1 -1
  39. package/out/renderer/assets/chunk-YZCP3GAM-DLpllNY2.js.br +0 -0
  40. package/out/renderer/assets/chunk-YZCP3GAM-DLpllNY2.js.gz +0 -0
  41. package/out/renderer/assets/{circle-x-C9_Yp0Tg.js → circle-x-Cs_T6g0B.js} +1 -1
  42. package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-Bf4To0VI.js → classDiagram-6PBFFD2Q-BQx6mUfF.js} +7 -7
  43. package/out/renderer/assets/{classDiagram-6PBFFD2Q-Bf4To0VI.js → classDiagram-v2-HSJHXN6E-BQx6mUfF.js} +7 -7
  44. package/out/renderer/assets/{clone-Ci5J50Pn.js → clone-DsYDv_WX.js} +1 -1
  45. package/out/renderer/assets/{code-editor-BwPwET3m.js → code-editor-Ce-8PAj3.js} +2 -2
  46. package/out/renderer/assets/code-editor-Ce-8PAj3.js.br +0 -0
  47. package/out/renderer/assets/code-editor-Ce-8PAj3.js.gz +0 -0
  48. package/out/renderer/assets/{collapsible-CMQ8h8ID.js → collapsible-CKgHeKAs.js} +1 -1
  49. package/out/renderer/assets/collapsible-CKgHeKAs.js.br +0 -0
  50. package/out/renderer/assets/collapsible-CKgHeKAs.js.gz +0 -0
  51. package/out/renderer/assets/{command-nKlwdb6i.js → command-Cgkdasyl.js} +1 -1
  52. package/out/renderer/assets/{cose-bilkent-S5V4N54A-BZpcn2q9.js → cose-bilkent-S5V4N54A-hNCr9Ibx.js} +2 -2
  53. package/out/renderer/assets/cose-bilkent-S5V4N54A-hNCr9Ibx.js.br +0 -0
  54. package/out/renderer/assets/cose-bilkent-S5V4N54A-hNCr9Ibx.js.gz +0 -0
  55. package/out/renderer/assets/{dagre-KV5264BT-onHZodsn.js → dagre-KV5264BT-CGyBht8M.js} +7 -7
  56. package/out/renderer/assets/dagre-KV5264BT-CGyBht8M.js.br +0 -0
  57. package/out/renderer/assets/dagre-KV5264BT-CGyBht8M.js.gz +0 -0
  58. package/out/renderer/assets/{diagram-5BDNPKRD-y3QVYwp7.js → diagram-5BDNPKRD-BiZfiCzA.js} +7 -7
  59. package/out/renderer/assets/diagram-5BDNPKRD-BiZfiCzA.js.br +0 -0
  60. package/out/renderer/assets/diagram-5BDNPKRD-BiZfiCzA.js.gz +0 -0
  61. package/out/renderer/assets/{diagram-G4DWMVQ6-LB3RUjrJ.js → diagram-G4DWMVQ6-CWj4c_xE.js} +7 -7
  62. package/out/renderer/assets/diagram-G4DWMVQ6-CWj4c_xE.js.br +0 -0
  63. package/out/renderer/assets/diagram-G4DWMVQ6-CWj4c_xE.js.gz +0 -0
  64. package/out/renderer/assets/{diagram-MMDJMWI5-7NpY26IL.js → diagram-MMDJMWI5-iBnXAZDJ.js} +6 -6
  65. package/out/renderer/assets/diagram-MMDJMWI5-iBnXAZDJ.js.br +0 -0
  66. package/out/renderer/assets/diagram-MMDJMWI5-iBnXAZDJ.js.gz +0 -0
  67. package/out/renderer/assets/{diagram-TYMM5635-DRM1RRr4.js → diagram-TYMM5635-C7IGtL42.js} +6 -6
  68. package/out/renderer/assets/diagram-TYMM5635-C7IGtL42.js.br +0 -0
  69. package/out/renderer/assets/diagram-TYMM5635-C7IGtL42.js.gz +0 -0
  70. package/out/renderer/assets/{ellipsis-D_eCkauJ.js → ellipsis-DSui0Rae.js} +1 -1
  71. package/out/renderer/assets/{erDiagram-SMLLAGMA-DNIZ639C.js → erDiagram-SMLLAGMA-ChPUl_x9.js} +5 -5
  72. package/out/renderer/assets/erDiagram-SMLLAGMA-ChPUl_x9.js.br +0 -0
  73. package/out/renderer/assets/erDiagram-SMLLAGMA-ChPUl_x9.js.gz +0 -0
  74. package/out/renderer/assets/external-link-CoW4VuI3.js +15 -0
  75. package/out/renderer/assets/{field-YZTi9Jxc.js → field-CXofwfDF.js} +1 -1
  76. package/out/renderer/assets/field-CXofwfDF.js.br +0 -0
  77. package/out/renderer/assets/field-CXofwfDF.js.gz +0 -0
  78. package/out/renderer/assets/{file-text-DDtS1g4Q.js → file-text-CwFSQGtt.js} +1 -1
  79. package/out/renderer/assets/{flowDiagram-DWJPFMVM-C0ghMibN.js → flowDiagram-DWJPFMVM-C5Zt4Sfn.js} +7 -7
  80. package/out/renderer/assets/flowDiagram-DWJPFMVM-C5Zt4Sfn.js.br +0 -0
  81. package/out/renderer/assets/flowDiagram-DWJPFMVM-C5Zt4Sfn.js.gz +0 -0
  82. package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-tqeHqMu4.js → ganttDiagram-T4ZO3ILL-Cp30RG1g.js} +3 -3
  83. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-Cp30RG1g.js.br +0 -0
  84. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-Cp30RG1g.js.gz +0 -0
  85. package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-D6acFhfE.js → gitGraphDiagram-UUTBAWPF-CkfzOUvM.js} +7 -7
  86. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CkfzOUvM.js.br +0 -0
  87. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CkfzOUvM.js.gz +0 -0
  88. package/out/renderer/assets/{graph-DqFGMaSz.js → graph-HfEt9-p2.js} +2 -2
  89. package/out/renderer/assets/graph-HfEt9-p2.js.br +0 -0
  90. package/out/renderer/assets/graph-HfEt9-p2.js.gz +0 -0
  91. package/out/renderer/assets/{highlighted-body-KPVGNVTW-D2IZ7Enb.js → highlighted-body-KPVGNVTW-CUokS6r6.js} +2 -2
  92. package/out/renderer/assets/index-Byirxhp5.css +382 -0
  93. package/out/renderer/assets/index-Byirxhp5.css.br +0 -0
  94. package/out/renderer/assets/index-Byirxhp5.css.gz +0 -0
  95. package/out/renderer/assets/{index-BeEZXfJZ.js → index-CKDwIiIb.js} +2277 -923
  96. package/out/renderer/assets/index-CKDwIiIb.js.br +0 -0
  97. package/out/renderer/assets/index-CKDwIiIb.js.gz +0 -0
  98. package/out/renderer/assets/{infoDiagram-42DDH7IO-BJeydI3V.js → infoDiagram-42DDH7IO-DbQvToBf.js} +5 -5
  99. package/out/renderer/assets/infoDiagram-42DDH7IO-DbQvToBf.js.br +0 -0
  100. package/out/renderer/assets/infoDiagram-42DDH7IO-DbQvToBf.js.gz +0 -0
  101. package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-DPgToD8D.js → ishikawaDiagram-UXIWVN3A-Crmx2UcL.js} +2 -2
  102. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-Crmx2UcL.js.br +0 -0
  103. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-Crmx2UcL.js.gz +0 -0
  104. package/out/renderer/assets/{item-SQsw2AyN.js → item-2gdt65LZ.js} +1 -1
  105. package/out/renderer/assets/item-2gdt65LZ.js.br +0 -0
  106. package/out/renderer/assets/item-2gdt65LZ.js.gz +0 -0
  107. package/out/renderer/assets/{journeyDiagram-VCZTEJTY-CAnZXd__.js → journeyDiagram-VCZTEJTY-BD3MzoDm.js} +5 -5
  108. package/out/renderer/assets/journeyDiagram-VCZTEJTY-BD3MzoDm.js.br +0 -0
  109. package/out/renderer/assets/journeyDiagram-VCZTEJTY-BD3MzoDm.js.gz +0 -0
  110. package/out/renderer/assets/{kanban-definition-6JOO6SKY-VCfgwCTi.js → kanban-definition-6JOO6SKY-DlPWdcq8.js} +3 -3
  111. package/out/renderer/assets/kanban-definition-6JOO6SKY-DlPWdcq8.js.br +0 -0
  112. package/out/renderer/assets/kanban-definition-6JOO6SKY-DlPWdcq8.js.gz +0 -0
  113. package/out/renderer/assets/keyboard-BmncEbVT.js +21 -0
  114. package/out/renderer/assets/{layout-lw2KhCMB.js → layout-OHsAY-70.js} +4 -4
  115. package/out/renderer/assets/layout-OHsAY-70.js.br +0 -0
  116. package/out/renderer/assets/layout-OHsAY-70.js.gz +0 -0
  117. package/out/renderer/assets/{linear-DmbMR7a-.js → linear-CnBTAk2r.js} +1 -1
  118. package/out/renderer/assets/linear-CnBTAk2r.js.br +0 -0
  119. package/out/renderer/assets/linear-CnBTAk2r.js.gz +0 -0
  120. package/out/renderer/assets/{link-2-CWpGKoWR.js → link-2-CABYiITN.js} +3 -15
  121. package/out/renderer/assets/{mermaid-HWGCJPDP-BufLuIVq.js → mermaid-HWGCJPDP-OB5tfzeR.js} +58 -54
  122. package/out/renderer/assets/mermaid-HWGCJPDP-OB5tfzeR.js.br +0 -0
  123. package/out/renderer/assets/mermaid-HWGCJPDP-OB5tfzeR.js.gz +0 -0
  124. package/out/renderer/assets/{mic-UZHvCQhw.js → mic-Bu7eaTKt.js} +1 -1
  125. package/out/renderer/assets/{min-BCqStlR1.js → min-lM3sC06g.js} +2 -2
  126. package/out/renderer/assets/min-lM3sC06g.js.br +0 -0
  127. package/out/renderer/assets/min-lM3sC06g.js.gz +0 -0
  128. package/out/renderer/assets/{mindmap-definition-QFDTVHPH-C0dBO3f9.js → mindmap-definition-QFDTVHPH-2KHTVjBe.js} +4 -4
  129. package/out/renderer/assets/mindmap-definition-QFDTVHPH-2KHTVjBe.js.br +0 -0
  130. package/out/renderer/assets/mindmap-definition-QFDTVHPH-2KHTVjBe.js.gz +0 -0
  131. package/out/renderer/assets/{open-guide-CWoE2W97.js → open-guide-CKryRC5n.js} +1 -1
  132. package/out/renderer/assets/{package-search-SZPcR_1L.js → package-search-DPG91yZB.js} +1 -1
  133. package/out/renderer/assets/{pieDiagram-DEJITSTG-Dv_tH6Z_.js → pieDiagram-DEJITSTG-CWRoGvfQ.js} +7 -7
  134. package/out/renderer/assets/pieDiagram-DEJITSTG-CWRoGvfQ.js.br +0 -0
  135. package/out/renderer/assets/pieDiagram-DEJITSTG-CWRoGvfQ.js.gz +0 -0
  136. package/out/renderer/assets/{plus-BZ0wwKkh.js → plus-CFoa8VhI.js} +1 -1
  137. package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-Cvsj5cyu.js → quadrantDiagram-34T5L4WZ-BJVP6FPm.js} +3 -3
  138. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-BJVP6FPm.js.br +0 -0
  139. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-BJVP6FPm.js.gz +0 -0
  140. package/out/renderer/assets/{requirementDiagram-MS252O5E-CxM7H308.js → requirementDiagram-MS252O5E-_MTj_l0V.js} +4 -4
  141. package/out/renderer/assets/requirementDiagram-MS252O5E-_MTj_l0V.js.br +0 -0
  142. package/out/renderer/assets/requirementDiagram-MS252O5E-_MTj_l0V.js.gz +0 -0
  143. package/out/renderer/assets/{resizable-DD8ADhVy.js → resizable-CcCcCDAN.js} +1 -1
  144. package/out/renderer/assets/resizable-CcCcCDAN.js.br +0 -0
  145. package/out/renderer/assets/resizable-CcCcCDAN.js.gz +0 -0
  146. package/out/renderer/assets/{sankeyDiagram-XADWPNL6-BGTOA6ee.js → sankeyDiagram-XADWPNL6-BWkr3adU.js} +2 -2
  147. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BWkr3adU.js.br +0 -0
  148. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BWkr3adU.js.gz +0 -0
  149. package/out/renderer/assets/{schedule-DMyyz9A1.js → schedule-H1BEvD9Q.js} +24 -11
  150. package/out/renderer/assets/schedule-H1BEvD9Q.js.br +0 -0
  151. package/out/renderer/assets/schedule-H1BEvD9Q.js.gz +0 -0
  152. package/out/renderer/assets/{search-XexOgLud.js → search-B3aFhEKs.js} +1 -1
  153. package/out/renderer/assets/{sequenceDiagram-FGHM5R23-DezlXo1X.js → sequenceDiagram-FGHM5R23-CfRs3_Dx.js} +4 -4
  154. package/out/renderer/assets/sequenceDiagram-FGHM5R23-CfRs3_Dx.js.br +0 -0
  155. package/out/renderer/assets/sequenceDiagram-FGHM5R23-CfRs3_Dx.js.gz +0 -0
  156. package/out/renderer/assets/{session-DQEqHTKb.js → session-D6NE3yNE.js} +2327 -2791
  157. package/out/renderer/assets/session-D6NE3yNE.js.br +0 -0
  158. package/out/renderer/assets/session-D6NE3yNE.js.gz +0 -0
  159. package/out/renderer/assets/setting-dialog-CANlzubW.js +5086 -0
  160. package/out/renderer/assets/setting-dialog-CANlzubW.js.br +0 -0
  161. package/out/renderer/assets/setting-dialog-CANlzubW.js.gz +0 -0
  162. package/out/renderer/assets/{settings-agents-f3HkdiTw.js → settings-agents-DxIwcY7M.js} +12 -12
  163. package/out/renderer/assets/settings-agents-DxIwcY7M.js.br +0 -0
  164. package/out/renderer/assets/settings-agents-DxIwcY7M.js.gz +0 -0
  165. package/out/renderer/assets/settings-general-B85uTAij.js +624 -0
  166. package/out/renderer/assets/settings-general-B85uTAij.js.br +0 -0
  167. package/out/renderer/assets/settings-general-B85uTAij.js.gz +0 -0
  168. package/out/renderer/assets/{settings-ilink-Da336Koc.js → settings-ilink-BgeA04Nv.js} +51 -20
  169. package/out/renderer/assets/settings-ilink-BgeA04Nv.js.br +0 -0
  170. package/out/renderer/assets/settings-ilink-BgeA04Nv.js.gz +0 -0
  171. package/out/renderer/assets/{settings-image-generation-RmWs73ZG.js → settings-image-generation-CsWv4wXY.js} +6 -6
  172. package/out/renderer/assets/settings-image-generation-CsWv4wXY.js.br +0 -0
  173. package/out/renderer/assets/settings-image-generation-CsWv4wXY.js.gz +0 -0
  174. package/out/renderer/assets/{settings-layout-DL6ZEzgP.js → settings-layout-DJbT4rIH.js} +32 -7
  175. package/out/renderer/assets/settings-layout-DJbT4rIH.js.br +0 -0
  176. package/out/renderer/assets/settings-layout-DJbT4rIH.js.gz +0 -0
  177. package/out/renderer/assets/{settings-mcp-BKW8JKtO.js → settings-mcp-Cz6GMogp.js} +97 -29
  178. package/out/renderer/assets/settings-mcp-Cz6GMogp.js.br +0 -0
  179. package/out/renderer/assets/settings-mcp-Cz6GMogp.js.gz +0 -0
  180. package/out/renderer/assets/{settings-memory-o4r-_vob.js → settings-memory-Cidr_Ghw.js} +2 -2
  181. package/out/renderer/assets/settings-memory-Cidr_Ghw.js.br +0 -0
  182. package/out/renderer/assets/settings-memory-Cidr_Ghw.js.gz +0 -0
  183. package/out/renderer/assets/settings-peripherals-DilwOpyO.js +269 -0
  184. package/out/renderer/assets/settings-peripherals-DilwOpyO.js.br +0 -0
  185. package/out/renderer/assets/settings-peripherals-DilwOpyO.js.gz +0 -0
  186. package/out/renderer/assets/{settings-shortcuts-BmUGS1q_.js → settings-shortcuts-dWdRCFxB.js} +16 -6
  187. package/out/renderer/assets/settings-shortcuts-dWdRCFxB.js.br +0 -0
  188. package/out/renderer/assets/settings-shortcuts-dWdRCFxB.js.gz +0 -0
  189. package/out/renderer/assets/{settings-snippets-CpAOJmDF.js → settings-snippets-CrtJYwLV.js} +9 -9
  190. package/out/renderer/assets/settings-snippets-CrtJYwLV.js.br +0 -0
  191. package/out/renderer/assets/settings-snippets-CrtJYwLV.js.gz +0 -0
  192. package/out/renderer/assets/settings-speech-CteE8F4J.js +1369 -0
  193. package/out/renderer/assets/settings-speech-CteE8F4J.js.br +0 -0
  194. package/out/renderer/assets/settings-speech-CteE8F4J.js.gz +0 -0
  195. package/out/renderer/assets/{settings-webui-DIEKvTrI.js → settings-webui-DKiOzDSD.js} +3 -3
  196. package/out/renderer/assets/settings-webui-DKiOzDSD.js.br +0 -0
  197. package/out/renderer/assets/settings-webui-DKiOzDSD.js.gz +0 -0
  198. package/out/renderer/assets/{skills-installed-BuWBDk6r.js → skills-installed-Beqe9G7p.js} +4 -4
  199. package/out/renderer/assets/skills-installed-Beqe9G7p.js.br +0 -0
  200. package/out/renderer/assets/skills-installed-Beqe9G7p.js.gz +0 -0
  201. package/out/renderer/assets/{skills-layout-ByPhfHG6.js → skills-layout-BAzfcnkT.js} +2 -2
  202. package/out/renderer/assets/skills-layout-BAzfcnkT.js.br +0 -0
  203. package/out/renderer/assets/skills-layout-BAzfcnkT.js.gz +0 -0
  204. package/out/renderer/assets/{skills-skills-sh-wR7YNmAJ.js → skills-skills-sh-Dzocqfu4.js} +4 -4
  205. package/out/renderer/assets/skills-skills-sh-Dzocqfu4.js.br +0 -0
  206. package/out/renderer/assets/skills-skills-sh-Dzocqfu4.js.gz +0 -0
  207. package/out/renderer/assets/{settings-general-BcCOVmx3.js → slider-DEK94SlL.js} +4 -623
  208. package/out/renderer/assets/slider-DEK94SlL.js.br +0 -0
  209. package/out/renderer/assets/slider-DEK94SlL.js.gz +0 -0
  210. package/out/renderer/assets/{sortable.esm-BWvT9HIK.js → sortable.esm-Cm4rqFU7.js} +1 -1
  211. package/out/renderer/assets/sortable.esm-Cm4rqFU7.js.br +0 -0
  212. package/out/renderer/assets/sortable.esm-Cm4rqFU7.js.gz +0 -0
  213. package/out/renderer/assets/{standard-schema-BkTVIf7N.js → standard-schema-B43ZsT5c.js} +52 -1
  214. package/out/renderer/assets/standard-schema-B43ZsT5c.js.br +0 -0
  215. package/out/renderer/assets/standard-schema-B43ZsT5c.js.gz +0 -0
  216. package/out/renderer/assets/{stateDiagram-FHFEXIEX-xdKKCs-p.js → stateDiagram-FHFEXIEX-CaiRC_NA.js} +9 -9
  217. package/out/renderer/assets/stateDiagram-FHFEXIEX-CaiRC_NA.js.br +0 -0
  218. package/out/renderer/assets/stateDiagram-FHFEXIEX-CaiRC_NA.js.gz +0 -0
  219. package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-BaaiQ6Kr.js → stateDiagram-v2-QKLJ7IA2-EA3ViVW-.js} +5 -5
  220. package/out/renderer/assets/{task-PHAOSE-W.js → task-DM-bWOFz.js} +13 -11
  221. package/out/renderer/assets/task-DM-bWOFz.js.br +0 -0
  222. package/out/renderer/assets/task-DM-bWOFz.js.gz +0 -0
  223. package/out/renderer/assets/{textarea-1ZWl3C-x.js → textarea-B0OAhnF7.js} +1 -1
  224. package/out/renderer/assets/{timeline-definition-GMOUNBTQ-DFLeLybV.js → timeline-definition-GMOUNBTQ-D-M9BAZz.js} +3 -3
  225. package/out/renderer/assets/timeline-definition-GMOUNBTQ-D-M9BAZz.js.br +0 -0
  226. package/out/renderer/assets/timeline-definition-GMOUNBTQ-D-M9BAZz.js.gz +0 -0
  227. package/out/renderer/assets/{vennDiagram-DHZGUBPP-PKmZLY7H.js → vennDiagram-DHZGUBPP-B_yjg_0U.js} +2 -2
  228. package/out/renderer/assets/vennDiagram-DHZGUBPP-B_yjg_0U.js.br +0 -0
  229. package/out/renderer/assets/vennDiagram-DHZGUBPP-B_yjg_0U.js.gz +0 -0
  230. package/out/renderer/assets/{wardley-RL74JXVD-BCH2vKnv.js → wardley-RL74JXVD-2Xd7XlwA.js} +4 -4
  231. package/out/renderer/assets/wardley-RL74JXVD-2Xd7XlwA.js.br +0 -0
  232. package/out/renderer/assets/wardley-RL74JXVD-2Xd7XlwA.js.gz +0 -0
  233. package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-Bkd3xMno.js → wardleyDiagram-NUSXRM2D-BA9tzMk-.js} +6 -6
  234. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-BA9tzMk-.js.br +0 -0
  235. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-BA9tzMk-.js.gz +0 -0
  236. package/out/renderer/assets/{wrench-8H0LZ83G.js → wrench-QfEqqx9F.js} +1 -1
  237. package/out/renderer/assets/wrench-QfEqqx9F.js.br +0 -0
  238. package/out/renderer/assets/wrench-QfEqqx9F.js.gz +0 -0
  239. package/out/renderer/assets/{xlsx-view-BYx5Ha2P.js → xlsx-view-Bov2NXsI.js} +141 -79
  240. package/out/renderer/assets/xlsx-view-Bov2NXsI.js.br +0 -0
  241. package/out/renderer/assets/xlsx-view-Bov2NXsI.js.gz +0 -0
  242. package/out/renderer/assets/{xychartDiagram-5P7HB3ND-DCOm4uPr.js → xychartDiagram-5P7HB3ND-svs-11is.js} +3 -3
  243. package/out/renderer/assets/xychartDiagram-5P7HB3ND-svs-11is.js.br +0 -0
  244. package/out/renderer/assets/xychartDiagram-5P7HB3ND-svs-11is.js.gz +0 -0
  245. package/out/renderer/index.html +2 -2
  246. package/out/renderer/index.html.br +0 -0
  247. package/out/renderer/index.html.gz +0 -0
  248. package/out/scripts/mcp-toolbox/server.mjs +80 -1
  249. package/out/server/main.js +1576 -353
  250. package/package.json +3 -2
  251. package/out/renderer/assets/_baseUniq-BdLqGxOi.js.br +0 -0
  252. package/out/renderer/assets/_baseUniq-BdLqGxOi.js.gz +0 -0
  253. package/out/renderer/assets/arc-UpL8po5R.js.br +0 -0
  254. package/out/renderer/assets/arc-UpL8po5R.js.gz +0 -0
  255. package/out/renderer/assets/architectureDiagram-Q4EWVU46-ManEkEbl.js.br +0 -0
  256. package/out/renderer/assets/architectureDiagram-Q4EWVU46-ManEkEbl.js.gz +0 -0
  257. package/out/renderer/assets/automation-cVl19jbl.js.br +0 -0
  258. package/out/renderer/assets/automation-cVl19jbl.js.gz +0 -0
  259. package/out/renderer/assets/blockDiagram-DXYQGD6D-B7n8X8F6.js.br +0 -0
  260. package/out/renderer/assets/blockDiagram-DXYQGD6D-B7n8X8F6.js.gz +0 -0
  261. package/out/renderer/assets/c4Diagram-AHTNJAMY-BoP_JNY0.js.br +0 -0
  262. package/out/renderer/assets/c4Diagram-AHTNJAMY-BoP_JNY0.js.gz +0 -0
  263. package/out/renderer/assets/chunk-4TB4RGXK-B2HhnMqB.js.br +0 -0
  264. package/out/renderer/assets/chunk-4TB4RGXK-B2HhnMqB.js.gz +0 -0
  265. package/out/renderer/assets/chunk-EDXVE4YY-Xfghcg7b.js.br +0 -0
  266. package/out/renderer/assets/chunk-EDXVE4YY-Xfghcg7b.js.gz +0 -0
  267. package/out/renderer/assets/chunk-OYMX7WX6-Cv4jgShd.js.br +0 -0
  268. package/out/renderer/assets/chunk-OYMX7WX6-Cv4jgShd.js.gz +0 -0
  269. package/out/renderer/assets/chunk-YZCP3GAM-CJK_tBTq.js.br +0 -0
  270. package/out/renderer/assets/chunk-YZCP3GAM-CJK_tBTq.js.gz +0 -0
  271. package/out/renderer/assets/code-editor-BwPwET3m.js.br +0 -0
  272. package/out/renderer/assets/code-editor-BwPwET3m.js.gz +0 -0
  273. package/out/renderer/assets/collapsible-CMQ8h8ID.js.br +0 -0
  274. package/out/renderer/assets/collapsible-CMQ8h8ID.js.gz +0 -0
  275. package/out/renderer/assets/cose-bilkent-S5V4N54A-BZpcn2q9.js.br +0 -0
  276. package/out/renderer/assets/cose-bilkent-S5V4N54A-BZpcn2q9.js.gz +0 -0
  277. package/out/renderer/assets/dagre-KV5264BT-onHZodsn.js.br +0 -0
  278. package/out/renderer/assets/dagre-KV5264BT-onHZodsn.js.gz +0 -0
  279. package/out/renderer/assets/diagram-5BDNPKRD-y3QVYwp7.js.br +0 -0
  280. package/out/renderer/assets/diagram-5BDNPKRD-y3QVYwp7.js.gz +0 -0
  281. package/out/renderer/assets/diagram-G4DWMVQ6-LB3RUjrJ.js.br +0 -0
  282. package/out/renderer/assets/diagram-G4DWMVQ6-LB3RUjrJ.js.gz +0 -0
  283. package/out/renderer/assets/diagram-MMDJMWI5-7NpY26IL.js.br +0 -0
  284. package/out/renderer/assets/diagram-MMDJMWI5-7NpY26IL.js.gz +0 -0
  285. package/out/renderer/assets/diagram-TYMM5635-DRM1RRr4.js.br +0 -0
  286. package/out/renderer/assets/diagram-TYMM5635-DRM1RRr4.js.gz +0 -0
  287. package/out/renderer/assets/erDiagram-SMLLAGMA-DNIZ639C.js.br +0 -0
  288. package/out/renderer/assets/erDiagram-SMLLAGMA-DNIZ639C.js.gz +0 -0
  289. package/out/renderer/assets/field-YZTi9Jxc.js.br +0 -0
  290. package/out/renderer/assets/field-YZTi9Jxc.js.gz +0 -0
  291. package/out/renderer/assets/flowDiagram-DWJPFMVM-C0ghMibN.js.br +0 -0
  292. package/out/renderer/assets/flowDiagram-DWJPFMVM-C0ghMibN.js.gz +0 -0
  293. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-tqeHqMu4.js.br +0 -0
  294. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-tqeHqMu4.js.gz +0 -0
  295. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-D6acFhfE.js.br +0 -0
  296. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-D6acFhfE.js.gz +0 -0
  297. package/out/renderer/assets/graph-DqFGMaSz.js.br +0 -0
  298. package/out/renderer/assets/graph-DqFGMaSz.js.gz +0 -0
  299. package/out/renderer/assets/index-BZTYKZe8.css +0 -382
  300. package/out/renderer/assets/index-BZTYKZe8.css.br +0 -0
  301. package/out/renderer/assets/index-BZTYKZe8.css.gz +0 -0
  302. package/out/renderer/assets/index-BeEZXfJZ.js.br +0 -0
  303. package/out/renderer/assets/index-BeEZXfJZ.js.gz +0 -0
  304. package/out/renderer/assets/infoDiagram-42DDH7IO-BJeydI3V.js.br +0 -0
  305. package/out/renderer/assets/infoDiagram-42DDH7IO-BJeydI3V.js.gz +0 -0
  306. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-DPgToD8D.js.br +0 -0
  307. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-DPgToD8D.js.gz +0 -0
  308. package/out/renderer/assets/item-SQsw2AyN.js.br +0 -0
  309. package/out/renderer/assets/item-SQsw2AyN.js.gz +0 -0
  310. package/out/renderer/assets/journeyDiagram-VCZTEJTY-CAnZXd__.js.br +0 -0
  311. package/out/renderer/assets/journeyDiagram-VCZTEJTY-CAnZXd__.js.gz +0 -0
  312. package/out/renderer/assets/kanban-definition-6JOO6SKY-VCfgwCTi.js.br +0 -0
  313. package/out/renderer/assets/kanban-definition-6JOO6SKY-VCfgwCTi.js.gz +0 -0
  314. package/out/renderer/assets/layout-lw2KhCMB.js.br +0 -0
  315. package/out/renderer/assets/layout-lw2KhCMB.js.gz +0 -0
  316. package/out/renderer/assets/linear-DmbMR7a-.js.br +0 -0
  317. package/out/renderer/assets/linear-DmbMR7a-.js.gz +0 -0
  318. package/out/renderer/assets/link-2-CWpGKoWR.js.br +0 -0
  319. package/out/renderer/assets/link-2-CWpGKoWR.js.gz +0 -0
  320. package/out/renderer/assets/mermaid-HWGCJPDP-BufLuIVq.js.br +0 -0
  321. package/out/renderer/assets/mermaid-HWGCJPDP-BufLuIVq.js.gz +0 -0
  322. package/out/renderer/assets/min-BCqStlR1.js.br +0 -0
  323. package/out/renderer/assets/min-BCqStlR1.js.gz +0 -0
  324. package/out/renderer/assets/mindmap-definition-QFDTVHPH-C0dBO3f9.js.br +0 -0
  325. package/out/renderer/assets/mindmap-definition-QFDTVHPH-C0dBO3f9.js.gz +0 -0
  326. package/out/renderer/assets/pieDiagram-DEJITSTG-Dv_tH6Z_.js.br +0 -0
  327. package/out/renderer/assets/pieDiagram-DEJITSTG-Dv_tH6Z_.js.gz +0 -0
  328. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-Cvsj5cyu.js.br +0 -0
  329. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-Cvsj5cyu.js.gz +0 -0
  330. package/out/renderer/assets/requirementDiagram-MS252O5E-CxM7H308.js.br +0 -0
  331. package/out/renderer/assets/requirementDiagram-MS252O5E-CxM7H308.js.gz +0 -0
  332. package/out/renderer/assets/resizable-DD8ADhVy.js.br +0 -0
  333. package/out/renderer/assets/resizable-DD8ADhVy.js.gz +0 -0
  334. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BGTOA6ee.js.br +0 -0
  335. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BGTOA6ee.js.gz +0 -0
  336. package/out/renderer/assets/schedule-DMyyz9A1.js.br +0 -0
  337. package/out/renderer/assets/schedule-DMyyz9A1.js.gz +0 -0
  338. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DezlXo1X.js.br +0 -0
  339. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DezlXo1X.js.gz +0 -0
  340. package/out/renderer/assets/session-DQEqHTKb.js.br +0 -0
  341. package/out/renderer/assets/session-DQEqHTKb.js.gz +0 -0
  342. package/out/renderer/assets/setting-dialog-BdiZbd8J.js +0 -642
  343. package/out/renderer/assets/setting-dialog-BdiZbd8J.js.br +0 -0
  344. package/out/renderer/assets/setting-dialog-BdiZbd8J.js.gz +0 -0
  345. package/out/renderer/assets/settings-2-q22Cv4G7.js +0 -16
  346. package/out/renderer/assets/settings-agents-f3HkdiTw.js.br +0 -0
  347. package/out/renderer/assets/settings-agents-f3HkdiTw.js.gz +0 -0
  348. package/out/renderer/assets/settings-general-BcCOVmx3.js.br +0 -0
  349. package/out/renderer/assets/settings-general-BcCOVmx3.js.gz +0 -0
  350. package/out/renderer/assets/settings-ilink-Da336Koc.js.br +0 -0
  351. package/out/renderer/assets/settings-ilink-Da336Koc.js.gz +0 -0
  352. package/out/renderer/assets/settings-image-generation-RmWs73ZG.js.br +0 -0
  353. package/out/renderer/assets/settings-image-generation-RmWs73ZG.js.gz +0 -0
  354. package/out/renderer/assets/settings-layout-DL6ZEzgP.js.br +0 -0
  355. package/out/renderer/assets/settings-layout-DL6ZEzgP.js.gz +0 -0
  356. package/out/renderer/assets/settings-mcp-BKW8JKtO.js.br +0 -0
  357. package/out/renderer/assets/settings-mcp-BKW8JKtO.js.gz +0 -0
  358. package/out/renderer/assets/settings-memory-o4r-_vob.js.br +0 -0
  359. package/out/renderer/assets/settings-memory-o4r-_vob.js.gz +0 -0
  360. package/out/renderer/assets/settings-shortcuts-BmUGS1q_.js.br +0 -0
  361. package/out/renderer/assets/settings-shortcuts-BmUGS1q_.js.gz +0 -0
  362. package/out/renderer/assets/settings-snippets-CpAOJmDF.js.br +0 -0
  363. package/out/renderer/assets/settings-snippets-CpAOJmDF.js.gz +0 -0
  364. package/out/renderer/assets/settings-speech-to-text-C8KHNGE4.js +0 -1054
  365. package/out/renderer/assets/settings-speech-to-text-C8KHNGE4.js.br +0 -0
  366. package/out/renderer/assets/settings-speech-to-text-C8KHNGE4.js.gz +0 -0
  367. package/out/renderer/assets/settings-webui-DIEKvTrI.js.br +0 -0
  368. package/out/renderer/assets/settings-webui-DIEKvTrI.js.gz +0 -0
  369. package/out/renderer/assets/skills-installed-BuWBDk6r.js.br +0 -0
  370. package/out/renderer/assets/skills-installed-BuWBDk6r.js.gz +0 -0
  371. package/out/renderer/assets/skills-layout-ByPhfHG6.js.br +0 -0
  372. package/out/renderer/assets/skills-layout-ByPhfHG6.js.gz +0 -0
  373. package/out/renderer/assets/skills-skills-sh-wR7YNmAJ.js.br +0 -0
  374. package/out/renderer/assets/skills-skills-sh-wR7YNmAJ.js.gz +0 -0
  375. package/out/renderer/assets/sortable.esm-BWvT9HIK.js.br +0 -0
  376. package/out/renderer/assets/sortable.esm-BWvT9HIK.js.gz +0 -0
  377. package/out/renderer/assets/standard-schema-BkTVIf7N.js.br +0 -0
  378. package/out/renderer/assets/standard-schema-BkTVIf7N.js.gz +0 -0
  379. package/out/renderer/assets/stateDiagram-FHFEXIEX-xdKKCs-p.js.br +0 -0
  380. package/out/renderer/assets/stateDiagram-FHFEXIEX-xdKKCs-p.js.gz +0 -0
  381. package/out/renderer/assets/task-PHAOSE-W.js.br +0 -0
  382. package/out/renderer/assets/task-PHAOSE-W.js.gz +0 -0
  383. package/out/renderer/assets/timeline-definition-GMOUNBTQ-DFLeLybV.js.br +0 -0
  384. package/out/renderer/assets/timeline-definition-GMOUNBTQ-DFLeLybV.js.gz +0 -0
  385. package/out/renderer/assets/vennDiagram-DHZGUBPP-PKmZLY7H.js.br +0 -0
  386. package/out/renderer/assets/vennDiagram-DHZGUBPP-PKmZLY7H.js.gz +0 -0
  387. package/out/renderer/assets/wardley-RL74JXVD-BCH2vKnv.js.br +0 -0
  388. package/out/renderer/assets/wardley-RL74JXVD-BCH2vKnv.js.gz +0 -0
  389. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-Bkd3xMno.js.br +0 -0
  390. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-Bkd3xMno.js.gz +0 -0
  391. package/out/renderer/assets/wrench-8H0LZ83G.js.br +0 -0
  392. package/out/renderer/assets/wrench-8H0LZ83G.js.gz +0 -0
  393. package/out/renderer/assets/xlsx-view-BYx5Ha2P.js.br +0 -0
  394. package/out/renderer/assets/xlsx-view-BYx5Ha2P.js.gz +0 -0
  395. package/out/renderer/assets/xychartDiagram-5P7HB3ND-DCOm4uPr.js.br +0 -0
  396. package/out/renderer/assets/xychartDiagram-5P7HB3ND-DCOm4uPr.js.gz +0 -0
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./session-DQEqHTKb.js","./settings-2-q22Cv4G7.js","./xlsx-view-BYx5Ha2P.js","./mermaid-HWGCJPDP-BufLuIVq.js","./collapsible-CMQ8h8ID.js","./item-SQsw2AyN.js","./file-text-DDtS1g4Q.js","./link-2-CWpGKoWR.js","./search-XexOgLud.js","./schemas-BL-_QQNp.js","./wrench-8H0LZ83G.js","./ellipsis-D_eCkauJ.js","./xlsx-view-CylPXjk8.css","./arrow-up-quD46bYg.js","./mic-UZHvCQhw.js","./arrow-left-BnuWerui.js","./plus-BZ0wwKkh.js","./resizable-DD8ADhVy.js","./settings-layout-DL6ZEzgP.js","./command-nKlwdb6i.js","./settings-general-BcCOVmx3.js","./field-YZTi9Jxc.js","./settings-shortcuts-BmUGS1q_.js","./settings-agents-f3HkdiTw.js","./open-guide-CWoE2W97.js","./sortable.esm-BWvT9HIK.js","./standard-schema-BkTVIf7N.js","./textarea-1ZWl3C-x.js","./circle-x-C9_Yp0Tg.js","./settings-mcp-BKW8JKtO.js","./settings-webui-DIEKvTrI.js","./settings-ilink-Da336Koc.js","./settings-snippets-CpAOJmDF.js","./settings-memory-o4r-_vob.js","./settings-image-generation-RmWs73ZG.js","./settings-speech-to-text-C8KHNGE4.js","./skills-layout-ByPhfHG6.js","./package-search-SZPcR_1L.js","./skills-installed-BuWBDk6r.js","./skills-skills-sh-wR7YNmAJ.js","./automation-cVl19jbl.js","./setting-dialog-BdiZbd8J.js","./schedule-DMyyz9A1.js","./task-PHAOSE-W.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./session-D6NE3yNE.js","./slider-DEK94SlL.js","./PopoverPopup-BcHY-oda.js","./xlsx-view-Bov2NXsI.js","./mermaid-HWGCJPDP-OB5tfzeR.js","./collapsible-CKgHeKAs.js","./item-2gdt65LZ.js","./file-text-CwFSQGtt.js","./link-2-CABYiITN.js","./external-link-CoW4VuI3.js","./search-B3aFhEKs.js","./schemas-BL-_QQNp.js","./wrench-QfEqqx9F.js","./ellipsis-DSui0Rae.js","./xlsx-view-CylPXjk8.css","./arrow-up-D7cOrlYE.js","./mic-Bu7eaTKt.js","./keyboard-BmncEbVT.js","./arrow-left-Ck_ZVJr5.js","./plus-CFoa8VhI.js","./resizable-CcCcCDAN.js","./settings-layout-DJbT4rIH.js","./command-Cgkdasyl.js","./settings-general-B85uTAij.js","./field-CXofwfDF.js","./settings-shortcuts-dWdRCFxB.js","./settings-agents-DxIwcY7M.js","./open-guide-CKryRC5n.js","./sortable.esm-Cm4rqFU7.js","./standard-schema-B43ZsT5c.js","./textarea-B0OAhnF7.js","./circle-x-Cs_T6g0B.js","./settings-mcp-Cz6GMogp.js","./settings-webui-DKiOzDSD.js","./settings-ilink-BgeA04Nv.js","./settings-snippets-CrtJYwLV.js","./settings-memory-Cidr_Ghw.js","./settings-image-generation-CsWv4wXY.js","./settings-speech-CteE8F4J.js","./settings-peripherals-DilwOpyO.js","./skills-layout-BAzfcnkT.js","./package-search-DPG91yZB.js","./skills-installed-Beqe9G7p.js","./skills-skills-sh-Dzocqfu4.js","./automation-W30eYgU8.js","./setting-dialog-CANlzubW.js","./schedule-H1BEvD9Q.js","./task-DM-bWOFz.js"])))=>i.map(i=>d[i]);
2
2
  function _mergeNamespaces(n, m) {
3
3
  for (var i = 0; i < m.length; i++) {
4
4
  const e = m[i];
@@ -14969,7 +14969,7 @@ var Immer2 = class {
14969
14969
  if (!isDraftable(base))
14970
14970
  die(8);
14971
14971
  if (isDraft(base))
14972
- base = current(base);
14972
+ base = current$1(base);
14973
14973
  const scope = enterScope(this);
14974
14974
  const proxy = createProxy$1(scope, base, void 0);
14975
14975
  proxy[DRAFT_STATE].isManual_ = true;
@@ -15053,7 +15053,7 @@ function createProxy$1(rootScope, value, parent, key) {
15053
15053
  }
15054
15054
  return draft;
15055
15055
  }
15056
- function current(value) {
15056
+ function current$1(value) {
15057
15057
  if (!isDraft(value))
15058
15058
  die(10, value);
15059
15059
  return currentImpl(value);
@@ -15940,8 +15940,9 @@ const useAppStore = create((set2, get2) => ({
15940
15940
  voiceInput: { altDoublePress: true },
15941
15941
  snippets: [],
15942
15942
  imageGeneration: [],
15943
- speechToText: [],
15943
+ speechProviders: [],
15944
15944
  shortcuts: {},
15945
+ peripherals: [],
15945
15946
  webUIStatus: { enabled: false, url: null },
15946
15947
  ilinkStatus: { connected: false },
15947
15948
  activeIlinkSessionId: null,
@@ -16076,8 +16077,9 @@ const useAppStore = create((set2, get2) => ({
16076
16077
  setVoiceInput: (voiceInput) => set2({ voiceInput }),
16077
16078
  setSnippets: (snippets) => set2({ snippets }),
16078
16079
  setImageGeneration: (imageGeneration2) => set2({ imageGeneration: imageGeneration2 }),
16079
- setSpeechToText: (speechToText) => set2({ speechToText }),
16080
+ setSpeechProviders: (speechProviders) => set2({ speechProviders }),
16080
16081
  setShortcuts: (shortcuts) => set2({ shortcuts }),
16082
+ setPeripherals: (peripherals) => set2({ peripherals }),
16081
16083
  setWebUIStatus: (status) => set2({ webUIStatus: status }),
16082
16084
  setIlinkStatus: (status) => set2({ ilinkStatus: status }),
16083
16085
  setActiveIlinkSessionId: (sessionId) => set2({ activeIlinkSessionId: sessionId }),
@@ -18421,7 +18423,7 @@ const hasLoadedNamespace = (ns, i18n, options = {}) => {
18421
18423
  });
18422
18424
  };
18423
18425
  const isString = (obj) => typeof obj === "string";
18424
- const isObject = (obj) => typeof obj === "object" && obj !== null;
18426
+ const isObject$1 = (obj) => typeof obj === "object" && obj !== null;
18425
18427
  const matchHtmlEntity = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g;
18426
18428
  const htmlEntities = {
18427
18429
  "&amp;": "&",
@@ -18553,7 +18555,7 @@ function requireShim() {
18553
18555
  var shimExports = requireShim();
18554
18556
  const notReadyT = (k2, optsOrDefaultValue) => {
18555
18557
  if (isString(optsOrDefaultValue)) return optsOrDefaultValue;
18556
- if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue;
18558
+ if (isObject$1(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue;
18557
18559
  if (typeof k2 === "function") return "";
18558
18560
  if (Array.isArray(k2)) {
18559
18561
  const last = k2[k2.length - 1];
@@ -18721,17 +18723,17 @@ const useTranslation = (ns, props = {}) => {
18721
18723
  }
18722
18724
  return ret;
18723
18725
  };
18724
- const sidebar$1 = { "welcome": "Welcome", "skills": "Skills", "automation": "Automation", "projects": "Projects", "noProjects": "No projects yet", "noProjectsHint": "Add a project folder to get started", "addProject": "Add Project", "noAgentsHint": "Configure an agent first", "configureAgent": "Configure Agent", "revealInFinder": "Reveal in Finder", "rename": "Rename", "delete": "Delete", "renameProject": "Rename Project", "renameSession": "Rename Session", "deleteSession": "Delete Session", "enterNewProjectName": "Enter a new project name.", "enterNewSessionName": "Enter a new session name.", "addProjectTitle": "Add Project", "enterProjectPath": "Enter absolute path to project on the server:", "pathCannotBeEmpty": "Path cannot be empty", "webuiEnabled": "WebUI Enabled", "deleteProject": "Delete Project", "deleteProjectConfirm": 'Delete project "{{title}}" and all sessions in it?\nThis action cannot be undone.', "deleteSessionConfirm": 'Delete session "{{title}}"?\nThis action cannot be undone.', "cancel": "Cancel", "save": "Save", "settings": "Settings", "agents": "Agents", "addProjectFailed": "Failed to add project.", "newSessionFailed": "Failed to create a new session.", "loadSessionFailed": "Failed to load session.", "revealInFinderFailed": "Failed to reveal in Finder.", "openInEditorFailed": "Failed to open in editor.", "projectExists": "Project already exists.", "projectNameEmpty": "Project name cannot be empty.", "sessionNameEmpty": "Session name cannot be empty.", "newSession": "New Session", "viewSession": "View Session", "projectActions": "Project actions for {{title}}", "createSessionInProject": "Create session in {{title}}", "sessionActions": "Session actions for {{title}}", "ilinkSetActive": "Set WeChat Active", "ilinkUnsetActive": "Unset WeChat Active", "failedToUpdateIlinkSession": "Failed to update WeChat active session.", "sessionOperationInProgress": "Another session operation is already in progress.", "ilinkActiveStatus": "WeChat Active", "ilinkInactiveStatus": "Not WeChat Active", "restartSession": "Restart Session", "closeSession": "Close Session", "closingSession": "Closing…", "failedToCloseSession": "Failed to close session.", "failedToDeleteSession": "Failed to delete session.", "failedToDeleteProject": "Failed to delete project.", "failedToLoadSession": "Failed to load session.", "failedToUpdateMcpServers": "Failed to update MCP servers", "collapse": "Collapse sidebar", "expand": "Expand sidebar", "updated": "Updated", "created": "Created", "sessionCount": "{{count}} sessions", "sessionCount_one": "{{count}} session", "newSessionDialog": { "title": "Create session", "agent": "Agent", "selectAgent": "Select an agent", "mcp": "MCP", "noMcp": "No MCP servers configured", "permission": "Permission", "permissionAsk": "Ask", "permissionAllowAll": "Allow all", "create": "Create" } };
18726
+ const sidebar$1 = { "welcome": "Welcome", "skills": "Skills", "automation": "Automation", "projects": "Projects", "noProjects": "No projects yet", "noProjectsHint": "Add a project folder to get started", "addProject": "Add Project", "noAgentsHint": "Configure an agent first", "configureAgent": "Configure Agent", "revealInFinder": "Reveal in Finder", "copyProjectPath": "Copy Project Path", "rename": "Rename", "delete": "Delete", "renameProject": "Rename Project", "renameSession": "Rename Session", "deleteSession": "Delete Session", "enterNewProjectName": "Enter a new project name.", "enterNewSessionName": "Enter a new session name.", "addProjectTitle": "Add Project", "enterProjectPath": "Enter absolute path to project on the server:", "pathCannotBeEmpty": "Path cannot be empty", "webuiEnabled": "WebUI Enabled", "deleteProject": "Delete Project", "deleteProjectConfirm": 'Delete project "{{title}}" and all sessions in it?\nThis action cannot be undone.', "deleteSessionConfirm": 'Delete session "{{title}}"?\nThis action cannot be undone.', "cancel": "Cancel", "save": "Save", "settings": "Settings", "agents": "Agents", "addProjectFailed": "Failed to add project.", "dropFolderOnly": "Only folders can be dropped to add a project.", "newSessionFailed": "Failed to create a new session.", "loadSessionFailed": "Failed to load session.", "revealInFinderFailed": "Failed to reveal in Finder.", "openInEditorFailed": "Failed to open in editor.", "projectExists": "Project already exists.", "projectNameEmpty": "Project name cannot be empty.", "sessionNameEmpty": "Session name cannot be empty.", "newSession": "New Session", "viewSession": "View Session", "projectActions": "Project actions for {{title}}", "createSessionInProject": "Create session in {{title}}", "sessionActions": "Session actions for {{title}}", "ilinkSetActive": "Set WeChat Active", "ilinkUnsetActive": "Unset WeChat Active", "failedToUpdateIlinkSession": "Failed to update WeChat active session.", "sessionOperationInProgress": "Another session operation is already in progress.", "ilinkActiveStatus": "WeChat Active", "ilinkInactiveStatus": "Not WeChat Active", "restartSession": "Restart Session", "closeSession": "Close Session", "closingSession": "Closing…", "failedToCloseSession": "Failed to close session.", "failedToDeleteSession": "Failed to delete session.", "failedToDeleteProject": "Failed to delete project.", "failedToLoadSession": "Failed to load session.", "failedToUpdateMcpServers": "Failed to update MCP servers", "collapse": "Collapse sidebar", "expand": "Expand sidebar", "updated": "Updated", "created": "Created", "sessionCount": "{{count}} sessions", "sessionCount_one": "{{count}} session", "newSessionDialog": { "title": "Create session", "agent": "Agent", "selectAgent": "Select an agent", "permission": "Permission", "permissionAsk": "Ask", "permissionAllowAll": "Allow all", "create": "Create" } };
18725
18727
  const skills$1 = { "title": "Skills", "installed": { "label": "Installed", "noDescription": "No description provided.", "revealInFinder": "Reveal in Finder", "viewSkill": "View SKILL.md", "uninstall": "Uninstall", "uninstallTitle": "Uninstall Skill", "uninstallConfirm": "Are you sure you want to uninstall {{name}}? ", "cancel": "Cancel", "uninstalledSuccess": "Skill {{name}} uninstalled", "uninstallFailed": "Failed to uninstall: {{message}}", "revealFailed": "Failed to reveal in Finder: {{message}}", "loadFailed": "Failed to load skills", "loadContentFailed": "Error loading skill content: {{message}}", "noSkills": "No skills installed." }, "skillsSh": { "label": "skills.sh", "title": "Discover Skills", "desc": "Explore and install community skills to enhance your agents.", "searchPlaceholder": "Search skills (e.g. pr-review, react-best-practices)...", "install": "Install", "installSuccess": "Skill {{name}} installed", "installFailed": "Install failed", "noResults": "No skills found", "installs": "{{count}} installs", "installedStatus": "Installed", "authorPrefix": "by " } };
18726
18728
  const contextMenu$1 = { "cut": "Cut", "copy": "Copy", "paste": "Paste", "selectAll": "Select All" };
18727
18729
  const commands$1 = { "focusChatInput": { "title": "Focus chat input", "description": "Move focus to the chat input." }, "focusChatArea": { "title": "Focus chat area", "description": "Move focus to the chat conversation." }, "focusFileTree": { "title": "Focus file tree", "description": "Move focus to the project file tree." }, "focusTerminalList": { "title": "Focus terminal list", "description": "Move focus to the terminal list." }, "focusSidebarSessions": { "title": "Focus session list", "description": "Move focus to the session list." } };
18728
- const settings$1 = /* @__PURE__ */ JSON.parse(`{"themeDesc":"Customize the appearance of the application","languageDesc":"Select the display language","title":"Settings","general":{"title":"General","desc":"Manage your application settings.","theme":"Theme","light":"Light","dark":"Dark","system":"System","language":"Language","english":"English","chinese":"简体中文","saveThemeFailed":"Failed to save theme setting.","saveLanguageFailed":"Failed to save language setting.","groupAppearance":"Appearance","groupProject":"Project","autoWatchFiles":"Auto Watch Files","autoWatchFilesDesc":"Automatically watch project file changes","saveFileWatcherFailed":"Failed to save file watcher setting.","groupVoiceInput":"Voice Input","altDoublePress":"Double-press Alt","altDoublePressDesc":"Double-press Alt quickly while an input is focused to start or stop voice input.","saveVoiceInputFailed":"Failed to save voice input setting.","groupNetwork":"Network","proxy":"Network Proxy","proxyDesc":"Configure the network proxy used by Fello for Node requests, subprocesses and the UI.","proxyEdit":"Edit proxy settings","proxyOff":"Direct","proxySystem":"System","groupSound":"Sound","soundEnabled":"Sound Effects","soundEnabledDesc":"Play sound effects for notifications","soundVolume":"Volume","soundVolumeDesc":"Adjust notification volume","soundTheme":"Sound Style","soundThemeDesc":"Select the sound effect style","soundSoft":"Soft","soundCrisp":"Crisp","soundTest":"Test","soundTestDesc":"Preview the current sound effect","soundTestPlay":"Play","saveSoundFailed":"Failed to save sound setting.","groupEditor":"Editor","editor":"Open in Editor","editorDesc":"Choose the editor to open files","selectEditor":"Select editor","saveEditorFailed":"Failed to save editor setting."},"shortcuts":{"title":"Shortcuts","desc":"Customize keyboard shortcuts for common actions.","navigation":"Navigation","recording":"Press a shortcut","changeShortcut":"Change shortcut","unassigned":"Unassigned","delete":"Delete","set":"Set","restore":"Restore","setTitle":"Set shortcut","setDesc":"Press the shortcut you want to use.","cancel":"Cancel","confirm":"Confirm","reset":"Reset to default","conflict":"Already used by {{command}}.","saveFailed":"Failed to save shortcuts."},"agents":{"desc":"Manage agent configurations and startup commands.","description":"Configure agents","title":"Agents","guide":"User Guide","addStdioAgent":"Add via ACP Agent","addApiAgent":"Add via API Key","editStdioAgent":"Edit ACP Agent","editApiAgent":"Edit API Agent","dialogDesc":"Configure the agent ID, command, arguments and environment variables.","apiDialogDesc":"Configure provider, endpoint and authentication for API agent.","agentId":"Agent ID (Name)","apiProvider":"Provider","apiBaseUrl":"Base URL","apiKey":"API Key","apiHeaders":"Headers (JSON)","contextWindowTokens":"Context Window (tokens)","modelIdTemplate":"Model Id Template","models":"Models (one per line)","modelsHint":"Usually not needed — models are fetched automatically. Only specify if the API doesn't return the models you need","command":"Command","args":"Arguments","envVars":"Environment Variables (JSON)","envJson":"Environment Variables (JSON)","quickPreset":"Quick preset","cancel":"Cancel","save":"Save","edit":"Edit","delete":"Delete","dragToReorder":"Drag to reorder","noAgents":"No agents configured","errorIdCommand":"ID and Command are required.","errorDuplicateId":"An agent with this ID already exists.","errorEnvJson":"Env must be a valid JSON object.","errorContextWindowTokens":"Context Window must be a positive integer.","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Agent","confirmDeleteDesc":"Are you sure you want to delete this agent? This action cannot be undone.","confirmDeleteDescWithSessions":"Are you sure you want to delete this agent? {{count}} session(s) will also be deleted. This action cannot be undone.","reset":"Reset Agent","resetSuccess":"Agent reset.","resetFailed":"Failed to reset agent.","deleteAllSessions":"Delete All Sessions","confirmDeleteAllSessionsTitle":"Delete All Sessions","confirmDeleteAllSessionsDesc":"All sessions for this agent will be permanently deleted. This action cannot be undone.","deleteAllSessionsSuccess":"{{count}} session(s) deleted.","deleteAllSessionsFailed":"Failed to delete sessions.","validation":{"enterId":"Please enter an agent ID.","idFormat":"Only letters, numbers, _ and - allowed.","selectProvider":"Please select a provider.","enterBaseUrl":"Please enter a base URL.","enterApiKey":"Please enter an API key.","enterCommand":"Please enter a command.","jsonObject":"Must be a valid JSON object with string values.","positiveInteger":"Must be a positive integer."}},"mcp":{"desc":"Manage MCP server configurations and startup commands.","description":"Configure MCP Servers","title":"MCP Servers","guide":"User Guide","addMcp":"Add MCP Server","addStdioMcp":"Add Stdio MCP","addHttpMcp":"Add HTTP MCP","addSseMcp":"Add SSE MCP","recommended":{"exa":{"name":"Exa","desc":"Exa MCP Server — AI Web Search"}},"editMcp":"Edit MCP Server","dialogDesc":"Configure the MCP server ID, command, arguments and environment variables.","mcpId":"MCP Server ID (Name)","command":"Command","args":"Arguments","envVars":"Environment Variables (JSON)","envJson":"Environment Variables (JSON)","url":"URL","headers":"Headers (JSON)","headersJson":"Headers (JSON)","httpDialogDesc":"Configure the MCP server ID, HTTP URL and request headers.","sseDialogDesc":"Configure the MCP server ID, SSE endpoint URL and request headers.","cancel":"Cancel","save":"Save","edit":"Edit","delete":"Delete","dragToReorder":"Drag to reorder","noMcpServers":"No MCP servers configured","errorIdRequired":"ID is required.","errorUrlRequired":"URL is required.","errorIdCommand":"ID and Command are required.","errorDuplicateId":"An MCP server with this ID already exists.","errorHeadersJson":"Headers must be a valid JSON object.","errorEnvJson":"Env must be a valid JSON object.","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete MCP Server","confirmDeleteDesc":"Are you sure you want to delete this MCP server? This action cannot be undone.","validation":{"enterId":"Please enter a server ID.","idFormat":"Only letters, numbers, _ and - allowed.","enterCommand":"Please enter a command.","enterUrl":"Please enter a URL.","jsonObject":"Must be a valid JSON object with string values."}},"webui":{"desc":"Configure remote access to the Fello interface.","title":"WebUI","guide":"User Guide","enable":"Enable WebUI","enableDesc":"Allow remote access to Fello interface via browser","accessUrl":"Access URL","accessDesc":"Note: This URL contains a temporary token. The WebUI will be disabled when you restart the application.","enableAccess":"Enable WebUI Access","port":"Port:","token":"Token:","autoGenerated":"Auto-generated","copy":"Copy","note":"Note: This URL contains a temporary token. The WebUI will be disabled when you restart the application.","updateFailed":"Failed to update configuration.","disabledInWebUI":"WebUI configuration is disabled when running inside the browser."},"ilink":{"title":"WeChat iLink","guide":"User Guide","description":"Connect Fello to WeChat via iLink Bot API. Once connected, a ClawBot contact will appear in your WeChat, allowing you to chat with your AI agent from anywhere.","connected":"Connected","notConnected":"Not Connected","notConnectedHint":"Scan QR code with WeChat to connect","login":"Login","loggingIn":"Waiting...","disconnect":"Disconnect","scanPrompt":"Scan with WeChat to login","qrcodeExpired":"QR code expired, please try again","scanned":"Scanned! Please confirm on your phone.","browserOpened":"QR code opened in browser. Scan with WeChat to login.","activeSession":"Active Session for WeChat","activeSessionHint":"Messages from WeChat will be routed to this session. Right-click a session in the sidebar to set it as active.","selectSession":"-- Select a session --","none":"-- None --","howItWorks":"How it works","step1":"Click \\"Login\\" to open the QR code in your browser","step2":"Scan the QR code with WeChat","step3":"A \\"ClawBot\\" contact appears in your WeChat","step4":"Chat with ClawBot — messages are routed to the active session above","step5":"Right-click any session in the sidebar to set it as active for WeChat","useOriginalImage":"Use Original Image","useOriginalImageDesc":"Send full-size images to the model (uses more tokens). When off, thumbnails are used.","keepaliveMaxCount":"Keepalive Messages","keepaliveMaxCountDesc":"Max consecutive keepalive messages while thinking.","commandsTitle":"WeChat \\"!\\" Commands","commandsDesc":"Messages starting with ! or ! are commands. They interrupt the current agent execution (cancelling an ongoing reply) and then run the corresponding action.","cmdSwitchSession":"Switch Session","cmdSwitchSessionDesc":"List all sessions (grouped by project); reply with a number to switch.","cmdNewSession":"New Session","cmdNewSessionDesc":"List all projects; reply with a number to create a session in that project and switch to it.","cmdSwitchModel":"Switch Model","cmdSwitchModelDesc":"List models available in the current session; reply with a number to switch (API agents only).","cmdSnippet":"Quick Snippet","cmdSnippetDesc":"List configured snippets; reply with a number to send that content to the agent.","cmdStatus":"View Status","cmdStatusDesc":"Show active session info (title, project, agent, features, MCP status)."},"snippets":{"title":"Snippets","desc":"Manage reusable text snippets for quick insertion.","description":"Configure Snippets","add":"Add Snippet","edit":"Edit Snippet","delete":"Delete","save":"Save","cancel":"Cancel","empty":"No snippets configured","saveFailed":"Failed to save snippets.","titleLabel":"Title","titlePlaceholder":"Title","contentLabel":"Content","contentPlaceholder":"Content","dialogDesc":"Configure the snippet title and content.","confirmDeleteTitle":"Delete Snippet","confirmDeleteDesc":"Are you sure you want to delete this snippet? This action cannot be undone.","validation":{"enterTitle":"Please enter a title.","enterContent":"Please enter content."}},"memory":{"title":"Memory","desc":"View and manage persistent project-level memory.","projectMemories":"Project Memories","refresh":"Refresh","clear":"Clear","cleared":"Memory cleared.","clearFailed":"Failed to clear memory.","delete":"Delete","deleted":"Memory entry deleted.","deleteFailed":"Failed to delete memory entry.","confirmDeleteTitle":"Delete Memory Entry","confirmDeleteDesc":"Are you sure you want to delete this memory entry? This cannot be undone.","confirmClearTitle":"Clear Memory","confirmClearDesc":"Are you sure you want to clear all memory for project \\"{{title}}\\"? This cannot be undone.","revealInFinder":"Reveal in Finder","revealFailed":"Failed to reveal file.","noFile":"No memory file found.","noProjects":"No projects found.","empty":"No memories stored.","entries":"Entries"},"imageGeneration":{"title":"Image Generation","desc":"Configure image generation providers. The active provider will be available as a tool for agents.","providers":"Providers","add":"Add Provider","addProvider":"Add Provider","editProvider":"Edit Provider","dialogDesc":"Configure provider, endpoint and authentication for image generation.","edit":"Edit","delete":"Delete","empty":"No image generation providers configured","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Provider","confirmDeleteDesc":"Are you sure you want to delete this image generation provider?","cancel":"Cancel","save":"Save","form":{"name":"Name","provider":"Provider","baseUrl":"Base URL","baseUrlHint":"Request → {baseUrl}/images/generations","apiKey":"API Key","headers":"Headers (JSON)","extraBody":"Extra Body (JSON)","extraBodyHint":"Additional parameters merged into request body (e.g. quality, style).","model":"Model"},"validation":{"enterName":"Please enter a name.","selectProvider":"Please select a provider.","enterBaseUrl":"Please enter a base URL.","enterApiKey":"Please enter an API key.","enterModel":"Please enter the model name.","jsonObject":"Must be a valid JSON object with string values."}},"speechToText":{"title":"Speech to Text","desc":"Configure realtime speech recognition providers for chat voice input.","guide":"User Guide","providers":"Providers","add":"Add Provider","addProvider":"Add Provider","editProvider":"Edit Provider","dialogDesc":"Configure a realtime speech-to-text provider for voice input.","edit":"Edit","delete":"Delete","empty":"No speech-to-text providers configured","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Provider","confirmDeleteDesc":"Delete this speech provider?","cancel":"Cancel","save":"Save","validation":{"enterName":"Please enter a name.","selectProvider":"Please select a provider.","enterApiKey":"Please enter an API key.","enterAppId":"Please enter an App ID.","enterApiSecret":"Please enter an API secret."},"form":{"name":"Name","provider":"Provider","appId":"App ID","apiKey":"API Key","apiSecret":"API Secret","optional":" (optional)","language":"Language (optional)"}},"proxy":{"title":"Network Proxy","mode":"Proxy Mode","off":"Direct","offDesc":"Connect directly without a proxy","manual":"Manual","manualDesc":"Specify a proxy server manually for all network requests","system":"System","systemDesc":"Use the system proxy settings (macOS/Windows or environment variables)","httpProxy":"HTTP Proxy","httpProxyPlaceholder":"e.g. http://127.0.0.1:7890","httpsProxy":"HTTPS Proxy (optional)","httpsProxyHint":"Falls back to the HTTP proxy when empty.","noProxy":"No Proxy (optional)","noProxyPlaceholder":"e.g. localhost,127.0.0.1,*.internal","username":"Username (optional)","password":"Password (optional)","cancel":"Cancel","save":"Save","later":"Later","restartNow":"Restart Now","restartTitle":"Restart Required","restartDesc":"Proxy settings saved. Restart Fello to apply the new proxy configuration.","restartManualDesc":"Proxy settings saved. Please manually restart the Fello server process to apply the new configuration.","saveFailed":"Failed to save proxy settings.","validation":{"httpProxyRequired":"HTTP proxy URL is required in manual mode.","invalidProxyUrl":"{{field}} should start with http:// or https://, or use a host:port address (e.g. 127.0.0.1:7890)."}}}`);
18729
- const chat$1 = { "failedToLoadSession": "Failed to load session.", "failedToUpdateMcpServers": "Failed to update MCP servers." };
18730
+ const settings$1 = /* @__PURE__ */ JSON.parse(`{"themeDesc":"Customize the appearance of the application","languageDesc":"Select the display language","title":"Settings","general":{"title":"General","desc":"Manage your application settings.","theme":"Theme","light":"Light","dark":"Dark","system":"System","language":"Language","english":"English","chinese":"简体中文","saveThemeFailed":"Failed to save theme setting.","saveLanguageFailed":"Failed to save language setting.","groupAppearance":"Appearance","groupProject":"Project","autoWatchFiles":"Auto Watch Files","autoWatchFilesDesc":"Automatically watch project file changes","saveFileWatcherFailed":"Failed to save file watcher setting.","groupVoiceInput":"Voice Input","altDoublePress":"Double-press Alt","altDoublePressDesc":"Double-press Alt quickly while an input is focused to start or stop voice input.","saveVoiceInputFailed":"Failed to save voice input setting.","groupNetwork":"Network","proxy":"Network Proxy","proxyDesc":"Configure the network proxy used by Fello for Node requests, subprocesses and the UI.","proxyEdit":"Edit proxy settings","proxyOff":"Direct","proxySystem":"System","groupSound":"Sound","soundEnabled":"Sound Effects","soundEnabledDesc":"Play sound effects for notifications","soundVolume":"Volume","soundVolumeDesc":"Adjust notification volume","soundTheme":"Sound Style","soundThemeDesc":"Select the sound effect style","soundSoft":"Soft","soundCrisp":"Crisp","soundTest":"Test","soundTestDesc":"Preview the current sound effect","soundTestPlay":"Play","saveSoundFailed":"Failed to save sound setting.","groupEditor":"Editor","editor":"Open in Editor","editorDesc":"Choose the editor to open files","selectEditor":"Select editor","saveEditorFailed":"Failed to save editor setting."},"shortcuts":{"title":"Shortcuts","desc":"Customize keyboard shortcuts for common actions.","navigation":"Navigation","recording":"Press a shortcut","changeShortcut":"Change shortcut","unassigned":"Unassigned","delete":"Delete","set":"Set","restore":"Restore","setTitle":"Set shortcut","setDesc":"Press the shortcut you want to use.","cancel":"Cancel","confirm":"Confirm","reset":"Reset to default","conflict":"Already used by {{command}}.","saveFailed":"Failed to save shortcuts."},"agents":{"desc":"Manage agent configurations and startup commands.","description":"Configure agents","title":"Agents","guide":"User Guide","addStdioAgent":"Add via ACP Agent","addApiAgent":"Add via API Key","editStdioAgent":"Edit ACP Agent","editApiAgent":"Edit API Agent","dialogDesc":"Configure the agent ID, command, arguments and environment variables.","apiDialogDesc":"Configure provider, endpoint and authentication for API agent.","agentId":"Agent ID (Name)","apiProvider":"Provider","apiBaseUrl":"Base URL","apiKey":"API Key","apiHeaders":"Headers (JSON)","contextWindowTokens":"Context Window (tokens)","modelIdTemplate":"Model Id Template","models":"Models (one per line)","modelsHint":"Usually not needed — models are fetched automatically. Only specify if the API doesn't return the models you need","command":"Command","args":"Arguments","envVars":"Environment Variables (JSON)","envJson":"Environment Variables (JSON)","quickPreset":"Quick preset","cancel":"Cancel","save":"Save","edit":"Edit","delete":"Delete","dragToReorder":"Drag to reorder","noAgents":"No agents configured","errorIdCommand":"ID and Command are required.","errorDuplicateId":"An agent with this ID already exists.","errorEnvJson":"Env must be a valid JSON object.","errorContextWindowTokens":"Context Window must be a positive integer.","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Agent","confirmDeleteDesc":"Are you sure you want to delete this agent? This action cannot be undone.","confirmDeleteDescWithSessions":"Are you sure you want to delete this agent? {{count}} session(s) will also be deleted. This action cannot be undone.","reset":"Reset Agent","resetSuccess":"Agent reset.","resetFailed":"Failed to reset agent.","deleteAllSessions":"Delete All Sessions","confirmDeleteAllSessionsTitle":"Delete All Sessions","confirmDeleteAllSessionsDesc":"All sessions for this agent will be permanently deleted. This action cannot be undone.","deleteAllSessionsSuccess":"{{count}} session(s) deleted.","deleteAllSessionsFailed":"Failed to delete sessions.","validation":{"enterId":"Please enter an agent ID.","idFormat":"Only letters, numbers, _ and - allowed.","selectProvider":"Please select a provider.","enterBaseUrl":"Please enter a base URL.","enterApiKey":"Please enter an API key.","enterCommand":"Please enter a command.","jsonObject":"Must be a valid JSON object with string values.","positiveInteger":"Must be a positive integer."}},"mcp":{"desc":"Manage MCP server configurations and startup commands.","description":"Configure MCP Servers","title":"MCP Servers","guide":"User Guide","addMcp":"Add MCP Server","addStdioMcp":"Add Stdio MCP","addHttpMcp":"Add HTTP MCP","addSseMcp":"Add SSE MCP","recommended":{"exa":{"name":"Exa","desc":"Exa MCP Server — AI Web Search"},"chrome-devtools":{"name":"Chrome DevTools","desc":"Chrome DevTools MCP Server — Browser Automation & Debugging"},"mijia-api":{"name":"Mijia","desc":"Mijia MCP Server — Xiaomi Smart Home Control"},"blender":{"name":"Blender","desc":"Blender MCP Server — Natural Language Blender Control"}},"editMcp":"Edit MCP Server","dialogDesc":"Configure the MCP server ID, command, arguments and environment variables.","mcpId":"MCP Server ID (Name)","command":"Command","args":"Arguments","envVars":"Environment Variables (JSON)","envJson":"Environment Variables (JSON)","url":"URL","headers":"Headers (JSON)","headersJson":"Headers (JSON)","httpDialogDesc":"Configure the MCP server ID, HTTP URL and request headers.","sseDialogDesc":"Configure the MCP server ID, SSE endpoint URL and request headers.","cancel":"Cancel","save":"Save","edit":"Edit","delete":"Delete","dragToReorder":"Drag to reorder","noMcpServers":"No MCP servers configured","errorIdRequired":"ID is required.","errorUrlRequired":"URL is required.","errorIdCommand":"ID and Command are required.","errorDuplicateId":"An MCP server with this ID already exists.","errorHeadersJson":"Headers must be a valid JSON object.","errorEnvJson":"Env must be a valid JSON object.","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete MCP Server","confirmDeleteDesc":"Are you sure you want to delete this MCP server? This action cannot be undone.","validation":{"enterId":"Please enter a server ID.","idFormat":"Only letters, numbers, _ and - allowed.","enterCommand":"Please enter a command.","enterUrl":"Please enter a URL.","jsonObject":"Must be a valid JSON object with string values."}},"webui":{"desc":"Configure remote access to the Fello interface.","title":"WebUI","guide":"User Guide","enable":"Enable WebUI","enableDesc":"Allow remote access to Fello interface via browser","accessUrl":"Access URL","accessDesc":"Note: This URL contains a temporary token. The WebUI will be disabled when you restart the application.","enableAccess":"Enable WebUI Access","port":"Port:","token":"Token:","autoGenerated":"Auto-generated","copy":"Copy","note":"Note: This URL contains a temporary token. The WebUI will be disabled when you restart the application.","updateFailed":"Failed to update configuration.","disabledInWebUI":"WebUI configuration is disabled when running inside the browser."},"ilink":{"title":"WeChat iLink","guide":"User Guide","description":"Connect Fello to WeChat via iLink Bot API. Once connected, a ClawBot contact will appear in your WeChat, allowing you to chat with your AI agent from anywhere.","connected":"Connected","notConnected":"Not Connected","notConnectedHint":"Scan QR code with WeChat to connect","login":"Login","loggingIn":"Waiting...","disconnect":"Disconnect","qrcodeExpired":"QR code expired, please try again","scanned":"Scanned! Please confirm on your phone.","browserOpened":"QR code opened in browser. Scan with WeChat to login.","activeSession":"Active Session for WeChat","activeSessionHint":"Messages from WeChat will be routed to this session. Right-click a session in the sidebar to set it as active.","selectSession":"-- Select a session --","none":"-- None --","howItWorks":"How it works","step1":"Click \\"Login\\" to open the QR code in your browser","step2":"Scan the QR code with WeChat","step3":"A \\"ClawBot\\" contact appears in your WeChat","step4":"Chat with ClawBot — messages are routed to the active session above","step5":"Right-click any session in the sidebar to set it as active for WeChat","useOriginalImage":"Use Original Image","useOriginalImageDesc":"Send full-size images to the model (uses more tokens). When off, thumbnails are used.","commandsTitle":"WeChat \\"!\\" Commands","commandsDesc":"Messages starting with ! are commands. They interrupt the current agent execution (cancelling an ongoing reply) and then run the corresponding action.","cmdSwitchSession":"Switch Session","cmdSwitchSessionDesc":"List all sessions (grouped by project); reply with a number to switch.","cmdNewSession":"New Session","cmdNewSessionDesc":"List all projects; reply with a number to create a session in that project and switch to it.","cmdSwitchModel":"Switch Model","cmdSwitchModelDesc":"List models available in the current session; reply with a number to switch (API agents only).","cmdSnippet":"Quick Snippet","cmdSnippetDesc":"List configured snippets; reply with a number to send that content to the agent.","cmdFeature":"Toggle Features","cmdFeatureDesc":"List the current session's features; reply with a number to toggle one. The session restarts so the change takes effect.","cmdToggleMcp":"Toggle MCP Servers","cmdToggleMcpDesc":"List the session's MCP servers; reply with a number to toggle one. The session restarts so the change takes effect.","cmdPermission":"Switch Permission","cmdPermissionDesc":"List the permission modes (Ask / Allow all); reply with a number to switch. Applied immediately, without restarting the session.","cmdStatus":"View Status","cmdStatusDesc":"Show active session info (title, project, agent, permission, features, MCP status)."},"snippets":{"title":"Snippets","desc":"Manage reusable text snippets for quick insertion.","description":"Configure Snippets","add":"Add Snippet","edit":"Edit Snippet","delete":"Delete","dragToReorder":"Drag to reorder","save":"Save","cancel":"Cancel","empty":"No snippets configured","saveFailed":"Failed to save snippets.","titleLabel":"Title","titlePlaceholder":"Title","contentLabel":"Content","contentPlaceholder":"Content","dialogDesc":"Configure the snippet title and content.","confirmDeleteTitle":"Delete Snippet","confirmDeleteDesc":"Are you sure you want to delete this snippet? This action cannot be undone.","validation":{"enterTitle":"Please enter a title.","enterContent":"Please enter content."}},"memory":{"title":"Memory","desc":"View and manage persistent project-level memory.","projectMemories":"Project Memories","refresh":"Refresh","clear":"Clear","cleared":"Memory cleared.","clearFailed":"Failed to clear memory.","delete":"Delete","deleted":"Memory entry deleted.","deleteFailed":"Failed to delete memory entry.","confirmDeleteTitle":"Delete Memory Entry","confirmDeleteDesc":"Are you sure you want to delete this memory entry? This cannot be undone.","confirmClearTitle":"Clear Memory","confirmClearDesc":"Are you sure you want to clear all memory for project \\"{{title}}\\"? This cannot be undone.","revealInFinder":"Reveal in Finder","revealFailed":"Failed to reveal file.","noFile":"No memory file found.","noProjects":"No projects found.","empty":"No memories stored.","entries":"Entries"},"imageGeneration":{"title":"Image Generation","desc":"Configure image generation providers. The active provider will be available as a tool for agents.","providers":"Providers","add":"Add Provider","addProvider":"Add Provider","editProvider":"Edit Provider","dialogDesc":"Configure provider, endpoint and authentication for image generation.","edit":"Edit","delete":"Delete","empty":"No image generation providers configured","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Provider","confirmDeleteDesc":"Are you sure you want to delete this image generation provider?","cancel":"Cancel","save":"Save","form":{"name":"Name","provider":"Provider","baseUrl":"Base URL","baseUrlHint":"Request → {baseUrl}/images/generations","apiKey":"API Key","headers":"Headers (JSON)","extraBody":"Extra Body (JSON)","extraBodyHint":"Additional parameters merged into request body (e.g. quality, style).","model":"Model"},"validation":{"enterName":"Please enter a name.","selectProvider":"Please select a provider.","enterBaseUrl":"Please enter a base URL.","enterApiKey":"Please enter an API key.","enterModel":"Please enter the model name.","jsonObject":"Must be a valid JSON object with string values."}},"speech":{"title":"Speech","desc":"Configure voice input (speech-to-text) and voice output (speech synthesis).","guide":"User Guide","providers":"Providers","add":"Add Provider","addProvider":"Add Provider","editProvider":"Edit Provider","dialogDesc":"Configure one provider for speech recognition and/or speech synthesis.","edit":"Edit","delete":"Delete","empty":"No speech providers configured","updateFailed":"Failed to update configuration.","confirmDeleteTitle":"Delete Provider","confirmDeleteDesc":"Are you sure you want to delete this speech provider?","cancel":"Cancel","save":"Save","shortRecognition":"ASR","shortSynthesis":"TTS","form":{"name":"Name","provider":"Provider","apiKey":"API Key","appId":"App ID","apiSecret":"API Secret","language":"Language","voice":"Voice","optional":" (optional)","recognition":"Recognition","recognitionDesc":"Realtime speech-to-text for chat voice input.","synthesis":"Synthesis","synthesisDesc":"Read agent replies out loud with this voice."},"validation":{"enterName":"Please enter a name.","selectProvider":"Please select a provider.","enterApiKey":"Please enter an API key.","enterAppId":"Please enter an App ID.","enterApiSecret":"Please enter an API secret."}},"proxy":{"title":"Network Proxy","mode":"Proxy Mode","off":"Direct","offDesc":"Connect directly without a proxy","manual":"Manual","manualDesc":"Specify a proxy server manually for all network requests","system":"System","systemDesc":"Use the system proxy settings (macOS/Windows or environment variables)","httpProxy":"HTTP Proxy","httpProxyPlaceholder":"e.g. http://127.0.0.1:7890","httpsProxy":"HTTPS Proxy (optional)","httpsProxyHint":"Falls back to the HTTP proxy when empty.","noProxy":"No Proxy (optional)","noProxyPlaceholder":"e.g. localhost,127.0.0.1,*.internal","username":"Username (optional)","password":"Password (optional)","cancel":"Cancel","save":"Save","later":"Later","restartNow":"Restart Now","restartTitle":"Restart Required","restartDesc":"Proxy settings saved. Restart Fello to apply the new proxy configuration.","restartManualDesc":"Proxy settings saved. Please manually restart the Fello server process to apply the new configuration.","saveFailed":"Failed to save proxy settings.","validation":{"httpProxyRequired":"HTTP proxy URL is required in manual mode.","invalidProxyUrl":"{{field}} should start with http:// or https://, or use a host:port address (e.g. 127.0.0.1:7890)."}},"peripherals":{"title":"Peripherals","desc":"Connect Bluetooth / HID peripherals and enable the ones whose special behavior should apply.","desktopOnly":"Peripherals are available in the desktop app only.","unsupportedPlatform":"Not supported on this platform","voiceCapable":"Voice input","productPage":"Product page","connectFailed":"Failed to connect the peripheral.","updateFailed":"Failed to update peripheral settings.","status":{"bluetooth":"Bluetooth connected"},"keys":{"voice":"Voice","back":"Back"},"phase":{"inactive":"Inactive","unsupported":"Unsupported","unavailable":"Unavailable","preparing":"Preparing","scanning":"Searching","connecting":"Connecting","connected":"Connected","error":"Error","disconnected":"Disconnected"},"openPermissionSettings":"Input Monitoring settings","permissionHint":"macOS needs the Input Monitoring permission to read HID keys, enable Fello here (press + if it is not listed). ","reconnect":"Reconnect"}}`);
18731
+ const chat$1 = { "failedToLoadSession": "Failed to load session.", "failedToUpdateMcpServers": "Failed to update MCP servers.", "readAloud": "Read aloud" };
18730
18732
  const tokenUsage$1 = { "title": "Token Usage Breakdown", "loading": "Loading…", "noData": "No token breakdown data available", "promptCount": "{{count}} prompts", "promptLabel": "Prompt #{{count}}", "noBreakdown": "No detailed breakdown available for this prompt", "input": "Input", "output": "Output", "inputShort": "in", "outputShort": "out", "totalTokens": "Total", "thought": "Thought", "cacheRead": "Cache Read", "cacheWrite": "Cache Write", "cached": "Cached", "tokens": "tokens", "token": "token", "sessionTotal": "Session Total", "systemPrompt": "System Prompt", "tools": "Tools", "history": "History", "userMessage": "User Message", "inputComposition": "Input Composition", "delta": "Estimation delta", "toolsDefinition": "Tools Definition", "toolsCount": "{{count}} tools · {{tokens}} tokens", "more": "+{{count}} more", "steps": "Steps", "stepsCount": "{{count}} steps", "toolCalls": "tool calls", "noCache": "No Cache", "text": "Text", "stepTime": "Step Time", "responseTime": "Response Time", "arguments": "Arguments", "result": "Result", "execution": "Execution", "performance": "Performance", "totalTime": "Total Time", "outputSpeed": "Output Speed", "tokensPerSecond": "token/s", "firstToken": "First Token" };
18731
18733
  const planBubble$1 = { "title": "Plan", "summary": "{{completed}} / {{total}} completed", "status": { "pending": "Pending", "inProgress": "In progress", "completed": "Completed" }, "priority": { "high": "High", "medium": "Medium", "low": "Low" } };
18732
- const constant$1 = { "feature": { "title": "Features", "askUser": "Ask Me", "skills": "Skills", "shareToUser": "Share to User", "search": "Search", "memory": "Memory", "imageGeneration": "Image Generation" } };
18733
- const chatHeader$1 = { "sessionId": "Session", "openSessionFolder": "Open Session Folder", "projectId": "Project", "openProjectFolder": "Open Project Folder", "refresh": "Restart Session", "closeSession": "Close Session", "closingSession": "Closing…", "failedToCloseSession": "Failed to close session.", "sessionOperationInProgress": "Another session operation is already in progress.", "contextWindow": "Context Window", "used": "Used", "size": "Size", "percentUsed": "{{pct}}% used", "remaining": "{{count}} remaining", "lastTurn": "Last Turn", "input": "Input", "output": "Output", "total": "Total", "thought": "Thought", "cacheRead": "Cache read", "cacheWrite": "Cache write", "cost": "Cost", "viewBreakdown": "View Breakdown" };
18734
- const chatInput$1 = { "placeholderDisabled": "Start a new chat to begin...", "placeholderActive": "Ask anything... (Enter to send)", "mode": "Mode", "suggestions": "Suggestions", "messageInput": "Message input", "stop": "Stop", "send": "Send", "tokensUsage": "In: {{input}} Out: {{output}} Total: {{total}}{{thoughtStr}}", "tokensTotal": "{{total}}k tokens", "generationFailed": "Generation failed.", "attach": "Attach file", "reference": "Reference", "mention": "Mention", "snippets": "Snippets", "snippetsEmpty": "No snippets. Click to add in Settings.", "selectModel": "Select model", "stopReasonMaxTokens": "Reached maximum token limit", "stopReasonMaxTurnRequests": "Reached maximum turn limit", "stopReasonRefusal": "Model refused to respond", "stopReasonCancelled": "Generation was cancelled", "voiceInput": "Voice input", "selectVoiceInputDevice": "Select microphone", "stopVoiceInput": "Stop voice input", "startingVoiceInput": "Starting voice input", "voiceInputNotConfigured": "Configure voice input in Settings", "noAudioInputDevices": "No audio input devices found" };
18734
+ const constant$1 = { "feature": { "askUser": "Ask Me", "skills": "Skills", "shareToUser": "Share to User", "search": "Search", "memory": "Memory", "imageGeneration": "Image Generation" } };
18735
+ const chatHeader$1 = { "sessionId": "Session", "openSessionFolder": "Open Session Folder", "projectId": "Project", "openProjectFolder": "Open Project Folder", "refresh": "Restart Session", "closeSession": "Close Session", "closingSession": "Closing…", "failedToCloseSession": "Failed to close session.", "sessionOperationInProgress": "Another session operation is already in progress.", "contextWindow": "Context Window", "used": "Used", "size": "Size", "percentUsed": "{{pct}}% used", "remaining": "{{count}} remaining", "lastTurn": "Last Turn", "input": "Input", "output": "Output", "total": "Total", "thought": "Thought", "cacheRead": "Cache read", "cacheWrite": "Cache write", "cost": "Cost", "viewBreakdown": "View Breakdown", "autoRead": "Auto-read replies", "volume": "Volume", "reading": "Reading…", "stopReading": "Stop reading" };
18736
+ const chatInput$1 = { "placeholderDisabled": "Start a new chat to begin...", "placeholderActive": "Ask anything... (Enter to send)", "mode": "Mode", "suggestions": "Suggestions", "messageInput": "Message input", "stop": "Stop", "send": "Send", "tokensUsage": "In: {{input}} Out: {{output}} Total: {{total}}{{thoughtStr}}", "tokensTotal": "{{total}}k tokens", "generationFailed": "Generation failed.", "attach": "Attach file", "reference": "Reference", "mention": "Mention", "snippets": "Snippets", "snippetsEmpty": "No snippets. Click to add in Settings.", "selectModel": "Select model", "stopReasonMaxTokens": "Reached maximum token limit", "stopReasonMaxTurnRequests": "Reached maximum turn limit", "stopReasonRefusal": "Model refused to respond", "stopReasonCancelled": "Generation was cancelled", "voiceInput": "Voice input", "selectVoiceInputDevice": "Select microphone", "stopVoiceInput": "Stop voice input", "startingVoiceInput": "Starting voice input", "voiceInputNotConfigured": "Configure voice input in Settings", "noAudioInputDevices": "No audio input devices found", "voicePanelTitle": "Voice input", "voicePanelEmpty": "No speech detected", "voicePanelCancel": "Cancel", "voicePanelSend": "Send", "voicePanelAutoSendLabel": "s until auto-send", "voicePanelHint": "Speak now — keep going for as long as you need", "voicePanelHoldHint": "Release to finish — everything you said is kept", "voicePanelToInput": "Fill into input box" };
18735
18737
  const filePanel$1 = { "title": "Files", "view": "View", "emptyDirectory": "Empty directory", "newFile": "New File", "newFolder": "New Folder", "rename": "Rename", "copyPath": "Copy Path", "copyRelativePath": "Copy Relative Path", "revealInFinder": "Reveal in Finder", "openInEditor": "Open in {{editor}}", "delete": "Delete", "deleteConfirmSingle": 'How would you like to delete "{{name}}"?', "deleteConfirmMultiple": "How would you like to delete {{count}} items?", "dragItems": "{{count}} items", "cancel": "Cancel", "moveToTrash": "Move to Trash", "moveToRecycleBin": "Move to Recycle Bin", "moveConfirmTitle": "Move", "moveConfirmSingle": 'Move "{{name}}" to "{{dest}}"?', "moveConfirmMultiple": 'Move {{count}} items to "{{dest}}"?', "moveConfirmSingleToRoot": 'Move "{{name}}" to the root directory?', "moveConfirmMultipleToRoot": "Move {{count}} items to the root directory?", "loading": "Loading...", "collapseFolders": "Collapse Folders", "refresh": "Refresh", "addToChat": "Add to Chat", "noProject": "No project selected" };
18736
18738
  const toolBubble$1 = { "tool": "Tool", "stop": "Stop", "stopping": "Stopping…", "result": "Result", "terminal": "Terminal" };
18737
18739
  const imageView$1 = { "fallbackAlt": "Image" };
@@ -18744,20 +18746,21 @@ const subagentBubble$1 = { "thinking": "Thinking...", "waitingForTask": "Waiting
18744
18746
  const chatTimeline$1 = { "userMessageIndex": "User message {{index}}", "jumpToUserMessage": "Jump to {{label}}" };
18745
18747
  const thinkingBubble$1 = { "thinking": "Thinking...", "thought": "Thought" };
18746
18748
  const userBubble$1 = { "copy": "Copy", "locate": "Locate" };
18747
- const session$1 = { "usageSummary": "{{total}} tk ( ↓ {{input}} ↑ {{output}}{{cachedPart}} )", "usageSummaryCached": " ↻ {{cached}}", "connecting": "Connecting to agent...", "connectionFailed": "Failed to connect to agent", "retry": "Retry" };
18749
+ const session$1 = { "usageSummary": "{{total}} tk ( ↓ {{input}} ↑ {{output}}{{cachedPart}} )", "usageSummaryCached": " ↻ {{cached}}", "connecting": "Connecting to agent...", "connectionFailed": "Failed to connect to agent", "connectionFailedHint": "If a session setting (such as MCP or features) is causing this, adjust it below and reload.", "reload": "Reload", "operationInProgress": "Another session operation is already in progress.", "failedToLoadSession": "Failed to load session.", "failedToUpdateMcpServers": "Failed to update MCP servers" };
18750
+ const sessionConfig$1 = { "permission": "Permission", "permissionAsk": "Ask", "permissionAllowAll": "Allow all", "features": "Features", "mcpServers": "MCP Servers", "noMcpServers": "No MCP servers configured" };
18748
18751
  const terminalPanel$1 = { "title": "Terminal", "noTerminalSelected": "No terminal selected", "addTerminal": "Add terminal", "view": "View", "delete": "Delete terminal" };
18749
18752
  const permission$1 = { "title": "Permission Required", "error": "Failed to respond to permission request" };
18750
18753
  const errorBoundary$1 = { "title": "Something went wrong", "defaultMessage": "An unexpected error occurred in the application.", "reload": "Reload Application" };
18751
18754
  const panel$1 = { "files": "Files", "terminal": "Terminal", "noProjectSelected": "No project selected" };
18752
18755
  const contentBlock$1 = { "unsupported": "Unsupported block type: {{type}}", "download": "Download", "revealInFinder": "Reveal in Finder", "copyToWorkspace": "Copy to Workspace", "copySuccess": "Copied to workspace", "openLink": "Open Link", "invalidImage": "[Invalid Image block]", "invalidAudio": "[Invalid Audio block]", "image": "Image:", "audio": "Audio:", "imageAlt": "Agent provided image", "fileTooLarge": "File is too large (exceeds 20MB)" };
18753
18756
  const detail$1 = { "noContent": "No content" };
18754
- const fileDetail$1 = { "fileNotFound": "File not found", "fileTooLarge": "File is too large to preview", "fileFormatNotSupported": "File format not supported for preview", "errorLoading": "Error loading file", "loading": "Loading...", "noChanges": "No changes detected", "resetZoom": "Reset", "preview": "Preview", "code": "Code", "compare": "Compare", "fileModifiedNotice": "File modified. Please refresh.", "edit": "Edit", "save": "Save", "saving": "Saving…", "saved": "Saved", "saveSuccess": "Saved", "saveFailed": "Save failed", "unsaved": "Unsaved", "copyFileContent": "Copy File Content", "addFileToChat": "Add File to Chat", "addSelectionToChat": "Add Selection to Chat", "loadError": "Failed to load", "zoomOut": "Zoom out", "zoomIn": "Zoom in", "previousPage": "Previous page", "nextPage": "Next page", "previousSlide": "Previous slide", "nextSlide": "Next slide", "speakerNotes": "Notes", "closeNotes": "Close" };
18757
+ const fileDetail$1 = { "fileNotFound": "File not found", "fileTooLarge": "File is too large to preview", "fileFormatNotSupported": "File format not supported for preview", "mediaPlaybackFailed": "Failed to play this file", "errorLoading": "Error loading file", "loading": "Loading...", "noChanges": "No changes detected", "resetZoom": "Reset", "preview": "Preview", "code": "Code", "compare": "Compare", "fileModifiedNotice": "File modified. Please refresh.", "edit": "Edit", "save": "Save", "saving": "Saving…", "saved": "Saved", "saveSuccess": "Saved", "saveFailed": "Save failed", "unsaved": "Unsaved", "copyFileContent": "Copy File Content", "addFileToChat": "Add File to Chat", "addSelectionToChat": "Add Selection to Chat", "loadError": "Failed to load", "zoomOut": "Zoom out", "zoomIn": "Zoom in", "previousPage": "Previous page", "nextPage": "Next page", "previousSlide": "Previous slide", "nextSlide": "Next slide", "speakerNotes": "Notes", "closeNotes": "Close" };
18755
18758
  const htmlDetail$1 = { "fileNotFound": "File not found or inaccessible", "errorLoading": "Failed to load", "loadFailed": "Page load failed", "retry": "Retry", "openInBrowser": "Open in browser", "webuiNotEnabled": "Enable WebUI first to open in browser", "open": "Open", "refresh": "Refresh" };
18756
18759
  const welcome$1 = { "title": "Fello", "desc": "Yet another Agent client based on the ACP protocol. Connect with the agent and begin collaborating.", "hint": "Start a new chat from the sidebar", "getStarted": "Get Started", "stepAgent": "Configure Agent", "stepAgentDesc": "Add an AI agent (Stdio or API) to power your conversations", "stepAgentDone": "{{count}} agent(s) configured", "stepProject": "Add Project", "stepProjectDesc": "Select a local project folder as your workspace", "stepProjectDone": "{{count}} project(s) added", "stepChat": "Start Chat", "stepChatDesc": "Create a new AI conversation in your project", "allDone": "All set! Start a new chat from the sidebar" };
18757
18760
  const askUser$1 = { "title": "Request", "other": "Custom reply", "back": "Use options", "inputPlaceholder": "Type your response... (Enter to send)", "submit": "Submit", "autoClose": "Auto closes on timeout", "collapse": "Collapse", "expand": "Expand" };
18758
- const shareToUser$1 = { "copyToProject": "Copy to project", "reveal": "Reveal in Finder", "loadFailed": "Failed to load image", "download": "Download" };
18761
+ const shareToUser$1 = { "copyToProject": "Copy to project", "reveal": "Reveal in Finder", "loadFailed": "Failed to load image", "playbackFailed": "Failed to play this file", "download": "Download" };
18759
18762
  const imageGeneration$1 = { "copyToProject": "Copy to project", "reveal": "Reveal in Finder", "loadFailed": "Failed to load image", "download": "Download" };
18760
- const automation$1 = { "title": "Automation", "agent": "Agent", "alwaysDisabled": "always disabled", "anyMoment": "Any moment now", "back": "Back", "cancel": "Cancel", "create": "Create", "createFirst": "Create your first schedule", "delete": "Delete", "deleteConfirm": 'Delete "{{name}}" permanently?', "deleteTaskConfirm": "Delete this task permanently?", "deleteTaskTitle": "Delete Task", "deleteTitle": "Delete Schedule", "dialogDesc": "Configure the automated task schedule and prompt.", "editSchedule": "Edit Schedule", "editSettings": "Edit settings", "features": "Features", "files": "Files", "lastRun": "Last", "lessThanMinute": "Less than 1 min", "manual": "Manual", "manualDesc": "Manual trigger only. No automatic scheduling.", "mcpServers": "MCP Servers", "model": "Model", "modelPlaceholder": "Default model", "newSchedule": "New Schedule", "nextRun": "Next", "noFiles": "No files generated", "noSchedules": "No schedules yet", "emptyHint": "Schedule AI tasks to run automatically with cron expressions", "noTasksDesc": 'No runs yet. Click "Trigger now" to start the first run.', "prompt": "Prompt", "promptPlaceholder": "What should the agent do?", "remainingRuns": "Run Limit", "remainingRunsCount": "{{count}} runs left", "remainingRunsCount_one": "{{count}} run left", "remainingRunsDesc": "Empty = unlimited", "remainingRunsPlaceholder": "∞", "save": "Save", "schedule": "Schedule", "scheduleName": "Schedule Name", "scheduleNamePlaceholder": "e.g. Morning Standup Summary", "scheduleNotFound": "Schedule not found", "schedules": "schedules", "selectFileToPreview": "Select a file to preview", "selectTaskToPreview": "Select a task to preview files", "selectTask": "Select a task", "timed": "Timed", "triggerNow": "Trigger now", "validation": { "nameRequired": "Name is required", "agentRequired": "Agent is required", "promptRequired": "Prompt is required", "runLimitInvalid": "Run limit must be a non-negative integer." }, "cron": { "at": "at", "atMinute": "at min", "custom": "Custom", "daily": "Every day", "hourly": "Every N hours", "weekdays": "Weekdays", "weekly": "Every week", "nextRun": "Next run", "nextRunIn": "Next run in", "lastRun": "Last run", "days": { "0": "Sun", "1": "Mon", "2": "Tue", "3": "Wed", "4": "Thu", "5": "Fri", "6": "Sat" } } };
18763
+ const automation$1 = { "title": "Automation", "agent": "Agent", "alwaysDisabled": "always disabled", "anyMoment": "Any moment now", "back": "Back", "cancel": "Cancel", "create": "Create", "createFirst": "Create your first schedule", "delete": "Delete", "deleteConfirm": 'Delete "{{name}}" permanently?', "deleteTaskConfirm": "Delete this task permanently?", "deleteTaskTitle": "Delete Task", "deleteTitle": "Delete Schedule", "dialogDesc": "Configure the automated task schedule and prompt.", "editSchedule": "Edit Schedule", "editSettings": "Edit settings", "features": "Features", "files": "Files", "lastRun": "Last", "lessThanMinute": "Less than 1 min", "manual": "Manual", "manualDesc": "Manual trigger only. No automatic scheduling.", "mcpServers": "MCP Servers", "model": "Model", "modelPlaceholder": "Default model", "newSchedule": "New Schedule", "nextRun": "Next", "noFiles": "No files generated", "noSchedules": "No schedules yet", "emptyHint": "Schedule AI tasks to run automatically with cron expressions", "noTasksDesc": 'No runs yet. Click "Trigger now" to start the first run.', "prompt": "Prompt", "promptPlaceholder": "What should the agent do?", "remainingRuns": "Run Limit", "remainingRunsCount": "{{count}} runs left", "remainingRunsCount_one": "{{count}} run left", "remainingRunsDesc": "Empty = unlimited", "remainingRunsPlaceholder": "∞", "save": "Save", "schedule": "Schedule", "scheduleName": "Schedule Name", "scheduleNamePlaceholder": "e.g. Morning Standup Summary", "scheduleNotFound": "Schedule not found", "schedules": "schedules", "selectFileToPreview": "Select a file to preview", "selectTaskToPreview": "Select a task to preview files", "selectTask": "Select a task", "recurring": "Recurring", "once": "Once", "oncePickDate": "Pick a date", "onceDesc": "Runs once at the specified time.", "missed": "Missed", "lastTriggered": "Last triggered", "duration": { "minutes": "{{minutes}} min", "hoursMinutes": "{{hours}}h {{minutes}}m", "daysHours": "{{days}}d {{hours}}h" }, "triggerNow": "Trigger now", "validation": { "nameRequired": "Name is required", "agentRequired": "Agent is required", "promptRequired": "Prompt is required", "runLimitInvalid": "Run limit must be a non-negative integer.", "onceTimeInvalid": "Please pick a future time." }, "cron": { "at": "at", "atMinute": "at min", "custom": "Custom", "daily": "Every day", "hourly": "Every N hours", "weekdays": "Weekdays", "weekly": "Every week", "nextRun": "Next run", "nextRunIn": "Next run in", "days": { "0": "Sun", "1": "Mon", "2": "Tue", "3": "Wed", "4": "Thu", "5": "Fri", "6": "Sat" } } };
18761
18764
  const en = {
18762
18765
  sidebar: sidebar$1,
18763
18766
  skills: skills$1,
@@ -18783,6 +18786,7 @@ const en = {
18783
18786
  thinkingBubble: thinkingBubble$1,
18784
18787
  userBubble: userBubble$1,
18785
18788
  session: session$1,
18789
+ sessionConfig: sessionConfig$1,
18786
18790
  terminalPanel: terminalPanel$1,
18787
18791
  permission: permission$1,
18788
18792
  errorBoundary: errorBoundary$1,
@@ -18797,17 +18801,17 @@ const en = {
18797
18801
  imageGeneration: imageGeneration$1,
18798
18802
  automation: automation$1
18799
18803
  };
18800
- const sidebar = { "welcome": "欢迎使用", "skills": "技能", "automation": "自动化", "projects": "项目", "noProjects": "暂无项目", "noProjectsHint": "添加一个项目文件夹开始", "addProject": "添加项目", "noAgentsHint": "请先配置 Agent", "configureAgent": "配置 Agent", "revealInFinder": "在访达中显示", "rename": "重命名", "delete": "删除", "renameProject": "重命名项目", "renameSession": "重命名会话", "deleteSession": "删除会话", "enterNewProjectName": "请输入新的项目名称。", "enterNewSessionName": "请输入新的会话名称。", "addProjectTitle": "添加项目", "enterProjectPath": "请输入服务器上的项目绝对路径:", "pathCannotBeEmpty": "路径不能为空", "webuiEnabled": "已启用 WebUI", "deleteProject": "删除项目", "deleteProjectConfirm": "是否删除项目“{{title}}”及其所有会话?\n此操作无法撤销。", "deleteSessionConfirm": "是否删除会话“{{title}}”?\n此操作无法撤销。", "cancel": "取消", "save": "保存", "settings": "设置", "agents": "智能体", "addProjectFailed": "添加项目失败。", "newSessionFailed": "创建新会话失败。", "loadSessionFailed": "加载会话失败。", "revealInFinderFailed": "在访达中显示失败。", "openInEditorFailed": "在编辑器中打开失败。", "projectExists": "项目已存在。", "projectNameEmpty": "项目名称不能为空。", "sessionNameEmpty": "会话名称不能为空。", "newSession": "新会话", "viewSession": "查看会话", "projectActions": "项目操作:{{title}}", "createSessionInProject": "在 {{title}} 中创建会话", "sessionActions": "会话操作:{{title}}", "ilinkSetActive": "设为微信活跃会话", "ilinkUnsetActive": "取消微信活跃", "failedToUpdateIlinkSession": "更新微信活跃会话失败。", "sessionOperationInProgress": "已有其他会话操作正在进行中。", "ilinkActiveStatus": "微信活跃", "ilinkInactiveStatus": "非微信活跃", "restartSession": "重启会话", "closeSession": "关闭会话", "closingSession": "关闭中…", "failedToCloseSession": "关闭会话失败。", "failedToDeleteSession": "删除会话失败。", "failedToDeleteProject": "删除项目失败。", "failedToLoadSession": "加载会话失败。", "failedToUpdateMcpServers": "更新 MCP 服务器失败。", "collapse": "收起侧边栏", "expand": "展开侧边栏", "updated": "更新于", "created": "创建于", "sessionCount": "{{count}} 个会话", "newSessionDialog": { "title": "新建会话", "agent": "智能体", "selectAgent": "选择智能体", "mcp": "MCP", "noMcp": "未配置 MCP 服务器", "permission": "权限", "permissionAsk": "每次询问", "permissionAllowAll": "全部允许", "create": "创建" } };
18804
+ const sidebar = { "welcome": "欢迎使用", "skills": "技能", "automation": "自动化", "projects": "项目", "noProjects": "暂无项目", "noProjectsHint": "添加一个项目文件夹开始", "addProject": "添加项目", "noAgentsHint": "请先配置 Agent", "configureAgent": "配置 Agent", "revealInFinder": "在访达中显示", "copyProjectPath": "复制项目路径", "rename": "重命名", "delete": "删除", "renameProject": "重命名项目", "renameSession": "重命名会话", "deleteSession": "删除会话", "enterNewProjectName": "请输入新的项目名称。", "enterNewSessionName": "请输入新的会话名称。", "addProjectTitle": "添加项目", "enterProjectPath": "请输入服务器上的项目绝对路径:", "pathCannotBeEmpty": "路径不能为空", "webuiEnabled": "已启用 WebUI", "deleteProject": "删除项目", "deleteProjectConfirm": "是否删除项目“{{title}}”及其所有会话?\n此操作无法撤销。", "deleteSessionConfirm": "是否删除会话“{{title}}”?\n此操作无法撤销。", "cancel": "取消", "save": "保存", "settings": "设置", "agents": "智能体", "addProjectFailed": "添加项目失败。", "dropFolderOnly": "只能将文件夹拖入以添加项目。", "newSessionFailed": "创建新会话失败。", "loadSessionFailed": "加载会话失败。", "revealInFinderFailed": "在访达中显示失败。", "openInEditorFailed": "在编辑器中打开失败。", "projectExists": "项目已存在。", "projectNameEmpty": "项目名称不能为空。", "sessionNameEmpty": "会话名称不能为空。", "newSession": "新会话", "viewSession": "查看会话", "projectActions": "项目操作:{{title}}", "createSessionInProject": "在 {{title}} 中创建会话", "sessionActions": "会话操作:{{title}}", "ilinkSetActive": "设为微信活跃会话", "ilinkUnsetActive": "取消微信活跃", "failedToUpdateIlinkSession": "更新微信活跃会话失败。", "sessionOperationInProgress": "已有其他会话操作正在进行中。", "ilinkActiveStatus": "微信活跃", "ilinkInactiveStatus": "非微信活跃", "restartSession": "重启会话", "closeSession": "关闭会话", "closingSession": "关闭中…", "failedToCloseSession": "关闭会话失败。", "failedToDeleteSession": "删除会话失败。", "failedToDeleteProject": "删除项目失败。", "failedToLoadSession": "加载会话失败。", "failedToUpdateMcpServers": "更新 MCP 服务器失败。", "collapse": "收起侧边栏", "expand": "展开侧边栏", "updated": "更新于", "created": "创建于", "sessionCount": "{{count}} 个会话", "newSessionDialog": { "title": "新建会话", "agent": "智能体", "selectAgent": "选择智能体", "permission": "权限", "permissionAsk": "每次询问", "permissionAllowAll": "全部允许", "create": "创建" } };
18801
18805
  const skills = { "title": "技能", "installed": { "label": "已安装", "noDescription": "暂无描述。", "revealInFinder": "在访达中显示", "viewSkill": "查看 SKILL.md", "uninstall": "卸载", "uninstallTitle": "卸载技能", "uninstallConfirm": "确定要卸载 {{name}} 吗?", "cancel": "取消", "uninstalledSuccess": "技能 {{name}} 已卸载", "uninstallFailed": "卸载失败:{{message}}", "revealFailed": "在访达中显示失败:{{message}}", "loadFailed": "加载技能失败", "loadContentFailed": "加载技能内容出错:{{message}}", "noSkills": "暂无安装的技能" }, "skillsSh": { "label": "skills.sh", "title": "探索技能", "desc": "浏览并安装社区提供的技能,以增强你的 Agent 能力。", "searchPlaceholder": "搜索技能(例如:pr-review, react-best-practices)...", "install": "安装", "installSuccess": "技能 {{name}} 已安装", "installFailed": "安装失败", "noResults": "未找到相关技能", "installs": "{{count}} 次安装", "installedStatus": "已安装", "authorPrefix": "作者: " } };
18802
18806
  const contextMenu = { "cut": "剪切", "copy": "复制", "paste": "粘贴", "selectAll": "全选" };
18803
18807
  const commands = { "focusChatInput": { "title": "聚焦聊天输入框", "description": "将焦点移动到聊天输入框。" }, "focusChatArea": { "title": "聚焦聊天区域", "description": "将焦点移动到聊天内容区域。" }, "focusFileTree": { "title": "聚焦文件树", "description": "将焦点移动到项目文件树。" }, "focusTerminalList": { "title": "聚焦终端列表", "description": "将焦点移动到终端列表。" }, "focusSidebarSessions": { "title": "聚焦会话列表", "description": "将焦点移动到会话列表。" } };
18804
- const settings = { "themeDesc": "自定义应用的外观", "languageDesc": "选择应用显示的语言", "title": "设置", "general": { "title": "通用", "desc": "管理应用设置。", "theme": "主题", "light": "浅色", "dark": "深色", "system": "跟随系统", "language": "语言", "english": "English", "chinese": "简体中文", "saveThemeFailed": "保存主题设置失败。", "saveLanguageFailed": "保存语言设置失败。", "groupAppearance": "外观", "groupProject": "项目", "autoWatchFiles": "自动监听文件", "autoWatchFilesDesc": "自动监听项目文件变更", "saveFileWatcherFailed": "保存文件监听设置失败。", "groupVoiceInput": "语音输入", "altDoublePress": "双击 Alt 快捷键", "altDoublePressDesc": "输入框聚焦时快速双击 Alt,开始或停止语音输入。", "saveVoiceInputFailed": "保存语音输入设置失败。", "groupNetwork": "网络", "proxy": "网络代理", "proxyDesc": "配置 Fello 的网络代理,应用于 Node 请求、子进程与界面。", "proxyEdit": "修改代理设置", "proxyOff": "直连", "proxySystem": "跟随系统", "groupSound": "音效", "soundEnabled": "音效", "soundEnabledDesc": "播放通知音效", "soundVolume": "音量", "soundVolumeDesc": "调整通知音量", "soundTheme": "音效风格", "soundThemeDesc": "选择音效风格", "soundSoft": "柔和", "soundCrisp": "清脆", "soundTest": "试听", "soundTestDesc": "预览当前音效效果", "soundTestPlay": "播放", "saveSoundFailed": "保存音效设置失败。", "groupEditor": "编辑器", "editor": "使用编辑器打开", "editorDesc": "选择用于打开文件的编辑器", "selectEditor": "选择编辑器", "saveEditorFailed": "保存编辑器设置失败。" }, "shortcuts": { "title": "快捷键", "desc": "自定义常用操作的键盘快捷键。", "navigation": "导航", "recording": "请按下快捷键", "changeShortcut": "修改快捷键", "unassigned": "未设置", "delete": "删除", "set": "设置", "restore": "还原", "setTitle": "设置快捷键", "setDesc": "请按下要使用的快捷键。", "cancel": "取消", "confirm": "确认", "reset": "恢复默认", "conflict": "已被 {{command}} 使用。", "saveFailed": "保存快捷键失败。" }, "agents": { "desc": "管理智能体的配置和启动命令。", "description": "配置智能体", "title": "智能体", "guide": "使用手册", "addStdioAgent": "通过 ACP Agent 添加", "addApiAgent": "通过 API Key 添加", "editStdioAgent": "编辑 ACP Agent", "editApiAgent": "编辑 API Agent", "dialogDesc": "配置智能体 ID、命令、参数和环境变量。", "apiDialogDesc": "配置 API 智能体的提供商、端点和认证信息。", "agentId": "智能体 ID (名称)", "apiProvider": "提供商", "apiBaseUrl": "Base URL", "apiKey": "API Key", "apiHeaders": "请求头 (JSON)", "contextWindowTokens": "上下文窗口 (token)", "modelIdTemplate": "Model Id 模版", "models": "模型列表 (每行一个)", "modelsHint": "通常无需填写,会自动获取。仅在接口未返回所需模型时手动指定", "command": "命令", "args": "参数", "envVars": "环境变量 (JSON)", "envJson": "环境变量 (JSON)", "quickPreset": "快速预设", "cancel": "取消", "save": "保存", "edit": "编辑", "delete": "删除", "dragToReorder": "拖拽排序", "noAgents": "未配置任何智能体", "errorIdCommand": "ID 和命令不能为空。", "errorDuplicateId": "已存在具有该 ID 的智能体。", "errorEnvJson": "环境变量必须是有效的 JSON 对象。", "errorContextWindowTokens": "上下文窗口必须为正整数。", "updateFailed": "更新配置失败。", "confirmDeleteTitle": "删除智能体", "confirmDeleteDesc": "确定要删除此智能体吗?此操作无法撤销。", "confirmDeleteDescWithSessions": "确定要删除此智能体吗?{{count}} 个会话也将被同时删除。此操作无法撤销。", "reset": "重置智能体", "resetSuccess": "智能体已重置。", "resetFailed": "重置智能体失败。", "deleteAllSessions": "删除所有会话", "confirmDeleteAllSessionsTitle": "删除所有会话", "confirmDeleteAllSessionsDesc": "此智能体的所有会话将被永久删除。此操作无法撤销。", "deleteAllSessionsSuccess": "已删除 {{count}} 个会话。", "deleteAllSessionsFailed": "删除会话失败。", "validation": { "enterId": "请输入智能体 ID。", "idFormat": "只允许字母、数字、_ 和 -。", "selectProvider": "请选择提供商。", "enterBaseUrl": "请输入 Base URL。", "enterApiKey": "请输入 API Key。", "enterCommand": "请输入命令。", "jsonObject": "必须是有效的 JSON 对象(值为字符串)。", "positiveInteger": "必须为正整数。" } }, "mcp": { "desc": "管理 MCP 服务器的配置和启动命令。", "description": "配置 MCP 服务器", "title": "MCP 服务器", "guide": "使用手册", "addMcp": "添加 MCP 服务器", "addStdioMcp": "添加 Stdio MCP", "addHttpMcp": "添加 HTTP MCP", "addSseMcp": "添加 SSE MCP", "recommended": { "exa": { "name": "Exa", "desc": "Exa MCP 服务器 — AI 网络搜索" } }, "editMcp": "编辑 MCP 服务器", "dialogDesc": "配置 MCP 服务器 ID、命令、参数和环境变量。", "mcpId": "MCP 服务器 ID (名称)", "command": "命令", "args": "参数", "envVars": "环境变量 (JSON)", "envJson": "环境变量 (JSON)", "url": "URL", "headers": "请求头 (JSON)", "headersJson": "请求头 (JSON)", "httpDialogDesc": "配置 MCP 服务器 ID、HTTP URL 和请求头。", "sseDialogDesc": "配置 MCP 服务器 ID、SSE 端点 URL 和请求头。", "cancel": "取消", "save": "保存", "edit": "编辑", "delete": "删除", "dragToReorder": "拖拽排序", "noMcpServers": "未配置任何 MCP 服务器", "errorIdRequired": "ID 不能为空。", "errorUrlRequired": "URL 不能为空。", "errorIdCommand": "ID 和命令不能为空。", "errorDuplicateId": "已存在具有该 ID 的 MCP 服务器。", "errorHeadersJson": "请求头必须是有效的 JSON 对象。", "errorEnvJson": "环境变量必须是有效的 JSON 对象。", "updateFailed": "更新配置失败。", "confirmDeleteTitle": "删除 MCP 服务器", "confirmDeleteDesc": "确定要删除此 MCP 服务器吗?此操作无法撤销。", "validation": { "enterId": "请输入服务器 ID。", "idFormat": "只允许字母、数字、_ 和 -。", "enterCommand": "请输入命令。", "enterUrl": "请输入 URL。", "jsonObject": "必须是有效的 JSON 对象(值为字符串)。" } }, "webui": { "desc": "配置对 Fello 界面的远程访问。", "title": "WebUI", "guide": "使用手册", "enable": "启用 WebUI", "enableDesc": "允许通过浏览器远程访问 Fello 界面", "enableAccess": "启用 WebUI 访问", "port": "端口:", "token": "Token:", "autoGenerated": "自动生成", "accessUrl": "访问地址", "accessDesc": "注意:此地址包含临时访问令牌,应用重启后将失效。", "copy": "复制", "note": "注意:此地址包含临时访问令牌,应用重启后将失效。", "updateFailed": "更新配置失败。", "disabledInWebUI": "WebUI 配置在浏览器中不可用。" }, "ilink": { "title": "微信 iLink", "guide": "使用手册", "description": "通过 iLink Bot API 将 Fello 接入微信。连接后,微信中会出现一个 ClawBot 联系人,你可以随时随地与 AI 智能体对话。", "connected": "已连接", "notConnected": "未连接", "notConnectedHint": "使用微信扫描二维码连接", "login": "登录", "loggingIn": "等待扫码...", "disconnect": "断开连接", "scanPrompt": "使用微信扫描二维码登录", "qrcodeExpired": "二维码已过期,请重新获取", "scanned": "已扫码!请在手机上确认登录。", "browserOpened": "二维码已在浏览器中打开,请使用微信扫码登录。", "activeSession": "微信活跃会话", "activeSessionHint": "微信消息将路由到此会话。可在侧边栏右键会话设为活跃。", "selectSession": "-- 选择一个会话 --", "none": "-- 无 --", "howItWorks": "使用说明", "step1": "点击「登录」在浏览器中打开二维码。", "step2": "使用微信扫描二维码。", "step3": "微信中出现「ClawBot」联系人。", "step4": "与 ClawBot 聊天 — 消息将路由到上方选择的活跃会话。", "step5": "在侧边栏右键任意会话即可设为微信活跃会话。", "useOriginalImage": "使用原图", "useOriginalImageDesc": "向模型发送原始大小的图片(消耗更多 token)。关闭时使用缩略图。", "keepaliveMaxCount": "保活消息", "keepaliveMaxCountDesc": "AI 思考时最大连续保活消息数。", "commandsTitle": "微信 ! 命令", "commandsDesc": "在微信中发送以 ! 或 ! 开头的消息即为命令。命令会中断当前 Agent 执行(如正在生成回复则立即取消),然后执行对应操作。", "cmdSwitchSession": "切换会话", "cmdSwitchSessionDesc": "列出所有会话(按项目分组),回复序号切换到对应会话。", "cmdNewSession": "新建会话", "cmdNewSessionDesc": "列出所有项目,回复序号在该项目下创建新会话并自动切换。", "cmdSwitchModel": "切换模型", "cmdSwitchModelDesc": "列出当前会话可用的模型,回复序号切换(仅 API Agent 支持)。", "cmdSnippet": "快捷短语", "cmdSnippetDesc": "列出已配置的 Snippets,回复序号将对应内容发送给 Agent。", "cmdStatus": "查看状态", "cmdStatusDesc": "显示当前活跃会话信息(标题、项目、Agent、Features、MCP 状态)。" }, "snippets": { "title": "快捷短语", "desc": "管理可复用的文本片段,快速插入到对话中。", "description": "配置快捷短语", "add": "添加短语", "edit": "编辑短语", "delete": "删除", "save": "保存", "cancel": "取消", "empty": "暂无快捷短语", "saveFailed": "保存快捷短语失败。", "titleLabel": "标题", "titlePlaceholder": "标题", "contentLabel": "内容", "contentPlaceholder": "内容", "dialogDesc": "配置短语的标题和内容。", "confirmDeleteTitle": "删除短语", "confirmDeleteDesc": "确定要删除此短语吗?此操作无法撤销。", "validation": { "enterTitle": "请输入标题。", "enterContent": "请输入内容。" } }, "memory": { "title": "记忆", "desc": "查看和管理项目级持久化记忆。", "projectMemories": "项目记忆", "refresh": "刷新", "clear": "清除", "cleared": "记忆已清除。", "clearFailed": "清除记忆失败。", "delete": "删除", "deleted": "记忆条目已删除。", "deleteFailed": "删除记忆条目失败。", "confirmDeleteTitle": "删除记忆条目", "confirmDeleteDesc": "确定要删除此记忆条目吗?此操作不可撤销。", "confirmClearTitle": "清除记忆", "confirmClearDesc": '确定要清除项目 "{{title}}" 的所有记忆吗?此操作不可撤销。', "revealInFinder": "在访达中显示", "revealFailed": "显示文件失败。", "noFile": "未找到记忆文件。", "noProjects": "未找到项目。", "empty": "暂无记忆。", "entries": "条目" }, "imageGeneration": { "title": "图片生成", "desc": "配置图片生成服务提供商。激活的提供商将作为工具供 Agent 使用。", "providers": "提供商", "add": "添加提供商", "addProvider": "添加提供商", "editProvider": "编辑提供商", "dialogDesc": "配置图片生成的提供商、端点和认证信息。", "edit": "编辑", "delete": "删除", "empty": "尚未配置图片生成提供商", "updateFailed": "更新配置失败。", "confirmDeleteTitle": "删除提供商", "confirmDeleteDesc": "确定要删除此图片生成提供商吗?", "cancel": "取消", "save": "保存", "form": { "name": "名称", "provider": "提供商", "baseUrl": "Base URL", "baseUrlHint": "请求将发送至 {baseUrl}/images/generations", "apiKey": "API Key", "headers": "请求头 (JSON)", "extraBody": "额外参数 (JSON)", "extraBodyHint": "合并到请求体中的额外参数(如 quality、style 等厂商特有参数)。", "model": "模型" }, "validation": { "enterName": "请输入名称。", "selectProvider": "请选择提供商。", "enterBaseUrl": "请输入 Base URL。", "enterApiKey": "请输入 API Key。", "enterModel": "请输入模型名称。", "jsonObject": "必须是有效的 JSON 对象(值为字符串)。" } }, "speechToText": { "title": "语音识别", "desc": "配置聊天输入框使用的实时语音识别服务商。", "guide": "使用手册", "providers": "服务商", "add": "添加服务商", "addProvider": "添加服务商", "editProvider": "编辑服务商", "dialogDesc": "配置实时语音输入的服务商和认证信息。", "edit": "编辑", "delete": "删除", "empty": "尚未配置语音识别服务商", "updateFailed": "更新配置失败。", "confirmDeleteTitle": "删除服务商", "confirmDeleteDesc": "确定删除这个语音识别服务商吗?", "cancel": "取消", "save": "保存", "validation": { "enterName": "请输入名称。", "selectProvider": "请选择服务商。", "enterApiKey": "请输入 API Key。", "enterAppId": "请输入 App ID。", "enterApiSecret": "请输入 API Secret。" }, "form": { "name": "名称", "provider": "服务商", "appId": "App ID", "apiKey": "API Key", "apiSecret": "API Secret", "optional": "(可选)", "language": "语言(可选)" } }, "proxy": { "title": "网络代理", "mode": "代理模式", "off": "直连", "offDesc": "不使用代理,直接连接", "manual": "手动", "manualDesc": "手动指定代理服务器,所有网络请求走代理", "system": "跟随系统", "systemDesc": "使用系统代理设置(macOS/Windows 或环境变量)", "httpProxy": "HTTP 代理", "httpProxyPlaceholder": "例如 http://127.0.0.1:7890", "httpsProxy": "HTTPS 代理(可选)", "httpsProxyHint": "留空时回退到 HTTP 代理。", "noProxy": "不使用代理(可选)", "noProxyPlaceholder": "例如 localhost,127.0.0.1,*.internal", "username": "用户名(可选)", "password": "密码(可选)", "cancel": "取消", "save": "保存", "later": "稍后", "restartNow": "立即重启", "restartTitle": "需要重启", "restartDesc": "代理设置已保存,重启 Fello 后新的代理配置才会生效。", "restartManualDesc": "代理设置已保存,请手动重启 Fello 服务端进程后生效。", "saveFailed": "保存代理设置失败。", "validation": { "httpProxyRequired": "手动模式下必须填写 HTTP 代理地址。", "invalidProxyUrl": "{{field}} 应以 http:// 或 https:// 开头,或使用 host:port 形式(例如 127.0.0.1:7890)。" } } };
18805
- const chat = { "failedToLoadSession": "加载会话失败。", "failedToUpdateMcpServers": "更新 MCP 服务器失败。" };
18808
+ const settings = /* @__PURE__ */ JSON.parse('{"themeDesc":"自定义应用的外观","languageDesc":"选择应用显示的语言","title":"设置","general":{"title":"通用","desc":"管理应用设置。","theme":"主题","light":"浅色","dark":"深色","system":"跟随系统","language":"语言","english":"English","chinese":"简体中文","saveThemeFailed":"保存主题设置失败。","saveLanguageFailed":"保存语言设置失败。","groupAppearance":"外观","groupProject":"项目","autoWatchFiles":"自动监听文件","autoWatchFilesDesc":"自动监听项目文件变更","saveFileWatcherFailed":"保存文件监听设置失败。","groupVoiceInput":"语音输入","altDoublePress":"双击 Alt 快捷键","altDoublePressDesc":"输入框聚焦时快速双击 Alt,开始或停止语音输入。","saveVoiceInputFailed":"保存语音输入设置失败。","groupNetwork":"网络","proxy":"网络代理","proxyDesc":"配置 Fello 的网络代理,应用于 Node 请求、子进程与界面。","proxyEdit":"修改代理设置","proxyOff":"直连","proxySystem":"跟随系统","groupSound":"音效","soundEnabled":"音效","soundEnabledDesc":"播放通知音效","soundVolume":"音量","soundVolumeDesc":"调整通知音量","soundTheme":"音效风格","soundThemeDesc":"选择音效风格","soundSoft":"柔和","soundCrisp":"清脆","soundTest":"试听","soundTestDesc":"预览当前音效效果","soundTestPlay":"播放","saveSoundFailed":"保存音效设置失败。","groupEditor":"编辑器","editor":"使用编辑器打开","editorDesc":"选择用于打开文件的编辑器","selectEditor":"选择编辑器","saveEditorFailed":"保存编辑器设置失败。"},"shortcuts":{"title":"快捷键","desc":"自定义常用操作的键盘快捷键。","navigation":"导航","recording":"请按下快捷键","changeShortcut":"修改快捷键","unassigned":"未设置","delete":"删除","set":"设置","restore":"还原","setTitle":"设置快捷键","setDesc":"请按下要使用的快捷键。","cancel":"取消","confirm":"确认","reset":"恢复默认","conflict":"已被 {{command}} 使用。","saveFailed":"保存快捷键失败。"},"agents":{"desc":"管理智能体的配置和启动命令。","description":"配置智能体","title":"智能体","guide":"使用手册","addStdioAgent":"通过 ACP Agent 添加","addApiAgent":"通过 API Key 添加","editStdioAgent":"编辑 ACP Agent","editApiAgent":"编辑 API Agent","dialogDesc":"配置智能体 ID、命令、参数和环境变量。","apiDialogDesc":"配置 API 智能体的提供商、端点和认证信息。","agentId":"智能体 ID (名称)","apiProvider":"提供商","apiBaseUrl":"Base URL","apiKey":"API Key","apiHeaders":"请求头 (JSON)","contextWindowTokens":"上下文窗口 (token)","modelIdTemplate":"Model Id 模版","models":"模型列表 (每行一个)","modelsHint":"通常无需填写,会自动获取。仅在接口未返回所需模型时手动指定","command":"命令","args":"参数","envVars":"环境变量 (JSON)","envJson":"环境变量 (JSON)","quickPreset":"快速预设","cancel":"取消","save":"保存","edit":"编辑","delete":"删除","dragToReorder":"拖拽排序","noAgents":"未配置任何智能体","errorIdCommand":"ID 和命令不能为空。","errorDuplicateId":"已存在具有该 ID 的智能体。","errorEnvJson":"环境变量必须是有效的 JSON 对象。","errorContextWindowTokens":"上下文窗口必须为正整数。","updateFailed":"更新配置失败。","confirmDeleteTitle":"删除智能体","confirmDeleteDesc":"确定要删除此智能体吗?此操作无法撤销。","confirmDeleteDescWithSessions":"确定要删除此智能体吗?{{count}} 个会话也将被同时删除。此操作无法撤销。","reset":"重置智能体","resetSuccess":"智能体已重置。","resetFailed":"重置智能体失败。","deleteAllSessions":"删除所有会话","confirmDeleteAllSessionsTitle":"删除所有会话","confirmDeleteAllSessionsDesc":"此智能体的所有会话将被永久删除。此操作无法撤销。","deleteAllSessionsSuccess":"已删除 {{count}} 个会话。","deleteAllSessionsFailed":"删除会话失败。","validation":{"enterId":"请输入智能体 ID。","idFormat":"只允许字母、数字、_ 和 -。","selectProvider":"请选择提供商。","enterBaseUrl":"请输入 Base URL。","enterApiKey":"请输入 API Key。","enterCommand":"请输入命令。","jsonObject":"必须是有效的 JSON 对象(值为字符串)。","positiveInteger":"必须为正整数。"}},"mcp":{"desc":"管理 MCP 服务器的配置和启动命令。","description":"配置 MCP 服务器","title":"MCP 服务器","guide":"使用手册","addMcp":"添加 MCP 服务器","addStdioMcp":"添加 Stdio MCP","addHttpMcp":"添加 HTTP MCP","addSseMcp":"添加 SSE MCP","recommended":{"exa":{"name":"Exa","desc":"Exa MCP 服务器 — AI 网络搜索"},"chrome-devtools":{"name":"Chrome DevTools","desc":"Chrome DevTools MCP 服务器 — 浏览器自动化与调试"},"mijia-api":{"name":"Mijia","desc":"米家 MCP 服务器 — 小米智能家居控制"},"blender":{"name":"Blender","desc":"Blender MCP 服务器 — 自然语言控制 Blender"}},"editMcp":"编辑 MCP 服务器","dialogDesc":"配置 MCP 服务器 ID、命令、参数和环境变量。","mcpId":"MCP 服务器 ID (名称)","command":"命令","args":"参数","envVars":"环境变量 (JSON)","envJson":"环境变量 (JSON)","url":"URL","headers":"请求头 (JSON)","headersJson":"请求头 (JSON)","httpDialogDesc":"配置 MCP 服务器 ID、HTTP URL 和请求头。","sseDialogDesc":"配置 MCP 服务器 ID、SSE 端点 URL 和请求头。","cancel":"取消","save":"保存","edit":"编辑","delete":"删除","dragToReorder":"拖拽排序","noMcpServers":"未配置任何 MCP 服务器","errorIdRequired":"ID 不能为空。","errorUrlRequired":"URL 不能为空。","errorIdCommand":"ID 和命令不能为空。","errorDuplicateId":"已存在具有该 ID 的 MCP 服务器。","errorHeadersJson":"请求头必须是有效的 JSON 对象。","errorEnvJson":"环境变量必须是有效的 JSON 对象。","updateFailed":"更新配置失败。","confirmDeleteTitle":"删除 MCP 服务器","confirmDeleteDesc":"确定要删除此 MCP 服务器吗?此操作无法撤销。","validation":{"enterId":"请输入服务器 ID。","idFormat":"只允许字母、数字、_ 和 -。","enterCommand":"请输入命令。","enterUrl":"请输入 URL。","jsonObject":"必须是有效的 JSON 对象(值为字符串)。"}},"webui":{"desc":"配置对 Fello 界面的远程访问。","title":"WebUI","guide":"使用手册","enable":"启用 WebUI","enableDesc":"允许通过浏览器远程访问 Fello 界面","enableAccess":"启用 WebUI 访问","port":"端口:","token":"Token:","autoGenerated":"自动生成","accessUrl":"访问地址","accessDesc":"注意:此地址包含临时访问令牌,应用重启后将失效。","copy":"复制","note":"注意:此地址包含临时访问令牌,应用重启后将失效。","updateFailed":"更新配置失败。","disabledInWebUI":"WebUI 配置在浏览器中不可用。"},"ilink":{"title":"微信 iLink","guide":"使用手册","description":"通过 iLink Bot API 将 Fello 接入微信。连接后,微信中会出现一个 ClawBot 联系人,你可以随时随地与 AI 智能体对话。","connected":"已连接","notConnected":"未连接","notConnectedHint":"使用微信扫描二维码连接","login":"登录","loggingIn":"等待扫码...","disconnect":"断开连接","qrcodeExpired":"二维码已过期,请重新获取","scanned":"已扫码!请在手机上确认登录。","browserOpened":"二维码已在浏览器中打开,请使用微信扫码登录。","activeSession":"微信活跃会话","activeSessionHint":"微信消息将路由到此会话。可在侧边栏右键会话设为活跃。","selectSession":"-- 选择一个会话 --","none":"-- 无 --","howItWorks":"使用说明","step1":"点击「登录」在浏览器中打开二维码。","step2":"使用微信扫描二维码。","step3":"微信中出现「ClawBot」联系人。","step4":"与 ClawBot 聊天 — 消息将路由到上方选择的活跃会话。","step5":"在侧边栏右键任意会话即可设为微信活跃会话。","useOriginalImage":"使用原图","useOriginalImageDesc":"向模型发送原始大小的图片(消耗更多 token)。关闭时使用缩略图。","commandsTitle":"微信 ! 命令","commandsDesc":"在微信中发送以 ! 开头的消息即为命令。命令会中断当前 Agent 执行(如正在生成回复则立即取消),然后执行对应操作。","cmdSwitchSession":"切换会话","cmdSwitchSessionDesc":"列出所有会话(按项目分组),回复序号切换到对应会话。","cmdNewSession":"新建会话","cmdNewSessionDesc":"列出所有项目,回复序号在该项目下创建新会话并自动切换。","cmdSwitchModel":"切换模型","cmdSwitchModelDesc":"列出当前会话可用的模型,回复序号切换(仅 API Agent 支持)。","cmdSnippet":"快捷短语","cmdSnippetDesc":"列出已配置的 Snippets,回复序号将对应内容发送给 Agent。","cmdFeature":"切换功能","cmdFeatureDesc":"列出当前会话的功能,回复序号切换该项。会话会重启以使改动生效。","cmdToggleMcp":"切换 MCP 服务器","cmdToggleMcpDesc":"列出当前会话的 MCP 服务器,回复序号切换该项。会话会重启以使改动生效。","cmdPermission":"切换权限","cmdPermissionDesc":"列出权限模式(每次询问 / 全部允许),回复序号切换。即时生效,不会重启会话。","cmdStatus":"查看状态","cmdStatusDesc":"显示当前活跃会话信息(标题、项目、Agent、权限、Features、MCP 状态)。"},"snippets":{"title":"快捷短语","desc":"管理可复用的文本片段,快速插入到对话中。","description":"配置快捷短语","add":"添加短语","edit":"编辑短语","delete":"删除","dragToReorder":"拖拽排序","save":"保存","cancel":"取消","empty":"暂无快捷短语","saveFailed":"保存快捷短语失败。","titleLabel":"标题","titlePlaceholder":"标题","contentLabel":"内容","contentPlaceholder":"内容","dialogDesc":"配置短语的标题和内容。","confirmDeleteTitle":"删除短语","confirmDeleteDesc":"确定要删除此短语吗?此操作无法撤销。","validation":{"enterTitle":"请输入标题。","enterContent":"请输入内容。"}},"memory":{"title":"记忆","desc":"查看和管理项目级持久化记忆。","projectMemories":"项目记忆","refresh":"刷新","clear":"清除","cleared":"记忆已清除。","clearFailed":"清除记忆失败。","delete":"删除","deleted":"记忆条目已删除。","deleteFailed":"删除记忆条目失败。","confirmDeleteTitle":"删除记忆条目","confirmDeleteDesc":"确定要删除此记忆条目吗?此操作不可撤销。","confirmClearTitle":"清除记忆","confirmClearDesc":"确定要清除项目 \\"{{title}}\\" 的所有记忆吗?此操作不可撤销。","revealInFinder":"在访达中显示","revealFailed":"显示文件失败。","noFile":"未找到记忆文件。","noProjects":"未找到项目。","empty":"暂无记忆。","entries":"条目"},"imageGeneration":{"title":"图片生成","desc":"配置图片生成服务提供商。激活的提供商将作为工具供 Agent 使用。","providers":"提供商","add":"添加提供商","addProvider":"添加提供商","editProvider":"编辑提供商","dialogDesc":"配置图片生成的提供商、端点和认证信息。","edit":"编辑","delete":"删除","empty":"尚未配置图片生成提供商","updateFailed":"更新配置失败。","confirmDeleteTitle":"删除提供商","confirmDeleteDesc":"确定要删除此图片生成提供商吗?","cancel":"取消","save":"保存","form":{"name":"名称","provider":"提供商","baseUrl":"Base URL","baseUrlHint":"请求将发送至 {baseUrl}/images/generations","apiKey":"API Key","headers":"请求头 (JSON)","extraBody":"额外参数 (JSON)","extraBodyHint":"合并到请求体中的额外参数(如 quality、style 等厂商特有参数)。","model":"模型"},"validation":{"enterName":"请输入名称。","selectProvider":"请选择提供商。","enterBaseUrl":"请输入 Base URL。","enterApiKey":"请输入 API Key。","enterModel":"请输入模型名称。","jsonObject":"必须是有效的 JSON 对象(值为字符串)。"}},"speech":{"title":"语音","desc":"配置语音输入(识别)与语音输出(合成朗读)。","guide":"使用手册","providers":"服务商","add":"添加服务商","addProvider":"添加服务商","editProvider":"编辑服务商","dialogDesc":"为一个服务商配置语音识别和(或)语音合成。","edit":"编辑","delete":"删除","empty":"尚未配置语音服务商","updateFailed":"更新配置失败。","confirmDeleteTitle":"删除服务商","confirmDeleteDesc":"确定要删除此语音服务商吗?","cancel":"取消","save":"保存","shortRecognition":"识别","shortSynthesis":"合成","form":{"name":"名称","provider":"服务商","apiKey":"API Key","appId":"App ID","apiSecret":"API Secret","language":"语言","voice":"音色","optional":"(可选)","recognition":"识别","recognitionDesc":"聊天输入框使用的实时语音识别。","synthesis":"合成","synthesisDesc":"用该音色朗读 Agent 回复。"},"validation":{"enterName":"请输入名称。","selectProvider":"请选择服务商。","enterApiKey":"请输入 API Key。","enterAppId":"请输入 App ID。","enterApiSecret":"请输入 API Secret。"}},"proxy":{"title":"网络代理","mode":"代理模式","off":"直连","offDesc":"不使用代理,直接连接","manual":"手动","manualDesc":"手动指定代理服务器,所有网络请求走代理","system":"跟随系统","systemDesc":"使用系统代理设置(macOS/Windows 或环境变量)","httpProxy":"HTTP 代理","httpProxyPlaceholder":"例如 http://127.0.0.1:7890","httpsProxy":"HTTPS 代理(可选)","httpsProxyHint":"留空时回退到 HTTP 代理。","noProxy":"不使用代理(可选)","noProxyPlaceholder":"例如 localhost,127.0.0.1,*.internal","username":"用户名(可选)","password":"密码(可选)","cancel":"取消","save":"保存","later":"稍后","restartNow":"立即重启","restartTitle":"需要重启","restartDesc":"代理设置已保存,重启 Fello 后新的代理配置才会生效。","restartManualDesc":"代理设置已保存,请手动重启 Fello 服务端进程后生效。","saveFailed":"保存代理设置失败。","validation":{"httpProxyRequired":"手动模式下必须填写 HTTP 代理地址。","invalidProxyUrl":"{{field}} 应以 http:// 或 https:// 开头,或使用 host:port 形式(例如 127.0.0.1:7890)。"}},"peripherals":{"title":"外设","desc":"连接蓝牙 / HID 外设,并开启需要生效的外设(只有生效的外设才会启用它的专用逻辑)。","desktopOnly":"外设仅在桌面应用中可用。","unsupportedPlatform":"当前平台不支持","voiceCapable":"支持语音输入","productPage":"产品页面","connectFailed":"连接外设失败。","updateFailed":"更新外设设置失败。","status":{"bluetooth":"蓝牙已连接"},"keys":{"voice":"语音","back":"返回"},"phase":{"inactive":"未生效","unsupported":"不支持","unavailable":"不可用","preparing":"准备中","scanning":"查找中","connecting":"连接中","connected":"已连接","error":"错误","disconnected":"已断开"},"openPermissionSettings":"输入监控设置","permissionHint":"macOS 需要「输入监控」权限才能读取 HID 按键;请手动勾选(列表中没有 Fello 时点「+」添加)。","reconnect":"重新连接"}}');
18809
+ const chat = { "failedToLoadSession": "加载会话失败。", "failedToUpdateMcpServers": "更新 MCP 服务器失败。", "readAloud": "朗读" };
18806
18810
  const tokenUsage = { "title": "Token 使用明细", "loading": "加载中…", "noData": "暂无 Token 使用明细", "promptCount": "{{count}} 轮对话", "promptLabel": "第 {{count}} 轮对话", "noBreakdown": "此提示词没有详细的 Token 明细", "input": "输入", "output": "输出", "inputShort": "输入", "outputShort": "输出", "totalTokens": "总计", "thought": "思考", "cacheRead": "缓存读取", "cacheWrite": "缓存写入", "cached": "缓存", "tokens": "Token", "token": "Token", "sessionTotal": "会话总计", "systemPrompt": "系统提示词", "tools": "工具", "history": "历史消息", "userMessage": "用户消息", "inputComposition": "输入组成", "delta": "估算差值", "toolsDefinition": "工具定义", "toolsCount": "{{count}} 个工具 · {{tokens}} tokens", "more": "+{{count}} 项", "steps": "步骤", "stepsCount": "{{count}} 步", "toolCalls": "工具调用", "noCache": "未缓存", "text": "文本", "stepTime": "步骤耗时", "responseTime": "响应耗时", "arguments": "参数", "result": "结果", "execution": "执行耗时", "performance": "性能", "totalTime": "总耗时", "outputSpeed": "输出速度", "tokensPerSecond": "Token/s", "firstToken": "首个 Token" };
18807
18811
  const planBubble = { "title": "计划", "summary": "已完成 {{completed}} / {{total}}", "status": { "pending": "等待中", "inProgress": "进行中", "completed": "已完成" }, "priority": { "high": "高优先级", "medium": "中优先级", "low": "低优先级" } };
18808
- const constant = { "feature": { "title": "功能", "askUser": "向我提问", "skills": "技能", "shareToUser": "分享给用户", "search": "项目检索", "memory": "记忆", "imageGeneration": "图片生成" } };
18809
- const chatHeader = { "sessionId": "会话编号", "openSessionFolder": "打开会话文件夹", "projectId": "项目目录", "openProjectFolder": "打开项目文件夹", "refresh": "重启会话", "closeSession": "关闭会话", "closingSession": "关闭中…", "failedToCloseSession": "关闭会话失败。", "sessionOperationInProgress": "已有其他会话操作正在进行中。", "contextWindow": "上下文窗口", "used": "已用", "size": "上限", "percentUsed": "已用 {{pct}}%", "remaining": "剩余 {{count}}", "lastTurn": "上一轮", "input": "输入", "output": "输出", "total": "总计", "thought": "思考", "cacheRead": "缓存读取", "cacheWrite": "缓存写入", "cost": "费用", "viewBreakdown": "查看明细" };
18810
- const chatInput = { "placeholderDisabled": "请先开启新的会话...", "placeholderActive": "有任何问题请问我... (回车发送)", "mode": "模式", "suggestions": "建议", "messageInput": "消息输入", "stop": "停止", "send": "发送", "tokensUsage": "输入: {{input}} 输出: {{output}} 总计: {{total}}{{thoughtStr}}", "tokensTotal": "{{total}}k tokens", "generationFailed": "生成失败。", "attach": "附加文件", "reference": "引用", "mention": "应用", "snippets": "快捷短语", "snippetsEmpty": "暂无短语,点击前往设置添加。", "selectModel": "选择模型", "stopReasonMaxTokens": "已达到最大 token 限制", "stopReasonMaxTurnRequests": "已达到最大轮次限制", "stopReasonRefusal": "模型拒绝回答", "stopReasonCancelled": "生成已取消", "voiceInput": "语音输入", "selectVoiceInputDevice": "选择麦克风", "stopVoiceInput": "停止语音输入", "startingVoiceInput": "正在启动语音输入", "voiceInputNotConfigured": "请先在设置中配置语音识别", "noAudioInputDevices": "未找到音频输入设备" };
18812
+ const constant = { "feature": { "askUser": "向我提问", "skills": "技能", "shareToUser": "分享给用户", "search": "项目检索", "memory": "记忆", "imageGeneration": "图片生成" } };
18813
+ const chatHeader = { "sessionId": "会话编号", "openSessionFolder": "打开会话文件夹", "projectId": "项目目录", "openProjectFolder": "打开项目文件夹", "refresh": "重启会话", "closeSession": "关闭会话", "closingSession": "关闭中…", "failedToCloseSession": "关闭会话失败。", "sessionOperationInProgress": "已有其他会话操作正在进行中。", "contextWindow": "上下文窗口", "used": "已用", "size": "上限", "percentUsed": "已用 {{pct}}%", "remaining": "剩余 {{count}}", "lastTurn": "上一轮", "input": "输入", "output": "输出", "total": "总计", "thought": "思考", "cacheRead": "缓存读取", "cacheWrite": "缓存写入", "cost": "费用", "viewBreakdown": "查看明细", "autoRead": "自动朗读回复", "volume": "音量", "reading": "正在朗读…", "stopReading": "停止朗读" };
18814
+ const chatInput = { "placeholderDisabled": "请先开启新的会话...", "placeholderActive": "有任何问题请问我... (回车发送)", "mode": "模式", "suggestions": "建议", "messageInput": "消息输入", "stop": "停止", "send": "发送", "tokensUsage": "输入: {{input}} 输出: {{output}} 总计: {{total}}{{thoughtStr}}", "tokensTotal": "{{total}}k tokens", "generationFailed": "生成失败。", "attach": "附加文件", "reference": "引用", "mention": "应用", "snippets": "快捷短语", "snippetsEmpty": "暂无短语,点击前往设置添加。", "selectModel": "选择模型", "stopReasonMaxTokens": "已达到最大 token 限制", "stopReasonMaxTurnRequests": "已达到最大轮次限制", "stopReasonRefusal": "模型拒绝回答", "stopReasonCancelled": "生成已取消", "voiceInput": "语音输入", "selectVoiceInputDevice": "选择麦克风", "stopVoiceInput": "停止语音输入", "startingVoiceInput": "正在启动语音输入", "voiceInputNotConfigured": "请先在设置中配置语音识别", "noAudioInputDevices": "未找到音频输入设备", "voicePanelTitle": "语音输入", "voicePanelEmpty": "没有识别到语音", "voicePanelCancel": "取消", "voicePanelSend": "发送", "voicePanelAutoSendLabel": "秒后自动发送", "voicePanelHint": "可以开始说话,想讲多久都行", "voicePanelHoldHint": "松手结束,说过的内容都会保留", "voicePanelToInput": "填入输入框" };
18811
18815
  const filePanel = { "title": "文件", "view": "查看", "emptyDirectory": "空目录", "newFile": "新建文件", "newFolder": "新建文件夹", "rename": "重命名", "copyPath": "复制路径", "copyRelativePath": "复制相对路径", "revealInFinder": "在访达中显示", "openInEditor": "使用 {{editor}} 打开", "delete": "删除", "deleteConfirmSingle": "是否删除“{{name}}”?", "deleteConfirmMultiple": "是否删除选中的 {{count}} 个项目?", "dragItems": "{{count}} 个项目", "cancel": "取消", "moveToTrash": "移至废纸篓", "moveToRecycleBin": "移至回收站", "moveConfirmTitle": "移动", "moveConfirmSingle": "是否将“{{name}}”移动到“{{dest}}”?", "moveConfirmMultiple": "是否将 {{count}} 个项目移动到“{{dest}}”?", "moveConfirmSingleToRoot": "是否将“{{name}}”移动到根目录?", "moveConfirmMultipleToRoot": "是否将 {{count}} 个项目移动到根目录?", "loading": "加载中...", "collapseFolders": "折叠文件夹", "refresh": "刷新", "addToChat": "添加到对话", "noProject": "未选择项目" };
18812
18816
  const toolBubble = { "tool": "工具", "stop": "停止", "stopping": "停止中…", "result": "结果", "terminal": "终端" };
18813
18817
  const imageView = { "fallbackAlt": "图片" };
@@ -18820,20 +18824,21 @@ const subagentBubble = { "thinking": "思考中...", "waitingForTask": "等待
18820
18824
  const chatTimeline = { "userMessageIndex": "第 {{index}} 条用户消息", "jumpToUserMessage": "跳转到{{label}}" };
18821
18825
  const thinkingBubble = { "thinking": "思考中...", "thought": "思考过程" };
18822
18826
  const userBubble = { "copy": "复制", "locate": "定位" };
18823
- const session = { "usageSummary": "{{total}} tk ( ↓ {{input}} ↑ {{output}}{{cachedPart}} )", "usageSummaryCached": " ↻ {{cached}}", "connecting": "正在连接智能体...", "connectionFailed": "连接智能体失败", "retry": "重试" };
18827
+ const session = { "usageSummary": "{{total}} tk ( ↓ {{input}} ↑ {{output}}{{cachedPart}} )", "usageSummaryCached": " ↻ {{cached}}", "connecting": "正在连接智能体...", "connectionFailed": "连接智能体失败", "connectionFailedHint": "会话设置(如 MCP、功能)异常时可调整下方选项,再重新加载。", "reload": "重新加载", "operationInProgress": "已有其他会话操作正在进行中。", "failedToLoadSession": "加载会话失败。", "failedToUpdateMcpServers": "更新 MCP 服务器失败" };
18828
+ const sessionConfig = { "permission": "权限", "permissionAsk": "每次询问", "permissionAllowAll": "全部允许", "features": "功能", "mcpServers": "MCP 服务器", "noMcpServers": "未配置 MCP 服务器" };
18824
18829
  const terminalPanel = { "title": "终端", "noTerminalSelected": "未选择终端", "addTerminal": "添加终端", "view": "查看", "delete": "删除终端" };
18825
18830
  const permission = { "title": "需要权限", "error": "响应权限请求失败" };
18826
18831
  const errorBoundary = { "title": "出了点问题", "defaultMessage": "应用程序发生意外错误。", "reload": "重新加载应用" };
18827
18832
  const panel = { "files": "文件", "terminal": "终端", "noProjectSelected": "未选择项目" };
18828
18833
  const contentBlock = { "unsupported": "不支持的内容块:{{type}}", "download": "下载", "revealInFinder": "在访达中显示", "copyToWorkspace": "复制到工作区", "copySuccess": "已复制到工作区", "openLink": "打开链接", "invalidImage": "[无效的图片块]", "invalidAudio": "[无效的音频块]", "image": "图片:", "audio": "音频:", "imageAlt": "智能体提供的图片", "fileTooLarge": "文件过大 (超过 20MB)" };
18829
18834
  const detail = { "noContent": "无内容" };
18830
- const fileDetail = { "fileNotFound": "文件不存在", "fileTooLarge": "该文件过大不支持预览", "fileFormatNotSupported": "该文件格式不支持预览", "errorLoading": "加载文件失败", "loading": "加载中...", "noChanges": "无变更", "resetZoom": "重置", "preview": "预览", "code": "源码", "compare": "对比", "fileModifiedNotice": "文件已被修改,请刷新", "edit": "编辑", "save": "保存", "saving": "保存中…", "saved": "已保存", "saveSuccess": "已保存", "saveFailed": "保存失败", "unsaved": "未保存", "copyFileContent": "复制文件内容", "addFileToChat": "添加文件到会话", "addSelectionToChat": "添加选中内容到会话", "loadError": "加载失败", "zoomOut": "缩小", "zoomIn": "放大", "previousPage": "上一页", "nextPage": "下一页", "previousSlide": "上一张", "nextSlide": "下一张", "speakerNotes": "备注", "closeNotes": "关闭" };
18835
+ const fileDetail = { "fileNotFound": "文件不存在", "fileTooLarge": "该文件过大不支持预览", "fileFormatNotSupported": "该文件格式不支持预览", "mediaPlaybackFailed": "该文件无法播放", "errorLoading": "加载文件失败", "loading": "加载中...", "noChanges": "无变更", "resetZoom": "重置", "preview": "预览", "code": "源码", "compare": "对比", "fileModifiedNotice": "文件已被修改,请刷新", "edit": "编辑", "save": "保存", "saving": "保存中…", "saved": "已保存", "saveSuccess": "已保存", "saveFailed": "保存失败", "unsaved": "未保存", "copyFileContent": "复制文件内容", "addFileToChat": "添加文件到会话", "addSelectionToChat": "添加选中内容到会话", "loadError": "加载失败", "zoomOut": "缩小", "zoomIn": "放大", "previousPage": "上一页", "nextPage": "下一页", "previousSlide": "上一张", "nextSlide": "下一张", "speakerNotes": "备注", "closeNotes": "关闭" };
18831
18836
  const htmlDetail = { "fileNotFound": "文件不存在或无法访问", "errorLoading": "加载失败", "loadFailed": "页面加载失败", "retry": "重试", "openInBrowser": "在浏览器中打开", "webuiNotEnabled": "请先启用 WebUI 才能在浏览器中打开", "open": "打开", "refresh": "刷新" };
18832
18837
  const welcome = { "title": "Fello", "desc": "又一个基于 ACP 协议的 Agent 客户端,连接智能体并开始协作。", "hint": "从左侧边栏新建会话开始", "getStarted": "开始使用", "stepAgent": "配置 Agent", "stepAgentDesc": "添加一个 AI 智能体(本地 Stdio 或 API)来驱动对话", "stepAgentDone": "已配置 {{count}} 个 Agent", "stepProject": "添加项目", "stepProjectDesc": "选择一个本地项目文件夹作为工作区", "stepProjectDone": "已添加 {{count}} 个项目", "stepChat": "开始会话", "stepChatDesc": "在项目中创建一个新的 AI 对话", "allDone": "一切就绪!从左侧边栏开始新会话吧" };
18833
18838
  const askUser = { "title": "请求", "other": "自定义回复", "back": "使用选项", "inputPlaceholder": "输入你的回复...(回车发送)", "submit": "提交", "autoClose": "超时后自动关闭", "collapse": "收起", "expand": "展开" };
18834
- const shareToUser = { "copyToProject": "复制到项目", "reveal": "在访达中显示", "loadFailed": "图片加载失败" };
18839
+ const shareToUser = { "copyToProject": "复制到项目", "reveal": "在访达中显示", "loadFailed": "图片加载失败", "playbackFailed": "该文件无法播放", "download": "下载" };
18835
18840
  const imageGeneration = { "copyToProject": "复制到项目", "reveal": "在访达中显示", "loadFailed": "图片加载失败", "download": "下载" };
18836
- const automation = { "title": "自动化", "agent": "智能体", "alwaysDisabled": "始终禁用", "anyMoment": "即将执行", "back": "返回", "cancel": "取消", "create": "创建", "createFirst": "创建你的第一个计划", "delete": "删除", "deleteConfirm": "确定永久删除「{{name}}」?", "deleteTaskConfirm": "确定永久删除此任务?", "deleteTaskTitle": "删除任务", "deleteTitle": "删除计划", "dialogDesc": "配置自动化计划的执行规则和 Prompt。", "editSchedule": "编辑计划", "editSettings": "编辑设置", "features": "功能", "files": "文件", "lastRun": "上次", "lessThanMinute": "不到 1 分钟", "manual": "手动", "manualDesc": "仅手动触发,无定时计划。", "mcpServers": "MCP 服务器", "model": "模型", "modelPlaceholder": "默认模型", "newSchedule": "新建计划", "nextRun": "下次", "noFiles": "暂无生成文件", "noSchedules": "暂无计划", "emptyHint": "通过 cron 表达式配置定时 AI 任务,让 Agent 自动执行", "noTasksDesc": "暂无运行记录。点击「立即触发」开始第一次运行。", "prompt": "提示词", "promptPlaceholder": "智能体需要执行什么任务?", "remainingRuns": "执行次数", "remainingRunsCount": "剩余 {{count}} 次", "remainingRunsDesc": "留空为不限次数", "remainingRunsPlaceholder": "∞", "save": "保存", "schedule": "计划", "scheduleName": "计划名称", "scheduleNamePlaceholder": "例如:每日站会摘要", "scheduleNotFound": "计划未找到", "schedules": "计划", "selectFileToPreview": "选择文件预览", "selectTaskToPreview": "选择任务查看文件", "selectTask": "选择一个任务", "timed": "定时", "triggerNow": "立即触发", "validation": { "nameRequired": "名称不能为空", "agentRequired": "请选择智能体", "promptRequired": "提示词不能为空", "runLimitInvalid": "执行次数必须是非负整数。" }, "cron": { "at": "在", "atMinute": "第", "custom": "自定义", "daily": "每天", "hourly": "每隔 N 小时", "weekdays": "工作日", "weekly": "每周", "nextRun": "下次执行", "nextRunIn": "距离下次执行还有", "lastRun": "上次执行", "days": { "0": "周日", "1": "周一", "2": "周二", "3": "周三", "4": "周四", "5": "周五", "6": "周六" } } };
18841
+ const automation = { "title": "自动化", "agent": "智能体", "alwaysDisabled": "始终禁用", "anyMoment": "即将执行", "back": "返回", "cancel": "取消", "create": "创建", "createFirst": "创建你的第一个计划", "delete": "删除", "deleteConfirm": "确定永久删除「{{name}}」?", "deleteTaskConfirm": "确定永久删除此任务?", "deleteTaskTitle": "删除任务", "deleteTitle": "删除计划", "dialogDesc": "配置自动化计划的执行规则和 Prompt。", "editSchedule": "编辑计划", "editSettings": "编辑设置", "features": "功能", "files": "文件", "lastRun": "上次", "lessThanMinute": "不到 1 分钟", "manual": "手动", "manualDesc": "仅手动触发,无周期计划。", "mcpServers": "MCP 服务器", "model": "模型", "modelPlaceholder": "默认模型", "newSchedule": "新建计划", "nextRun": "下次", "noFiles": "暂无生成文件", "noSchedules": "暂无计划", "emptyHint": "通过 cron 表达式配置周期 AI 任务,让 Agent 自动执行", "noTasksDesc": "暂无运行记录。点击「立即触发」开始第一次运行。", "prompt": "提示词", "promptPlaceholder": "智能体需要执行什么任务?", "remainingRuns": "执行次数", "remainingRunsCount": "剩余 {{count}} 次", "remainingRunsDesc": "留空为不限次数", "remainingRunsPlaceholder": "∞", "save": "保存", "schedule": "计划", "scheduleName": "计划名称", "scheduleNamePlaceholder": "例如:每日站会摘要", "scheduleNotFound": "计划未找到", "schedules": "计划", "selectFileToPreview": "选择文件预览", "selectTaskToPreview": "选择任务查看文件", "selectTask": "选择一个任务", "recurring": "周期", "once": "单次", "oncePickDate": "选择日期", "onceDesc": "在指定时间触发一次。", "missed": "已错过", "lastTriggered": "上次触发", "duration": { "minutes": "{{minutes}} 分钟", "hoursMinutes": "{{hours}} 小时 {{minutes}} 分钟", "daysHours": "{{days}} 天 {{hours}} 小时" }, "triggerNow": "立即触发", "validation": { "nameRequired": "名称不能为空", "agentRequired": "请选择智能体", "promptRequired": "提示词不能为空", "runLimitInvalid": "执行次数必须是非负整数。", "onceTimeInvalid": "请选择一个未来的时间。" }, "cron": { "at": "在", "atMinute": "第", "custom": "自定义", "daily": "每天", "hourly": "每隔 N 小时", "weekdays": "工作日", "weekly": "每周", "nextRun": "下次执行", "nextRunIn": "距离下次执行还有", "days": { "0": "周日", "1": "周一", "2": "周二", "3": "周三", "4": "周四", "5": "周五", "6": "周六" } } };
18837
18842
  const zhCN = {
18838
18843
  sidebar,
18839
18844
  skills,
@@ -18859,6 +18864,7 @@ const zhCN = {
18859
18864
  thinkingBubble,
18860
18865
  userBubble,
18861
18866
  session,
18867
+ sessionConfig,
18862
18868
  terminalPanel,
18863
18869
  permission,
18864
18870
  errorBoundary,
@@ -22238,26 +22244,6 @@ const twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
22238
22244
  function cn$1(...inputs) {
22239
22245
  return twMerge(clsx(inputs));
22240
22246
  }
22241
- function formatUpdatedTime(updatedAt) {
22242
- const date = new Date(updatedAt);
22243
- const now = /* @__PURE__ */ new Date();
22244
- const isSameYear = date.getFullYear() === now.getFullYear();
22245
- const isSameMonth = isSameYear && date.getMonth() === now.getMonth();
22246
- const isSameDay = isSameMonth && date.getDate() === now.getDate();
22247
- const hours = date.getHours().toString().padStart(2, "0");
22248
- const minutes = date.getMinutes().toString().padStart(2, "0");
22249
- const timeStr = `${hours}:${minutes}`;
22250
- if (isSameDay) {
22251
- return timeStr;
22252
- }
22253
- const month = (date.getMonth() + 1).toString().padStart(2, "0");
22254
- const day = date.getDate().toString().padStart(2, "0");
22255
- if (isSameYear) {
22256
- return `${month}-${day} ${timeStr}`;
22257
- }
22258
- const year = date.getFullYear();
22259
- return `${year}-${month}-${day} ${timeStr}`;
22260
- }
22261
22247
  function formatDuration(ms) {
22262
22248
  if (ms < 0) return "0s";
22263
22249
  if (ms < 1e3) return `${ms}ms`;
@@ -22451,6 +22437,13 @@ bridge.on("task-update", (payload) => emit("task-update", payload));
22451
22437
  bridge.on("asr-transcript", (payload) => emit("asr-transcript", payload));
22452
22438
  bridge.on("asr-error", (payload) => emit("asr-error", payload));
22453
22439
  bridge.on("asr-closed", (payload) => emit("asr-closed", payload));
22440
+ bridge.on("tts-audio", (payload) => emit("tts-audio", payload));
22441
+ bridge.on("tts-error", (payload) => emit("tts-error", payload));
22442
+ bridge.on("tts-closed", (payload) => emit("tts-closed", payload));
22443
+ bridge.on("peripheral-status", (payload) => emit("peripheral-status", payload));
22444
+ bridge.on("peripheral-key", (payload) => emit("peripheral-key", payload));
22445
+ bridge.on("peripheral-audio", (payload) => emit("peripheral-audio", payload));
22446
+ bridge.on("peripheral-audio-state", (payload) => emit("peripheral-audio-state", payload));
22454
22447
  void invokeIPC("registerClient", { clientId }).catch(() => {
22455
22448
  });
22456
22449
  function calculateUserMessageChunk(state, subSessionId, update2, displayId, receivedAt) {
@@ -22928,7 +22921,12 @@ async function loadSession(sessionId, { force = false, loadHistory = false } = {
22928
22921
  }
22929
22922
  async function restartSession(options) {
22930
22923
  return withSessionLifecycleLock(options.session.id, "restart", async () => {
22931
- const { session: session2, mcpServers = session2.mcpServers, features = session2.features } = options;
22924
+ const {
22925
+ session: session2,
22926
+ mcpServers = session2.mcpServers,
22927
+ features = session2.features,
22928
+ permissionMode
22929
+ } = options;
22932
22930
  let loadingStarted = false;
22933
22931
  try {
22934
22932
  const store = useAppStore.getState();
@@ -22941,7 +22939,8 @@ async function restartSession(options) {
22941
22939
  await request.updateSession({
22942
22940
  sessionId: session2.id,
22943
22941
  mcpServers,
22944
- features
22942
+ features,
22943
+ ...permissionMode !== void 0 ? { permissionMode } : {}
22945
22944
  });
22946
22945
  } catch (error) {
22947
22946
  throw new RestartSessionError("update", error);
@@ -22951,6 +22950,7 @@ async function restartSession(options) {
22951
22950
  ...currentSession,
22952
22951
  mcpServers,
22953
22952
  features,
22953
+ ...permissionMode !== void 0 ? { permissionMode } : {},
22954
22954
  isStreaming: false
22955
22955
  });
22956
22956
  let historyDisplayIds;
@@ -40632,6 +40632,44 @@ const IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
40632
40632
  function isImageMimeType(mimeType) {
40633
40633
  return !!mimeType && IMAGE_MIME_TYPES.has(mimeType);
40634
40634
  }
40635
+ const AUDIO_EXTENSIONS = /* @__PURE__ */ new Set([
40636
+ "mp3",
40637
+ "wav",
40638
+ "m4a",
40639
+ "aac",
40640
+ "flac",
40641
+ "ogg",
40642
+ "oga",
40643
+ "opus",
40644
+ "amr",
40645
+ "wma",
40646
+ "aiff",
40647
+ "aif",
40648
+ "caf"
40649
+ ]);
40650
+ const VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
40651
+ "mp4",
40652
+ "m4v",
40653
+ "mov",
40654
+ "webm",
40655
+ "mkv",
40656
+ "avi",
40657
+ "wmv",
40658
+ "flv",
40659
+ "mpg",
40660
+ "mpeg",
40661
+ "3gp"
40662
+ ]);
40663
+ function getMediaKind(mimeType, filename) {
40664
+ const mime = mimeType?.trim().toLowerCase();
40665
+ if (mime?.startsWith("audio/")) return "audio";
40666
+ if (mime?.startsWith("video/")) return "video";
40667
+ const ext = filename?.split(".").pop()?.trim().toLowerCase();
40668
+ if (!ext) return null;
40669
+ if (AUDIO_EXTENSIONS.has(ext)) return "audio";
40670
+ if (VIDEO_EXTENSIONS.has(ext)) return "video";
40671
+ return null;
40672
+ }
40635
40673
  const ALL_FEATURES = [
40636
40674
  "skills",
40637
40675
  "search",
@@ -40750,6 +40788,39 @@ const electron = {
40750
40788
  };
40751
40789
  const handler = (updaterEvent) => callback(updaterEvent);
40752
40790
  return window.fello.onUpdater(handler);
40791
+ },
40792
+ /**
40793
+ * 外设(Electron 专属)。
40794
+ *
40795
+ * WebUI 下所有方法都是 no-op / 空数组:外设运行时只在桌面应用里装载,
40796
+ * 设置页据此显示「仅桌面应用可用」。
40797
+ */
40798
+ peripherals: {
40799
+ /**
40800
+ * 打开系统的「输入监控」隐私面板:权限状态无法查询,且用户拒绝过之后系统不会再弹窗
40801
+ * (首次打开 HID 设备时系统本来就会弹),所以给一个由用户主动点的直达入口。
40802
+ */
40803
+ openPermissionSettings: async () => {
40804
+ if (isWebUI || !window.fello) return;
40805
+ return window.fello.invoke("openPeripheralPermissionSettings");
40806
+ },
40807
+ getStatuses: async () => {
40808
+ if (isWebUI || !window.fello) return [];
40809
+ return window.fello.invoke("getPeripheralStatuses");
40810
+ },
40811
+ connect: async (peripheralId) => {
40812
+ if (isWebUI || !window.fello) return;
40813
+ return window.fello.invoke("peripheralConnect", peripheralId);
40814
+ },
40815
+ /** @returns captureId:用于过滤属于上一次采集的迟到音频帧。 */
40816
+ voiceStart: async (peripheralId) => {
40817
+ if (isWebUI || !window.fello) return 0;
40818
+ return window.fello.invoke("peripheralVoiceStart", peripheralId);
40819
+ },
40820
+ voiceStop: async (peripheralId) => {
40821
+ if (isWebUI || !window.fello) return;
40822
+ return window.fello.invoke("peripheralVoiceStop", peripheralId);
40823
+ }
40753
40824
  }
40754
40825
  };
40755
40826
  function useRender(params) {
@@ -44076,6 +44147,120 @@ function Switch({
44076
44147
  }
44077
44148
  );
44078
44149
  }
44150
+ function SessionConfigFields({
44151
+ mcpServers,
44152
+ onMcpServersChange,
44153
+ features,
44154
+ onFeaturesChange,
44155
+ permissionMode,
44156
+ onPermissionModeChange,
44157
+ variant = "popover",
44158
+ className
44159
+ }) {
44160
+ const { t } = useTranslation();
44161
+ const configuredMcpServers = useAppStore((state) => state.configuredMcpServers);
44162
+ const isCard = variant === "card";
44163
+ const toggleFeature = (feature, checked2) => {
44164
+ onFeaturesChange(
44165
+ checked2 ? [...features, feature] : features.filter((item) => item !== feature)
44166
+ );
44167
+ };
44168
+ const toggleMcpServer = (mcpServerId, checked2) => {
44169
+ onMcpServersChange(
44170
+ checked2 ? [...mcpServers, mcpServerId] : mcpServers.filter((id) => id !== mcpServerId)
44171
+ );
44172
+ };
44173
+ const sectionTitleClassName = isCard ? "text-xs text-muted-foreground" : "px-2 py-1 text-xs font-semibold text-foreground/80";
44174
+ const showMcpSection = isCard || configuredMcpServers.length > 0;
44175
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1(isCard ? "flex flex-col gap-4" : void 0, className), children: [
44176
+ permissionMode !== void 0 && onPermissionModeChange !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1(isCard && "flex flex-col gap-2"), children: [
44177
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: sectionTitleClassName, children: t("sessionConfig.permission", "Permission") }),
44178
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
44179
+ Tabs,
44180
+ {
44181
+ value: permissionMode,
44182
+ onValueChange: (value) => {
44183
+ if (value === "ask" || value === "allow-all") onPermissionModeChange(value);
44184
+ },
44185
+ className: cn$1("w-full", !isCard && "px-2 my-2"),
44186
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { className: "w-full", children: [
44187
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "ask", className: "text-xs", children: t("sessionConfig.permissionAsk", "Ask") }),
44188
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "allow-all", className: "text-xs", children: t("sessionConfig.permissionAllowAll", "Allow all") })
44189
+ ] })
44190
+ }
44191
+ ),
44192
+ !isCard && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-border/50 my-1" })
44193
+ ] }),
44194
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1(isCard && "flex flex-col gap-2"), children: [
44195
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: sectionTitleClassName, children: t("sessionConfig.features", "Features") }),
44196
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: toggleGridClassName(ALL_FEATURES.length, variant), children: ALL_FEATURES.map((feature) => /* @__PURE__ */ jsxRuntimeExports.jsx(
44197
+ ToggleRow,
44198
+ {
44199
+ variant,
44200
+ label: t(FEATURE_I18N_KEYS[feature], { defaultValue: feature }),
44201
+ checked: features.includes(feature),
44202
+ onToggle: (checked2) => toggleFeature(feature, checked2)
44203
+ },
44204
+ feature
44205
+ )) }),
44206
+ !isCard && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-border/50 my-1" })
44207
+ ] }),
44208
+ showMcpSection && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1(isCard && "flex flex-col gap-2"), children: [
44209
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1(sectionTitleClassName, !isCard && "mt-1"), children: t("sessionConfig.mcpServers", "MCP Servers") }),
44210
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: toggleGridClassName(configuredMcpServers.length, variant), children: configuredMcpServers.map((mcpServer) => /* @__PURE__ */ jsxRuntimeExports.jsx(
44211
+ ToggleRow,
44212
+ {
44213
+ variant,
44214
+ label: mcpServer.id,
44215
+ title: mcpServer.id,
44216
+ checked: mcpServers.includes(mcpServer.id),
44217
+ onToggle: (checked2) => toggleMcpServer(mcpServer.id, checked2)
44218
+ },
44219
+ mcpServer.id
44220
+ )) }),
44221
+ configuredMcpServers.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: t("sessionConfig.noMcpServers", "No MCP servers configured") })
44222
+ ] })
44223
+ ] });
44224
+ }
44225
+ function toggleGridClassName(count, variant) {
44226
+ if (variant === "card") {
44227
+ return count >= 2 ? "grid grid-cols-2 gap-1" : "flex flex-col gap-1";
44228
+ }
44229
+ return count >= 2 ? "grid grid-cols-2 gap-0.5" : void 0;
44230
+ }
44231
+ function ToggleRow({
44232
+ variant,
44233
+ label,
44234
+ title,
44235
+ checked: checked2,
44236
+ onToggle
44237
+ }) {
44238
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
44239
+ "div",
44240
+ {
44241
+ className: cn$1(
44242
+ "flex items-center justify-between cursor-default transition-colors",
44243
+ variant === "card" ? "h-7 rounded border bg-secondary/50 px-2 text-xs hover:bg-accent" : "rounded px-2 py-1.5 text-xs hover:bg-accent/50"
44244
+ ),
44245
+ onClick: () => onToggle(!checked2),
44246
+ children: [
44247
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
44248
+ "span",
44249
+ {
44250
+ className: cn$1(
44251
+ "truncate",
44252
+ variant === "popover" && "mr-2",
44253
+ checked2 ? "text-muted-foreground" : "text-muted-foreground/50"
44254
+ ),
44255
+ title,
44256
+ children: label
44257
+ }
44258
+ ),
44259
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Switch, { size: "sm", checked: checked2, onCheckedChange: onToggle }) })
44260
+ ]
44261
+ }
44262
+ );
44263
+ }
44079
44264
  const ScrollAreaRootContext = /* @__PURE__ */ reactExports.createContext(void 0);
44080
44265
  function useScrollAreaRootContext() {
44081
44266
  const context = reactExports.useContext(ScrollAreaRootContext);
@@ -44917,27 +45102,6 @@ function ScrollArea({
44917
45102
  hideScrollBar,
44918
45103
  ...props
44919
45104
  }) {
44920
- const handleViewportKeyDown = (event) => {
44921
- const viewport = event.currentTarget;
44922
- if (event.defaultPrevented || event.target !== viewport || viewport.ownerDocument.activeElement !== viewport) {
44923
- return;
44924
- }
44925
- const isMetaEdgeShortcut = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
44926
- const isPlainEdgeShortcut = !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
44927
- let top = null;
44928
- if (isMetaEdgeShortcut && event.key === "ArrowUp") {
44929
- top = 0;
44930
- } else if (isMetaEdgeShortcut && event.key === "ArrowDown") {
44931
- top = viewport.scrollHeight;
44932
- } else if (isPlainEdgeShortcut && event.key === "Home") {
44933
- top = 0;
44934
- } else if (isPlainEdgeShortcut && event.key === "End") {
44935
- top = viewport.scrollHeight;
44936
- }
44937
- if (top === null) return;
44938
- event.preventDefault();
44939
- viewport.scrollTo({ top, behavior: "smooth" });
44940
- };
44941
45105
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
44942
45106
  ScrollAreaRoot,
44943
45107
  {
@@ -44951,7 +45115,6 @@ function ScrollArea({
44951
45115
  "data-slot": "scroll-area-viewport",
44952
45116
  className: cn$1("size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:outline-1", viewportClassName),
44953
45117
  style: { overflowAnchor: "auto" },
44954
- onKeyDown: handleViewportKeyDown,
44955
45118
  children
44956
45119
  }
44957
45120
  ),
@@ -45702,6 +45865,23 @@ function useFocusTarget(id, handler, enabled = true) {
45702
45865
  function useFocusTargetRegistry() {
45703
45866
  return useFocusTargetContext();
45704
45867
  }
45868
+ function isContextMenuKey(event) {
45869
+ return event.key === "ContextMenu" || event.shiftKey && event.code === "F10";
45870
+ }
45871
+ function openContextMenuFromKeyboard(element) {
45872
+ const rect = element.getBoundingClientRect();
45873
+ element.dispatchEvent(
45874
+ new MouseEvent("contextmenu", {
45875
+ bubbles: true,
45876
+ cancelable: true,
45877
+ view: window,
45878
+ button: 2,
45879
+ buttons: 2,
45880
+ clientX: rect.left + rect.width / 2,
45881
+ clientY: rect.top + rect.height / 2
45882
+ })
45883
+ );
45884
+ }
45705
45885
  const keyCodeMap = {
45706
45886
  esc: "Escape",
45707
45887
  escape: "Escape",
@@ -45816,9 +45996,25 @@ function projectHoverId(id) {
45816
45996
  function sessionHoverId(id) {
45817
45997
  return `s:${id}`;
45818
45998
  }
45999
+ const TREE_NODES_TYPE = "application/x-fello-tree-nodes";
45819
46000
  function sidebarNavigationItemKey(item) {
45820
46001
  return item.type === "project" ? `project:${item.id}` : `session:${item.id}`;
45821
46002
  }
46003
+ function SessionRowStatus({ sessionId, isStreaming }) {
46004
+ const kind = useAppStore((s) => {
46005
+ const state = s.sessionStates.get(sessionId);
46006
+ if ((state?.askUserRequests?.length ?? 0) > 0) return "ask";
46007
+ if (state?.completedStatus === "error") return "error";
46008
+ if (state?.completedStatus === "success") return "success";
46009
+ if (state?.completedAt) return "success";
46010
+ return "none";
46011
+ });
46012
+ if (kind === "ask") return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "size-3 shrink-0 text-sky-500" });
46013
+ if (isStreaming) return /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin shrink-0" });
46014
+ if (kind === "success") return /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 shrink-0 text-green-500" });
46015
+ if (kind === "error") return /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3 shrink-0 text-yellow-500" });
46016
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-3 shrink-0" });
46017
+ }
45822
46018
  function Sidebar() {
45823
46019
  const { t, i18n } = useTranslation();
45824
46020
  const navigate = useNavigate();
@@ -45851,46 +46047,44 @@ function Sidebar() {
45851
46047
  const currentPath = optimisticPath ?? location.pathname;
45852
46048
  const matchSession = currentPath.match(/^\/session-view\/(.+)$/);
45853
46049
  const activeSessionId = matchSession ? matchSession[1] : null;
45854
- const {
45855
- isMacApp,
45856
- isFullScreen,
45857
- projects,
45858
- sessions,
45859
- setProjects,
45860
- setSessions,
45861
- sidebarOpen,
45862
- setSidebarOpen,
45863
- configuredAgents,
45864
- configuredMcpServers,
45865
- sessionStates,
45866
- webUIStatus,
45867
- ilinkStatus,
45868
- activeIlinkSessionId
45869
- } = useAppStore();
46050
+ const isMacApp = useAppStore((s) => s.isMacApp);
46051
+ const isFullScreen = useAppStore((s) => s.isFullScreen);
46052
+ const projects = useAppStore((s) => s.projects);
46053
+ const sessions = useAppStore((s) => s.sessions);
46054
+ const setProjects = useAppStore((s) => s.setProjects);
46055
+ const setSessions = useAppStore((s) => s.setSessions);
46056
+ const sidebarOpen = useAppStore((s) => s.sidebarOpen);
46057
+ const setSidebarOpen = useAppStore((s) => s.setSidebarOpen);
46058
+ const configuredAgents = useAppStore((s) => s.configuredAgents);
46059
+ const configuredMcpServers = useAppStore((s) => s.configuredMcpServers);
46060
+ const webUIStatus = useAppStore((s) => s.webUIStatus);
46061
+ const ilinkStatus = useAppStore((s) => s.ilinkStatus);
46062
+ const activeIlinkSessionId = useAppStore((s) => s.activeIlinkSessionId);
46063
+ const activeCompletedAt = useAppStore(
46064
+ (s) => activeSessionId ? s.sessionStates.get(activeSessionId)?.completedAt ?? null : null
46065
+ );
45870
46066
  const completedAtTimerRef = reactExports.useRef(null);
45871
46067
  reactExports.useEffect(() => {
45872
46068
  if (!activeSessionId) return;
45873
46069
  if (!sessions.some((s) => s.id === activeSessionId)) return;
45874
- const state = useAppStore.getState().sessionStates.get(activeSessionId);
45875
- if (state?.completedAt) {
45876
- if (completedAtTimerRef.current) {
45877
- clearTimeout(completedAtTimerRef.current);
45878
- }
45879
- completedAtTimerRef.current = setTimeout(() => {
45880
- useAppStore.getState().updateSessionState(activeSessionId, () => ({
45881
- completedAt: null,
45882
- completedStatus: null
45883
- }));
45884
- completedAtTimerRef.current = null;
45885
- }, 3e3);
46070
+ if (!activeCompletedAt) return;
46071
+ if (completedAtTimerRef.current) {
46072
+ clearTimeout(completedAtTimerRef.current);
45886
46073
  }
46074
+ completedAtTimerRef.current = setTimeout(() => {
46075
+ useAppStore.getState().updateSessionState(activeSessionId, () => ({
46076
+ completedAt: null,
46077
+ completedStatus: null
46078
+ }));
46079
+ completedAtTimerRef.current = null;
46080
+ }, 3e3);
45887
46081
  return () => {
45888
46082
  if (completedAtTimerRef.current) {
45889
46083
  clearTimeout(completedAtTimerRef.current);
45890
46084
  completedAtTimerRef.current = null;
45891
46085
  }
45892
46086
  };
45893
- }, [activeSessionId, sessionStates, sessions]);
46087
+ }, [activeSessionId, activeCompletedAt, sessions]);
45894
46088
  const enabledAgents = reactExports.useMemo(
45895
46089
  () => configuredAgents.filter((a) => !a.disabled),
45896
46090
  [configuredAgents]
@@ -45953,6 +46147,81 @@ function Sidebar() {
45953
46147
  reactExports.useEffect(() => {
45954
46148
  handleAddProjectRef.current = handleAddProject;
45955
46149
  });
46150
+ const [projectDropActive, setProjectDropActive] = reactExports.useState(false);
46151
+ const projectDragLeaveTimer = reactExports.useRef(null);
46152
+ reactExports.useEffect(
46153
+ () => () => {
46154
+ if (projectDragLeaveTimer.current) clearTimeout(projectDragLeaveTimer.current);
46155
+ },
46156
+ []
46157
+ );
46158
+ const extractDropPaths = (dataTransfer) => {
46159
+ const paths = [];
46160
+ for (const file of Array.from(dataTransfer.files ?? [])) {
46161
+ const absPath = electron.getPathForFile(file);
46162
+ if (absPath) paths.push(absPath);
46163
+ }
46164
+ if (paths.length > 0) return paths;
46165
+ const uriList = dataTransfer.getData("text/uri-list");
46166
+ return (uriList ? uriList.split("\n") : []).map((uri) => uri.trim()).filter((uri) => uri.startsWith("file://")).map((uri) => decodeURIComponent(uri.replace(/^file:\/\//, ""))).filter(Boolean);
46167
+ };
46168
+ const handleProjectDropDragOver = (event) => {
46169
+ if (isWebUI) return;
46170
+ const types = event.dataTransfer.types;
46171
+ if (types.includes(TREE_NODES_TYPE)) return;
46172
+ if (!types.includes("Files") && !types.includes("text/uri-list")) return;
46173
+ event.preventDefault();
46174
+ event.stopPropagation();
46175
+ event.dataTransfer.dropEffect = "copy";
46176
+ if (projectDragLeaveTimer.current) {
46177
+ clearTimeout(projectDragLeaveTimer.current);
46178
+ projectDragLeaveTimer.current = null;
46179
+ }
46180
+ setProjectDropActive(true);
46181
+ };
46182
+ const handleProjectDropDragLeave = (event) => {
46183
+ event.preventDefault();
46184
+ if (projectDragLeaveTimer.current) clearTimeout(projectDragLeaveTimer.current);
46185
+ projectDragLeaveTimer.current = setTimeout(() => setProjectDropActive(false), 50);
46186
+ };
46187
+ const handleProjectDropDrop = async (event) => {
46188
+ event.preventDefault();
46189
+ event.stopPropagation();
46190
+ if (projectDragLeaveTimer.current) {
46191
+ clearTimeout(projectDragLeaveTimer.current);
46192
+ projectDragLeaveTimer.current = null;
46193
+ }
46194
+ setProjectDropActive(false);
46195
+ if (event.dataTransfer.getData(TREE_NODES_TYPE)) return;
46196
+ if (isWebUI) return;
46197
+ const paths = [...new Set(extractDropPaths(event.dataTransfer))];
46198
+ if (paths.length === 0) return;
46199
+ const added = [];
46200
+ let failedCount = 0;
46201
+ for (const path of paths) {
46202
+ try {
46203
+ added.push(await request.addProject(path));
46204
+ } catch (err) {
46205
+ failedCount += 1;
46206
+ console.error("Failed to add dropped path as project:", err);
46207
+ }
46208
+ }
46209
+ if (failedCount > 0) {
46210
+ toast2.error(t("sidebar.dropFolderOnly", "Only folders can be dropped to add a project."));
46211
+ }
46212
+ if (added.length === 0) return;
46213
+ try {
46214
+ await refreshData();
46215
+ setExpandedProjects((prev) => {
46216
+ const next = { ...prev };
46217
+ for (const project of added) next[project.id] = true;
46218
+ return next;
46219
+ });
46220
+ if (added.length === 1) openNewSessionDialog(added[0].id);
46221
+ } catch (err) {
46222
+ toast2.error(getErrorMessage(err, t("sidebar.addProjectFailed", "Failed to add project.")));
46223
+ }
46224
+ };
45956
46225
  const handleNewSession = async (projectId, agentId, params) => {
45957
46226
  try {
45958
46227
  setExpandedProjects((prev) => ({ ...prev, [projectId]: true }));
@@ -46214,6 +46483,9 @@ function Sidebar() {
46214
46483
  );
46215
46484
  }
46216
46485
  };
46486
+ const handleCopyProjectPath = async (project) => {
46487
+ await copyText(project.cwd);
46488
+ };
46217
46489
  const sortedProjects = reactExports.useMemo(
46218
46490
  () => [...projects].sort((a, b2) => a.title.localeCompare(b2.title)),
46219
46491
  [projects]
@@ -46291,7 +46563,7 @@ function Sidebar() {
46291
46563
  useFocusTarget("sidebar-sessions", focusSidebarSessions);
46292
46564
  const handleNavigationKeyDown = (event, item) => {
46293
46565
  const { key } = event;
46294
- const isVerticalNavigationKey = key === "ArrowUp" || key === "ArrowDown" || key === "Home" || key === "End";
46566
+ const isVerticalNavigationKey = key === "ArrowUp" || key === "ArrowDown";
46295
46567
  if (isVerticalNavigationKey) {
46296
46568
  event.preventDefault();
46297
46569
  event.stopPropagation();
@@ -46303,7 +46575,7 @@ function Sidebar() {
46303
46575
  addProjectButtonRef.current?.focus({ preventScroll: true });
46304
46576
  return;
46305
46577
  }
46306
- const nextIndex = key === "Home" ? 0 : key === "End" ? navigationItems.length - 1 : Math.max(
46578
+ const nextIndex = Math.max(
46307
46579
  0,
46308
46580
  Math.min(navigationItems.length - 1, currentIndex + (key === "ArrowDown" ? 1 : -1))
46309
46581
  );
@@ -46311,22 +46583,10 @@ function Sidebar() {
46311
46583
  if (nextItem) focusNavigationItem(nextItem);
46312
46584
  return;
46313
46585
  }
46314
- if (key === "/") {
46586
+ if (isContextMenuKey(event)) {
46315
46587
  event.preventDefault();
46316
46588
  event.stopPropagation();
46317
- const target = event.currentTarget;
46318
- const rect = target.getBoundingClientRect();
46319
- target.dispatchEvent(
46320
- new MouseEvent("contextmenu", {
46321
- bubbles: true,
46322
- cancelable: true,
46323
- view: window,
46324
- button: 2,
46325
- buttons: 2,
46326
- clientX: rect.left + rect.width / 2,
46327
- clientY: rect.top + rect.height / 2
46328
- })
46329
- );
46589
+ openContextMenuFromKeyboard(event.currentTarget);
46330
46590
  return;
46331
46591
  }
46332
46592
  if (key === "ArrowLeft" || key === "ArrowRight") {
@@ -46363,7 +46623,7 @@ function Sidebar() {
46363
46623
  };
46364
46624
  const handleAddProjectKeyDown = (event) => {
46365
46625
  const { key } = event;
46366
- if (key === "ArrowLeft" || key === "ArrowRight" || key === "Home" || key === "End") {
46626
+ if (key === "ArrowLeft" || key === "ArrowRight") {
46367
46627
  event.preventDefault();
46368
46628
  event.stopPropagation();
46369
46629
  addProjectButtonRef.current?.focus({ preventScroll: true });
@@ -46437,512 +46697,533 @@ function Sidebar() {
46437
46697
  }
46438
46698
  )
46439
46699
  ] }),
46440
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 pt-2", children: [
46441
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-normal tracking-wide text-sidebar-foreground/40 uppercase select-none", children: t("sidebar.projects") }),
46442
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46443
- Button,
46444
- {
46445
- ref: addProjectButtonRef,
46446
- variant: "ghost",
46447
- size: "icon",
46448
- tabIndex: -1,
46449
- className: "size-6 -mr-1 text-sidebar-foreground/45 hover:bg-sidebar-accent/30 hover:text-sidebar-foreground/70",
46450
- onClick: handleAddProject,
46451
- onKeyDown: handleAddProjectKeyDown,
46452
- "aria-label": t("sidebar.addProject", "Add project"),
46453
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderPlus, { className: "size-3.5" })
46454
- }
46455
- )
46456
- ] }),
46457
- /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
46700
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
46458
46701
  "div",
46459
46702
  {
46460
- ref: sessionListRef,
46461
- role: "tree",
46462
- "aria-label": t("sidebar.sessions", "Sessions"),
46463
- className: "space-y-0.5 p-1.5 outline-none focus:ring-1 focus:ring-ring/50",
46464
- children: sortedProjects.map((project) => {
46465
- const projectSessions = sessionsByProject[project.id] ?? [];
46466
- const expanded = isProjectExpanded(project.id);
46467
- const currentHoverId = projectHoverId(project.id);
46468
- const latestProjectUpdateAt = projectSessions.length > 0 ? projectSessions.reduce((max2, session2) => Math.max(max2, session2.updatedAt), 0) : project.createdAt;
46469
- const connectedSessions = projectSessions.filter(
46470
- (session2) => session2.connectionStatus === "connected"
46471
- );
46472
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5 group", children: [
46473
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46474
- HoverCard,
46703
+ className: "relative flex min-h-0 flex-1 flex-col",
46704
+ onDragOver: handleProjectDropDragOver,
46705
+ onDragLeave: handleProjectDropDragLeave,
46706
+ onDrop: (event) => void handleProjectDropDrop(event),
46707
+ children: [
46708
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 pt-2", children: [
46709
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-normal tracking-wide text-sidebar-foreground/40 uppercase select-none", children: t("sidebar.projects") }),
46710
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
46711
+ Button,
46475
46712
  {
46476
- open: hoverId === currentHoverId,
46477
- onOpenChange: (open2) => {
46478
- if (open2) {
46479
- if (contextMenuId === null) setHoverId(currentHoverId);
46480
- } else if (hoverId === currentHoverId && hoveredTriggerIdRef.current !== currentHoverId) {
46481
- setHoverId(null);
46482
- }
46483
- },
46484
- children: [
46485
- /* @__PURE__ */ jsxRuntimeExports.jsx(HoverCardTrigger, { render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}), delay: 0, closeDelay: 100, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46486
- ContextMenu,
46713
+ ref: addProjectButtonRef,
46714
+ variant: "ghost",
46715
+ size: "icon",
46716
+ tabIndex: -1,
46717
+ className: "size-6 -mr-1 text-sidebar-foreground/45 hover:bg-sidebar-accent/30 hover:text-sidebar-foreground/70",
46718
+ onClick: handleAddProject,
46719
+ onKeyDown: handleAddProjectKeyDown,
46720
+ "aria-label": t("sidebar.addProject", "Add project"),
46721
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderPlus, { className: "size-3.5" })
46722
+ }
46723
+ )
46724
+ ] }),
46725
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
46726
+ "div",
46727
+ {
46728
+ ref: sessionListRef,
46729
+ role: "tree",
46730
+ "aria-label": t("sidebar.sessions", "Sessions"),
46731
+ className: "space-y-0.5 p-1.5 outline-none focus:ring-1 focus:ring-ring/50",
46732
+ children: sortedProjects.map((project) => {
46733
+ const projectSessions = sessionsByProject[project.id] ?? [];
46734
+ const expanded = isProjectExpanded(project.id);
46735
+ const currentHoverId = projectHoverId(project.id);
46736
+ const latestProjectUpdateAt = projectSessions.length > 0 ? projectSessions.reduce((max2, session2) => Math.max(max2, session2.updatedAt), 0) : project.createdAt;
46737
+ const connectedSessions = projectSessions.filter(
46738
+ (session2) => session2.connectionStatus === "connected"
46739
+ );
46740
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5 group", children: [
46741
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
46742
+ HoverCard,
46487
46743
  {
46744
+ open: hoverId === currentHoverId,
46488
46745
  onOpenChange: (open2) => {
46489
- setContextMenuId(open2 ? currentHoverId : null);
46490
46746
  if (open2) {
46747
+ if (contextMenuId === null) setHoverId(currentHoverId);
46748
+ } else if (hoverId === currentHoverId && hoveredTriggerIdRef.current !== currentHoverId) {
46491
46749
  setHoverId(null);
46492
- } else if (hoveredTriggerIdRef.current === currentHoverId) {
46493
- setHoverId(currentHoverId);
46494
46750
  }
46495
46751
  },
46496
46752
  children: [
46497
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46498
- ContextMenuTrigger,
46753
+ /* @__PURE__ */ jsxRuntimeExports.jsx(HoverCardTrigger, { render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}), delay: 0, closeDelay: 100, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46754
+ ContextMenu,
46499
46755
  {
46500
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
46501
- "div",
46502
- {
46503
- ref: (element) => {
46504
- const key = sidebarNavigationItemKey({
46505
- type: "project",
46506
- id: project.id
46507
- });
46508
- if (element) {
46509
- navigationItemRefs.current.set(key, element);
46510
- } else {
46511
- navigationItemRefs.current.delete(key);
46512
- }
46513
- },
46514
- tabIndex: -1,
46515
- role: "treeitem",
46516
- "aria-level": 1,
46517
- "aria-expanded": expanded,
46518
- onClick: () => toggleProject(project.id),
46519
- onKeyDown: (event) => handleNavigationKeyDown(event, {
46520
- type: "project",
46521
- id: project.id
46522
- }),
46523
- onPointerEnter: () => {
46524
- hoveredTriggerIdRef.current = currentHoverId;
46525
- },
46526
- onPointerLeave: () => {
46527
- hoveredTriggerIdRef.current = null;
46528
- },
46529
- className: cn$1(
46530
- "flex h-7 cursor-default items-center gap-1.5 rounded-md px-1.5 text-xs font-normal transition-colors text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-foreground/95 outline-none focus-visible:ring-1 focus-visible:ring-ring",
46531
- connectedSessions.length > 0 ? "text-sidebar-foreground/60" : "",
46532
- hoverId === currentHoverId || contextMenuId === currentHoverId ? "bg-sidebar-accent" : ""
46533
- )
46756
+ onOpenChange: (open2) => {
46757
+ setContextMenuId(open2 ? currentHoverId : null);
46758
+ if (open2) {
46759
+ setHoverId(null);
46760
+ } else if (hoveredTriggerIdRef.current === currentHoverId) {
46761
+ setHoverId(currentHoverId);
46534
46762
  }
46535
- ),
46763
+ },
46536
46764
  children: [
46537
- expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(FolderClosed, { className: "size-3.5" }),
46538
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate leading-normal font-normal uppercase select-none", children: project.title }),
46539
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46540
- "button",
46765
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
46766
+ ContextMenuTrigger,
46541
46767
  {
46542
- type: "button",
46543
- onClick: (e) => {
46544
- e.stopPropagation();
46545
- openNewSessionDialog(project.id);
46546
- },
46547
- className: `flex size-4 items-center justify-center rounded-sm text-sidebar-foreground/40 hover:bg-sidebar-accent/25 hover:text-sidebar-foreground/70 transition-opacity duration-300 ${projectSessions.length === 0 ? "opacity-100" : "opacity-0 group-hover:opacity-100"}`,
46548
- "aria-label": t("sidebar.createSessionInProject", {
46549
- defaultValue: "Create session in {{title}}",
46550
- title: project.title
46551
- }),
46552
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCirclePlus, { className: "size-3.5" })
46768
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
46769
+ "div",
46770
+ {
46771
+ ref: (element) => {
46772
+ const key = sidebarNavigationItemKey({
46773
+ type: "project",
46774
+ id: project.id
46775
+ });
46776
+ if (element) {
46777
+ navigationItemRefs.current.set(key, element);
46778
+ } else {
46779
+ navigationItemRefs.current.delete(key);
46780
+ }
46781
+ },
46782
+ tabIndex: -1,
46783
+ role: "treeitem",
46784
+ "aria-level": 1,
46785
+ "aria-expanded": expanded,
46786
+ onClick: () => toggleProject(project.id),
46787
+ onKeyDown: (event) => handleNavigationKeyDown(event, {
46788
+ type: "project",
46789
+ id: project.id
46790
+ }),
46791
+ onPointerEnter: () => {
46792
+ hoveredTriggerIdRef.current = currentHoverId;
46793
+ },
46794
+ onPointerLeave: () => {
46795
+ hoveredTriggerIdRef.current = null;
46796
+ },
46797
+ className: cn$1(
46798
+ "flex h-7 cursor-default items-center gap-1.5 rounded-md px-1.5 text-xs font-normal transition-colors text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-foreground/95 outline-none focus-visible:ring-1 focus-visible:ring-ring",
46799
+ connectedSessions.length > 0 ? "text-sidebar-foreground/60" : "",
46800
+ hoverId === currentHoverId || contextMenuId === currentHoverId ? "bg-sidebar-accent" : ""
46801
+ )
46802
+ }
46803
+ ),
46804
+ children: [
46805
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(FolderClosed, { className: "size-3.5" }),
46806
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate leading-normal font-normal uppercase select-none", children: project.title }),
46807
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
46808
+ "button",
46809
+ {
46810
+ type: "button",
46811
+ onClick: (e) => {
46812
+ e.stopPropagation();
46813
+ openNewSessionDialog(project.id);
46814
+ },
46815
+ className: `flex size-4 items-center justify-center rounded-sm text-sidebar-foreground/40 hover:bg-sidebar-accent/25 hover:text-sidebar-foreground/70 transition-opacity duration-300 ${projectSessions.length === 0 ? "opacity-100" : "opacity-0 group-hover:opacity-100"}`,
46816
+ "aria-label": t("sidebar.createSessionInProject", {
46817
+ defaultValue: "Create session in {{title}}",
46818
+ title: project.title
46819
+ }),
46820
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCirclePlus, { className: "size-3.5" })
46821
+ }
46822
+ )
46823
+ ]
46553
46824
  }
46554
- )
46825
+ ),
46826
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { className: "w-48", children: [
46827
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
46828
+ ContextMenuItem,
46829
+ {
46830
+ onClick: () => openNewSessionDialog(project.id),
46831
+ className: "font-medium text-foreground",
46832
+ children: [
46833
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCirclePlus, { className: "size-3" }),
46834
+ t("sidebar.newSession", "New Session")
46835
+ ]
46836
+ }
46837
+ ),
46838
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46839
+ !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46840
+ ContextMenuItem,
46841
+ {
46842
+ onClick: () => void handleRevealProjectInFinder(project),
46843
+ children: [
46844
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" }),
46845
+ t("sidebar.revealInFinder")
46846
+ ]
46847
+ }
46848
+ ),
46849
+ !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46850
+ ContextMenuItem,
46851
+ {
46852
+ onClick: () => void handleOpenProjectInEditor(project),
46853
+ children: [
46854
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "size-3" }),
46855
+ t("filePanel.openInEditor", {
46856
+ editor: EDITOR_LABELS[useAppStore.getState().editor.name] ?? "Editor"
46857
+ })
46858
+ ]
46859
+ }
46860
+ ),
46861
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => void handleCopyProjectPath(project), children: [
46862
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" }),
46863
+ t("sidebar.copyProjectPath")
46864
+ ] }),
46865
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => void handleRenameProject(project), children: [
46866
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
46867
+ t("sidebar.rename")
46868
+ ] }),
46869
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46870
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
46871
+ ContextMenuItem,
46872
+ {
46873
+ variant: "destructive",
46874
+ onClick: () => void handleDeleteProject(project),
46875
+ children: [
46876
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash, { className: "size-3" }),
46877
+ t("sidebar.deleteProject")
46878
+ ]
46879
+ }
46880
+ )
46881
+ ] })
46555
46882
  ]
46556
46883
  }
46557
- ),
46558
- /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { className: "w-48", children: [
46559
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46560
- ContextMenuItem,
46561
- {
46562
- onClick: () => openNewSessionDialog(project.id),
46563
- className: "font-medium text-foreground",
46564
- children: [
46565
- /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCirclePlus, { className: "size-3" }),
46566
- t("sidebar.newSession", "New Session")
46567
- ]
46568
- }
46569
- ),
46570
- /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46571
- !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46572
- ContextMenuItem,
46573
- {
46574
- onClick: () => void handleRevealProjectInFinder(project),
46575
- children: [
46576
- /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" }),
46577
- t("sidebar.revealInFinder")
46578
- ]
46579
- }
46580
- ),
46581
- !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => void handleOpenProjectInEditor(project), children: [
46582
- /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "size-3" }),
46583
- t("filePanel.openInEditor", {
46584
- editor: EDITOR_LABELS[useAppStore.getState().editor.name] ?? "Editor"
46585
- })
46586
- ] }),
46587
- /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => void handleRenameProject(project), children: [
46588
- /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
46589
- t("sidebar.rename")
46590
- ] }),
46591
- /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46592
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46593
- ContextMenuItem,
46594
- {
46595
- variant: "destructive",
46596
- onClick: () => void handleDeleteProject(project),
46597
- children: [
46598
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trash, { className: "size-3" }),
46599
- t("sidebar.deleteProject")
46600
- ]
46601
- }
46602
- )
46603
- ] })
46884
+ ) }),
46885
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
46886
+ HoverCardContent,
46887
+ {
46888
+ side: "right",
46889
+ align: "start",
46890
+ sideOffset: 12,
46891
+ alignOffset: 0,
46892
+ className: "w-54 max-w-[20vw] p-3",
46893
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
46894
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-medium leading-snug line-clamp-2 wrap-break-word", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: project.title }) }),
46895
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46896
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "size-3 shrink-0" }),
46897
+ isWebUI ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: project.cwd, children: project.cwd }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
46898
+ "button",
46899
+ {
46900
+ type: "button",
46901
+ onClick: () => void handleRevealProjectInFinder(project),
46902
+ title: project.cwd,
46903
+ className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
46904
+ children: project.cwd
46905
+ }
46906
+ )
46907
+ ] }),
46908
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46909
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 shrink-0" }),
46910
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: t("sidebar.sessionCount", "{{count}} sessions", {
46911
+ count: projectSessions.length
46912
+ }) })
46913
+ ] }),
46914
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46915
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3 shrink-0" }),
46916
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
46917
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
46918
+ t(
46919
+ projectSessions.length > 0 ? "sidebar.updated" : "sidebar.created",
46920
+ projectSessions.length > 0 ? "Updated" : "Created"
46921
+ ),
46922
+ ":"
46923
+ ] }),
46924
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(latestProjectUpdateAt, i18n.language) })
46925
+ ] })
46926
+ ] })
46927
+ ] })
46928
+ }
46929
+ )
46604
46930
  ]
46605
46931
  }
46606
- ) }),
46607
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46608
- HoverCardContent,
46609
- {
46610
- side: "right",
46611
- align: "start",
46612
- sideOffset: 12,
46613
- alignOffset: 0,
46614
- className: "w-54 max-w-[20vw] p-3",
46615
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
46616
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-medium leading-snug line-clamp-2 wrap-break-word", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: project.title }) }),
46617
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46618
- /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "size-3 shrink-0" }),
46619
- isWebUI ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: project.cwd, children: project.cwd }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
46620
- "button",
46621
- {
46622
- type: "button",
46623
- onClick: () => void handleRevealProjectInFinder(project),
46624
- title: project.cwd,
46625
- className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
46626
- children: project.cwd
46627
- }
46628
- )
46629
- ] }),
46630
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46631
- /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 shrink-0" }),
46632
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: t("sidebar.sessionCount", "{{count}} sessions", {
46633
- count: projectSessions.length
46634
- }) })
46635
- ] }),
46636
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46637
- /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3 shrink-0" }),
46638
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
46639
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
46640
- t(
46641
- projectSessions.length > 0 ? "sidebar.updated" : "sidebar.created",
46642
- projectSessions.length > 0 ? "Updated" : "Created"
46643
- ),
46644
- ":"
46645
- ] }),
46646
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(latestProjectUpdateAt, i18n.language) })
46647
- ] })
46648
- ] })
46649
- ] })
46650
- }
46651
- )
46652
- ]
46653
- }
46654
- ),
46655
- expanded && projectSessions.map((session2) => {
46656
- const currentHoverId2 = sessionHoverId(session2.id);
46657
- const agentLabel = configuredAgents.find((a) => a.id === session2.agentId)?.id || session2.agentId;
46658
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
46659
- HoverCard,
46660
- {
46661
- open: hoverId === currentHoverId2,
46662
- onOpenChange: (open2) => {
46663
- if (open2) {
46664
- if (contextMenuId === null) setHoverId(currentHoverId2);
46665
- } else if (hoverId === currentHoverId2 && hoveredTriggerIdRef.current !== currentHoverId2) {
46666
- setHoverId(null);
46667
- }
46668
- },
46669
- children: [
46670
- /* @__PURE__ */ jsxRuntimeExports.jsx(HoverCardTrigger, { render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}), delay: 0, closeDelay: 100, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46671
- ContextMenu,
46932
+ ),
46933
+ expanded && projectSessions.map((session2) => {
46934
+ const currentHoverId2 = sessionHoverId(session2.id);
46935
+ const agentLabel = configuredAgents.find((a) => a.id === session2.agentId)?.id || session2.agentId;
46936
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
46937
+ HoverCard,
46672
46938
  {
46939
+ open: hoverId === currentHoverId2,
46673
46940
  onOpenChange: (open2) => {
46674
- setContextMenuId(open2 ? currentHoverId2 : null);
46675
46941
  if (open2) {
46942
+ if (contextMenuId === null) setHoverId(currentHoverId2);
46943
+ } else if (hoverId === currentHoverId2 && hoveredTriggerIdRef.current !== currentHoverId2) {
46676
46944
  setHoverId(null);
46677
- } else if (hoveredTriggerIdRef.current === currentHoverId2) {
46678
- setHoverId(currentHoverId2);
46679
46945
  }
46680
46946
  },
46681
46947
  children: [
46682
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46683
- ContextMenuTrigger,
46948
+ /* @__PURE__ */ jsxRuntimeExports.jsx(HoverCardTrigger, { render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}), delay: 0, closeDelay: 100, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46949
+ ContextMenu,
46684
46950
  {
46685
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
46686
- "div",
46687
- {
46688
- ref: (element) => {
46689
- const key = sidebarNavigationItemKey({
46690
- type: "session",
46691
- id: session2.id,
46692
- projectId: project.id
46693
- });
46694
- if (element) {
46695
- navigationItemRefs.current.set(key, element);
46696
- } else {
46697
- navigationItemRefs.current.delete(key);
46698
- }
46699
- },
46700
- tabIndex: -1,
46701
- role: "treeitem",
46702
- "aria-level": 2,
46703
- "aria-current": activeSessionId === session2.id ? "page" : void 0,
46704
- onClick: () => handleSelectSession(session2),
46705
- onKeyDown: (event) => handleNavigationKeyDown(event, {
46706
- type: "session",
46707
- id: session2.id,
46708
- projectId: project.id
46709
- }),
46710
- onPointerEnter: () => {
46711
- hoveredTriggerIdRef.current = currentHoverId2;
46712
- },
46713
- onPointerLeave: () => {
46714
- hoveredTriggerIdRef.current = null;
46715
- },
46716
- className: `group flex h-7 cursor-default items-center justify-between rounded-md pl-1.5 pr-2 text-xs font-normal transition-colors outline-none focus-visible:ring-1 focus-visible:ring-ring ${activeSessionId === session2.id ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-foreground/95"} ${hoverId === currentHoverId2 || contextMenuId === currentHoverId2 ? "bg-sidebar-accent" : ""}`
46951
+ onOpenChange: (open2) => {
46952
+ setContextMenuId(open2 ? currentHoverId2 : null);
46953
+ if (open2) {
46954
+ setHoverId(null);
46955
+ } else if (hoveredTriggerIdRef.current === currentHoverId2) {
46956
+ setHoverId(currentHoverId2);
46717
46957
  }
46718
- ),
46719
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46720
- "div",
46721
- {
46722
- className: cn$1(
46723
- "flex min-w-0 flex-1 items-center gap-1.5",
46724
- session2.connectionStatus === "connected" ? "" : "opacity-60"
46725
- ),
46726
- children: [
46727
- (() => {
46728
- const state = sessionStates.get(session2.id);
46729
- const hasAskUser = (state?.askUserRequests?.length ?? 0) > 0;
46730
- const isStreaming = session2.isStreaming;
46731
- const completedStatus = state?.completedStatus;
46732
- if (hasAskUser) {
46733
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "size-3 shrink-0 text-sky-500" });
46734
- }
46735
- if (isStreaming) {
46736
- return /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin shrink-0" });
46737
- }
46738
- if (completedStatus === "success" || state?.completedAt && !completedStatus) {
46739
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 shrink-0 text-green-500" });
46958
+ },
46959
+ children: [
46960
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
46961
+ ContextMenuTrigger,
46962
+ {
46963
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
46964
+ "div",
46965
+ {
46966
+ ref: (element) => {
46967
+ const key = sidebarNavigationItemKey({
46968
+ type: "session",
46969
+ id: session2.id,
46970
+ projectId: project.id
46971
+ });
46972
+ if (element) {
46973
+ navigationItemRefs.current.set(key, element);
46974
+ } else {
46975
+ navigationItemRefs.current.delete(key);
46976
+ }
46977
+ },
46978
+ tabIndex: -1,
46979
+ role: "treeitem",
46980
+ "aria-level": 2,
46981
+ "aria-current": activeSessionId === session2.id ? "page" : void 0,
46982
+ onClick: () => handleSelectSession(session2),
46983
+ onKeyDown: (event) => handleNavigationKeyDown(event, {
46984
+ type: "session",
46985
+ id: session2.id,
46986
+ projectId: project.id
46987
+ }),
46988
+ onPointerEnter: () => {
46989
+ hoveredTriggerIdRef.current = currentHoverId2;
46990
+ },
46991
+ onPointerLeave: () => {
46992
+ hoveredTriggerIdRef.current = null;
46993
+ },
46994
+ className: `group flex h-7 cursor-default items-center justify-between rounded-md pl-1.5 pr-2 text-xs font-normal transition-colors outline-none focus-visible:ring-1 focus-visible:ring-ring ${activeSessionId === session2.id ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-foreground/95"} ${hoverId === currentHoverId2 || contextMenuId === currentHoverId2 ? "bg-sidebar-accent" : ""}`
46740
46995
  }
46741
- if (completedStatus === "error") {
46742
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3 shrink-0 text-yellow-500" });
46996
+ ),
46997
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46998
+ "div",
46999
+ {
47000
+ className: cn$1(
47001
+ "flex min-w-0 flex-1 items-center gap-1.5",
47002
+ session2.connectionStatus === "connected" ? "" : "opacity-60"
47003
+ ),
47004
+ children: [
47005
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47006
+ SessionRowStatus,
47007
+ {
47008
+ sessionId: session2.id,
47009
+ isStreaming: session2.isStreaming
47010
+ }
47011
+ ),
47012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47013
+ Badge,
47014
+ {
47015
+ variant: "outline",
47016
+ className: "px-1 -ml-0.5 text-[10px] uppercase select-none max-w-24",
47017
+ children: agentLabel
47018
+ }
47019
+ ),
47020
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate leading-normal select-none", children: session2.title || t("sidebar.newSession", "New Session") }),
47021
+ activeIlinkSessionId === session2.id && /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 shrink-0 text-green-500" })
47022
+ ]
46743
47023
  }
46744
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-3 shrink-0" });
46745
- })(),
46746
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46747
- Badge,
47024
+ )
47025
+ }
47026
+ ),
47027
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { className: "w-48", children: [
47028
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
47029
+ ContextMenuItem,
47030
+ {
47031
+ onClick: () => handleSelectSession(session2),
47032
+ className: "font-medium text-foreground",
47033
+ children: [
47034
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3" }),
47035
+ t("sidebar.viewSession", "View Session")
47036
+ ]
47037
+ }
47038
+ ),
47039
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
47040
+ !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
47041
+ ContextMenuItem,
47042
+ {
47043
+ onClick: () => void handleRevealProjectInFinder(project),
47044
+ children: [
47045
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" }),
47046
+ t("sidebar.revealInFinder")
47047
+ ]
47048
+ }
47049
+ ),
47050
+ !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
47051
+ ContextMenuItem,
47052
+ {
47053
+ onClick: () => void handleOpenProjectInEditor(project),
47054
+ children: [
47055
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "size-3" }),
47056
+ t("filePanel.openInEditor", {
47057
+ editor: EDITOR_LABELS[useAppStore.getState().editor.name] ?? "Editor"
47058
+ })
47059
+ ]
47060
+ }
47061
+ ),
47062
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
47063
+ ContextMenuItem,
47064
+ {
47065
+ onClick: () => void handleCopyProjectPath(project),
47066
+ children: [
47067
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" }),
47068
+ t("sidebar.copyProjectPath")
47069
+ ]
47070
+ }
47071
+ ),
47072
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => handleRenameSession(session2), children: [
47073
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
47074
+ t("sidebar.rename")
47075
+ ] }),
47076
+ ilinkStatus.connected && activeIlinkSessionId !== session2.id && /* @__PURE__ */ jsxRuntimeExports.jsxs(
47077
+ ContextMenuItem,
47078
+ {
47079
+ onClick: () => void handleSetActiveIlinkSession(session2.id),
47080
+ children: [
47081
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3" }),
47082
+ t("sidebar.ilinkSetActive", "Set as WeChat Active")
47083
+ ]
47084
+ }
47085
+ ),
47086
+ ilinkStatus.connected && activeIlinkSessionId === session2.id && /* @__PURE__ */ jsxRuntimeExports.jsxs(
47087
+ ContextMenuItem,
47088
+ {
47089
+ onClick: () => void handleSetActiveIlinkSession(""),
47090
+ children: [
47091
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 text-green-500" }),
47092
+ t("sidebar.ilinkUnsetActive", "Unset WeChat Active")
47093
+ ]
47094
+ }
47095
+ ),
47096
+ session2.connectionStatus === "connected" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
47097
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
47098
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
47099
+ ContextMenuItem,
46748
47100
  {
46749
- variant: "outline",
46750
- className: "px-1 -ml-0.5 text-[10px] uppercase select-none max-w-24",
46751
- children: agentLabel
47101
+ onClick: () => void handleRestartSession(session2),
47102
+ disabled: sessionAction !== null,
47103
+ children: [
47104
+ sessionAction?.id === session2.id && sessionAction.type === "restart" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3" }),
47105
+ t("sidebar.restartSession", "Restart Session")
47106
+ ]
46752
47107
  }
46753
47108
  ),
46754
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate leading-normal select-none", children: session2.title || t("sidebar.newSession", "New Session") }),
46755
- activeIlinkSessionId === session2.id && /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 shrink-0 text-green-500" })
46756
- ]
46757
- }
46758
- )
47109
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
47110
+ ContextMenuItem,
47111
+ {
47112
+ onClick: () => void handleCloseSession(session2),
47113
+ disabled: sessionAction !== null,
47114
+ children: [
47115
+ sessionAction?.id === session2.id && sessionAction.type === "close" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Power, { className: "size-3" }),
47116
+ sessionAction?.id === session2.id && sessionAction.type === "close" ? t("sidebar.closingSession", "Closing…") : t("sidebar.closeSession", "Close Session")
47117
+ ]
47118
+ }
47119
+ )
47120
+ ] }),
47121
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
47122
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
47123
+ ContextMenuItem,
47124
+ {
47125
+ variant: "destructive",
47126
+ onClick: () => void handleDeleteSession(session2),
47127
+ disabled: sessionAction !== null,
47128
+ children: [
47129
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash, { className: "size-3" }),
47130
+ t("sidebar.deleteSession")
47131
+ ]
47132
+ }
47133
+ )
47134
+ ] })
47135
+ ]
46759
47136
  }
46760
- ),
46761
- /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { className: "w-48", children: [
46762
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46763
- ContextMenuItem,
46764
- {
46765
- onClick: () => handleSelectSession(session2),
46766
- className: "font-medium text-foreground",
46767
- children: [
46768
- /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3" }),
46769
- t("sidebar.viewSession", "View Session")
46770
- ]
46771
- }
46772
- ),
46773
- /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46774
- !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46775
- ContextMenuItem,
46776
- {
46777
- onClick: () => void handleRevealProjectInFinder(project),
46778
- children: [
46779
- /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" }),
46780
- t("sidebar.revealInFinder")
46781
- ]
46782
- }
46783
- ),
46784
- !isWebUI && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46785
- ContextMenuItem,
46786
- {
46787
- onClick: () => void handleOpenProjectInEditor(project),
46788
- children: [
46789
- /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "size-3" }),
46790
- t("filePanel.openInEditor", {
46791
- editor: EDITOR_LABELS[useAppStore.getState().editor.name] ?? "Editor"
46792
- })
46793
- ]
46794
- }
46795
- ),
46796
- /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => handleRenameSession(session2), children: [
46797
- /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
46798
- t("sidebar.rename")
46799
- ] }),
46800
- ilinkStatus.connected && activeIlinkSessionId !== session2.id && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46801
- ContextMenuItem,
46802
- {
46803
- onClick: () => void handleSetActiveIlinkSession(session2.id),
46804
- children: [
46805
- /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3" }),
46806
- t("sidebar.ilinkSetActive", "Set as WeChat Active")
46807
- ]
46808
- }
46809
- ),
46810
- ilinkStatus.connected && activeIlinkSessionId === session2.id && /* @__PURE__ */ jsxRuntimeExports.jsxs(
46811
- ContextMenuItem,
46812
- {
46813
- onClick: () => void handleSetActiveIlinkSession(""),
46814
- children: [
46815
- /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircle, { className: "size-3 text-green-500" }),
46816
- t("sidebar.ilinkUnsetActive", "Unset WeChat Active")
46817
- ]
46818
- }
46819
- ),
46820
- session2.connectionStatus === "connected" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
46821
- /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46822
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46823
- ContextMenuItem,
46824
- {
46825
- onClick: () => void handleRestartSession(session2),
46826
- disabled: sessionAction !== null,
46827
- children: [
46828
- sessionAction?.id === session2.id && sessionAction.type === "restart" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3" }),
46829
- t("sidebar.restartSession", "Restart Session")
46830
- ]
46831
- }
46832
- ),
46833
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46834
- ContextMenuItem,
46835
- {
46836
- onClick: () => void handleCloseSession(session2),
46837
- disabled: sessionAction !== null,
46838
- children: [
46839
- sessionAction?.id === session2.id && sessionAction.type === "close" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Power, { className: "size-3" }),
46840
- sessionAction?.id === session2.id && sessionAction.type === "close" ? t("sidebar.closingSession", "Closing…") : t("sidebar.closeSession", "Close Session")
46841
- ]
46842
- }
46843
- )
46844
- ] }),
46845
- /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
46846
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
46847
- ContextMenuItem,
46848
- {
46849
- variant: "destructive",
46850
- onClick: () => void handleDeleteSession(session2),
46851
- disabled: sessionAction !== null,
46852
- children: [
46853
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trash, { className: "size-3" }),
46854
- t("sidebar.deleteSession")
46855
- ]
46856
- }
46857
- )
46858
- ] })
47137
+ ) }),
47138
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47139
+ HoverCardContent,
47140
+ {
47141
+ side: "right",
47142
+ align: "start",
47143
+ sideOffset: 12,
47144
+ alignOffset: 0,
47145
+ className: "w-54 max-w-[20vw] p-3",
47146
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
47147
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-medium leading-snug line-clamp-2 wrap-break-word", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: session2.title || t("sidebar.newSession", "New Session") }) }),
47148
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
47149
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Bot, { className: "size-3 shrink-0" }),
47150
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: session2.agentId })
47151
+ ] }),
47152
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
47153
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "size-3 shrink-0" }),
47154
+ isWebUI ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: project.title }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
47155
+ "button",
47156
+ {
47157
+ type: "button",
47158
+ onClick: () => void handleRevealProjectInFinder(project),
47159
+ title: project.cwd,
47160
+ className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
47161
+ children: project.title
47162
+ }
47163
+ )
47164
+ ] }),
47165
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
47166
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3 shrink-0" }),
47167
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
47168
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
47169
+ t("sidebar.updated", "Updated"),
47170
+ ":"
47171
+ ] }),
47172
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(session2.updatedAt, i18n.language) })
47173
+ ] })
47174
+ ] }),
47175
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
47176
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CalendarDays, { className: "size-3 shrink-0" }),
47177
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
47178
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
47179
+ t("sidebar.created", "Created"),
47180
+ ":"
47181
+ ] }),
47182
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(session2.createdAt, i18n.language) })
47183
+ ] })
47184
+ ] }),
47185
+ ilinkStatus.connected && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
47186
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47187
+ MessageCircle,
47188
+ {
47189
+ className: cn$1(
47190
+ "size-3 shrink-0",
47191
+ activeIlinkSessionId === session2.id && "text-green-500"
47192
+ )
47193
+ }
47194
+ ),
47195
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47196
+ "button",
47197
+ {
47198
+ type: "button",
47199
+ onClick: () => void handleSetActiveIlinkSession(
47200
+ activeIlinkSessionId === session2.id ? "" : session2.id
47201
+ ),
47202
+ className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
47203
+ children: t(
47204
+ activeIlinkSessionId === session2.id ? "sidebar.ilinkActiveStatus" : "sidebar.ilinkInactiveStatus",
47205
+ activeIlinkSessionId === session2.id ? "WeChat Active" : "Not WeChat Active"
47206
+ )
47207
+ }
47208
+ )
47209
+ ] })
47210
+ ] })
47211
+ }
47212
+ )
46859
47213
  ]
46860
- }
46861
- ) }),
46862
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46863
- HoverCardContent,
46864
- {
46865
- side: "right",
46866
- align: "start",
46867
- sideOffset: 12,
46868
- alignOffset: 0,
46869
- className: "w-54 max-w-[20vw] p-3",
46870
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
46871
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-medium leading-snug line-clamp-2 wrap-break-word", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: session2.title || t("sidebar.newSession", "New Session") }) }),
46872
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46873
- /* @__PURE__ */ jsxRuntimeExports.jsx(Bot, { className: "size-3 shrink-0" }),
46874
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: session2.agentId })
46875
- ] }),
46876
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46877
- /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "size-3 shrink-0" }),
46878
- isWebUI ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: project.title }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
46879
- "button",
46880
- {
46881
- type: "button",
46882
- onClick: () => void handleRevealProjectInFinder(project),
46883
- title: project.cwd,
46884
- className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
46885
- children: project.title
46886
- }
46887
- )
46888
- ] }),
46889
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46890
- /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3 shrink-0" }),
46891
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
46892
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
46893
- t("sidebar.updated", "Updated"),
46894
- ":"
46895
- ] }),
46896
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(session2.updatedAt, i18n.language) })
46897
- ] })
46898
- ] }),
46899
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46900
- /* @__PURE__ */ jsxRuntimeExports.jsx(CalendarDays, { className: "size-3 shrink-0" }),
46901
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
46902
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-1", children: [
46903
- t("sidebar.created", "Created"),
46904
- ":"
46905
- ] }),
46906
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatRelativeTime(session2.createdAt, i18n.language) })
46907
- ] })
46908
- ] }),
46909
- ilinkStatus.connected && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground py-px", children: [
46910
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46911
- MessageCircle,
46912
- {
46913
- className: cn$1(
46914
- "size-3 shrink-0",
46915
- activeIlinkSessionId === session2.id && "text-green-500"
46916
- )
46917
- }
46918
- ),
46919
- /* @__PURE__ */ jsxRuntimeExports.jsx(
46920
- "button",
46921
- {
46922
- type: "button",
46923
- onClick: () => void handleSetActiveIlinkSession(
46924
- activeIlinkSessionId === session2.id ? "" : session2.id
46925
- ),
46926
- className: "min-w-0 flex-1 truncate text-left transition-colors hover:text-foreground",
46927
- children: t(
46928
- activeIlinkSessionId === session2.id ? "sidebar.ilinkActiveStatus" : "sidebar.ilinkInactiveStatus",
46929
- activeIlinkSessionId === session2.id ? "WeChat Active" : "Not WeChat Active"
46930
- )
46931
- }
46932
- )
46933
- ] })
46934
- ] })
46935
- }
46936
- )
46937
- ]
46938
- },
46939
- session2.id
46940
- );
46941
- })
46942
- ] }, project.id);
46943
- })
47214
+ },
47215
+ session2.id
47216
+ );
47217
+ })
47218
+ ] }, project.id);
47219
+ })
47220
+ }
47221
+ ) }),
47222
+ projectDropActive && // 与 FilePanel 项目根落点同款的整块高亮(不参与布局)
47223
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute inset-x-1 inset-y-1 rounded-md bg-primary/5 inset-ring-1 inset-ring-primary" })
47224
+ ]
46944
47225
  }
46945
- ) }),
47226
+ ),
46946
47227
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
46947
47228
  Button,
46948
47229
  {
@@ -47036,106 +47317,16 @@ function Sidebar() {
47036
47317
  })()
47037
47318
  ] })
47038
47319
  ] }),
47039
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
47040
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: t("constant.feature.title", { defaultValue: "Features" }) }),
47041
- /* @__PURE__ */ jsxRuntimeExports.jsx(
47042
- "div",
47043
- {
47044
- className: ALL_FEATURES.length >= 2 ? "grid grid-cols-2 gap-1" : "flex flex-col gap-1",
47045
- children: ALL_FEATURES.map((feature) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
47046
- "div",
47047
- {
47048
- className: "flex items-center justify-between rounded border bg-secondary/50 px-2 h-7 cursor-default hover:bg-accent transition-colors",
47049
- onClick: () => setNewSessionFeatures(
47050
- (prev) => prev.includes(feature) ? prev.filter((f2) => f2 !== feature) : [...prev, feature]
47051
- ),
47052
- children: [
47053
- /* @__PURE__ */ jsxRuntimeExports.jsx(
47054
- "div",
47055
- {
47056
- className: cn$1(
47057
- "text-xs truncate",
47058
- !newSessionFeatures.includes(feature) ? "text-muted-foreground/50" : "text-muted-foreground"
47059
- ),
47060
- children: t(FEATURE_I18N_KEYS[feature], { defaultValue: feature })
47061
- }
47062
- ),
47063
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
47064
- Switch,
47065
- {
47066
- size: "sm",
47067
- checked: newSessionFeatures.includes(feature),
47068
- onCheckedChange: (checked2) => {
47069
- setNewSessionFeatures(
47070
- (prev) => checked2 ? [...prev, feature] : prev.filter((f2) => f2 !== feature)
47071
- );
47072
- }
47073
- }
47074
- ) })
47075
- ]
47076
- },
47077
- feature
47078
- ))
47079
- }
47080
- )
47081
- ] }),
47082
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
47083
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: t("sidebar.newSessionDialog.mcp", { defaultValue: "MCP" }) }),
47084
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
47085
- "div",
47086
- {
47087
- className: configuredMcpServers.length >= 2 ? "grid grid-cols-2 gap-1" : "flex flex-col gap-1",
47088
- children: [
47089
- configuredMcpServers.map((mcp) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
47090
- "div",
47091
- {
47092
- className: "flex items-center justify-between rounded border bg-secondary/50 px-2 h-7 cursor-default hover:bg-accent transition-colors",
47093
- onClick: () => {
47094
- setNewSessionMcpIds((prev) => {
47095
- const next = new Set(prev);
47096
- if (next.has(mcp.id)) next.delete(mcp.id);
47097
- else next.add(mcp.id);
47098
- return next;
47099
- });
47100
- },
47101
- children: [
47102
- /* @__PURE__ */ jsxRuntimeExports.jsx(
47103
- "div",
47104
- {
47105
- className: cn$1(
47106
- "text-xs truncate",
47107
- !newSessionMcpIds.has(mcp.id) ? "text-muted-foreground/50" : "text-muted-foreground"
47108
- ),
47109
- title: mcp.id,
47110
- children: mcp.id
47111
- }
47112
- ),
47113
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
47114
- Switch,
47115
- {
47116
- size: "sm",
47117
- checked: newSessionMcpIds.has(mcp.id),
47118
- onCheckedChange: (checked2) => {
47119
- setNewSessionMcpIds((prev) => {
47120
- const next = new Set(prev);
47121
- if (checked2) next.add(mcp.id);
47122
- else next.delete(mcp.id);
47123
- return next;
47124
- });
47125
- }
47126
- }
47127
- ) })
47128
- ]
47129
- },
47130
- mcp.id
47131
- )),
47132
- configuredMcpServers.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: t("sidebar.newSessionDialog.noMcp", {
47133
- defaultValue: "No MCP servers configured"
47134
- }) })
47135
- ]
47136
- }
47137
- )
47138
- ] })
47320
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47321
+ SessionConfigFields,
47322
+ {
47323
+ variant: "card",
47324
+ features: newSessionFeatures,
47325
+ onFeaturesChange: setNewSessionFeatures,
47326
+ mcpServers: Array.from(newSessionMcpIds),
47327
+ onMcpServersChange: (next) => setNewSessionMcpIds(new Set(next))
47328
+ }
47329
+ )
47139
47330
  ] }),
47140
47331
  /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogFooter, { children: [
47141
47332
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -47383,7 +47574,7 @@ function Welcome() {
47383
47574
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-3 right-4 text-[11px] text-muted-foreground/40 z-10 pointer-events-none", children: [
47384
47575
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
47385
47576
  "v",
47386
- "0.1.55"
47577
+ "0.1.57"
47387
47578
  ] }),
47388
47579
  false
47389
47580
  ] }),
@@ -47545,63 +47736,67 @@ function Welcome() {
47545
47736
  function lazyNamed(loader, name) {
47546
47737
  return reactExports.lazy(() => loader().then((mod) => ({ default: mod[name] })));
47547
47738
  }
47548
- const Session = lazyNamed(() => __vitePreload(() => import("./session-DQEqHTKb.js"), true ? __vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]) : void 0, import.meta.url), "Session");
47739
+ const Session = lazyNamed(() => __vitePreload(() => import("./session-D6NE3yNE.js"), true ? __vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]) : void 0, import.meta.url), "Session");
47549
47740
  const SettingsLayout = lazyNamed(
47550
- () => __vitePreload(() => import("./settings-layout-DL6ZEzgP.js"), true ? __vite__mapDeps([18,19,10,14]) : void 0, import.meta.url),
47741
+ () => __vitePreload(() => import("./settings-layout-DJbT4rIH.js"), true ? __vite__mapDeps([21,22,12,16]) : void 0, import.meta.url),
47551
47742
  "SettingsLayout"
47552
47743
  );
47553
47744
  const SettingsGeneral = lazyNamed(
47554
- () => __vitePreload(() => import("./settings-general-BcCOVmx3.js"), true ? __vite__mapDeps([20,21]) : void 0, import.meta.url),
47745
+ () => __vitePreload(() => import("./settings-general-B85uTAij.js"), true ? __vite__mapDeps([23,24,1]) : void 0, import.meta.url),
47555
47746
  "SettingsGeneral"
47556
47747
  );
47557
47748
  const SettingsShortcuts = lazyNamed(
47558
- () => __vitePreload(() => import("./settings-shortcuts-BmUGS1q_.js"), true ? __vite__mapDeps([22,19,13,15]) : void 0, import.meta.url),
47749
+ () => __vitePreload(() => import("./settings-shortcuts-dWdRCFxB.js"), true ? __vite__mapDeps([25,22,15,18]) : void 0, import.meta.url),
47559
47750
  "SettingsShortcuts"
47560
47751
  );
47561
47752
  const SettingsAgents = lazyNamed(
47562
- () => __vitePreload(() => import("./settings-agents-f3HkdiTw.js"), true ? __vite__mapDeps([23,24,25,26,27,21,11,9,16,28]) : void 0, import.meta.url),
47753
+ () => __vitePreload(() => import("./settings-agents-DxIwcY7M.js"), true ? __vite__mapDeps([26,27,28,29,30,24,13,11,19,31]) : void 0, import.meta.url),
47563
47754
  "SettingsAgents"
47564
47755
  );
47565
47756
  const SettingsMcp = lazyNamed(
47566
- () => __vitePreload(() => import("./settings-mcp-BKW8JKtO.js"), true ? __vite__mapDeps([29,24,25,26,27,21,9,16]) : void 0, import.meta.url),
47757
+ () => __vitePreload(() => import("./settings-mcp-Cz6GMogp.js"), true ? __vite__mapDeps([32,27,28,29,30,24,11,19,9]) : void 0, import.meta.url),
47567
47758
  "SettingsMcp"
47568
47759
  );
47569
47760
  const SettingsWebUI = lazyNamed(
47570
- () => __vitePreload(() => import("./settings-webui-DIEKvTrI.js"), true ? __vite__mapDeps([30,24]) : void 0, import.meta.url),
47761
+ () => __vitePreload(() => import("./settings-webui-DKiOzDSD.js"), true ? __vite__mapDeps([33,27]) : void 0, import.meta.url),
47571
47762
  "SettingsWebUI"
47572
47763
  );
47573
47764
  const SettingsILink = lazyNamed(
47574
- () => __vitePreload(() => import("./settings-ilink-Da336Koc.js"), true ? __vite__mapDeps([31,24,7]) : void 0, import.meta.url),
47765
+ () => __vitePreload(() => import("./settings-ilink-BgeA04Nv.js"), true ? __vite__mapDeps([34,27,8,9]) : void 0, import.meta.url),
47575
47766
  "SettingsILink"
47576
47767
  );
47577
47768
  const SettingsSnippets = lazyNamed(
47578
- () => __vitePreload(() => import("./settings-snippets-CpAOJmDF.js"), true ? __vite__mapDeps([32,26,27,21,9,25,16]) : void 0, import.meta.url),
47769
+ () => __vitePreload(() => import("./settings-snippets-CrtJYwLV.js"), true ? __vite__mapDeps([35,29,30,24,11,28,19]) : void 0, import.meta.url),
47579
47770
  "SettingsSnippets"
47580
47771
  );
47581
47772
  const SettingsMemory = lazyNamed(
47582
- () => __vitePreload(() => import("./settings-memory-o4r-_vob.js"), true ? __vite__mapDeps([33,4]) : void 0, import.meta.url),
47773
+ () => __vitePreload(() => import("./settings-memory-Cidr_Ghw.js"), true ? __vite__mapDeps([36,5]) : void 0, import.meta.url),
47583
47774
  "SettingsMemory"
47584
47775
  );
47585
47776
  const SettingsImageGeneration = lazyNamed(
47586
- () => __vitePreload(() => import("./settings-image-generation-RmWs73ZG.js"), true ? __vite__mapDeps([34,26,27,21,16,11,9]) : void 0, import.meta.url),
47777
+ () => __vitePreload(() => import("./settings-image-generation-CsWv4wXY.js"), true ? __vite__mapDeps([37,29,30,24,19,13,11]) : void 0, import.meta.url),
47587
47778
  "SettingsImageGeneration"
47588
47779
  );
47589
- const SettingsSpeechToText = lazyNamed(
47590
- () => __vitePreload(() => import("./settings-speech-to-text-C8KHNGE4.js"), true ? __vite__mapDeps([35,26,21,24,9,16]) : void 0, import.meta.url),
47591
- "SettingsSpeechToText"
47780
+ const SettingsSpeech = lazyNamed(
47781
+ () => __vitePreload(() => import("./settings-speech-CteE8F4J.js"), true ? __vite__mapDeps([38,27,29,24,11,19]) : void 0, import.meta.url),
47782
+ "SettingsSpeech"
47592
47783
  );
47593
- const SkillsLayout = lazyNamed(() => __vitePreload(() => import("./skills-layout-ByPhfHG6.js"), true ? __vite__mapDeps([36,37]) : void 0, import.meta.url), "SkillsLayout");
47784
+ const SettingsPeripherals = lazyNamed(
47785
+ () => __vitePreload(() => import("./settings-peripherals-DilwOpyO.js"), true ? __vite__mapDeps([39,17,9]) : void 0, import.meta.url),
47786
+ "SettingsPeripherals"
47787
+ );
47788
+ const SkillsLayout = lazyNamed(() => __vitePreload(() => import("./skills-layout-BAzfcnkT.js"), true ? __vite__mapDeps([40,41]) : void 0, import.meta.url), "SkillsLayout");
47594
47789
  const SkillsInstalled = lazyNamed(
47595
- () => __vitePreload(() => import("./skills-installed-BuWBDk6r.js"), true ? __vite__mapDeps([38,3,5,6]) : void 0, import.meta.url),
47790
+ () => __vitePreload(() => import("./skills-installed-Beqe9G7p.js"), true ? __vite__mapDeps([42,4,6,7]) : void 0, import.meta.url),
47596
47791
  "SkillsInstalled"
47597
47792
  );
47598
47793
  const SkillsSh = lazyNamed(
47599
- () => __vitePreload(() => import("./skills-skills-sh-wR7YNmAJ.js"), true ? __vite__mapDeps([39,5,8,37]) : void 0, import.meta.url),
47794
+ () => __vitePreload(() => import("./skills-skills-sh-Dzocqfu4.js"), true ? __vite__mapDeps([43,6,10,41]) : void 0, import.meta.url),
47600
47795
  "SkillsSh"
47601
47796
  );
47602
- const Automation = lazyNamed(() => __vitePreload(() => import("./automation-cVl19jbl.js"), true ? __vite__mapDeps([40,5,41,27,21,16,1]) : void 0, import.meta.url), "Automation");
47603
- const Schedule = lazyNamed(() => __vitePreload(() => import("./schedule-DMyyz9A1.js"), true ? __vite__mapDeps([42,17,41,27,21,15,1,6,28]) : void 0, import.meta.url), "Schedule");
47604
- const Task = lazyNamed(() => __vitePreload(() => import("./task-PHAOSE-W.js"), true ? __vite__mapDeps([43,17,3,2,4,5,6,7,8,9,10,11,12]) : void 0, import.meta.url), "Task");
47797
+ const Automation = lazyNamed(() => __vitePreload(() => import("./automation-W30eYgU8.js"), true ? __vite__mapDeps([44,6,45,30,24,2,19]) : void 0, import.meta.url), "Automation");
47798
+ const Schedule = lazyNamed(() => __vitePreload(() => import("./schedule-H1BEvD9Q.js"), true ? __vite__mapDeps([46,20,45,30,24,2,18,7,31]) : void 0, import.meta.url), "Schedule");
47799
+ const Task = lazyNamed(() => __vitePreload(() => import("./task-DM-bWOFz.js"), true ? __vite__mapDeps([47,20,4,3,5,6,7,8,9,10,11,12,13,14]) : void 0, import.meta.url), "Task");
47605
47800
  function SessionWrapper() {
47606
47801
  const { sessionId } = useParams();
47607
47802
  const sessionInfo = useAppStore((s) => s.sessions.find((x2) => x2.id === sessionId));
@@ -47637,7 +47832,8 @@ function AppRouter() {
47637
47832
  /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "snippets", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSnippets, {}) }),
47638
47833
  /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "memory", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsMemory, {}) }),
47639
47834
  /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "image-generation", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsImageGeneration, {}) }),
47640
- /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "speech-to-text", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSpeechToText, {}) })
47835
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "speech", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSpeech, {}) }),
47836
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "peripherals", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsPeripherals, {}) })
47641
47837
  ] }),
47642
47838
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Route, { path: "/skills", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SkillsLayout, {}), children: [
47643
47839
  /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "installed", element: /* @__PURE__ */ jsxRuntimeExports.jsx(SkillsInstalled, {}) }),
@@ -47651,7 +47847,7 @@ function AppRouter() {
47651
47847
  {
47652
47848
  className: cn$1(
47653
47849
  "flex h-12 w-12 absolute z-10 top-0 items-center justify-center transition-all duration-200",
47654
- sidebarOpen ? "left-49 -ml-px" : showMacTrafficLightSpace ? "left-16" : "left-0"
47850
+ sidebarOpen ? "left-49 -ml-px" : showMacTrafficLightSpace ? "left-17" : "left-0"
47655
47851
  ),
47656
47852
  style: { WebkitAppRegion: "no-drag" },
47657
47853
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -47671,6 +47867,690 @@ function AppRouter() {
47671
47867
  )
47672
47868
  ] });
47673
47869
  }
47870
+ const XIAOMI_RC003 = {
47871
+ id: "xiaomi-rc003",
47872
+ name: "Xiaomi Bluetooth Remote 2 Pro (RC003)",
47873
+ productUrl: "https://www.mi.com/xiaomi-bluetooth-remote-2-pro",
47874
+ platforms: ["darwin"],
47875
+ statusLabelKey: "settings.peripherals.status.bluetooth",
47876
+ keys: [
47877
+ {
47878
+ id: "voice",
47879
+ labelKey: "settings.peripherals.keys.voice",
47880
+ transport: "keyboard",
47881
+ kind: "hold",
47882
+ code: "F5"
47883
+ },
47884
+ {
47885
+ id: "back",
47886
+ labelKey: "settings.peripherals.keys.back",
47887
+ // 走 HID 报文(设备私有编码,见下方 `hid.keyMappings`)→ 注入系统按键,
47888
+ // 不是系统直接派发的键盘事件,所以没有 `code`。
47889
+ transport: "hid",
47890
+ kind: "press",
47891
+ // 注意:**这个字段不能删** —— 它是「HID 报文 → 注入系统按键」的唯一开关:
47892
+ // index.ts 把它塞进 peripheral-key 事件,渲染层 `if (!event.systemKey) return;`
47893
+ // 一旦为空就静默丢弃(按键没反应、也没有任何报错)。
47894
+ systemKey: "Escape"
47895
+ }
47896
+ ],
47897
+ hid: {
47898
+ vendorId: 10007,
47899
+ productId: 12984,
47900
+ keyMappings: [
47901
+ {
47902
+ keyId: "back",
47903
+ reportPrefix: [1, 0, 0],
47904
+ codeIndex: 3,
47905
+ pressCode: 241,
47906
+ releaseCode: 0
47907
+ }
47908
+ ]
47909
+ },
47910
+ bluetooth: {
47911
+ kind: "atvv-voice",
47912
+ audio: { sampleRate: 16e3, channels: 1, bitsPerSample: 16 }
47913
+ }
47914
+ };
47915
+ const BUILTIN_PERIPHERALS = [XIAOMI_RC003];
47916
+ function isPeripheralSupportedOnPlatform(peripheral, platform2) {
47917
+ return peripheral.platforms.includes(platform2);
47918
+ }
47919
+ function isPeripheralRuntimeAvailable(options) {
47920
+ if (options.isWebUI) return false;
47921
+ return BUILTIN_PERIPHERALS.some(
47922
+ (peripheral) => isPeripheralSupportedOnPlatform(peripheral, options.platform)
47923
+ );
47924
+ }
47925
+ function getRendererPlatform() {
47926
+ const bridge2 = window.fello;
47927
+ if (!bridge2) return "linux";
47928
+ if (bridge2.isMacApp) return "darwin";
47929
+ if (bridge2.isWinApp) return "win32";
47930
+ return "linux";
47931
+ }
47932
+ const RENDERER_PLATFORM = getRendererPlatform();
47933
+ const TARGET_SAMPLE_RATE = 16e3;
47934
+ const FRAME_SAMPLES = 320;
47935
+ const PERIPHERAL_LEVEL_FLOOR = 0.15;
47936
+ const PERIPHERAL_LEVEL_WARMUP_MS = 300;
47937
+ const workletSource = `
47938
+ class FelloPcmProcessor extends AudioWorkletProcessor {
47939
+ constructor() {
47940
+ super();
47941
+ this.buffer = [];
47942
+ this.position = 0;
47943
+ this.output = [];
47944
+ this.ratio = sampleRate / ${TARGET_SAMPLE_RATE};
47945
+ }
47946
+ process(inputs, outputs) {
47947
+ const input = inputs[0] && inputs[0][0];
47948
+ if (input) {
47949
+ for (let i = 0; i < input.length; i++) this.buffer.push(input[i]);
47950
+ while (this.position + 1 < this.buffer.length) {
47951
+ const index = Math.floor(this.position);
47952
+ const fraction = this.position - index;
47953
+ const sample = this.buffer[index] * (1 - fraction) + this.buffer[index + 1] * fraction;
47954
+ const clamped = Math.max(-1, Math.min(1, sample));
47955
+ this.output.push(clamped < 0 ? clamped * 32768 : clamped * 32767);
47956
+ this.position += this.ratio;
47957
+ if (this.output.length >= ${FRAME_SAMPLES}) {
47958
+ const pcm = new Int16Array(${FRAME_SAMPLES});
47959
+ for (let i = 0; i < ${FRAME_SAMPLES}; i++) pcm[i] = this.output[i];
47960
+ this.output = this.output.slice(${FRAME_SAMPLES});
47961
+ this.port.postMessage(pcm.buffer, [pcm.buffer]);
47962
+ }
47963
+ }
47964
+ const remove = Math.max(0, Math.floor(this.position));
47965
+ if (remove > 0) {
47966
+ this.buffer = this.buffer.slice(remove);
47967
+ this.position -= remove;
47968
+ }
47969
+ }
47970
+ const output = outputs[0] && outputs[0][0];
47971
+ if (output) output.fill(0);
47972
+ return true;
47973
+ }
47974
+ }
47975
+ registerProcessor("fello-pcm-processor", FelloPcmProcessor);
47976
+ `;
47977
+ function arrayBufferToBase64(buffer) {
47978
+ const bytes = new Uint8Array(buffer);
47979
+ let binary = "";
47980
+ const chunkSize = 32768;
47981
+ for (let offset2 = 0; offset2 < bytes.length; offset2 += chunkSize) {
47982
+ binary += String.fromCharCode(...bytes.subarray(offset2, offset2 + chunkSize));
47983
+ }
47984
+ return btoa(binary);
47985
+ }
47986
+ function errorMessage(error) {
47987
+ return error instanceof Error ? error.message : String(error);
47988
+ }
47989
+ function base64ToArrayBuffer(base64) {
47990
+ const binary = atob(base64);
47991
+ const bytes = new Uint8Array(binary.length);
47992
+ for (let index2 = 0; index2 < binary.length; index2 += 1) {
47993
+ bytes[index2] = binary.charCodeAt(index2);
47994
+ }
47995
+ return bytes.buffer;
47996
+ }
47997
+ function useRealtimeAsr(options) {
47998
+ const { onTranscript, onError, onRecordingChange, source = "microphone" } = options;
47999
+ const configured = useAppStore(
48000
+ (state) => state.speechProviders.some((provider) => provider.asrEnabled)
48001
+ );
48002
+ const [recording, setRecording] = reactExports.useState(false);
48003
+ const [audioLevel, setAudioLevel] = reactExports.useState(0);
48004
+ const [inputDevices, setInputDevices] = reactExports.useState([]);
48005
+ const asrSessionIdRef = reactExports.useRef(null);
48006
+ const peripheralIdRef = reactExports.useRef(null);
48007
+ const captureIdRef = reactExports.useRef(0);
48008
+ const streamRef = reactExports.useRef(null);
48009
+ const audioContextRef = reactExports.useRef(null);
48010
+ const sourceRef = reactExports.useRef(null);
48011
+ const nodeRef = reactExports.useRef(null);
48012
+ const stoppedRef = reactExports.useRef(false);
48013
+ const stoppingRef = reactExports.useRef(false);
48014
+ const recordingRef = reactExports.useRef(false);
48015
+ const audioLevelValueRef = reactExports.useRef(0);
48016
+ const audioLevelFrameRef = reactExports.useRef(null);
48017
+ const stopRef = reactExports.useRef(async () => {
48018
+ });
48019
+ const stopPromiseRef = reactExports.useRef(null);
48020
+ const callbacksRef = reactExports.useRef({ onTranscript, onError, onRecordingChange });
48021
+ callbacksRef.current = { onTranscript, onError, onRecordingChange };
48022
+ const setRecordingState = reactExports.useCallback((value) => {
48023
+ recordingRef.current = value;
48024
+ setRecording(value);
48025
+ callbacksRef.current.onRecordingChange?.(value);
48026
+ }, []);
48027
+ reactExports.useEffect(() => {
48028
+ const handleTranscript = (event) => {
48029
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
48030
+ callbacksRef.current.onTranscript?.({
48031
+ text: event.text,
48032
+ isFinal: event.isFinal,
48033
+ id: event.id,
48034
+ index: event.index,
48035
+ // 交给消费方做「会话隔离」用,见 RealtimeAsrTranscript.asrSessionId
48036
+ asrSessionId: event.asrSessionId
48037
+ });
48038
+ };
48039
+ const handleError = (event) => {
48040
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
48041
+ callbacksRef.current.onError?.(event.message);
48042
+ };
48043
+ const handleClosed = (event) => {
48044
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
48045
+ if (recordingRef.current && !stoppingRef.current) {
48046
+ callbacksRef.current.onError?.("实时语音识别连接已关闭。");
48047
+ void stopRef.current();
48048
+ }
48049
+ };
48050
+ subscribe.on("asr-transcript", handleTranscript);
48051
+ subscribe.on("asr-error", handleError);
48052
+ subscribe.on("asr-closed", handleClosed);
48053
+ return () => {
48054
+ subscribe.off("asr-transcript", handleTranscript);
48055
+ subscribe.off("asr-error", handleError);
48056
+ subscribe.off("asr-closed", handleClosed);
48057
+ };
48058
+ }, [setRecordingState]);
48059
+ const levelGateUntilRef = reactExports.useRef(0);
48060
+ const updateAudioLevel = reactExports.useCallback(
48061
+ (buffer) => {
48062
+ const samples = new Int16Array(buffer);
48063
+ if (samples.length === 0) return;
48064
+ if (source === "peripheral" && Date.now() < levelGateUntilRef.current) {
48065
+ audioLevelValueRef.current = 0;
48066
+ return;
48067
+ }
48068
+ let sumSquares = 0;
48069
+ for (const sample of samples) {
48070
+ const normalized = sample / 32768;
48071
+ sumSquares += normalized * normalized;
48072
+ }
48073
+ const rms = Math.sqrt(sumSquares / samples.length);
48074
+ const amplified = Math.min(1, rms * 4);
48075
+ const compressed = Math.sqrt(amplified);
48076
+ audioLevelValueRef.current = source === "peripheral" ? Math.max(0, (compressed - PERIPHERAL_LEVEL_FLOOR) / (1 - PERIPHERAL_LEVEL_FLOOR)) : amplified;
48077
+ if (audioLevelFrameRef.current !== null) return;
48078
+ audioLevelFrameRef.current = requestAnimationFrame(() => {
48079
+ audioLevelFrameRef.current = null;
48080
+ setAudioLevel(audioLevelValueRef.current);
48081
+ });
48082
+ },
48083
+ [source]
48084
+ );
48085
+ reactExports.useEffect(() => {
48086
+ if (source !== "peripheral") return;
48087
+ const handleAudio = (event) => {
48088
+ const asrSessionId = asrSessionIdRef.current;
48089
+ if (!asrSessionId || stoppedRef.current) return;
48090
+ if (event.peripheralId !== peripheralIdRef.current) return;
48091
+ if (event.captureId === 0 || event.captureId !== captureIdRef.current) return;
48092
+ updateAudioLevel(base64ToArrayBuffer(event.audioB64));
48093
+ void request.sendRealtimeAsrFrame({ clientId, asrSessionId, audioB64: event.audioB64 }).catch((error) => callbacksRef.current.onError?.(errorMessage(error)));
48094
+ };
48095
+ subscribe.on("peripheral-audio", handleAudio);
48096
+ return () => subscribe.off("peripheral-audio", handleAudio);
48097
+ }, [source, updateAudioLevel]);
48098
+ const cleanupAudio = reactExports.useCallback(async () => {
48099
+ if (audioLevelFrameRef.current !== null) {
48100
+ cancelAnimationFrame(audioLevelFrameRef.current);
48101
+ audioLevelFrameRef.current = null;
48102
+ }
48103
+ audioLevelValueRef.current = 0;
48104
+ setAudioLevel(0);
48105
+ nodeRef.current?.disconnect();
48106
+ sourceRef.current?.disconnect();
48107
+ streamRef.current?.getTracks().forEach((track) => track.stop());
48108
+ streamRef.current = null;
48109
+ nodeRef.current = null;
48110
+ sourceRef.current = null;
48111
+ const context = audioContextRef.current;
48112
+ audioContextRef.current = null;
48113
+ await context?.close().catch(() => void 0);
48114
+ }, []);
48115
+ const refreshInputDevices = reactExports.useCallback(async () => {
48116
+ if (!navigator.mediaDevices?.enumerateDevices) return [];
48117
+ try {
48118
+ let devices = await navigator.mediaDevices.enumerateDevices();
48119
+ if (devices.length > 0 && devices.every((device) => !device.label)) {
48120
+ try {
48121
+ const permissionStream = await navigator.mediaDevices.getUserMedia({ audio: true });
48122
+ permissionStream.getTracks().forEach((track) => track.stop());
48123
+ devices = await navigator.mediaDevices.enumerateDevices();
48124
+ } catch {
48125
+ }
48126
+ }
48127
+ const audioInputs = devices.filter((device) => device.kind === "audioinput").map((device, index2) => ({
48128
+ deviceId: device.deviceId,
48129
+ label: device.label || `Microphone ${index2 + 1}`
48130
+ }));
48131
+ setInputDevices(audioInputs);
48132
+ return audioInputs;
48133
+ } catch (error) {
48134
+ callbacksRef.current.onError?.(errorMessage(error));
48135
+ return [];
48136
+ }
48137
+ }, []);
48138
+ const start = reactExports.useCallback(
48139
+ async (deviceId = "default") => {
48140
+ if (stopPromiseRef.current) await stopPromiseRef.current.catch(() => void 0);
48141
+ if (asrSessionIdRef.current) await stopRef.current().catch(() => void 0);
48142
+ if (recordingRef.current || !configured) return;
48143
+ const asrSessionId = `asr_${Date.now()}_${Math.random().toString(36).slice(2)}`;
48144
+ asrSessionIdRef.current = asrSessionId;
48145
+ stoppedRef.current = false;
48146
+ stoppingRef.current = false;
48147
+ if (source === "peripheral") {
48148
+ const peripheralId = deviceId;
48149
+ peripheralIdRef.current = peripheralId;
48150
+ captureIdRef.current = 0;
48151
+ try {
48152
+ await request.startRealtimeAsr({ clientId, asrSessionId });
48153
+ const captureId = await electron.peripherals.voiceStart(peripheralId);
48154
+ if (!Number.isFinite(captureId) || captureId <= 0) {
48155
+ throw new Error(
48156
+ `外设音频通道未返回有效的采集标识(captureId=${String(captureId)}),本次录音已取消。`
48157
+ );
48158
+ }
48159
+ captureIdRef.current = captureId;
48160
+ levelGateUntilRef.current = Date.now() + PERIPHERAL_LEVEL_WARMUP_MS;
48161
+ audioLevelValueRef.current = 0;
48162
+ setAudioLevel(0);
48163
+ setRecordingState(true);
48164
+ } catch (error) {
48165
+ await request.stopRealtimeAsr({ clientId, asrSessionId }).catch(() => void 0);
48166
+ await electron.peripherals.voiceStop(peripheralId).catch(() => void 0);
48167
+ asrSessionIdRef.current = null;
48168
+ peripheralIdRef.current = null;
48169
+ captureIdRef.current = 0;
48170
+ callbacksRef.current.onError?.(errorMessage(error));
48171
+ setRecordingState(false);
48172
+ }
48173
+ return;
48174
+ }
48175
+ try {
48176
+ const stream = await navigator.mediaDevices.getUserMedia({
48177
+ audio: {
48178
+ channelCount: 1,
48179
+ echoCancellation: true,
48180
+ noiseSuppression: true,
48181
+ ...deviceId !== "default" ? { deviceId: { exact: deviceId } } : {}
48182
+ }
48183
+ });
48184
+ streamRef.current = stream;
48185
+ const audioContext = new AudioContext();
48186
+ audioContextRef.current = audioContext;
48187
+ await audioContext.resume();
48188
+ const workletUrl = URL.createObjectURL(
48189
+ new Blob([workletSource], { type: "application/javascript" })
48190
+ );
48191
+ try {
48192
+ await audioContext.audioWorklet.addModule(workletUrl);
48193
+ } finally {
48194
+ URL.revokeObjectURL(workletUrl);
48195
+ }
48196
+ const source2 = audioContext.createMediaStreamSource(stream);
48197
+ const node = new AudioWorkletNode(audioContext, "fello-pcm-processor");
48198
+ const silentGain = audioContext.createGain();
48199
+ silentGain.gain.value = 0;
48200
+ node.port.onmessage = (message2) => {
48201
+ if (stoppedRef.current || asrSessionIdRef.current !== asrSessionId) return;
48202
+ updateAudioLevel(message2.data);
48203
+ void request.sendRealtimeAsrFrame({
48204
+ clientId,
48205
+ asrSessionId,
48206
+ audioB64: arrayBufferToBase64(message2.data)
48207
+ }).catch((error) => callbacksRef.current.onError?.(errorMessage(error)));
48208
+ };
48209
+ sourceRef.current = source2;
48210
+ nodeRef.current = node;
48211
+ await request.startRealtimeAsr({ clientId, asrSessionId });
48212
+ source2.connect(node);
48213
+ node.connect(silentGain);
48214
+ silentGain.connect(audioContext.destination);
48215
+ setRecordingState(true);
48216
+ } catch (error) {
48217
+ await cleanupAudio();
48218
+ await request.stopRealtimeAsr({ clientId, asrSessionId }).catch(() => void 0);
48219
+ asrSessionIdRef.current = null;
48220
+ callbacksRef.current.onError?.(errorMessage(error));
48221
+ setRecordingState(false);
48222
+ }
48223
+ },
48224
+ [cleanupAudio, configured, setRecordingState, updateAudioLevel, source]
48225
+ );
48226
+ const runStopStreaming = reactExports.useCallback(async () => {
48227
+ if (!asrSessionIdRef.current) return;
48228
+ setRecordingState(false);
48229
+ audioLevelValueRef.current = 0;
48230
+ setAudioLevel(0);
48231
+ if (source === "peripheral") {
48232
+ const peripheralId = peripheralIdRef.current;
48233
+ if (peripheralId) {
48234
+ await electron.peripherals.voiceStop(peripheralId).catch(() => void 0);
48235
+ }
48236
+ stoppedRef.current = true;
48237
+ return;
48238
+ }
48239
+ stoppedRef.current = true;
48240
+ await cleanupAudio();
48241
+ }, [cleanupAudio, setRecordingState, source]);
48242
+ const runCloseSession = reactExports.useCallback(async () => {
48243
+ const asrSessionId = asrSessionIdRef.current;
48244
+ if (!asrSessionId) return;
48245
+ stoppingRef.current = true;
48246
+ await request.stopRealtimeAsr({ clientId, asrSessionId }).catch((error) => {
48247
+ callbacksRef.current.onError?.(errorMessage(error));
48248
+ });
48249
+ asrSessionIdRef.current = null;
48250
+ peripheralIdRef.current = null;
48251
+ captureIdRef.current = 0;
48252
+ stoppingRef.current = false;
48253
+ }, []);
48254
+ const runStop = reactExports.useCallback(async () => {
48255
+ await runStopStreaming();
48256
+ await runCloseSession();
48257
+ setRecordingState(false);
48258
+ }, [runCloseSession, runStopStreaming, setRecordingState]);
48259
+ const stop = reactExports.useCallback(async () => {
48260
+ if (stopPromiseRef.current) return stopPromiseRef.current;
48261
+ const run = runStop();
48262
+ stopPromiseRef.current = run;
48263
+ try {
48264
+ await run;
48265
+ } finally {
48266
+ if (stopPromiseRef.current === run) stopPromiseRef.current = null;
48267
+ }
48268
+ }, [runStop]);
48269
+ stopRef.current = stop;
48270
+ reactExports.useEffect(() => {
48271
+ return () => {
48272
+ stoppedRef.current = true;
48273
+ void stopRef.current();
48274
+ };
48275
+ }, []);
48276
+ const toggle = reactExports.useCallback(() => {
48277
+ void (recordingRef.current ? stop() : start());
48278
+ }, [start, stop]);
48279
+ return {
48280
+ recording,
48281
+ audioLevel,
48282
+ configured,
48283
+ inputDevices,
48284
+ refreshInputDevices,
48285
+ start,
48286
+ stop,
48287
+ toggle
48288
+ };
48289
+ }
48290
+ const DEFAULT_MAX_DURATION = 5 * 60 * 1e3;
48291
+ const VoicePanelContext = reactExports.createContext(null);
48292
+ const VoicePanelDataContext = reactExports.createContext(null);
48293
+ function useVoicePanelContext() {
48294
+ const context = useVoicePanelContextOptional();
48295
+ if (!context) {
48296
+ throw new Error("Voice panel hooks must be used within VoicePanelProvider");
48297
+ }
48298
+ return context;
48299
+ }
48300
+ function useVoicePanelContextOptional() {
48301
+ return reactExports.useContext(VoicePanelContext);
48302
+ }
48303
+ function useTranscriptSegments() {
48304
+ const [segments, setSegments] = reactExports.useState([]);
48305
+ const segmentsRef = reactExports.useRef([]);
48306
+ const currentKeyRef = reactExports.useRef(null);
48307
+ const streamSeqRef = reactExports.useRef(0);
48308
+ const textRef = reactExports.useRef("");
48309
+ const append = reactExports.useCallback(
48310
+ (transcript) => {
48311
+ const scope = transcript.asrSessionId ?? "session";
48312
+ const key = transcript.id !== void 0 ? `${scope}:id:${transcript.id}` : transcript.index !== void 0 ? `${scope}:index:${transcript.index}` : currentKeyRef.current ?? `${scope}:stream:${streamSeqRef.current++}`;
48313
+ currentKeyRef.current = transcript.isFinal ? null : key;
48314
+ const list = segmentsRef.current;
48315
+ const at = list.findIndex((segment) => segment.key === key);
48316
+ if (at >= 0) list[at] = { key, text: transcript.text };
48317
+ else list.push({ key, text: transcript.text });
48318
+ textRef.current = list.map((segment) => segment.text).join("");
48319
+ setSegments([...list]);
48320
+ },
48321
+ []
48322
+ );
48323
+ const clear = reactExports.useCallback(() => {
48324
+ segmentsRef.current = [];
48325
+ currentKeyRef.current = null;
48326
+ textRef.current = "";
48327
+ setSegments([]);
48328
+ }, []);
48329
+ const text = segments.map((segment) => segment.text).join("");
48330
+ return { text, textRef, append, clear };
48331
+ }
48332
+ function VoicePanelProvider({ children }) {
48333
+ const { t } = useTranslation();
48334
+ const { toast: toast2 } = useMessage();
48335
+ const [phase, setPhase] = reactExports.useState("idle");
48336
+ const [error, setErrorState] = reactExports.useState(null);
48337
+ const errorRef = reactExports.useRef(null);
48338
+ const setError = reactExports.useCallback((next) => {
48339
+ errorRef.current = next;
48340
+ setErrorState(next);
48341
+ }, []);
48342
+ const [activeTargetId, setActiveTargetId] = reactExports.useState(null);
48343
+ const targetsRef = reactExports.useRef(/* @__PURE__ */ new Map());
48344
+ const phaseRef = reactExports.useRef("idle");
48345
+ const activeTargetRef = reactExports.useRef(null);
48346
+ const stopAsrRef = reactExports.useRef(async () => {
48347
+ });
48348
+ const transcript = useTranscriptSegments();
48349
+ const setPhaseBoth = reactExports.useCallback((next) => {
48350
+ phaseRef.current = next;
48351
+ setPhase(next);
48352
+ }, []);
48353
+ const resolveTarget = reactExports.useCallback(() => {
48354
+ let found = null;
48355
+ for (const [id, record] of targetsRef.current) {
48356
+ if (record.enabled) found = [id, record];
48357
+ }
48358
+ return found;
48359
+ }, []);
48360
+ const asr = useRealtimeAsr({
48361
+ source: "peripheral",
48362
+ onTranscript: (result) => transcript.append(result),
48363
+ onError: (message2) => setError(message2)
48364
+ });
48365
+ const { start: startAsr, stop: stopAsr } = asr;
48366
+ const { clear: clearTranscript, text: transcriptText, textRef } = transcript;
48367
+ const reset = reactExports.useCallback(() => {
48368
+ clearTranscript();
48369
+ setError(null);
48370
+ setActiveTargetId(null);
48371
+ activeTargetRef.current = null;
48372
+ setPhaseBoth("idle");
48373
+ }, [clearTranscript, setError, setPhaseBoth]);
48374
+ const close = reactExports.useCallback(() => {
48375
+ if (phaseRef.current === "idle") return;
48376
+ void stopAsr();
48377
+ reset();
48378
+ }, [reset, stopAsr]);
48379
+ const stop = reactExports.useCallback(() => {
48380
+ if (phaseRef.current === "idle") return;
48381
+ if (textRef.current.trim().length === 0 && !errorRef.current) {
48382
+ close();
48383
+ return;
48384
+ }
48385
+ setPhaseBoth("review");
48386
+ void stopAsr();
48387
+ }, [close, setPhaseBoth, stopAsr, textRef]);
48388
+ const start = reactExports.useCallback(
48389
+ (peripheralId) => {
48390
+ if (phaseRef.current === "recording") return;
48391
+ const appending = phaseRef.current === "review";
48392
+ const target = resolveTarget();
48393
+ if (!target) return;
48394
+ if (!asr.configured) {
48395
+ toast2.error(t("chatInput.voiceInputNotConfigured", "Configure voice input in Settings"));
48396
+ return;
48397
+ }
48398
+ if (!appending) clearTranscript();
48399
+ setError(null);
48400
+ activeTargetRef.current = target[0];
48401
+ setActiveTargetId(target[0]);
48402
+ setPhaseBoth("recording");
48403
+ void startAsr(peripheralId);
48404
+ },
48405
+ [asr.configured, clearTranscript, resolveTarget, setError, setPhaseBoth, startAsr, t, toast2]
48406
+ );
48407
+ const registerTarget = reactExports.useCallback(
48408
+ (id, record) => {
48409
+ targetsRef.current.set(id, record);
48410
+ return () => {
48411
+ if (targetsRef.current.get(id) !== record) return;
48412
+ targetsRef.current.delete(id);
48413
+ if (activeTargetRef.current === id) {
48414
+ void stopAsrRef.current();
48415
+ clearTranscript();
48416
+ setError(null);
48417
+ activeTargetRef.current = null;
48418
+ setActiveTargetId(null);
48419
+ setPhaseBoth("idle");
48420
+ }
48421
+ };
48422
+ },
48423
+ [clearTranscript, setError, setPhaseBoth]
48424
+ );
48425
+ reactExports.useEffect(() => {
48426
+ stopAsrRef.current = stopAsr;
48427
+ }, [stopAsr]);
48428
+ reactExports.useEffect(() => {
48429
+ if (phase !== "recording") return;
48430
+ const timer = setTimeout(() => stop(), DEFAULT_MAX_DURATION);
48431
+ return () => clearTimeout(timer);
48432
+ }, [phase, stop]);
48433
+ reactExports.useEffect(() => {
48434
+ if (phase === "idle") return;
48435
+ const handleKeyDown = (event) => {
48436
+ if (event.key !== "Escape" || event.isComposing) return;
48437
+ event.preventDefault();
48438
+ event.stopPropagation();
48439
+ close();
48440
+ };
48441
+ window.addEventListener("keydown", handleKeyDown, true);
48442
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
48443
+ }, [phase, close]);
48444
+ const value = reactExports.useMemo(
48445
+ () => ({ registerTarget, start, stop, active: phase !== "idle" }),
48446
+ [registerTarget, start, stop, phase]
48447
+ );
48448
+ const dataValue = reactExports.useMemo(
48449
+ () => ({
48450
+ panel: phase === "idle" ? null : {
48451
+ phase,
48452
+ transcript: transcriptText,
48453
+ error,
48454
+ audioLevel: asr.audioLevel,
48455
+ audioActive: asr.recording,
48456
+ close
48457
+ },
48458
+ activeTargetId,
48459
+ phase
48460
+ }),
48461
+ [phase, transcriptText, error, asr.audioLevel, asr.recording, close, activeTargetId]
48462
+ );
48463
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(VoicePanelContext.Provider, { value, children: /* @__PURE__ */ jsxRuntimeExports.jsx(VoicePanelDataContext.Provider, { value: dataValue, children }) });
48464
+ }
48465
+ function useVoicePanelTarget(options) {
48466
+ const registerTarget = useVoicePanelContextOptional()?.registerTarget;
48467
+ const dataValue = reactExports.useContext(VoicePanelDataContext);
48468
+ const [id] = reactExports.useState(() => /* @__PURE__ */ Symbol("VoicePanelTarget"));
48469
+ const recordRef = reactExports.useRef({ enabled: false });
48470
+ reactExports.useEffect(() => {
48471
+ recordRef.current.enabled = options.enabled;
48472
+ }, [options.enabled]);
48473
+ reactExports.useEffect(() => {
48474
+ if (!registerTarget) return;
48475
+ return registerTarget(id, recordRef.current);
48476
+ }, [id, registerTarget]);
48477
+ if (!dataValue || dataValue.phase === "idle" || dataValue.activeTargetId !== id) {
48478
+ return null;
48479
+ }
48480
+ return dataValue.panel;
48481
+ }
48482
+ function useVoicePanelController() {
48483
+ const { start, stop, active } = useVoicePanelContext();
48484
+ return { start, stop, active };
48485
+ }
48486
+ function PeripheralRuntime() {
48487
+ const peripherals = useAppStore((state) => state.peripherals);
48488
+ const { start: startVoicePanel, stop: stopVoicePanel } = useVoicePanelController();
48489
+ const platform2 = RENDERER_PLATFORM;
48490
+ const available = isPeripheralRuntimeAvailable({ isWebUI, platform: platform2 });
48491
+ const enabledIds = reactExports.useMemo(
48492
+ () => new Set(peripherals.filter((entry) => entry.enabled).map((entry) => entry.id)),
48493
+ [peripherals]
48494
+ );
48495
+ const holdKeyOwners = reactExports.useMemo(() => {
48496
+ const owners = /* @__PURE__ */ new Map();
48497
+ if (!available) return owners;
48498
+ for (const peripheral of BUILTIN_PERIPHERALS) {
48499
+ if (!enabledIds.has(peripheral.id)) continue;
48500
+ if (!isPeripheralSupportedOnPlatform(peripheral, platform2)) continue;
48501
+ for (const key of peripheral.keys) {
48502
+ if (key.transport !== "keyboard" || key.kind !== "hold" || !key.code) continue;
48503
+ owners.set(key.code, peripheral.id);
48504
+ }
48505
+ }
48506
+ return owners;
48507
+ }, [available, enabledIds, platform2]);
48508
+ reactExports.useEffect(() => {
48509
+ if (holdKeyOwners.size === 0) return;
48510
+ const handleKeyDown = (event) => {
48511
+ if (event.repeat || event.isComposing) return;
48512
+ const peripheralId = holdKeyOwners.get(event.code);
48513
+ if (!peripheralId) return;
48514
+ event.preventDefault();
48515
+ event.stopPropagation();
48516
+ startVoicePanel(peripheralId);
48517
+ };
48518
+ const handleKeyUp = (event) => {
48519
+ if (!holdKeyOwners.has(event.code)) return;
48520
+ event.preventDefault();
48521
+ stopVoicePanel();
48522
+ };
48523
+ const handleBlur = () => stopVoicePanel();
48524
+ window.addEventListener("keydown", handleKeyDown, true);
48525
+ window.addEventListener("keyup", handleKeyUp, true);
48526
+ window.addEventListener("blur", handleBlur);
48527
+ return () => {
48528
+ window.removeEventListener("keydown", handleKeyDown, true);
48529
+ window.removeEventListener("keyup", handleKeyUp, true);
48530
+ window.removeEventListener("blur", handleBlur);
48531
+ };
48532
+ }, [holdKeyOwners, startVoicePanel, stopVoicePanel]);
48533
+ reactExports.useEffect(() => {
48534
+ if (!available) return;
48535
+ const handlePeripheralKey = (event) => {
48536
+ if (!enabledIds.has(event.peripheralId)) return;
48537
+ if (!event.systemKey) return;
48538
+ if (!document.hasFocus()) return;
48539
+ const target = document.activeElement ?? document.body;
48540
+ target.dispatchEvent(
48541
+ new KeyboardEvent(event.action === "down" ? "keydown" : "keyup", {
48542
+ key: event.systemKey,
48543
+ code: event.systemKey,
48544
+ bubbles: true,
48545
+ cancelable: true
48546
+ })
48547
+ );
48548
+ };
48549
+ subscribe.on("peripheral-key", handlePeripheralKey);
48550
+ return () => subscribe.off("peripheral-key", handlePeripheralKey);
48551
+ }, [available, enabledIds]);
48552
+ return null;
48553
+ }
47674
48554
  const COMMAND_DEFINITIONS = [
47675
48555
  {
47676
48556
  id: "focus.chatInput",
@@ -47846,6 +48726,449 @@ function useCommandShortcuts(commands2, overrides) {
47846
48726
  );
47847
48727
  useKeyboardShortcuts(shortcuts);
47848
48728
  }
48729
+ const STORAGE_KEY = "fello.tts.prefs";
48730
+ const DEFAULT_PREFS = { autoRead: false, volume: 100 };
48731
+ function isObject(value) {
48732
+ return typeof value === "object" && value !== null && !Array.isArray(value);
48733
+ }
48734
+ function clampVolume(value) {
48735
+ if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_PREFS.volume;
48736
+ return Math.min(100, Math.max(0, Math.round(value)));
48737
+ }
48738
+ function readPrefs() {
48739
+ try {
48740
+ const raw = localStorage.getItem(STORAGE_KEY);
48741
+ if (!raw) return DEFAULT_PREFS;
48742
+ const parsed = JSON.parse(raw);
48743
+ if (!isObject(parsed)) return DEFAULT_PREFS;
48744
+ return {
48745
+ autoRead: typeof parsed.autoRead === "boolean" ? parsed.autoRead : DEFAULT_PREFS.autoRead,
48746
+ volume: clampVolume(parsed.volume)
48747
+ };
48748
+ } catch {
48749
+ return DEFAULT_PREFS;
48750
+ }
48751
+ }
48752
+ function writePrefs(prefs) {
48753
+ try {
48754
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
48755
+ } catch {
48756
+ }
48757
+ }
48758
+ const useTtsPrefsStore = create((set2, get2) => ({
48759
+ ...readPrefs(),
48760
+ setAutoRead: (autoRead) => {
48761
+ set2({ autoRead });
48762
+ writePrefs({ autoRead, volume: get2().volume });
48763
+ },
48764
+ setVolume: (volume) => {
48765
+ const next = clampVolume(volume);
48766
+ set2({ volume: next });
48767
+ writePrefs({ autoRead: get2().autoRead, volume: next });
48768
+ }
48769
+ }));
48770
+ function getTtsPrefs() {
48771
+ const { autoRead, volume } = useTtsPrefsStore.getState();
48772
+ return { autoRead, volume };
48773
+ }
48774
+ const useTtsPlaybackStore = create((set2) => ({
48775
+ activeSessionId: null,
48776
+ setActiveSessionId: (activeSessionId) => set2({ activeSessionId })
48777
+ }));
48778
+ class TtsPlayer {
48779
+ context = null;
48780
+ masterGain = null;
48781
+ session = null;
48782
+ /** master GainNode 的目标增益(0-1),来自渲染层音量偏好 */
48783
+ gain = getTtsPrefs().volume / 100;
48784
+ constructor() {
48785
+ useTtsPrefsStore.subscribe((state) => this.applyVolume(state.volume));
48786
+ }
48787
+ applyVolume(volume) {
48788
+ this.gain = Math.min(1, Math.max(0, volume / 100));
48789
+ if (this.masterGain) this.masterGain.gain.value = this.gain;
48790
+ }
48791
+ ensureContext() {
48792
+ if (!this.context) {
48793
+ this.context = new AudioContext();
48794
+ this.masterGain = this.context.createGain();
48795
+ this.masterGain.gain.value = this.gain;
48796
+ this.masterGain.connect(this.context.destination);
48797
+ }
48798
+ if (this.context.state === "suspended") {
48799
+ void this.context.resume();
48800
+ }
48801
+ return this.context;
48802
+ }
48803
+ stopSession(playback) {
48804
+ for (const source of playback.sources) {
48805
+ try {
48806
+ source.stop();
48807
+ } catch {
48808
+ }
48809
+ source.disconnect();
48810
+ }
48811
+ playback.sources.clear();
48812
+ }
48813
+ /**
48814
+ * 追加一段 PCM16(base64)到播放队列。
48815
+ * 新会话到达时抢占:停旧会话、清队列、切换。
48816
+ */
48817
+ enqueueChunk(params) {
48818
+ const { sessionId, audioB64, sampleRate } = params;
48819
+ const context = this.ensureContext();
48820
+ const masterGain = this.masterGain;
48821
+ if (!this.session || this.session.sessionId !== sessionId) {
48822
+ if (this.session) this.stopSession(this.session);
48823
+ this.session = { sessionId, nextStartTime: 0, sources: /* @__PURE__ */ new Set() };
48824
+ useTtsPlaybackStore.getState().setActiveSessionId(sessionId);
48825
+ }
48826
+ const playback = this.session;
48827
+ const bytes = Uint8Array.from(atob(audioB64), (c) => c.charCodeAt(0));
48828
+ if (bytes.length % 2 !== 0) return;
48829
+ const pcm = new Int16Array(bytes.buffer, bytes.byteOffset, bytes.length / 2);
48830
+ const buffer = context.createBuffer(1, pcm.length, sampleRate);
48831
+ const channel = buffer.getChannelData(0);
48832
+ for (let i = 0; i < pcm.length; i++) channel[i] = pcm[i] / 32768;
48833
+ const source = context.createBufferSource();
48834
+ source.buffer = buffer;
48835
+ source.connect(masterGain);
48836
+ const startAt = Math.max(context.currentTime + 0.05, playback.nextStartTime);
48837
+ playback.nextStartTime = startAt + buffer.duration;
48838
+ source.onended = () => {
48839
+ playback.sources.delete(source);
48840
+ source.disconnect();
48841
+ if (playback.sources.size === 0 && this.session === playback && playback.nextStartTime <= (this.context?.currentTime ?? 0) + 0.02) {
48842
+ this.session = null;
48843
+ useTtsPlaybackStore.getState().setActiveSessionId(null);
48844
+ }
48845
+ };
48846
+ playback.sources.add(source);
48847
+ source.start(startAt);
48848
+ }
48849
+ /** 停止当前播放(清队列 + 清状态)。 */
48850
+ stop() {
48851
+ if (!this.session) return;
48852
+ this.stopSession(this.session);
48853
+ this.session = null;
48854
+ useTtsPlaybackStore.getState().setActiveSessionId(null);
48855
+ }
48856
+ }
48857
+ const ttsPlayer = new TtsPlayer();
48858
+ function removeMarkdown(md, options) {
48859
+ options = options || {};
48860
+ options.listUnicodeChar = options.hasOwnProperty("listUnicodeChar") ? options.listUnicodeChar : false;
48861
+ options.stripListLeaders = options.hasOwnProperty("stripListLeaders") ? options.stripListLeaders : true;
48862
+ options.gfm = options.hasOwnProperty("gfm") ? options.gfm : true;
48863
+ options.useImgAltText = options.hasOwnProperty("useImgAltText") ? options.useImgAltText : true;
48864
+ options.abbr = options.hasOwnProperty("abbr") ? options.abbr : false;
48865
+ options.replaceLinksWithURL = options.hasOwnProperty("replaceLinksWithURL") ? options.replaceLinksWithURL : false;
48866
+ options.separateLinksAndTexts = options.hasOwnProperty("separateLinksAndTexts") ? options.separateLinksAndTexts : null;
48867
+ options.htmlTagsToSkip = options.hasOwnProperty("htmlTagsToSkip") ? options.htmlTagsToSkip : [];
48868
+ options.throwError = options.hasOwnProperty("throwError") ? options.throwError : false;
48869
+ var output = md || "";
48870
+ output = output.replace(/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/gm, "");
48871
+ try {
48872
+ if (options.stripListLeaders) {
48873
+ if (options.listUnicodeChar)
48874
+ output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, options.listUnicodeChar + " $1");
48875
+ else
48876
+ output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, "$1");
48877
+ }
48878
+ if (options.gfm) {
48879
+ output = output.replace(/\n={2,}/g, "\n").replace(/~{3}.*\n/g, "").replace(/~~/g, "").replace(/```(?:.*)\n([\s\S]*?)```/g, (_2, code) => code.trim());
48880
+ }
48881
+ if (options.abbr) {
48882
+ output = output.replace(/\*\[.*\]:.*\n/, "");
48883
+ }
48884
+ let htmlReplaceRegex = /<[^>]*>/g;
48885
+ if (options.htmlTagsToSkip && options.htmlTagsToSkip.length > 0) {
48886
+ const joinedHtmlTagsToSkip = options.htmlTagsToSkip.join("|");
48887
+ htmlReplaceRegex = new RegExp(
48888
+ `<(?!/?(${joinedHtmlTagsToSkip})(?=>|s[^>]*>))[^>]*>`,
48889
+ "g"
48890
+ );
48891
+ }
48892
+ if (options.separateLinksAndTexts) {
48893
+ output = output.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1" + options.separateLinksAndTexts + "$2");
48894
+ }
48895
+ output = output.replace(htmlReplaceRegex, "").replace(/^[=\-]{2,}\s*$/g, "").replace(/\[\^.+?\](\: .*?$)?/g, "").replace(/\s{0,2}\[.*?\]: .*?$/g, "").replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? "$1" : "").replace(/\[([\s\S]*?)\]\s*[\(\[](.*?)[\)\]]/g, options.replaceLinksWithURL ? "$2" : "$1").replace(/^(\n)?\s{0,3}>\s?/gm, "$1").replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "").replace(/^(\n)?\s{0,}#{1,6}\s*( (.+))? +#+$|^(\n)?\s{0,}#{1,6}\s*( (.+))?$/gm, "$1$3$4$6").replace(/([\*]+)(\S)(.*?\S)??\1/g, "$2$3").replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, "$1$3$4$5").replace(/(`{3,})(.*?)\1/gm, "$2").replace(/`(.+?)`/g, "$1").replace(/~(.*?)~/g, "$1");
48896
+ } catch (e) {
48897
+ if (options.throwError) throw e;
48898
+ console.error("remove-markdown encountered error: %s", e);
48899
+ return md;
48900
+ }
48901
+ return output;
48902
+ }
48903
+ const MAX_SENTENCE_LEN = 120;
48904
+ const CJK_TERMINATORS = /* @__PURE__ */ new Set(["。", "!", "?", ";", "…"]);
48905
+ const SPEAKABLE_RE = /[\p{L}\p{N}]/u;
48906
+ const FENCE_LINE_RE = /^\s{0,3}(`{3,}|~{3,})[A-Za-z0-9+#._-]*\s*$/;
48907
+ const RESIDUE_RE = new RegExp("[`*_~|\\u005B\\u005D]|\\p{Cf}", "gu");
48908
+ function sanitizeLine(line) {
48909
+ return removeMarkdown(line).replace(/https?:\/\/\S+/g, " ").replace(/^\s*\[[ xX]\]\s*/, "").replace(RESIDUE_RE, " ").replace(/\s+/g, " ").trim();
48910
+ }
48911
+ function createTtsTextSplitter() {
48912
+ let inFence = false;
48913
+ let lineBuf = "";
48914
+ let sentence = "";
48915
+ let pendingDot = false;
48916
+ const out = [];
48917
+ const pushSentence = (text) => {
48918
+ const trimmed = text.trim();
48919
+ if (!trimmed || !SPEAKABLE_RE.test(trimmed)) return;
48920
+ out.push(trimmed);
48921
+ };
48922
+ const flushSentence = () => {
48923
+ const text = sentence;
48924
+ sentence = "";
48925
+ pushSentence(text);
48926
+ };
48927
+ const splitLongSentence = () => {
48928
+ const from = Math.max(0, sentence.length - 60);
48929
+ let cut = -1;
48930
+ for (let i = sentence.length - 1; i >= from; i--) {
48931
+ if (",,;; ".includes(sentence[i])) {
48932
+ cut = i + 1;
48933
+ break;
48934
+ }
48935
+ }
48936
+ if (cut > 0) {
48937
+ pushSentence(sentence.slice(0, cut));
48938
+ sentence = sentence.slice(cut);
48939
+ } else {
48940
+ pushSentence(sentence.slice(0, MAX_SENTENCE_LEN));
48941
+ sentence = sentence.slice(MAX_SENTENCE_LEN);
48942
+ }
48943
+ };
48944
+ const scanChar = (ch) => {
48945
+ if (pendingDot) {
48946
+ pendingDot = false;
48947
+ if (!/[A-Za-z0-9]/.test(ch)) flushSentence();
48948
+ }
48949
+ if (CJK_TERMINATORS.has(ch) || ch === "!" || ch === "?") {
48950
+ sentence += ch;
48951
+ flushSentence();
48952
+ return;
48953
+ }
48954
+ if (ch === "." && /[A-Za-z]/.test(sentence.charAt(sentence.length - 1))) {
48955
+ sentence += ch;
48956
+ pendingDot = true;
48957
+ return;
48958
+ }
48959
+ sentence += ch;
48960
+ if (sentence.length > MAX_SENTENCE_LEN) splitLongSentence();
48961
+ };
48962
+ const processLine = (line) => {
48963
+ if (FENCE_LINE_RE.test(line)) {
48964
+ inFence = !inFence;
48965
+ return;
48966
+ }
48967
+ if (inFence) return;
48968
+ const sanitized = sanitizeLine(line);
48969
+ if (!SPEAKABLE_RE.test(sanitized)) return;
48970
+ if (sentence && !/\s$/.test(sentence)) sentence += " ";
48971
+ for (const ch of sanitized) scanChar(ch);
48972
+ };
48973
+ const lastCommitableTerminator = (text) => {
48974
+ for (let i = text.length - 1; i >= 0; i--) {
48975
+ const ch = text[i];
48976
+ if (CJK_TERMINATORS.has(ch) || ch === "!" || ch === "?") return i;
48977
+ if (ch === "." && i + 1 < text.length && /\s/.test(text[i + 1])) return i;
48978
+ }
48979
+ return -1;
48980
+ };
48981
+ const drainPartialLine = () => {
48982
+ const cut = lastCommitableTerminator(lineBuf);
48983
+ if (cut === -1) return;
48984
+ processLine(lineBuf.slice(0, cut + 1));
48985
+ lineBuf = lineBuf.slice(cut + 1);
48986
+ };
48987
+ return {
48988
+ feed(chunk) {
48989
+ out.length = 0;
48990
+ lineBuf += chunk;
48991
+ let idx;
48992
+ while ((idx = lineBuf.indexOf("\n")) !== -1) {
48993
+ processLine(lineBuf.slice(0, idx));
48994
+ lineBuf = lineBuf.slice(idx + 1);
48995
+ }
48996
+ drainPartialLine();
48997
+ return out.slice();
48998
+ },
48999
+ end() {
49000
+ out.length = 0;
49001
+ if (!inFence) processLine(lineBuf);
49002
+ lineBuf = "";
49003
+ pendingDot = false;
49004
+ flushSentence();
49005
+ return out.slice();
49006
+ }
49007
+ };
49008
+ }
49009
+ const START_RETRY_COOLDOWN_MS = 3e4;
49010
+ let audioSubscribed = false;
49011
+ let errorListener = null;
49012
+ const MANUAL_KEY = "manual";
49013
+ const suppressedAutoKeys = /* @__PURE__ */ new Set();
49014
+ const erroredSessions = /* @__PURE__ */ new Set();
49015
+ let liveTtsSessionId = null;
49016
+ let lastAutoKey = null;
49017
+ let startFailedAt = 0;
49018
+ let current = null;
49019
+ function ensureAudioSubscription() {
49020
+ if (audioSubscribed) return;
49021
+ audioSubscribed = true;
49022
+ subscribe.on("tts-audio", (detail2) => {
49023
+ if (detail2.ttsSessionId !== liveTtsSessionId) return;
49024
+ if (detail2.format !== "pcm") return;
49025
+ ttsPlayer.enqueueChunk({
49026
+ sessionId: detail2.ttsSessionId,
49027
+ audioB64: detail2.audioB64,
49028
+ sampleRate: detail2.sampleRate ?? 16e3
49029
+ });
49030
+ });
49031
+ subscribe.on("tts-closed", (detail2) => {
49032
+ const session2 = current;
49033
+ if (session2?.ttsSessionId === detail2.ttsSessionId) {
49034
+ session2.ttsSessionId = null;
49035
+ session2.dead = true;
49036
+ }
49037
+ });
49038
+ subscribe.on("tts-error", (detail2) => {
49039
+ const session2 = current;
49040
+ if (!session2 || session2.ttsSessionId !== detail2.ttsSessionId) return;
49041
+ if (erroredSessions.has(detail2.ttsSessionId)) return;
49042
+ erroredSessions.add(detail2.ttsSessionId);
49043
+ const ttsSessionId = detail2.ttsSessionId;
49044
+ session2.dead = true;
49045
+ session2.ttsSessionId = null;
49046
+ current = null;
49047
+ if (liveTtsSessionId === ttsSessionId) liveTtsSessionId = null;
49048
+ startFailedAt = Date.now();
49049
+ ttsPlayer.stop();
49050
+ errorListener?.(detail2.message);
49051
+ void request.endTts({ ttsSessionId }).catch(() => {
49052
+ });
49053
+ });
49054
+ }
49055
+ function onTtsError(listener) {
49056
+ errorListener = listener;
49057
+ }
49058
+ function newTtsSessionId() {
49059
+ return generateUUID().replace(/-/g, "").slice(0, 16);
49060
+ }
49061
+ function appendStep(session2, step) {
49062
+ session2.chain = session2.chain.then(step).catch(() => {
49063
+ });
49064
+ }
49065
+ async function ensureBackendSession(session2) {
49066
+ if (session2.ttsSessionId) return session2.ttsSessionId;
49067
+ if (session2.dead) return null;
49068
+ if (session2.respectCooldown && Date.now() - startFailedAt < START_RETRY_COOLDOWN_MS) return null;
49069
+ const ttsSessionId = newTtsSessionId();
49070
+ try {
49071
+ await request.startTts({ ttsSessionId });
49072
+ } catch (error) {
49073
+ session2.dead = true;
49074
+ erroredSessions.add(ttsSessionId);
49075
+ if (session2.respectCooldown) startFailedAt = Date.now();
49076
+ errorListener?.(error instanceof Error ? error.message : String(error));
49077
+ return null;
49078
+ }
49079
+ session2.ttsSessionId = ttsSessionId;
49080
+ if (session2.dead) return null;
49081
+ liveTtsSessionId = ttsSessionId;
49082
+ return ttsSessionId;
49083
+ }
49084
+ function appendSentence(session2, sentence) {
49085
+ appendStep(session2, async () => {
49086
+ if (session2.dead) return;
49087
+ const ttsSessionId = await ensureBackendSession(session2);
49088
+ if (!ttsSessionId) return;
49089
+ await request.speakTts({ ttsSessionId, text: sentence });
49090
+ });
49091
+ }
49092
+ function appendEnd(session2) {
49093
+ appendStep(session2, async () => {
49094
+ const ttsSessionId = session2.ttsSessionId;
49095
+ session2.ttsSessionId = null;
49096
+ if (!ttsSessionId) return;
49097
+ try {
49098
+ await request.endTts({ ttsSessionId });
49099
+ } finally {
49100
+ erroredSessions.delete(ttsSessionId);
49101
+ }
49102
+ });
49103
+ }
49104
+ function endSession(session2, options = {}) {
49105
+ if (session2.ended) return;
49106
+ session2.ended = true;
49107
+ if (!options.flushTail) {
49108
+ session2.dead = true;
49109
+ liveTtsSessionId = null;
49110
+ }
49111
+ if (options.flushTail && !session2.dead) {
49112
+ for (const sentence of session2.splitter.end()) appendSentence(session2, sentence);
49113
+ }
49114
+ appendEnd(session2);
49115
+ if (current === session2) current = null;
49116
+ }
49117
+ function startSession(key, respectCooldown) {
49118
+ liveTtsSessionId = null;
49119
+ if (current) endSession(current);
49120
+ ttsPlayer.stop();
49121
+ const session2 = {
49122
+ key,
49123
+ splitter: createTtsTextSplitter(),
49124
+ ttsSessionId: null,
49125
+ chain: Promise.resolve(),
49126
+ ended: false,
49127
+ dead: false,
49128
+ respectCooldown
49129
+ };
49130
+ current = session2;
49131
+ return session2;
49132
+ }
49133
+ function getOrCreateAutoSession(key) {
49134
+ if (current && current.key === key && !current.ended) return current;
49135
+ return startSession(key, true);
49136
+ }
49137
+ function feedTtsStream(key, rawText) {
49138
+ ensureAudioSubscription();
49139
+ lastAutoKey = key;
49140
+ if (suppressedAutoKeys.has(key)) return;
49141
+ const session2 = getOrCreateAutoSession(key);
49142
+ for (const sentence of session2.splitter.feed(rawText)) appendSentence(session2, sentence);
49143
+ }
49144
+ function endTtsStream(key) {
49145
+ suppressedAutoKeys.delete(key);
49146
+ const session2 = current;
49147
+ if (!session2 || session2.key !== key) return;
49148
+ endSession(session2, { flushTail: true });
49149
+ }
49150
+ function speakOnce(rawText) {
49151
+ ensureAudioSubscription();
49152
+ if (lastAutoKey) suppressedAutoKeys.add(lastAutoKey);
49153
+ const session2 = startSession(MANUAL_KEY, false);
49154
+ for (const sentence of session2.splitter.feed(rawText)) appendSentence(session2, sentence);
49155
+ endSession(session2, { flushTail: true });
49156
+ }
49157
+ function leaveTtsSession() {
49158
+ stopActiveTts();
49159
+ if (lastAutoKey) suppressedAutoKeys.delete(lastAutoKey);
49160
+ lastAutoKey = null;
49161
+ }
49162
+ function resetTtsForNewPrompt(key) {
49163
+ stopActiveTts();
49164
+ suppressedAutoKeys.delete(key);
49165
+ }
49166
+ function stopActiveTts() {
49167
+ if (lastAutoKey) suppressedAutoKeys.add(lastAutoKey);
49168
+ liveTtsSessionId = null;
49169
+ ttsPlayer.stop();
49170
+ if (current) endSession(current);
49171
+ }
47849
49172
  var h = { name: "soft", baseFreq: 440, noiseColor: "pink", oscType: "sine", filterFreq: 3e3, filterQ: 2, attack: 2e-3, decay: 1, brightness: 2e3 }, D$1 = { name: "crisp", baseFreq: 523, noiseColor: "white", oscType: "triangle", filterFreq: 5e3, filterQ: 3, attack: 15e-4, decay: 0.6, brightness: 4e3 }, L$1 = { name: "arcade", baseFreq: 392, noiseColor: "white", oscType: "square", filterFreq: 2200, filterQ: 5, attack: 1e-3, decay: 0.5, brightness: 2800 }, P = { name: "glass", baseFreq: 660, noiseColor: "pink", oscType: "sine", filterFreq: 6500, filterQ: 6, attack: 4e-3, decay: 1.4, brightness: 5500 }, Q = { soft: h, crisp: D$1, arcade: L$1, glass: P };
47850
49173
  function v$1(n) {
47851
49174
  if (typeof n == "string") {
@@ -48138,26 +49461,25 @@ function D(t) {
48138
49461
  }
48139
49462
  const UPDATE_TOAST_ID = "fello-app-update";
48140
49463
  function AppContent() {
48141
- const {
48142
- setSessions,
48143
- setProjects,
48144
- setConfiguredAgents,
48145
- setConfiguredMcpServers,
48146
- setWebUIStatus,
48147
- setTheme,
48148
- setI18n: setI18n2,
48149
- setEditor,
48150
- setSnippets,
48151
- setImageGeneration,
48152
- setSpeechToText,
48153
- setVoiceInput,
48154
- setShortcuts,
48155
- setProxy,
48156
- isMacApp,
48157
- setIsFullScreen,
48158
- setIlinkStatus,
48159
- setActiveIlinkSessionId
48160
- } = useAppStore();
49464
+ const isMacApp = useAppStore((s) => s.isMacApp);
49465
+ const setSessions = useAppStore((s) => s.setSessions);
49466
+ const setProjects = useAppStore((s) => s.setProjects);
49467
+ const setConfiguredAgents = useAppStore((s) => s.setConfiguredAgents);
49468
+ const setConfiguredMcpServers = useAppStore((s) => s.setConfiguredMcpServers);
49469
+ const setWebUIStatus = useAppStore((s) => s.setWebUIStatus);
49470
+ const setTheme = useAppStore((s) => s.setTheme);
49471
+ const setI18n2 = useAppStore((s) => s.setI18n);
49472
+ const setEditor = useAppStore((s) => s.setEditor);
49473
+ const setSnippets = useAppStore((s) => s.setSnippets);
49474
+ const setImageGeneration = useAppStore((s) => s.setImageGeneration);
49475
+ const setSpeechProviders = useAppStore((s) => s.setSpeechProviders);
49476
+ const setVoiceInput = useAppStore((s) => s.setVoiceInput);
49477
+ const setShortcuts = useAppStore((s) => s.setShortcuts);
49478
+ const setPeripherals = useAppStore((s) => s.setPeripherals);
49479
+ const setProxy = useAppStore((s) => s.setProxy);
49480
+ const setIsFullScreen = useAppStore((s) => s.setIsFullScreen);
49481
+ const setIlinkStatus = useAppStore((s) => s.setIlinkStatus);
49482
+ const setActiveIlinkSessionId = useAppStore((s) => s.setActiveIlinkSessionId);
48161
49483
  const shortcuts = useAppStore((state) => state.shortcuts);
48162
49484
  const { resolvedTheme } = z();
48163
49485
  const { i18n, t } = useTranslation();
@@ -48172,6 +49494,12 @@ function AppContent() {
48172
49494
  reactExports.useEffect(() => {
48173
49495
  activeSessionIdRef.current = activeSessionId;
48174
49496
  }, [activeSessionId]);
49497
+ reactExports.useEffect(() => {
49498
+ onTtsError((message2) => {
49499
+ toast2.error(message2);
49500
+ });
49501
+ return () => onTtsError(null);
49502
+ }, [toast2]);
48175
49503
  const navigate = useNavigate();
48176
49504
  const [isReady, setIsReady] = reactExports.useState(false);
48177
49505
  const pendingSessionNotificationRef = reactExports.useRef(
@@ -48209,9 +49537,10 @@ function AppContent() {
48209
49537
  if (settings2.editor) setEditor(settings2.editor);
48210
49538
  if (settings2.snippets) setSnippets(settings2.snippets);
48211
49539
  if (settings2.imageGeneration) setImageGeneration(settings2.imageGeneration);
48212
- if (settings2.speechToText) setSpeechToText(settings2.speechToText);
49540
+ if (settings2.speechProviders) setSpeechProviders(settings2.speechProviders);
48213
49541
  if (settings2.voiceInput) setVoiceInput(settings2.voiceInput);
48214
49542
  if (settings2.shortcuts) setShortcuts(settings2.shortcuts);
49543
+ if (settings2.peripherals) setPeripherals(settings2.peripherals);
48215
49544
  for (const session2 of sessions ?? []) {
48216
49545
  try {
48217
49546
  const pending = await request.getPendingAskUserRequests({ sessionId: session2.id });
@@ -48244,9 +49573,10 @@ function AppContent() {
48244
49573
  setEditor,
48245
49574
  setSnippets,
48246
49575
  setImageGeneration,
48247
- setSpeechToText,
49576
+ setSpeechProviders,
48248
49577
  setVoiceInput,
48249
49578
  setShortcuts,
49579
+ setPeripherals,
48250
49580
  setProxy,
48251
49581
  setIlinkStatus,
48252
49582
  setActiveIlinkSessionId
@@ -48570,7 +49900,10 @@ function AppContent() {
48570
49900
  ] });
48571
49901
  }
48572
49902
  function App() {
48573
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HashRouter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(FocusTargetProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(AppContent, {}) }) }) }) }) });
49903
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HashRouter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(FocusTargetProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(VoicePanelProvider, { children: [
49904
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AppContent, {}),
49905
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PeripheralRuntime, {})
49906
+ ] }) }) }) }) }) });
48574
49907
  }
48575
49908
  clientExports.createRoot(document.getElementById("root")).render(
48576
49909
  /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) })
@@ -48608,201 +49941,222 @@ export {
48608
49941
  ClockCheck as Z,
48609
49942
  Badge as _,
48610
49943
  useAppStore as a,
48611
- useValueChanged as a$,
49944
+ useHoverReferenceInteraction as a$,
48612
49945
  CircleCheck as a0,
48613
- Bot as a1,
48614
- Globe as a2,
48615
- MessageCircle as a3,
48616
- SelectGroup as a4,
48617
- SelectLabel as a5,
48618
- RefreshCw as a6,
48619
- ChevronRight as a7,
48620
- cva as a8,
48621
- Separator as a9,
48622
- transitionStatusMapping as aA,
48623
- useRenderElement as aB,
48624
- useButton as aC,
48625
- useMergedRefs as aD,
48626
- useValueAsRef as aE,
48627
- useAnimationsFinished as aF,
48628
- useIsoLayoutEffect as aG,
48629
- useOpenChangeComplete as aH,
48630
- AnimationFrame as aI,
48631
- addEventListener as aJ,
48632
- getWindow as aK,
48633
- none as aL,
48634
- resolveStyle as aM,
48635
- webUIBaseUrl as aN,
48636
- reduceSessionNotification as aO,
48637
- reduceFlushStreaming as aP,
48638
- Clock as aQ,
48639
- Folder as aR,
48640
- shimExports as aS,
48641
- NOOP as aT,
48642
- useCSPContext as aU,
48643
- clamp as aV,
48644
- fieldValidityMapping as aW,
48645
- useFormContext as aX,
48646
- useFieldRootContext as aY,
48647
- useLabelableContext as aZ,
48648
- useRegisterFieldControl as a_,
48649
- useRender as aa,
48650
- mergeProps as ab,
48651
- resetAgent as ac,
48652
- clearAgentSessions as ad,
48653
- updateAgentSettings as ae,
48654
- ALL_FEATURES as af,
48655
- FEATURE_I18N_KEYS as ag,
48656
- Tabs as ah,
48657
- TabsList as ai,
48658
- TabsTrigger as aj,
48659
- COMMAND_DEFINITIONS as ak,
48660
- getCommandShortcuts as al,
48661
- shortcutPreviewFromKeyboardEvent as am,
48662
- shortcutFromKeyboardEvent as an,
48663
- findShortcutConflicts as ao,
48664
- React as ap,
48665
- reactDomExports as aq,
48666
- getDefaultExportFromCjs as ar,
48667
- useControlled as as,
48668
- useTransitionStatus as at,
48669
- useBaseUiId as au,
48670
- useStableCallback as av,
48671
- createChangeEventDetails as aw,
48672
- triggerPress as ax,
49946
+ ChevronRight as a1,
49947
+ EDITOR_LABELS as a2,
49948
+ L as a3,
49949
+ N as a4,
49950
+ _ as a5,
49951
+ D as a6,
49952
+ w as a7,
49953
+ SelectGroup as a8,
49954
+ SelectLabel as a9,
49955
+ popupStoreSelectors as aA,
49956
+ triggerHover as aB,
49957
+ triggerPress as aC,
49958
+ escapeKey as aD,
49959
+ attachPreventUnmountOnClose as aE,
49960
+ closePress as aF,
49961
+ PATIENT_CLICK_THRESHOLD as aG,
49962
+ focusOut as aH,
49963
+ createPopupOpenState as aI,
49964
+ createInitialPopupStoreState as aJ,
49965
+ Timeout as aK,
49966
+ PopupTriggerMap as aL,
49967
+ FloatingTree as aM,
49968
+ fastComponent as aN,
49969
+ usePopupRootSync as aO,
49970
+ useImplicitActiveTrigger as aP,
49971
+ useOpenStateTransitions as aQ,
49972
+ createChangeEventDetails as aR,
49973
+ imperativeAction as aS,
49974
+ PopupHandleAttachment as aT,
49975
+ usePopupRootStore as aU,
49976
+ useDismiss as aV,
49977
+ usePopupInteractionProps as aW,
49978
+ fastComponentRef as aX,
49979
+ usePopupHandleStore as aY,
49980
+ useBaseUiId as aZ,
49981
+ useTriggerDataForwarding as a_,
49982
+ RefreshCw as aa,
49983
+ cva as ab,
49984
+ Separator as ac,
49985
+ useRender as ad,
49986
+ mergeProps as ae,
49987
+ Bot as af,
49988
+ Globe as ag,
49989
+ MessageCircle as ah,
49990
+ resetAgent as ai,
49991
+ clearAgentSessions as aj,
49992
+ updateAgentSettings as ak,
49993
+ React as al,
49994
+ COMMAND_DEFINITIONS as am,
49995
+ getCommandShortcuts as an,
49996
+ shortcutPreviewFromKeyboardEvent as ao,
49997
+ shortcutFromKeyboardEvent as ap,
49998
+ findShortcutConflicts as aq,
49999
+ reactDomExports as ar,
50000
+ isPeripheralRuntimeAvailable as as,
50001
+ Info as at,
50002
+ BUILTIN_PERIPHERALS as au,
50003
+ RENDERER_PLATFORM as av,
50004
+ isPeripheralSupportedOnPlatform as aw,
50005
+ getDefaultExportFromCjs as ax,
48673
50006
  formatErrorMessage as ay,
48674
- startingStyle as az,
50007
+ ReactStore as az,
48675
50008
  useMessage as b,
48676
- useOpenMethodTriggerProps as b$,
48677
- areArraysEqual as b0,
48678
- keyboard as b1,
48679
- inputChange as b2,
48680
- createGenericEventDetails as b3,
48681
- activeElement as b4,
48682
- ownerDocument as b5,
48683
- contains as b6,
48684
- CompositeList as b7,
48685
- getDefaultLabelId as b8,
48686
- resolveAriaLabelledBy as b9,
48687
- popupStoreSelectors as bA,
48688
- triggerHover as bB,
48689
- escapeKey as bC,
48690
- attachPreventUnmountOnClose as bD,
48691
- closePress as bE,
48692
- PATIENT_CLICK_THRESHOLD as bF,
48693
- focusOut as bG,
48694
- createPopupOpenState as bH,
48695
- createInitialPopupStoreState as bI,
48696
- Timeout as bJ,
48697
- PopupTriggerMap as bK,
48698
- FloatingTree as bL,
48699
- fastComponent as bM,
48700
- usePopupRootSync as bN,
48701
- useImplicitActiveTrigger as bO,
48702
- useOpenStateTransitions as bP,
48703
- imperativeAction as bQ,
48704
- PopupHandleAttachment as bR,
48705
- usePopupRootStore as bS,
48706
- useDismiss as bT,
48707
- usePopupInteractionProps as bU,
48708
- fastComponentRef as bV,
48709
- usePopupHandleStore as bW,
48710
- useTriggerDataForwarding as bX,
48711
- useHoverReferenceInteraction as bY,
48712
- safePolygon as bZ,
48713
- useClick as b_,
48714
- useDirection as ba,
48715
- getTarget as bb,
48716
- useAnimationFrame as bc,
48717
- isElement as bd,
48718
- drag as be,
48719
- trackPress as bf,
48720
- useLabelableId as bg,
48721
- useCompositeListItem as bh,
48722
- visuallyHidden as bi,
48723
- COMPOSITE_KEYS as bj,
48724
- PAGE_UP as bk,
48725
- PAGE_DOWN as bl,
48726
- HOME as bm,
48727
- END as bn,
48728
- ARROW_LEFT as bo,
48729
- ARROW_DOWN as bp,
48730
- ARROW_RIGHT as bq,
48731
- ARROW_UP as br,
48732
- matchesFocusVisible as bs,
48733
- EDITOR_LABELS as bt,
48734
- L as bu,
48735
- N as bv,
48736
- _ as bw,
48737
- D as bx,
48738
- w as by,
48739
- ReactStore as bz,
50009
+ inputChange as b$,
50010
+ safePolygon as b0,
50011
+ useClick as b1,
50012
+ useOpenMethodTriggerProps as b2,
50013
+ useButton as b3,
50014
+ useTriggerFocusGuards as b4,
50015
+ useRenderElement as b5,
50016
+ CLICK_TRIGGER_IDENTIFIER as b6,
50017
+ FocusGuard as b7,
50018
+ pressableTriggerOpenStateMapping as b8,
50019
+ triggerOpenStateMapping as b9,
50020
+ useMergedRefs as bA,
50021
+ useValueAsRef as bB,
50022
+ AnimationFrame as bC,
50023
+ addEventListener as bD,
50024
+ getWindow as bE,
50025
+ none as bF,
50026
+ resolveStyle as bG,
50027
+ Tabs as bH,
50028
+ TabsList as bI,
50029
+ TabsTrigger as bJ,
50030
+ webUIBaseUrl as bK,
50031
+ reduceSessionNotification as bL,
50032
+ reduceFlushStreaming as bM,
50033
+ Clock as bN,
50034
+ Folder as bO,
50035
+ shimExports as bP,
50036
+ NOOP as bQ,
50037
+ useCSPContext as bR,
50038
+ clamp as bS,
50039
+ fieldValidityMapping as bT,
50040
+ useFormContext as bU,
50041
+ useFieldRootContext as bV,
50042
+ useLabelableContext as bW,
50043
+ useRegisterFieldControl as bX,
50044
+ useValueChanged as bY,
50045
+ areArraysEqual as bZ,
50046
+ keyboard as b_,
50047
+ FloatingPortal as ba,
50048
+ POPUP_COLLISION_AVOIDANCE as bb,
50049
+ useFloatingNodeId as bc,
50050
+ useAnimationsFinished as bd,
50051
+ useAnchorPositioning as be,
50052
+ useIsoLayoutEffect as bf,
50053
+ useAnchoredPopupScrollLock as bg,
50054
+ usePositioner as bh,
50055
+ InternalBackdrop as bi,
50056
+ inertValue as bj,
50057
+ FloatingNode as bk,
50058
+ useStableCallback as bl,
50059
+ useToolbarRootContext as bm,
50060
+ useOpenChangeComplete as bn,
50061
+ useHoverFloatingInteraction as bo,
50062
+ createDefaultInitialFocus as bp,
50063
+ FOCUSABLE_POPUP_PROPS as bq,
50064
+ COMPOSITE_KEYS as br,
50065
+ getDisabledMountTransitionStyles as bs,
50066
+ FloatingFocusManager as bt,
50067
+ isHTMLElement as bu,
50068
+ popupTransitionStateMapping as bv,
50069
+ useControlled as bw,
50070
+ useTransitionStatus as bx,
50071
+ startingStyle as by,
50072
+ transitionStatusMapping as bz,
48740
50073
  createLucideIcon as c,
48741
- getAugmentedNamespace as c$,
48742
- useTriggerFocusGuards as c0,
48743
- CLICK_TRIGGER_IDENTIFIER as c1,
48744
- FocusGuard as c2,
48745
- pressableTriggerOpenStateMapping as c3,
48746
- triggerOpenStateMapping as c4,
48747
- FloatingPortal as c5,
48748
- POPUP_COLLISION_AVOIDANCE as c6,
48749
- useFloatingNodeId as c7,
48750
- useAnchorPositioning as c8,
48751
- useAnchoredPopupScrollLock as c9,
48752
- v as cA,
48753
- HoverCard as cB,
48754
- HoverCardTrigger as cC,
48755
- HoverCardContent as cD,
48756
- X as cE,
48757
- DropdownMenuSub as cF,
48758
- DropdownMenuSubTrigger as cG,
48759
- DropdownMenuSubContent as cH,
48760
- CircleQuestionMark as cI,
48761
- ChevronUp as cJ,
48762
- z as cK,
48763
- __vitePreload as cL,
48764
- Code as cM,
48765
- MessageSquare as cN,
48766
- FolderPlus as cO,
48767
- Eye as cP,
48768
- useProjectState as cQ,
48769
- useFocusTargetRegistry as cR,
48770
- TabsContent as cS,
48771
- loadSession as cT,
48772
- Card as cU,
48773
- getBasename as cV,
48774
- isSubPath as cW,
48775
- formatBytes as cX,
48776
- isImageMimeType as cY,
48777
- twMerge as cZ,
48778
- clsx as c_,
48779
- usePositioner as ca,
48780
- InternalBackdrop as cb,
48781
- inertValue as cc,
48782
- FloatingNode as cd,
48783
- useToolbarRootContext as ce,
48784
- useHoverFloatingInteraction as cf,
48785
- createDefaultInitialFocus as cg,
48786
- FOCUSABLE_POPUP_PROPS as ch,
48787
- getDisabledMountTransitionStyles as ci,
48788
- FloatingFocusManager as cj,
48789
- isHTMLElement as ck,
48790
- popupTransitionStateMapping as cl,
48791
- formatUpdatedTime as cm,
48792
- buttonVariants as cn,
48793
- restartSession as co,
48794
- SessionLifecycleBusyError as cp,
48795
- closeSession as cq,
48796
- RestartSessionError as cr,
48797
- Tooltip as cs,
48798
- TooltipTrigger as ct,
48799
- TooltipContent as cu,
48800
- useFocusTarget as cv,
48801
- formatDuration as cw,
48802
- ReactDOM as cx,
48803
- clientId as cy,
48804
- ChevronDown as cz,
50074
+ Code as c$,
50075
+ createGenericEventDetails as c0,
50076
+ activeElement as c1,
50077
+ ownerDocument as c2,
50078
+ contains as c3,
50079
+ CompositeList as c4,
50080
+ getDefaultLabelId as c5,
50081
+ resolveAriaLabelledBy as c6,
50082
+ useDirection as c7,
50083
+ getTarget as c8,
50084
+ useAnimationFrame as c9,
50085
+ TooltipContent as cA,
50086
+ useFocusTarget as cB,
50087
+ formatDuration as cC,
50088
+ speakOnce as cD,
50089
+ ReactDOM as cE,
50090
+ useRealtimeAsr as cF,
50091
+ ChevronDown as cG,
50092
+ useVoicePanelTarget as cH,
50093
+ HoverCard as cI,
50094
+ HoverCardTrigger as cJ,
50095
+ HoverCardContent as cK,
50096
+ X as cL,
50097
+ useFocusTargetRegistry as cM,
50098
+ v as cN,
50099
+ DropdownMenuSub as cO,
50100
+ DropdownMenuSubTrigger as cP,
50101
+ DropdownMenuSubContent as cQ,
50102
+ CircleQuestionMark as cR,
50103
+ ChevronUp as cS,
50104
+ leaveTtsSession as cT,
50105
+ feedTtsStream as cU,
50106
+ resetTtsForNewPrompt as cV,
50107
+ endTtsStream as cW,
50108
+ AUDIO_EXTENSIONS as cX,
50109
+ VIDEO_EXTENSIONS as cY,
50110
+ z as cZ,
50111
+ __vitePreload as c_,
50112
+ isElement as ca,
50113
+ drag as cb,
50114
+ trackPress as cc,
50115
+ useLabelableId as cd,
50116
+ useCompositeListItem as ce,
50117
+ visuallyHidden as cf,
50118
+ PAGE_UP as cg,
50119
+ PAGE_DOWN as ch,
50120
+ HOME as ci,
50121
+ END as cj,
50122
+ ARROW_LEFT as ck,
50123
+ ARROW_DOWN as cl,
50124
+ ARROW_RIGHT as cm,
50125
+ ARROW_UP as cn,
50126
+ matchesFocusVisible as co,
50127
+ SessionConfigFields as cp,
50128
+ useTtsPlaybackStore as cq,
50129
+ stopActiveTts as cr,
50130
+ useTtsPrefsStore as cs,
50131
+ buttonVariants as ct,
50132
+ restartSession as cu,
50133
+ SessionLifecycleBusyError as cv,
50134
+ closeSession as cw,
50135
+ RestartSessionError as cx,
50136
+ Tooltip as cy,
50137
+ TooltipTrigger as cz,
48805
50138
  Switch as d,
50139
+ MessageSquare as d0,
50140
+ isContextMenuKey as d1,
50141
+ openContextMenuFromKeyboard as d2,
50142
+ FolderPlus as d3,
50143
+ Eye as d4,
50144
+ useProjectState as d5,
50145
+ clientId as d6,
50146
+ TabsContent as d7,
50147
+ loadSession as d8,
50148
+ TriangleAlert as d9,
50149
+ Card as da,
50150
+ getBasename as db,
50151
+ isSubPath as dc,
50152
+ formatBytes as dd,
50153
+ isImageMimeType as de,
50154
+ getMediaKind as df,
50155
+ ALL_FEATURES as dg,
50156
+ FEATURE_I18N_KEYS as dh,
50157
+ twMerge as di,
50158
+ clsx as dj,
50159
+ getAugmentedNamespace as dk,
48806
50160
  Copy as e,
48807
50161
  request as f,
48808
50162
  extractErrorMessage as g,