@zythum02/fello-server 0.1.52 → 0.1.54

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 (382) hide show
  1. package/out/renderer/assets/{_baseUniq-C0L-SxO8.js → _baseUniq-DYINnBRJ.js} +1 -1
  2. package/out/renderer/assets/_baseUniq-DYINnBRJ.js.br +0 -0
  3. package/out/renderer/assets/_baseUniq-DYINnBRJ.js.gz +0 -0
  4. package/out/renderer/assets/{arc-bnlMIyYF.js → arc-BSUyw88k.js} +1 -1
  5. package/out/renderer/assets/arc-BSUyw88k.js.br +0 -0
  6. package/out/renderer/assets/arc-BSUyw88k.js.gz +0 -0
  7. package/out/renderer/assets/{architectureDiagram-Q4EWVU46-Cr0249Lu.js → architectureDiagram-Q4EWVU46-Cxb8DhYZ.js} +6 -6
  8. package/out/renderer/assets/architectureDiagram-Q4EWVU46-Cxb8DhYZ.js.br +0 -0
  9. package/out/renderer/assets/architectureDiagram-Q4EWVU46-Cxb8DhYZ.js.gz +0 -0
  10. package/out/renderer/assets/{arrow-left-D589pHHA.js → arrow-left-CQqe9rrj.js} +1 -1
  11. package/out/renderer/assets/arrow-up-1eI4tva4.js +21 -0
  12. package/out/renderer/assets/{automation-CvjrkE9z.js → automation-CQAOpWYp.js} +7 -8
  13. package/out/renderer/assets/automation-CQAOpWYp.js.br +0 -0
  14. package/out/renderer/assets/automation-CQAOpWYp.js.gz +0 -0
  15. package/out/renderer/assets/{blockDiagram-DXYQGD6D-Cw6vh5dx.js → blockDiagram-DXYQGD6D-CFozis-v.js} +7 -7
  16. package/out/renderer/assets/blockDiagram-DXYQGD6D-CFozis-v.js.br +0 -0
  17. package/out/renderer/assets/blockDiagram-DXYQGD6D-CFozis-v.js.gz +0 -0
  18. package/out/renderer/assets/{c4Diagram-AHTNJAMY-BUrvcyv2.js → c4Diagram-AHTNJAMY-CcDDkZUp.js} +3 -3
  19. package/out/renderer/assets/c4Diagram-AHTNJAMY-CcDDkZUp.js.br +0 -0
  20. package/out/renderer/assets/c4Diagram-AHTNJAMY-CcDDkZUp.js.gz +0 -0
  21. package/out/renderer/assets/{channel-DQr36qW5.js → channel-B-VC_ZcK.js} +1 -1
  22. package/out/renderer/assets/{chunk-4BX2VUAB-DOsXCDD7.js → chunk-4BX2VUAB-ClHmnOTC.js} +1 -1
  23. package/out/renderer/assets/{chunk-4TB4RGXK-D_fBnf0p.js → chunk-4TB4RGXK-mwOvn19G.js} +5 -5
  24. package/out/renderer/assets/chunk-4TB4RGXK-mwOvn19G.js.br +0 -0
  25. package/out/renderer/assets/chunk-4TB4RGXK-mwOvn19G.js.gz +0 -0
  26. package/out/renderer/assets/{chunk-55IACEB6-3ETNuedT.js → chunk-55IACEB6-CE8f6iip.js} +1 -1
  27. package/out/renderer/assets/{chunk-EDXVE4YY-BJLqHaER.js → chunk-EDXVE4YY-CU7aY96h.js} +1 -1
  28. package/out/renderer/assets/chunk-EDXVE4YY-CU7aY96h.js.br +0 -0
  29. package/out/renderer/assets/chunk-EDXVE4YY-CU7aY96h.js.gz +0 -0
  30. package/out/renderer/assets/{chunk-FMBD7UC4-DP-WGTNG.js → chunk-FMBD7UC4-B38kFkjv.js} +1 -1
  31. package/out/renderer/assets/{chunk-OYMX7WX6-CrS7loi7.js → chunk-OYMX7WX6-BriTCtaz.js} +3 -3
  32. package/out/renderer/assets/chunk-OYMX7WX6-BriTCtaz.js.br +0 -0
  33. package/out/renderer/assets/{chunk-OYMX7WX6-CrS7loi7.js.gz → chunk-OYMX7WX6-BriTCtaz.js.gz} +0 -0
  34. package/out/renderer/assets/{chunk-QZHKN3VN-C53USs5W.js → chunk-QZHKN3VN-BwJ37p5G.js} +1 -1
  35. package/out/renderer/assets/{chunk-YZCP3GAM-CMgdytZR.js → chunk-YZCP3GAM-BlHAYoQe.js} +1 -1
  36. package/out/renderer/assets/chunk-YZCP3GAM-BlHAYoQe.js.br +0 -0
  37. package/out/renderer/assets/chunk-YZCP3GAM-BlHAYoQe.js.gz +0 -0
  38. package/out/renderer/assets/{circle-x-OvXymUrf.js → circle-x-D_gJIgxf.js} +1 -1
  39. package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-DIYtEiO5.js → classDiagram-6PBFFD2Q-D01ZMeaj.js} +7 -7
  40. package/out/renderer/assets/{classDiagram-6PBFFD2Q-DIYtEiO5.js → classDiagram-v2-HSJHXN6E-D01ZMeaj.js} +7 -7
  41. package/out/renderer/assets/{clone-D45P7Iqc.js → clone-qctAbdCx.js} +1 -1
  42. package/out/renderer/assets/{code-editor-SuOcN-5B.js → code-editor-tm0AYOLz.js} +2 -2
  43. package/out/renderer/assets/code-editor-tm0AYOLz.js.br +0 -0
  44. package/out/renderer/assets/code-editor-tm0AYOLz.js.gz +0 -0
  45. package/out/renderer/assets/{collapsible-Bd85ZCyx.js → collapsible-DD6NlDid.js} +1 -1
  46. package/out/renderer/assets/collapsible-DD6NlDid.js.br +0 -0
  47. package/out/renderer/assets/collapsible-DD6NlDid.js.gz +0 -0
  48. package/out/renderer/assets/command-BF4jOeoL.js +11 -0
  49. package/out/renderer/assets/{cose-bilkent-S5V4N54A-Dq8VllNT.js → cose-bilkent-S5V4N54A-CF4brbsf.js} +2 -2
  50. package/out/renderer/assets/cose-bilkent-S5V4N54A-CF4brbsf.js.br +0 -0
  51. package/out/renderer/assets/cose-bilkent-S5V4N54A-CF4brbsf.js.gz +0 -0
  52. package/out/renderer/assets/{dagre-KV5264BT-Dec8q4UT.js → dagre-KV5264BT-C6I5VgRp.js} +7 -7
  53. package/out/renderer/assets/dagre-KV5264BT-C6I5VgRp.js.br +0 -0
  54. package/out/renderer/assets/dagre-KV5264BT-C6I5VgRp.js.gz +0 -0
  55. package/out/renderer/assets/{diagram-5BDNPKRD-CzA2u_EN.js → diagram-5BDNPKRD-BAO72D5R.js} +7 -7
  56. package/out/renderer/assets/diagram-5BDNPKRD-BAO72D5R.js.br +0 -0
  57. package/out/renderer/assets/diagram-5BDNPKRD-BAO72D5R.js.gz +0 -0
  58. package/out/renderer/assets/{diagram-G4DWMVQ6-C8h3R7jD.js → diagram-G4DWMVQ6-BmpkrtJs.js} +7 -7
  59. package/out/renderer/assets/diagram-G4DWMVQ6-BmpkrtJs.js.br +0 -0
  60. package/out/renderer/assets/diagram-G4DWMVQ6-BmpkrtJs.js.gz +0 -0
  61. package/out/renderer/assets/{diagram-MMDJMWI5-9tiXSF90.js → diagram-MMDJMWI5-8keZZjgB.js} +6 -6
  62. package/out/renderer/assets/diagram-MMDJMWI5-8keZZjgB.js.br +0 -0
  63. package/out/renderer/assets/diagram-MMDJMWI5-8keZZjgB.js.gz +0 -0
  64. package/out/renderer/assets/{diagram-TYMM5635-BrarGJNf.js → diagram-TYMM5635-Be4uDvHI.js} +6 -6
  65. package/out/renderer/assets/diagram-TYMM5635-Be4uDvHI.js.br +0 -0
  66. package/out/renderer/assets/diagram-TYMM5635-Be4uDvHI.js.gz +0 -0
  67. package/out/renderer/assets/{ellipsis-BafkhboM.js → ellipsis-92s4sSUO.js} +1 -1
  68. package/out/renderer/assets/{erDiagram-SMLLAGMA-1Q9StK7L.js → erDiagram-SMLLAGMA-C6IgAbhi.js} +5 -5
  69. package/out/renderer/assets/erDiagram-SMLLAGMA-C6IgAbhi.js.br +0 -0
  70. package/out/renderer/assets/erDiagram-SMLLAGMA-C6IgAbhi.js.gz +0 -0
  71. package/out/renderer/assets/{field-ohYLdnJa.js → field-BPsDfe4e.js} +1 -1
  72. package/out/renderer/assets/field-BPsDfe4e.js.br +0 -0
  73. package/out/renderer/assets/field-BPsDfe4e.js.gz +0 -0
  74. package/out/renderer/assets/{file-text-CshHuHgW.js → file-text-ITbWr8iW.js} +1 -1
  75. package/out/renderer/assets/{flowDiagram-DWJPFMVM-CpcXVOmj.js → flowDiagram-DWJPFMVM-vbYKBn4t.js} +7 -7
  76. package/out/renderer/assets/flowDiagram-DWJPFMVM-vbYKBn4t.js.br +0 -0
  77. package/out/renderer/assets/flowDiagram-DWJPFMVM-vbYKBn4t.js.gz +0 -0
  78. package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-C7Da4JBG.js → ganttDiagram-T4ZO3ILL-AxXIe9I4.js} +3 -3
  79. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-AxXIe9I4.js.br +0 -0
  80. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-AxXIe9I4.js.gz +0 -0
  81. package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-COBL52q_.js → gitGraphDiagram-UUTBAWPF-DSipARMB.js} +7 -7
  82. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-DSipARMB.js.br +0 -0
  83. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-DSipARMB.js.gz +0 -0
  84. package/out/renderer/assets/{graph-vKaAs6sc.js → graph-mNaHfg3r.js} +2 -2
  85. package/out/renderer/assets/graph-mNaHfg3r.js.br +0 -0
  86. package/out/renderer/assets/graph-mNaHfg3r.js.gz +0 -0
  87. package/out/renderer/assets/{highlighted-body-OFNGDK62-BGNnB1Ll.js → highlighted-body-OFNGDK62-8846XFj-.js} +2 -2
  88. package/out/renderer/assets/{index-BJPggXxf.js → index-BUwXrbHW.js} +1979 -663
  89. package/out/renderer/assets/index-BUwXrbHW.js.br +0 -0
  90. package/out/renderer/assets/index-BUwXrbHW.js.gz +0 -0
  91. package/out/renderer/assets/index-DOIzYXAl.css +382 -0
  92. package/out/renderer/assets/index-DOIzYXAl.css.br +0 -0
  93. package/out/renderer/assets/index-DOIzYXAl.css.gz +0 -0
  94. package/out/renderer/assets/{infoDiagram-42DDH7IO-z37e1cYq.js → infoDiagram-42DDH7IO-ThYxaM6U.js} +5 -5
  95. package/out/renderer/assets/infoDiagram-42DDH7IO-ThYxaM6U.js.br +0 -0
  96. package/out/renderer/assets/infoDiagram-42DDH7IO-ThYxaM6U.js.gz +0 -0
  97. package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-D0QTo3NZ.js → ishikawaDiagram-UXIWVN3A-EcWEDDQ3.js} +2 -2
  98. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-EcWEDDQ3.js.br +0 -0
  99. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-EcWEDDQ3.js.gz +0 -0
  100. package/out/renderer/assets/{item-Cv-tnX8b.js → item-Dph2aCdz.js} +20 -2
  101. package/out/renderer/assets/item-Dph2aCdz.js.br +0 -0
  102. package/out/renderer/assets/item-Dph2aCdz.js.gz +0 -0
  103. package/out/renderer/assets/{journeyDiagram-VCZTEJTY-CQV0gdjM.js → journeyDiagram-VCZTEJTY-Ct6VLkXK.js} +5 -5
  104. package/out/renderer/assets/journeyDiagram-VCZTEJTY-Ct6VLkXK.js.br +0 -0
  105. package/out/renderer/assets/journeyDiagram-VCZTEJTY-Ct6VLkXK.js.gz +0 -0
  106. package/out/renderer/assets/{kanban-definition-6JOO6SKY-DouRT6vc.js → kanban-definition-6JOO6SKY-DHWulQen.js} +3 -3
  107. package/out/renderer/assets/kanban-definition-6JOO6SKY-DHWulQen.js.br +0 -0
  108. package/out/renderer/assets/kanban-definition-6JOO6SKY-DHWulQen.js.gz +0 -0
  109. package/out/renderer/assets/{layout-BhbUjpvY.js → layout-pfquJ3N_.js} +4 -4
  110. package/out/renderer/assets/layout-pfquJ3N_.js.br +0 -0
  111. package/out/renderer/assets/layout-pfquJ3N_.js.gz +0 -0
  112. package/out/renderer/assets/{linear-D1WO10JK.js → linear-CdfVBcAB.js} +1 -1
  113. package/out/renderer/assets/linear-CdfVBcAB.js.br +0 -0
  114. package/out/renderer/assets/linear-CdfVBcAB.js.gz +0 -0
  115. package/out/renderer/assets/{link-2-C5KPDK2L.js → link-2-DpdZfD3m.js} +1 -1
  116. package/out/renderer/assets/{mermaid-GHXKKRXX-KwA2c0l5.js → mermaid-GHXKKRXX-CqMNodCp.js} +54 -54
  117. package/out/renderer/assets/mermaid-GHXKKRXX-CqMNodCp.js.br +0 -0
  118. package/out/renderer/assets/mermaid-GHXKKRXX-CqMNodCp.js.gz +0 -0
  119. package/out/renderer/assets/mic-CT1bYBEV.js +22 -0
  120. package/out/renderer/assets/{min-BP2C7bzG.js → min-CiK16GFr.js} +2 -2
  121. package/out/renderer/assets/min-CiK16GFr.js.br +0 -0
  122. package/out/renderer/assets/min-CiK16GFr.js.gz +0 -0
  123. package/out/renderer/assets/{mindmap-definition-QFDTVHPH-CxLYrjVp.js → mindmap-definition-QFDTVHPH-BvmM-Xdx.js} +4 -4
  124. package/out/renderer/assets/mindmap-definition-QFDTVHPH-BvmM-Xdx.js.br +0 -0
  125. package/out/renderer/assets/mindmap-definition-QFDTVHPH-BvmM-Xdx.js.gz +0 -0
  126. package/out/renderer/assets/{open-guide-UhaRhiI6.js → open-guide-BqiUQfrM.js} +1 -1
  127. package/out/renderer/assets/{package-search-DpyeQ5Lx.js → package-search-BxIuIl9F.js} +1 -1
  128. package/out/renderer/assets/{pieDiagram-DEJITSTG-D1LBRn_k.js → pieDiagram-DEJITSTG-CQY4NZ_4.js} +7 -7
  129. package/out/renderer/assets/pieDiagram-DEJITSTG-CQY4NZ_4.js.br +0 -0
  130. package/out/renderer/assets/pieDiagram-DEJITSTG-CQY4NZ_4.js.gz +0 -0
  131. package/out/renderer/assets/{plus-CsV_3veW.js → plus-C5XdFVFm.js} +1 -1
  132. package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-89cdKeKP.js → quadrantDiagram-34T5L4WZ-BiDWKNht.js} +3 -3
  133. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-BiDWKNht.js.br +0 -0
  134. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-BiDWKNht.js.gz +0 -0
  135. package/out/renderer/assets/{requirementDiagram-MS252O5E-BPmX5cbr.js → requirementDiagram-MS252O5E-D-KxL9Tf.js} +4 -4
  136. package/out/renderer/assets/requirementDiagram-MS252O5E-D-KxL9Tf.js.br +0 -0
  137. package/out/renderer/assets/requirementDiagram-MS252O5E-D-KxL9Tf.js.gz +0 -0
  138. package/out/renderer/assets/{resizable-C_plViAK.js → resizable-ObbINbIB.js} +1 -1
  139. package/out/renderer/assets/resizable-ObbINbIB.js.br +0 -0
  140. package/out/renderer/assets/resizable-ObbINbIB.js.gz +0 -0
  141. package/out/renderer/assets/{sankeyDiagram-XADWPNL6-Cy6CP9QU.js → sankeyDiagram-XADWPNL6-BIFbqkzs.js} +2 -2
  142. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BIFbqkzs.js.br +0 -0
  143. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BIFbqkzs.js.gz +0 -0
  144. package/out/renderer/assets/{schedule-BRGzJU8Q.js → schedule-BtuArq_x.js} +9 -10
  145. package/out/renderer/assets/schedule-BtuArq_x.js.br +0 -0
  146. package/out/renderer/assets/schedule-BtuArq_x.js.gz +0 -0
  147. package/out/renderer/assets/{search-BHyqy--Q.js → search-TCnuQyCi.js} +1 -1
  148. package/out/renderer/assets/{sequenceDiagram-FGHM5R23-B3c7Za1w.js → sequenceDiagram-FGHM5R23-DQX-_zuj.js} +4 -4
  149. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DQX-_zuj.js.br +0 -0
  150. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DQX-_zuj.js.gz +0 -0
  151. package/out/renderer/assets/{session-DAyi31xW.js → session-C_1BpGhU.js} +1511 -263
  152. package/out/renderer/assets/session-C_1BpGhU.js.br +0 -0
  153. package/out/renderer/assets/session-C_1BpGhU.js.gz +0 -0
  154. package/out/renderer/assets/{setting-dialog-B9QpnCSy.js → setting-dialog-D1_-4H95.js} +3 -3
  155. package/out/renderer/assets/setting-dialog-D1_-4H95.js.br +0 -0
  156. package/out/renderer/assets/setting-dialog-D1_-4H95.js.gz +0 -0
  157. package/out/renderer/assets/{settings-2-DTnwbGMG.js → settings-2-BwfZrIw6.js} +1 -1
  158. package/out/renderer/assets/{settings-agents-BrAPSjkm.js → settings-agents-BtuYXQ1V.js} +26 -15
  159. package/out/renderer/assets/settings-agents-BtuYXQ1V.js.br +0 -0
  160. package/out/renderer/assets/settings-agents-BtuYXQ1V.js.gz +0 -0
  161. package/out/renderer/assets/{settings-general-Ck2OiZjy.js → settings-general-cHqsKFFa.js} +36 -3
  162. package/out/renderer/assets/settings-general-cHqsKFFa.js.br +0 -0
  163. package/out/renderer/assets/settings-general-cHqsKFFa.js.gz +0 -0
  164. package/out/renderer/assets/{settings-ilink-D0Br74W_.js → settings-ilink-BR1Vd9t9.js} +3 -3
  165. package/out/renderer/assets/settings-ilink-BR1Vd9t9.js.br +0 -0
  166. package/out/renderer/assets/settings-ilink-BR1Vd9t9.js.gz +0 -0
  167. package/out/renderer/assets/{settings-image-generation-BdyKoUUZ.js → settings-image-generation-MUHYnpbm.js} +6 -7
  168. package/out/renderer/assets/settings-image-generation-MUHYnpbm.js.br +0 -0
  169. package/out/renderer/assets/settings-image-generation-MUHYnpbm.js.gz +0 -0
  170. package/out/renderer/assets/{settings-layout-B7d1oQnz.js → settings-layout-SBihzFfb.js} +16 -3
  171. package/out/renderer/assets/settings-layout-SBihzFfb.js.br +0 -0
  172. package/out/renderer/assets/settings-layout-SBihzFfb.js.gz +0 -0
  173. package/out/renderer/assets/{settings-mcp-DtnVGpCh.js → settings-mcp-Co88s-0y.js} +8 -9
  174. package/out/renderer/assets/settings-mcp-Co88s-0y.js.br +0 -0
  175. package/out/renderer/assets/settings-mcp-Co88s-0y.js.gz +0 -0
  176. package/out/renderer/assets/{settings-memory-Bff_D3Id.js → settings-memory-CNH9gHYC.js} +2 -3
  177. package/out/renderer/assets/settings-memory-CNH9gHYC.js.br +0 -0
  178. package/out/renderer/assets/settings-memory-CNH9gHYC.js.gz +0 -0
  179. package/out/renderer/assets/settings-shortcuts-C1s7YIMj.js +267 -0
  180. package/out/renderer/assets/settings-shortcuts-C1s7YIMj.js.br +0 -0
  181. package/out/renderer/assets/settings-shortcuts-C1s7YIMj.js.gz +0 -0
  182. package/out/renderer/assets/{settings-snippets-CuwNZ684.js → settings-snippets-DGgI6mxm.js} +7 -8
  183. package/out/renderer/assets/settings-snippets-DGgI6mxm.js.br +0 -0
  184. package/out/renderer/assets/settings-snippets-DGgI6mxm.js.gz +0 -0
  185. package/out/renderer/assets/settings-speech-to-text-CV0xM7yz.js +1054 -0
  186. package/out/renderer/assets/settings-speech-to-text-CV0xM7yz.js.br +0 -0
  187. package/out/renderer/assets/settings-speech-to-text-CV0xM7yz.js.gz +0 -0
  188. package/out/renderer/assets/{settings-webui-CcjZQw6G.js → settings-webui-Bj9dJDcm.js} +2 -2
  189. package/out/renderer/assets/settings-webui-Bj9dJDcm.js.br +0 -0
  190. package/out/renderer/assets/settings-webui-Bj9dJDcm.js.gz +0 -0
  191. package/out/renderer/assets/{skills-installed-BR3EmTKD.js → skills-installed-Bhd5a33I.js} +4 -4
  192. package/out/renderer/assets/skills-installed-Bhd5a33I.js.br +0 -0
  193. package/out/renderer/assets/skills-installed-Bhd5a33I.js.gz +0 -0
  194. package/out/renderer/assets/{skills-layout-Cgy95TdD.js → skills-layout-DZFDa1cw.js} +2 -2
  195. package/out/renderer/assets/skills-layout-DZFDa1cw.js.br +0 -0
  196. package/out/renderer/assets/skills-layout-DZFDa1cw.js.gz +0 -0
  197. package/out/renderer/assets/{skills-skills-sh-7eO0Llg2.js → skills-skills-sh-DF59nekm.js} +4 -4
  198. package/out/renderer/assets/skills-skills-sh-DF59nekm.js.br +0 -0
  199. package/out/renderer/assets/skills-skills-sh-DF59nekm.js.gz +0 -0
  200. package/out/renderer/assets/{sortable.esm-BTWJnVbf.js → sortable.esm-BlhN8Kkg.js} +1 -1
  201. package/out/renderer/assets/sortable.esm-BlhN8Kkg.js.br +0 -0
  202. package/out/renderer/assets/sortable.esm-BlhN8Kkg.js.gz +0 -0
  203. package/out/renderer/assets/{standard-schema-Dk273X__.js → standard-schema-CGAs4bGi.js} +1 -1
  204. package/out/renderer/assets/standard-schema-CGAs4bGi.js.br +0 -0
  205. package/out/renderer/assets/standard-schema-CGAs4bGi.js.gz +0 -0
  206. package/out/renderer/assets/{stateDiagram-FHFEXIEX-C-7WuE0a.js → stateDiagram-FHFEXIEX-DappRrGm.js} +9 -9
  207. package/out/renderer/assets/stateDiagram-FHFEXIEX-DappRrGm.js.br +0 -0
  208. package/out/renderer/assets/stateDiagram-FHFEXIEX-DappRrGm.js.gz +0 -0
  209. package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-IZRTgRo8.js → stateDiagram-v2-QKLJ7IA2-mbgJN2Ya.js} +5 -5
  210. package/out/renderer/assets/{task-Bd1x6VfK.js → task-C7tCZ6e3.js} +11 -12
  211. package/out/renderer/assets/task-C7tCZ6e3.js.br +0 -0
  212. package/out/renderer/assets/task-C7tCZ6e3.js.gz +0 -0
  213. package/out/renderer/assets/{textarea-CcqU8Ki7.js → textarea-DNSPhqFr.js} +2 -2
  214. package/out/renderer/assets/{timeline-definition-GMOUNBTQ-DVUMAatS.js → timeline-definition-GMOUNBTQ-o8XIUDyn.js} +3 -3
  215. package/out/renderer/assets/timeline-definition-GMOUNBTQ-o8XIUDyn.js.br +0 -0
  216. package/out/renderer/assets/timeline-definition-GMOUNBTQ-o8XIUDyn.js.gz +0 -0
  217. package/out/renderer/assets/{vennDiagram-DHZGUBPP-BtKdpgmE.js → vennDiagram-DHZGUBPP-mnc6bpmR.js} +2 -2
  218. package/out/renderer/assets/vennDiagram-DHZGUBPP-mnc6bpmR.js.br +0 -0
  219. package/out/renderer/assets/vennDiagram-DHZGUBPP-mnc6bpmR.js.gz +0 -0
  220. package/out/renderer/assets/{wardley-RL74JXVD-DRlpukah.js → wardley-RL74JXVD-wdJYeEiD.js} +4 -4
  221. package/out/renderer/assets/wardley-RL74JXVD-wdJYeEiD.js.br +0 -0
  222. package/out/renderer/assets/wardley-RL74JXVD-wdJYeEiD.js.gz +0 -0
  223. package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-c_iDOO-3.js → wardleyDiagram-NUSXRM2D-Ddx6k7tJ.js} +6 -6
  224. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-Ddx6k7tJ.js.br +0 -0
  225. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-Ddx6k7tJ.js.gz +0 -0
  226. package/out/renderer/assets/{wrench-C-pOIUrz.js → wrench-BsjH8RPm.js} +1 -1
  227. package/out/renderer/assets/wrench-BsjH8RPm.js.br +0 -0
  228. package/out/renderer/assets/wrench-BsjH8RPm.js.gz +0 -0
  229. package/out/renderer/assets/{xlsx-view-Ca3bToM7.js → xlsx-view-CQd4jbk-.js} +21 -13
  230. package/out/renderer/assets/xlsx-view-CQd4jbk-.js.br +0 -0
  231. package/out/renderer/assets/xlsx-view-CQd4jbk-.js.gz +0 -0
  232. package/out/renderer/assets/{xychartDiagram-5P7HB3ND-D65drBbj.js → xychartDiagram-5P7HB3ND-VIQ9wuz0.js} +3 -3
  233. package/out/renderer/assets/xychartDiagram-5P7HB3ND-VIQ9wuz0.js.br +0 -0
  234. package/out/renderer/assets/xychartDiagram-5P7HB3ND-VIQ9wuz0.js.gz +0 -0
  235. package/out/renderer/index.html +2 -2
  236. package/out/renderer/index.html.br +0 -0
  237. package/out/renderer/index.html.gz +0 -0
  238. package/out/scripts/mcp-skills/server.mjs +1 -1
  239. package/out/server/main.js +290 -11
  240. package/package.json +2 -1
  241. package/resources/tree-sitter-wasm/tree-sitter-dart.wasm +0 -0
  242. package/out/renderer/assets/_baseUniq-C0L-SxO8.js.br +0 -0
  243. package/out/renderer/assets/_baseUniq-C0L-SxO8.js.gz +0 -0
  244. package/out/renderer/assets/arc-bnlMIyYF.js.br +0 -0
  245. package/out/renderer/assets/arc-bnlMIyYF.js.gz +0 -0
  246. package/out/renderer/assets/architectureDiagram-Q4EWVU46-Cr0249Lu.js.br +0 -0
  247. package/out/renderer/assets/architectureDiagram-Q4EWVU46-Cr0249Lu.js.gz +0 -0
  248. package/out/renderer/assets/automation-CvjrkE9z.js.br +0 -0
  249. package/out/renderer/assets/automation-CvjrkE9z.js.gz +0 -0
  250. package/out/renderer/assets/blockDiagram-DXYQGD6D-Cw6vh5dx.js.br +0 -0
  251. package/out/renderer/assets/blockDiagram-DXYQGD6D-Cw6vh5dx.js.gz +0 -0
  252. package/out/renderer/assets/c4Diagram-AHTNJAMY-BUrvcyv2.js.br +0 -0
  253. package/out/renderer/assets/c4Diagram-AHTNJAMY-BUrvcyv2.js.gz +0 -0
  254. package/out/renderer/assets/chunk-4TB4RGXK-D_fBnf0p.js.br +0 -0
  255. package/out/renderer/assets/chunk-4TB4RGXK-D_fBnf0p.js.gz +0 -0
  256. package/out/renderer/assets/chunk-EDXVE4YY-BJLqHaER.js.br +0 -0
  257. package/out/renderer/assets/chunk-EDXVE4YY-BJLqHaER.js.gz +0 -0
  258. package/out/renderer/assets/chunk-OYMX7WX6-CrS7loi7.js.br +0 -0
  259. package/out/renderer/assets/chunk-YZCP3GAM-CMgdytZR.js.br +0 -0
  260. package/out/renderer/assets/chunk-YZCP3GAM-CMgdytZR.js.gz +0 -0
  261. package/out/renderer/assets/clipboard-xI9lExaB.js +0 -15
  262. package/out/renderer/assets/code-editor-SuOcN-5B.js.br +0 -0
  263. package/out/renderer/assets/code-editor-SuOcN-5B.js.gz +0 -0
  264. package/out/renderer/assets/collapsible-Bd85ZCyx.js.br +0 -0
  265. package/out/renderer/assets/collapsible-Bd85ZCyx.js.gz +0 -0
  266. package/out/renderer/assets/context-menu-BO9-4FYd.js +0 -282
  267. package/out/renderer/assets/context-menu-BO9-4FYd.js.br +0 -0
  268. package/out/renderer/assets/context-menu-BO9-4FYd.js.gz +0 -0
  269. package/out/renderer/assets/cose-bilkent-S5V4N54A-Dq8VllNT.js.br +0 -0
  270. package/out/renderer/assets/cose-bilkent-S5V4N54A-Dq8VllNT.js.gz +0 -0
  271. package/out/renderer/assets/dagre-KV5264BT-Dec8q4UT.js.br +0 -0
  272. package/out/renderer/assets/dagre-KV5264BT-Dec8q4UT.js.gz +0 -0
  273. package/out/renderer/assets/diagram-5BDNPKRD-CzA2u_EN.js.br +0 -0
  274. package/out/renderer/assets/diagram-5BDNPKRD-CzA2u_EN.js.gz +0 -0
  275. package/out/renderer/assets/diagram-G4DWMVQ6-C8h3R7jD.js.br +0 -0
  276. package/out/renderer/assets/diagram-G4DWMVQ6-C8h3R7jD.js.gz +0 -0
  277. package/out/renderer/assets/diagram-MMDJMWI5-9tiXSF90.js.br +0 -0
  278. package/out/renderer/assets/diagram-MMDJMWI5-9tiXSF90.js.gz +0 -0
  279. package/out/renderer/assets/diagram-TYMM5635-BrarGJNf.js.br +0 -0
  280. package/out/renderer/assets/diagram-TYMM5635-BrarGJNf.js.gz +0 -0
  281. package/out/renderer/assets/erDiagram-SMLLAGMA-1Q9StK7L.js.br +0 -0
  282. package/out/renderer/assets/erDiagram-SMLLAGMA-1Q9StK7L.js.gz +0 -0
  283. package/out/renderer/assets/field-ohYLdnJa.js.br +0 -0
  284. package/out/renderer/assets/field-ohYLdnJa.js.gz +0 -0
  285. package/out/renderer/assets/flowDiagram-DWJPFMVM-CpcXVOmj.js.br +0 -0
  286. package/out/renderer/assets/flowDiagram-DWJPFMVM-CpcXVOmj.js.gz +0 -0
  287. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-C7Da4JBG.js.br +0 -0
  288. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-C7Da4JBG.js.gz +0 -0
  289. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-COBL52q_.js.br +0 -0
  290. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-COBL52q_.js.gz +0 -0
  291. package/out/renderer/assets/graph-vKaAs6sc.js.br +0 -0
  292. package/out/renderer/assets/graph-vKaAs6sc.js.gz +0 -0
  293. package/out/renderer/assets/index-BJPggXxf.js.br +0 -0
  294. package/out/renderer/assets/index-BJPggXxf.js.gz +0 -0
  295. package/out/renderer/assets/index-DGML4zYf.css +0 -382
  296. package/out/renderer/assets/index-DGML4zYf.css.br +0 -0
  297. package/out/renderer/assets/index-DGML4zYf.css.gz +0 -0
  298. package/out/renderer/assets/infoDiagram-42DDH7IO-z37e1cYq.js.br +0 -0
  299. package/out/renderer/assets/infoDiagram-42DDH7IO-z37e1cYq.js.gz +0 -0
  300. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-D0QTo3NZ.js.br +0 -0
  301. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-D0QTo3NZ.js.gz +0 -0
  302. package/out/renderer/assets/item-Cv-tnX8b.js.br +0 -0
  303. package/out/renderer/assets/item-Cv-tnX8b.js.gz +0 -0
  304. package/out/renderer/assets/journeyDiagram-VCZTEJTY-CQV0gdjM.js.br +0 -0
  305. package/out/renderer/assets/journeyDiagram-VCZTEJTY-CQV0gdjM.js.gz +0 -0
  306. package/out/renderer/assets/kanban-definition-6JOO6SKY-DouRT6vc.js.br +0 -0
  307. package/out/renderer/assets/kanban-definition-6JOO6SKY-DouRT6vc.js.gz +0 -0
  308. package/out/renderer/assets/layout-BhbUjpvY.js.br +0 -0
  309. package/out/renderer/assets/layout-BhbUjpvY.js.gz +0 -0
  310. package/out/renderer/assets/linear-D1WO10JK.js.br +0 -0
  311. package/out/renderer/assets/linear-D1WO10JK.js.gz +0 -0
  312. package/out/renderer/assets/mermaid-GHXKKRXX-KwA2c0l5.js.br +0 -0
  313. package/out/renderer/assets/mermaid-GHXKKRXX-KwA2c0l5.js.gz +0 -0
  314. package/out/renderer/assets/min-BP2C7bzG.js.br +0 -0
  315. package/out/renderer/assets/min-BP2C7bzG.js.gz +0 -0
  316. package/out/renderer/assets/mindmap-definition-QFDTVHPH-CxLYrjVp.js.br +0 -0
  317. package/out/renderer/assets/mindmap-definition-QFDTVHPH-CxLYrjVp.js.gz +0 -0
  318. package/out/renderer/assets/pieDiagram-DEJITSTG-D1LBRn_k.js.br +0 -0
  319. package/out/renderer/assets/pieDiagram-DEJITSTG-D1LBRn_k.js.gz +0 -0
  320. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-89cdKeKP.js.br +0 -0
  321. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-89cdKeKP.js.gz +0 -0
  322. package/out/renderer/assets/refresh-cw-CYW3HR-E.js +0 -11
  323. package/out/renderer/assets/requirementDiagram-MS252O5E-BPmX5cbr.js.br +0 -0
  324. package/out/renderer/assets/requirementDiagram-MS252O5E-BPmX5cbr.js.gz +0 -0
  325. package/out/renderer/assets/resizable-C_plViAK.js.br +0 -0
  326. package/out/renderer/assets/resizable-C_plViAK.js.gz +0 -0
  327. package/out/renderer/assets/sankeyDiagram-XADWPNL6-Cy6CP9QU.js.br +0 -0
  328. package/out/renderer/assets/sankeyDiagram-XADWPNL6-Cy6CP9QU.js.gz +0 -0
  329. package/out/renderer/assets/schedule-BRGzJU8Q.js.br +0 -0
  330. package/out/renderer/assets/schedule-BRGzJU8Q.js.gz +0 -0
  331. package/out/renderer/assets/sequenceDiagram-FGHM5R23-B3c7Za1w.js.br +0 -0
  332. package/out/renderer/assets/sequenceDiagram-FGHM5R23-B3c7Za1w.js.gz +0 -0
  333. package/out/renderer/assets/session-DAyi31xW.js.br +0 -0
  334. package/out/renderer/assets/session-DAyi31xW.js.gz +0 -0
  335. package/out/renderer/assets/setting-dialog-B9QpnCSy.js.br +0 -0
  336. package/out/renderer/assets/setting-dialog-B9QpnCSy.js.gz +0 -0
  337. package/out/renderer/assets/settings-agents-BrAPSjkm.js.br +0 -0
  338. package/out/renderer/assets/settings-agents-BrAPSjkm.js.gz +0 -0
  339. package/out/renderer/assets/settings-general-Ck2OiZjy.js.br +0 -0
  340. package/out/renderer/assets/settings-general-Ck2OiZjy.js.gz +0 -0
  341. package/out/renderer/assets/settings-ilink-D0Br74W_.js.br +0 -0
  342. package/out/renderer/assets/settings-ilink-D0Br74W_.js.gz +0 -0
  343. package/out/renderer/assets/settings-image-generation-BdyKoUUZ.js.br +0 -0
  344. package/out/renderer/assets/settings-image-generation-BdyKoUUZ.js.gz +0 -0
  345. package/out/renderer/assets/settings-layout-B7d1oQnz.js.br +0 -0
  346. package/out/renderer/assets/settings-layout-B7d1oQnz.js.gz +0 -0
  347. package/out/renderer/assets/settings-mcp-DtnVGpCh.js.br +0 -0
  348. package/out/renderer/assets/settings-mcp-DtnVGpCh.js.gz +0 -0
  349. package/out/renderer/assets/settings-memory-Bff_D3Id.js.br +0 -0
  350. package/out/renderer/assets/settings-memory-Bff_D3Id.js.gz +0 -0
  351. package/out/renderer/assets/settings-snippets-CuwNZ684.js.br +0 -0
  352. package/out/renderer/assets/settings-snippets-CuwNZ684.js.gz +0 -0
  353. package/out/renderer/assets/settings-webui-CcjZQw6G.js.br +0 -0
  354. package/out/renderer/assets/settings-webui-CcjZQw6G.js.gz +0 -0
  355. package/out/renderer/assets/skills-installed-BR3EmTKD.js.br +0 -0
  356. package/out/renderer/assets/skills-installed-BR3EmTKD.js.gz +0 -0
  357. package/out/renderer/assets/skills-layout-Cgy95TdD.js.br +0 -0
  358. package/out/renderer/assets/skills-layout-Cgy95TdD.js.gz +0 -0
  359. package/out/renderer/assets/skills-skills-sh-7eO0Llg2.js.br +0 -0
  360. package/out/renderer/assets/skills-skills-sh-7eO0Llg2.js.gz +0 -0
  361. package/out/renderer/assets/sortable.esm-BTWJnVbf.js.br +0 -0
  362. package/out/renderer/assets/sortable.esm-BTWJnVbf.js.gz +0 -0
  363. package/out/renderer/assets/standard-schema-Dk273X__.js.br +0 -0
  364. package/out/renderer/assets/standard-schema-Dk273X__.js.gz +0 -0
  365. package/out/renderer/assets/stateDiagram-FHFEXIEX-C-7WuE0a.js.br +0 -0
  366. package/out/renderer/assets/stateDiagram-FHFEXIEX-C-7WuE0a.js.gz +0 -0
  367. package/out/renderer/assets/task-Bd1x6VfK.js.br +0 -0
  368. package/out/renderer/assets/task-Bd1x6VfK.js.gz +0 -0
  369. package/out/renderer/assets/timeline-definition-GMOUNBTQ-DVUMAatS.js.br +0 -0
  370. package/out/renderer/assets/timeline-definition-GMOUNBTQ-DVUMAatS.js.gz +0 -0
  371. package/out/renderer/assets/vennDiagram-DHZGUBPP-BtKdpgmE.js.br +0 -0
  372. package/out/renderer/assets/vennDiagram-DHZGUBPP-BtKdpgmE.js.gz +0 -0
  373. package/out/renderer/assets/wardley-RL74JXVD-DRlpukah.js.br +0 -0
  374. package/out/renderer/assets/wardley-RL74JXVD-DRlpukah.js.gz +0 -0
  375. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-c_iDOO-3.js.br +0 -0
  376. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-c_iDOO-3.js.gz +0 -0
  377. package/out/renderer/assets/wrench-C-pOIUrz.js.br +0 -0
  378. package/out/renderer/assets/wrench-C-pOIUrz.js.gz +0 -0
  379. package/out/renderer/assets/xlsx-view-Ca3bToM7.js.br +0 -0
  380. package/out/renderer/assets/xlsx-view-Ca3bToM7.js.gz +0 -0
  381. package/out/renderer/assets/xychartDiagram-5P7HB3ND-D65drBbj.js.br +0 -0
  382. package/out/renderer/assets/xychartDiagram-5P7HB3ND-D65drBbj.js.gz +0 -0
@@ -1,37 +1,21 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./code-editor-SuOcN-5B.js","./index-BJPggXxf.js","./index-DGML4zYf.css","./mermaid-GHXKKRXX-KwA2c0l5.js","./code-editor-BwxubRqT.css"])))=>i.map(i=>d[i]);
2
- import { c as createLucideIcon, r as reactExports, aC as formatErrorMessage, bB as ReactStore, bC as popupStoreSelectors, bD as triggerHover, ap as triggerPress, bE as escapeKey, bF as attachPreventUnmountOnClose, bG as closePress, bH as PATIENT_CLICK_THRESHOLD, aw as reactDomExports, bI as focusOut, bJ as setPopupOpenState, bK as createInitialPopupStoreState, bL as createPopupFloatingRootContext, bM as Timeout, bN as PopupTriggerMap, j as jsxRuntimeExports, bO as FloatingTree, bP as usePopupRootSync, bQ as useImplicitActiveTrigger, bR as useOpenStateTransitions, an as createChangeEventDetails, bS as imperativeAction, bT as PopupHandleAttachment, bU as usePopupRootStore, bV as useDismiss, bW as usePopupInteractionProps, bX as usePopupHandleStore, aA as useBaseUiId, bY as useTriggerDataForwarding, bZ as useHoverReferenceInteraction, b_ as safePolygon, b$ as useClick, c0 as useOpenMethodTriggerProps, aF as useButton, c1 as useTriggerFocusGuards, ah as useRenderElement, c2 as CLICK_TRIGGER_IDENTIFIER, c3 as FocusGuard, ak as pressableTriggerOpenStateMapping, c4 as triggerOpenStateMapping, c5 as FloatingPortal, c6 as POPUP_COLLISION_AVOIDANCE, c7 as useFloatingNodeId, aI as useAnimationsFinished, c8 as useAnchorPositioning, aJ as useIsoLayoutEffect, c9 as useAnchoredPopupScrollLock, ca as usePositioner, cb as InternalBackdrop, cc as inertValue, cd as FloatingNode, aB as useStableCallback, ce as useToolbarRootContext, aK as useOpenChangeComplete, cf as useHoverFloatingInteraction, cg as createDefaultInitialFocus, ch as FOCUSABLE_POPUP_PROPS, bl as COMPOSITE_KEYS, ci as getDisabledMountTransitionStyles, cj as FloatingFocusManager, ck as isHTMLElement, cl as popupTransitionStateMapping, u as useTranslation, b as useMessage, Q as useNavigate, a as useAppStore, V as Badge, cm as formatUpdatedTime, f as request, K as electron, a6 as ALL_FEATURES, a7 as FEATURE_I18N_KEYS, F as cn$1, d as Switch, B as Button, H as LoaderCircle, cn as buttonVariants, i as isWebUI, J as FolderOpen, C as Check, e as Copy, g as extractErrorMessage, aU as reduceFlushStreaming, aT as reduceSessionNotification, h as copyText, av as React, co as Tooltip, cp as TooltipTrigger, cq as TooltipContent, S as ScrollArea, a0 as Bot, cr as formatDuration, ax as getDefaultExportFromCjs, cs as ReactDOM, ct as v$2, A as generateUUID, cu as HoverCard, cv as HoverCardTrigger, cw as HoverCardContent, cx as X$1, aW as Folder, L as Library, v as DropdownMenu, w as DropdownMenuTrigger, cy as ChevronDown, x as DropdownMenuContent, y as DropdownMenuItem, cz as DropdownMenuSub, cA as DropdownMenuSubTrigger, cB as DropdownMenuSubContent, cC as CircleQuestionMark, cD as ChevronUp, aV as Clock, aP as Tabs, aQ as TabsList, aR as TabsTrigger, cE as z$1, X as CircleCheck, cF as __vitePreload, I as Input, cG as Code, bv as EDITOR_LABELS, aS as webUIBaseUrl, R as subscribe, a5 as ChevronRight, cH as MessageSquare, cI as FolderPlus, P as Pencil, T as Trash2, cJ as useProjectState, cK as clientId, cL as TabsContent } from "./index-BJPggXxf.js";
3
- import { S as Settings2 } from "./settings-2-DTnwbGMG.js";
4
- import { i as isValidMessageToDisplay, M as MessageBubble, A as ArrowUpToLine, S as Square, j as jsonToPrettyYamlExports, p as parseFileReference, c as CodeCompareView, r as resolveFileUrl, I as ImageView, P as PdfView, D as DocxView, a as PptxView, X as XlsxView, R as RotateCcw, F as Folders } from "./xlsx-view-Ca3bToM7.js";
5
- import { I as Image, W as Wrench } from "./wrench-C-pOIUrz.js";
6
- import { F as FileText } from "./file-text-CshHuHgW.js";
7
- import { C as Clipboard } from "./clipboard-xI9lExaB.js";
8
- import { A as ArrowLeft } from "./arrow-left-D589pHHA.js";
9
- import { aQ as CodeView, S as StreamMarkdown, aR as FileIcon } from "./mermaid-GHXKKRXX-KwA2c0l5.js";
10
- import { b as ContextMenuContent, c as ContextMenuItem, d as ContextMenuSeparator, C as ContextMenu, a as ContextMenuTrigger } from "./context-menu-BO9-4FYd.js";
11
- import { S as Search } from "./search-BHyqy--Q.js";
12
- import { E as ExternalLink } from "./link-2-C5KPDK2L.js";
13
- import { R as RefreshCw } from "./refresh-cw-CYW3HR-E.js";
14
- import { P as Plus } from "./plus-CsV_3veW.js";
15
- import { R as ResizablePanelGroup, a as ResizablePanel, b as ResizableHandle } from "./resizable-C_plViAK.js";
16
- import "./collapsible-Bd85ZCyx.js";
17
- import "./item-Cv-tnX8b.js";
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./code-editor-tm0AYOLz.js","./index-BUwXrbHW.js","./index-DOIzYXAl.css","./mermaid-GHXKKRXX-CqMNodCp.js","./code-editor-BwxubRqT.css"])))=>i.map(i=>d[i]);
2
+ import { c as createLucideIcon, r as reactExports, av as formatErrorMessage, bz as ReactStore, bA as popupStoreSelectors, bB as triggerHover, au as triggerPress, bC as escapeKey, bD as attachPreventUnmountOnClose, bE as closePress, bF as PATIENT_CLICK_THRESHOLD, an as reactDomExports, bG as focusOut, bH as setPopupOpenState, bI as createInitialPopupStoreState, bJ as createPopupFloatingRootContext, bK as Timeout, bL as PopupTriggerMap, j as jsxRuntimeExports, bM as FloatingTree, bN as usePopupRootSync, bO as useImplicitActiveTrigger, bP as useOpenStateTransitions, at as createChangeEventDetails, bQ as imperativeAction, bR as PopupHandleAttachment, bS as usePopupRootStore, bT as useDismiss, bU as usePopupInteractionProps, bV as usePopupHandleStore, ar as useBaseUiId, bW as useTriggerDataForwarding, bX as useHoverReferenceInteraction, bY as safePolygon, bZ as useClick, b_ as useOpenMethodTriggerProps, az as useButton, b$ as useTriggerFocusGuards, ay as useRenderElement, c0 as CLICK_TRIGGER_IDENTIFIER, c1 as FocusGuard, c2 as pressableTriggerOpenStateMapping, c3 as triggerOpenStateMapping, c4 as FloatingPortal, c5 as POPUP_COLLISION_AVOIDANCE, c6 as useFloatingNodeId, aC as useAnimationsFinished, c7 as useAnchorPositioning, aD as useIsoLayoutEffect, c8 as useAnchoredPopupScrollLock, c9 as usePositioner, ca as InternalBackdrop, cb as inertValue, cc as FloatingNode, as as useStableCallback, cd as useToolbarRootContext, aE as useOpenChangeComplete, ce as useHoverFloatingInteraction, cf as createDefaultInitialFocus, cg as FOCUSABLE_POPUP_PROPS, bj as COMPOSITE_KEYS, ch as getDisabledMountTransitionStyles, ci as FloatingFocusManager, cj as isHTMLElement, ck as popupTransitionStateMapping, u as useTranslation, b as useMessage, X as useNavigate, a as useAppStore, f as request, g as extractErrorMessage, _ as Badge, cl as formatUpdatedTime, V as electron, aK as Tabs, aL as TabsList, aM as TabsTrigger, af as ALL_FEATURES, ag as FEATURE_I18N_KEYS, M as cn$1, d as Switch, B as Button, R as LoaderCircle, cm as buttonVariants, C as Check, e as Copy, i as isWebUI, U as FolderOpen, cn as restartSession, co as SessionLifecycleBusyError, cp as closeSession, h as copyText, cq as RestartSessionError, am as React, cr as Tooltip, cs as TooltipTrigger, ct as TooltipContent, cu as useFocusTarget, S as ScrollArea, a1 as Bot, cv as formatDuration, ao as getDefaultExportFromCjs, cw as ReactDOM, Y as subscribe, cx as clientId, A as DropdownMenu, E as DropdownMenuTrigger, cy as ChevronDown, F as DropdownMenuContent, G as DropdownMenuItem, cz as v$2, J as generateUUID, aP as reduceFlushStreaming, cA as HoverCard, cB as HoverCardTrigger, cC as HoverCardContent, cD as X$1, aR as Folder, L as Library, cE as DropdownMenuSub, cF as DropdownMenuSubTrigger, cG as DropdownMenuSubContent, cH as CircleQuestionMark, cI as ChevronUp, aQ as Clock, cJ as z$1, a0 as CircleCheck, cK as __vitePreload, I as Input, m as ContextMenuContent, n as ContextMenuItem, o as ContextMenuSeparator, cL as Code, bt as EDITOR_LABELS, k as ContextMenu, l as ContextMenuTrigger, aN as webUIBaseUrl, a6 as RefreshCw, a7 as ChevronRight, cM as MessageSquare, cN as FolderPlus, cO as Eye, P as Pencil, T as Trash2, cP as useProjectState, cQ as useFocusTargetRegistry, cR as TabsContent, cS as loadSession } from "./index-BUwXrbHW.js";
3
+ import { S as Settings2 } from "./settings-2-BwfZrIw6.js";
4
+ import { i as isValidMessageToDisplay, M as MessageBubble, A as ArrowUpToLine, S as Square, j as jsonToPrettyYamlExports, p as parseFileReference, c as CodeCompareView, r as resolveFileUrl, I as ImageView, P as PdfView, D as DocxView, a as PptxView, X as XlsxView, R as RotateCcw, F as Folders } from "./xlsx-view-CQd4jbk-.js";
5
+ import { a as ArrowDown, A as ArrowUp, b as ArrowRight } from "./arrow-up-1eI4tva4.js";
6
+ import { M as Mic, C as Clipboard } from "./mic-CT1bYBEV.js";
7
+ import { I as Image, W as Wrench } from "./wrench-BsjH8RPm.js";
8
+ import { F as FileText } from "./file-text-ITbWr8iW.js";
9
+ import { A as ArrowLeft } from "./arrow-left-CQqe9rrj.js";
10
+ import { aQ as CodeView, S as StreamMarkdown, aR as FileIcon } from "./mermaid-GHXKKRXX-CqMNodCp.js";
11
+ import { S as Search } from "./search-TCnuQyCi.js";
12
+ import { E as ExternalLink } from "./link-2-DpdZfD3m.js";
13
+ import { P as Plus } from "./plus-C5XdFVFm.js";
14
+ import { R as ResizablePanelGroup, a as ResizablePanel, b as ResizableHandle } from "./resizable-ObbINbIB.js";
15
+ import "./collapsible-DD6NlDid.js";
16
+ import "./item-Dph2aCdz.js";
18
17
  import "./schemas-c0X-G5_P.js";
19
- import "./ellipsis-BafkhboM.js";
20
- const __iconNode$k = [
21
- ["path", { d: "M12 5v14", key: "s699le" }],
22
- ["path", { d: "m19 12-7 7-7-7", key: "1idqje" }]
23
- ];
24
- const ArrowDown = createLucideIcon("arrow-down", __iconNode$k);
25
- const __iconNode$j = [
26
- ["path", { d: "M5 12h14", key: "1ays0h" }],
27
- ["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
28
- ];
29
- const ArrowRight = createLucideIcon("arrow-right", __iconNode$j);
30
- const __iconNode$i = [
31
- ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
32
- ["path", { d: "M12 19V5", key: "x0mq9r" }]
33
- ];
34
- const ArrowUp = createLucideIcon("arrow-up", __iconNode$i);
18
+ import "./ellipsis-92s4sSUO.js";
35
19
  const __iconNode$h = [
36
20
  [
37
21
  "path",
@@ -816,76 +800,71 @@ function ChatHeader({ session }) {
816
800
  const currentProjectInfo = projects.find((project) => project.id === currentProjectId);
817
801
  const [localMcpServers, setLocalMcpServers] = reactExports.useState([]);
818
802
  const [localFeatures, setLocalFeatures] = reactExports.useState([]);
803
+ const [isRestarting, setIsRestarting] = reactExports.useState(false);
819
804
  const [isClosing, setIsClosing] = reactExports.useState(false);
820
805
  const handleToggle = reactExports.useCallback((mcpId) => {
821
806
  setLocalMcpServers(
822
807
  (prev) => prev.includes(mcpId) ? prev.filter((id) => id !== mcpId) : [...prev, mcpId]
823
808
  );
824
809
  }, []);
810
+ const handlePermissionChange = reactExports.useCallback(
811
+ async (permissionMode) => {
812
+ try {
813
+ await request.updateSession({ sessionId: session.id, permissionMode });
814
+ } catch (err) {
815
+ console.error("Failed to update session permission:", err);
816
+ toast.error(
817
+ extractErrorMessage(err) || t("chatHeader.failedToUpdatePermission", "Failed to update permission.")
818
+ );
819
+ }
820
+ },
821
+ [session.id, t, toast]
822
+ );
825
823
  const handleSyncAndRefresh = async () => {
826
- if (!session) return;
824
+ if (!session || isClosing || isRestarting) return;
825
+ setIsRestarting(true);
827
826
  try {
828
- await request.updateSession({
829
- sessionId: session.id,
827
+ await restartSession({
828
+ session,
830
829
  mcpServers: localMcpServers,
831
830
  features: localFeatures
832
831
  });
833
832
  } catch (err) {
834
- console.error("Failed to update MCP servers:", err);
835
- toast.error(
836
- extractErrorMessage(err) || t("chat.failedToUpdateMcpServers", "Failed to update MCP servers")
837
- );
838
- return;
839
- }
840
- const { resetSessionState, updateSessionState, updateSession } = useAppStore.getState();
841
- try {
842
- updateSession({ ...session, isStreaming: false });
843
- updateSessionState(session.id, (prev) => reduceFlushStreaming(prev));
844
- resetSessionState(session.id);
845
- updateSessionState(session.id, () => ({ isLoading: true }));
846
- const result = await request.getSessionHistory({ sessionId: session.id });
847
- if (!result) return;
848
- let state = useAppStore.getState().getSessionState(session.id);
849
- state = { ...state, messages: [], activeToolCalls: /* @__PURE__ */ new Map(), activeSubagents: /* @__PURE__ */ new Map() };
850
- for (const notification of result.messages) {
851
- state = reduceSessionNotification(session.id, state, notification);
852
- }
853
- const displayIds = new Set(
854
- result.messages.map((m) => m?.update?._meta?.fello?.displayId).filter(Boolean)
855
- );
856
- for (const notification of state.pendingNotifications) {
857
- const did = notification.update._meta?.fello?.displayId;
858
- if (did && displayIds.has(did)) continue;
859
- state = reduceSessionNotification(session.id, state, notification);
860
- }
861
- state = {
862
- ...state,
863
- pendingNotifications: [],
864
- isLoading: false
865
- };
866
- updateSessionState(session.id, () => state);
867
- request.loadSession({ sessionId: session.id, force: true }).catch((err) => {
868
- console.error("Failed to load session:", err);
833
+ console.error("Failed to restart session:", err);
834
+ if (err instanceof SessionLifecycleBusyError) {
869
835
  toast.error(
870
- extractErrorMessage(err) || t("chat.failedToLoadSession", "Failed to load session.")
836
+ t(
837
+ "chatHeader.sessionOperationInProgress",
838
+ "Another session operation is already in progress."
839
+ )
871
840
  );
872
- });
873
- } catch (err) {
874
- console.error("Failed to load session:", err);
875
- const message = extractErrorMessage(err) || t("chat.failedToLoadSession", "Failed to load session.");
876
- toast.error(message);
841
+ return;
842
+ }
843
+ const lifecycleError = err instanceof RestartSessionError ? err : null;
844
+ const cause = lifecycleError?.cause ?? err;
845
+ const fallback = lifecycleError?.stage === "update" ? t("chat.failedToUpdateMcpServers", "Failed to update MCP servers") : t("chat.failedToLoadSession", "Failed to load session.");
846
+ toast.error(extractErrorMessage(cause) || fallback);
877
847
  } finally {
878
- useAppStore.getState().updateSessionState(session.id, () => ({ isLoading: false }));
848
+ setIsRestarting(false);
879
849
  }
880
850
  };
881
851
  const handleCloseSession = async () => {
882
- if (isClosing) return;
852
+ if (isClosing || isRestarting) return;
883
853
  setIsClosing(true);
884
854
  try {
885
- await request.closeSession({ sessionId: session.id });
855
+ await closeSession(session.id);
886
856
  navigate("/");
887
857
  } catch (err) {
888
858
  console.error("Failed to close session:", err);
859
+ if (err instanceof SessionLifecycleBusyError) {
860
+ toast.error(
861
+ t(
862
+ "chatHeader.sessionOperationInProgress",
863
+ "Another session operation is already in progress."
864
+ )
865
+ );
866
+ return;
867
+ }
889
868
  toast.error(
890
869
  extractErrorMessage(err) || t("chatHeader.failedToCloseSession", "Failed to close session.")
891
870
  );
@@ -904,7 +883,7 @@ function ChatHeader({ session }) {
904
883
  children: [
905
884
  /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "px-1 text-[10px] uppercase select-none", children: session.agentId }),
906
885
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 min-w-0 items-baseline gap-2", children: [
907
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-[13px] font-normal text-sidebar-foreground/85", children: session.title || t("sidebar.newChat", "New Chat") }),
886
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-[13px] font-normal text-sidebar-foreground/85", children: session.title || t("sidebar.newSession", "New Session") }),
908
887
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 text-[10px] text-muted-foreground truncate", children: currentProjectInfo?.cwd }),
909
888
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-xs text-sidebar-foreground/70 whitespace-nowrap", children: formatUpdatedTime(session.updatedAt) })
910
889
  ] }),
@@ -921,21 +900,53 @@ function ChatHeader({ session }) {
921
900
  },
922
901
  children: [
923
902
  /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { className: "flex size-7 items-center justify-center rounded-md text-sidebar-foreground/45 hover:bg-sidebar-accent/30 hover:text-sidebar-foreground/70 outline-none transition-colors data-pressed:bg-sidebar-accent/40", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Settings2, { className: "size-4" }) }),
924
- /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverPositioner, { side: "bottom", align: "end", sideOffset: 4, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverPopup, { className: "z-10 min-w-96 rounded-lg border border-border bg-popover text-popover-foreground shadow-lg outline-none p-1.5 origin-(--transform-origin) data-ending-style:scale-90 data-starting-style:scale-90 data-ending-style:opacity-0 data-starting-style:opacity-0 transition-[transform,opacity] duration-100", children: [
925
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1.5 space-y-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
926
- CopyableRow,
927
- {
928
- label: t("chatHeader.sessionId", "Session"),
929
- value: session.id,
930
- openFolderTitle: t("chatHeader.openSessionFolder", "Open Session Folder"),
931
- onOpenFolder: async () => {
932
- const dirPath = await request.getSessionDataSystemPath({
933
- sessionId: session.id
934
- });
935
- if (dirPath) electron.revealInFinder(dirPath);
903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverPositioner, { side: "bottom", align: "end", sideOffset: 4, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverPopup, { className: "z-10 w-110 rounded-lg border border-border bg-popover text-popover-foreground shadow-lg outline-none p-1.5 origin-(--transform-origin) data-ending-style:scale-90 data-starting-style:scale-90 data-ending-style:opacity-0 data-starting-style:opacity-0 transition-[transform,opacity] duration-100", children: [
904
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-px py-1", children: [
905
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
906
+ CopyableRow,
907
+ {
908
+ label: t("chatHeader.sessionId", "Session"),
909
+ value: session.id,
910
+ openFolderTitle: t("chatHeader.openSessionFolder", "Open Session Folder"),
911
+ onOpenFolder: async () => {
912
+ const dirPath = await request.getSessionDataSystemPath({
913
+ sessionId: session.id
914
+ });
915
+ if (dirPath) electron.revealInFinder(dirPath);
916
+ }
936
917
  }
918
+ ) }),
919
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
920
+ CopyableRow,
921
+ {
922
+ label: t("chatHeader.projectId", "Project"),
923
+ value: currentProjectInfo?.cwd ?? session.projectTitle,
924
+ openFolderTitle: t("chatHeader.openProjectFolder", "Open Project Folder"),
925
+ onOpenFolder: async () => {
926
+ const dirPath = currentProjectInfo?.cwd;
927
+ if (dirPath) electron.revealInFinder(dirPath);
928
+ }
929
+ }
930
+ ) })
931
+ ] }),
932
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-border/50 my-1" }),
933
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1 text-xs font-semibold text-foreground/80", children: t("sidebar.newSessionDialog.permission", "Permission") }),
934
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
935
+ Tabs,
936
+ {
937
+ value: session.permissionMode,
938
+ onValueChange: (value) => {
939
+ if (value === "ask" || value === "allow-all") {
940
+ void handlePermissionChange(value);
941
+ }
942
+ },
943
+ className: "w-full px-2 my-2",
944
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { className: "w-full", children: [
945
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "ask", className: "text-xs", children: t("sidebar.newSessionDialog.permissionAsk", "Ask") }),
946
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "allow-all", className: "text-xs", children: t("sidebar.newSessionDialog.permissionAllowAll", "Allow all") })
947
+ ] })
937
948
  }
938
- ) }),
949
+ ),
939
950
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-border/50 my-1" }),
940
951
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1 text-xs font-semibold text-foreground/80", children: t("constant.feature.title", "Features") }),
941
952
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: ALL_FEATURES.length >= 2 ? "grid grid-cols-2 gap-0.5" : "", children: ALL_FEATURES.map((feature) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -1011,14 +1022,17 @@ function ChatHeader({ session }) {
1011
1022
  )
1012
1023
  ] }),
1013
1024
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex gap-1", children: [
1014
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1025
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1015
1026
  Button,
1016
1027
  {
1017
1028
  size: "xs",
1018
1029
  className: "flex flex-3 items-center gap-2 h-7 text-xs font-normal",
1019
1030
  onClick: handleSyncAndRefresh,
1020
- disabled: isClosing,
1021
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("chatHeader.refresh", "Restart Session") })
1031
+ disabled: isClosing || isRestarting,
1032
+ children: [
1033
+ isRestarting && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }),
1034
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("chatHeader.refresh", "Restart Session") })
1035
+ ]
1022
1036
  }
1023
1037
  ),
1024
1038
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -1028,7 +1042,7 @@ function ChatHeader({ session }) {
1028
1042
  variant: "destructive",
1029
1043
  className: "flex flex-1 items-center gap-1.5 h-7 text-xs font-normal",
1030
1044
  onClick: handleCloseSession,
1031
- disabled: isClosing,
1045
+ disabled: isClosing || isRestarting,
1032
1046
  children: [
1033
1047
  isClosing && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }),
1034
1048
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isClosing ? t("chatHeader.closingSession", "Closing…") : t("chatHeader.closeSession", "Close Session") })
@@ -1162,23 +1176,23 @@ function CopyableRow({
1162
1176
  ":"
1163
1177
  ] }),
1164
1178
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0 truncate font-mono text-muted-foreground/70 select-all", children: value }),
1165
- !isWebUI && onOpenFolder && /* @__PURE__ */ jsxRuntimeExports.jsx(
1179
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1166
1180
  "button",
1167
1181
  {
1168
1182
  type: "button",
1169
1183
  className: "shrink-0 flex items-center justify-center size-5 rounded hover:bg-accent/50 text-muted-foreground/50 hover:text-muted-foreground transition-colors -mr-1",
1170
- onClick: onOpenFolder,
1171
- title: openFolderTitle,
1172
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" })
1184
+ onClick: handleCopy,
1185
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
1173
1186
  }
1174
1187
  ),
1175
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1188
+ !isWebUI && onOpenFolder && /* @__PURE__ */ jsxRuntimeExports.jsx(
1176
1189
  "button",
1177
1190
  {
1178
1191
  type: "button",
1179
1192
  className: "shrink-0 flex items-center justify-center size-5 rounded hover:bg-accent/50 text-muted-foreground/50 hover:text-muted-foreground transition-colors -mr-1",
1180
- onClick: handleCopy,
1181
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
1193
+ onClick: onOpenFolder,
1194
+ title: openFolderTitle,
1195
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3" })
1182
1196
  }
1183
1197
  )
1184
1198
  ] });
@@ -1359,14 +1373,14 @@ function ChatTimeline({ items, activeDisplayId, onSelect: onSelect2 }) {
1359
1373
  {
1360
1374
  type: "button",
1361
1375
  onClick: () => handleSelect(item.displayId),
1362
- className: "p-1 group pointer-events-auto",
1376
+ className: "p-1 group/timeline pointer-events-auto outline-0",
1363
1377
  "aria-label": ariaLabel,
1364
1378
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1365
1379
  "div",
1366
1380
  {
1367
1381
  className: cn$1(
1368
- "size-2 rounded-full transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
1369
- isActive ? "bg-foreground/40" : "bg-muted-foreground/15 group-hover:bg-muted-foreground/40"
1382
+ "size-2 rounded-full transition-colors outline-none group-focus-visible/timeline:ring-1 group-focus-visible/timeline:ring-ring",
1383
+ isActive ? "bg-foreground/40" : "bg-muted-foreground/15 group-hover/timeline:bg-muted-foreground/40"
1370
1384
  )
1371
1385
  }
1372
1386
  )
@@ -1425,6 +1439,12 @@ function ChatArea({ session }) {
1425
1439
  const getViewport = reactExports.useCallback(() => {
1426
1440
  return scrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]');
1427
1441
  }, []);
1442
+ const focusChatArea = reactExports.useCallback(() => {
1443
+ const viewport = getViewport();
1444
+ if (!viewport) return;
1445
+ viewport.focus({ preventScroll: true });
1446
+ }, [getViewport]);
1447
+ useFocusTarget("chat-area", focusChatArea);
1428
1448
  const scrollToBottom = reactExports.useCallback((behavior) => {
1429
1449
  if (scrollTimeoutRef.current) {
1430
1450
  clearTimeout(scrollTimeoutRef.current);
@@ -1695,7 +1715,7 @@ function ChatArea({ session }) {
1695
1715
  const copyText$1 = reactExports.useCallback(async (text) => {
1696
1716
  return copyText(text);
1697
1717
  }, []);
1698
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full relative min-h-0 flex flex-1 overflow-hidden", children: [
1718
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full relative min-h-0 flex flex-1 group/chat-area", children: [
1699
1719
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 w-6 -mr-6 relative z-1 pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1700
1720
  ChatTimeline,
1701
1721
  {
@@ -1709,7 +1729,7 @@ function ChatArea({ session }) {
1709
1729
  {
1710
1730
  ref: scrollAreaRef,
1711
1731
  className: "flex-1 w-full transform-gpu",
1712
- viewportClassName: "animate-in fade-in",
1732
+ viewportClassName: "animate-in fade-in chat-area",
1713
1733
  children: [
1714
1734
  hasMore && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-10 py-3 max-w-5xl mx-auto", children: [
1715
1735
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 border-b border-dashed border-muted-foreground/30" }),
@@ -1879,7 +1899,8 @@ function ChatArea({ session }) {
1879
1899
  "aria-label": t("chatArea.scrollToBottom", "Scroll to bottom"),
1880
1900
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDown, { className: "size-4" })
1881
1901
  }
1882
- )
1902
+ ),
1903
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute -top-px left-0 right-0 h-px bg-linear-to-r from-ring/30 from-10% via-ring/80 via-30% to-ring/30 animate-out fill-mode-both fade-out ease-out duration-1500 hidden group-has-[.chat-area:focus-visible]/chat-area:block" })
1883
1904
  ] });
1884
1905
  }
1885
1906
  function _arrayWithoutHoles$1(arr) {
@@ -4314,6 +4335,669 @@ function nodesToMentionText(nodes) {
4314
4335
  return `@[${prefix}${n.name}](${n.id})`;
4315
4336
  }).join(" ");
4316
4337
  }
4338
+ const TARGET_SAMPLE_RATE = 16e3;
4339
+ const FRAME_SAMPLES = 320;
4340
+ const workletSource = `
4341
+ class FelloPcmProcessor extends AudioWorkletProcessor {
4342
+ constructor() {
4343
+ super();
4344
+ this.buffer = [];
4345
+ this.position = 0;
4346
+ this.output = [];
4347
+ this.ratio = sampleRate / ${TARGET_SAMPLE_RATE};
4348
+ }
4349
+ process(inputs, outputs) {
4350
+ const input = inputs[0] && inputs[0][0];
4351
+ if (input) {
4352
+ for (let i = 0; i < input.length; i++) this.buffer.push(input[i]);
4353
+ while (this.position + 1 < this.buffer.length) {
4354
+ const index = Math.floor(this.position);
4355
+ const fraction = this.position - index;
4356
+ const sample = this.buffer[index] * (1 - fraction) + this.buffer[index + 1] * fraction;
4357
+ const clamped = Math.max(-1, Math.min(1, sample));
4358
+ this.output.push(clamped < 0 ? clamped * 32768 : clamped * 32767);
4359
+ this.position += this.ratio;
4360
+ if (this.output.length >= ${FRAME_SAMPLES}) {
4361
+ const pcm = new Int16Array(${FRAME_SAMPLES});
4362
+ for (let i = 0; i < ${FRAME_SAMPLES}; i++) pcm[i] = this.output[i];
4363
+ this.output = this.output.slice(${FRAME_SAMPLES});
4364
+ this.port.postMessage(pcm.buffer, [pcm.buffer]);
4365
+ }
4366
+ }
4367
+ const remove = Math.max(0, Math.floor(this.position));
4368
+ if (remove > 0) {
4369
+ this.buffer = this.buffer.slice(remove);
4370
+ this.position -= remove;
4371
+ }
4372
+ }
4373
+ const output = outputs[0] && outputs[0][0];
4374
+ if (output) output.fill(0);
4375
+ return true;
4376
+ }
4377
+ }
4378
+ registerProcessor("fello-pcm-processor", FelloPcmProcessor);
4379
+ `;
4380
+ function arrayBufferToBase64(buffer) {
4381
+ const bytes = new Uint8Array(buffer);
4382
+ let binary = "";
4383
+ const chunkSize = 32768;
4384
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
4385
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
4386
+ }
4387
+ return btoa(binary);
4388
+ }
4389
+ function errorMessage(error) {
4390
+ return error instanceof Error ? error.message : String(error);
4391
+ }
4392
+ function useRealtimeAsr(options) {
4393
+ const { onTranscript, onError, onRecordingChange } = options;
4394
+ const configured = useAppStore((state) => state.speechToText.some((provider) => provider.active));
4395
+ const [recording, setRecording] = reactExports.useState(false);
4396
+ const [audioLevel, setAudioLevel] = reactExports.useState(0);
4397
+ const [inputDevices, setInputDevices] = reactExports.useState([]);
4398
+ const asrSessionIdRef = reactExports.useRef(null);
4399
+ const streamRef = reactExports.useRef(null);
4400
+ const audioContextRef = reactExports.useRef(null);
4401
+ const sourceRef = reactExports.useRef(null);
4402
+ const nodeRef = reactExports.useRef(null);
4403
+ const stoppedRef = reactExports.useRef(false);
4404
+ const stoppingRef = reactExports.useRef(false);
4405
+ const recordingRef = reactExports.useRef(false);
4406
+ const audioLevelValueRef = reactExports.useRef(0);
4407
+ const audioLevelFrameRef = reactExports.useRef(null);
4408
+ const stopRef = reactExports.useRef(async () => {
4409
+ });
4410
+ const callbacksRef = reactExports.useRef({ onTranscript, onError, onRecordingChange });
4411
+ callbacksRef.current = { onTranscript, onError, onRecordingChange };
4412
+ const setRecordingState = reactExports.useCallback((value) => {
4413
+ recordingRef.current = value;
4414
+ setRecording(value);
4415
+ callbacksRef.current.onRecordingChange?.(value);
4416
+ }, []);
4417
+ reactExports.useEffect(() => {
4418
+ const handleTranscript = (event) => {
4419
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
4420
+ callbacksRef.current.onTranscript?.({
4421
+ text: event.text,
4422
+ isFinal: event.isFinal,
4423
+ id: event.id,
4424
+ index: event.index
4425
+ });
4426
+ };
4427
+ const handleError = (event) => {
4428
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
4429
+ callbacksRef.current.onError?.(event.message);
4430
+ };
4431
+ const handleClosed = (event) => {
4432
+ if (event.clientId !== clientId || event.asrSessionId !== asrSessionIdRef.current) return;
4433
+ if (recordingRef.current && !stoppingRef.current) {
4434
+ callbacksRef.current.onError?.("实时语音识别连接已关闭。");
4435
+ void stopRef.current();
4436
+ }
4437
+ };
4438
+ subscribe.on("asr-transcript", handleTranscript);
4439
+ subscribe.on("asr-error", handleError);
4440
+ subscribe.on("asr-closed", handleClosed);
4441
+ return () => {
4442
+ subscribe.off("asr-transcript", handleTranscript);
4443
+ subscribe.off("asr-error", handleError);
4444
+ subscribe.off("asr-closed", handleClosed);
4445
+ };
4446
+ }, [setRecordingState]);
4447
+ const updateAudioLevel = reactExports.useCallback((buffer) => {
4448
+ const samples = new Int16Array(buffer);
4449
+ if (samples.length === 0) return;
4450
+ let sumSquares = 0;
4451
+ for (const sample of samples) {
4452
+ const normalized = sample / 32768;
4453
+ sumSquares += normalized * normalized;
4454
+ }
4455
+ const rms = Math.sqrt(sumSquares / samples.length);
4456
+ audioLevelValueRef.current = Math.min(1, rms * 4);
4457
+ if (audioLevelFrameRef.current !== null) return;
4458
+ audioLevelFrameRef.current = requestAnimationFrame(() => {
4459
+ audioLevelFrameRef.current = null;
4460
+ setAudioLevel(audioLevelValueRef.current);
4461
+ });
4462
+ }, []);
4463
+ const cleanupAudio = reactExports.useCallback(async () => {
4464
+ if (audioLevelFrameRef.current !== null) {
4465
+ cancelAnimationFrame(audioLevelFrameRef.current);
4466
+ audioLevelFrameRef.current = null;
4467
+ }
4468
+ audioLevelValueRef.current = 0;
4469
+ setAudioLevel(0);
4470
+ nodeRef.current?.disconnect();
4471
+ sourceRef.current?.disconnect();
4472
+ streamRef.current?.getTracks().forEach((track) => track.stop());
4473
+ streamRef.current = null;
4474
+ nodeRef.current = null;
4475
+ sourceRef.current = null;
4476
+ const context = audioContextRef.current;
4477
+ audioContextRef.current = null;
4478
+ await context?.close().catch(() => void 0);
4479
+ }, []);
4480
+ const refreshInputDevices = reactExports.useCallback(async () => {
4481
+ if (!navigator.mediaDevices?.enumerateDevices) return [];
4482
+ try {
4483
+ let devices = await navigator.mediaDevices.enumerateDevices();
4484
+ if (devices.length > 0 && devices.every((device) => !device.label)) {
4485
+ try {
4486
+ const permissionStream = await navigator.mediaDevices.getUserMedia({ audio: true });
4487
+ permissionStream.getTracks().forEach((track) => track.stop());
4488
+ devices = await navigator.mediaDevices.enumerateDevices();
4489
+ } catch {
4490
+ }
4491
+ }
4492
+ const audioInputs = devices.filter((device) => device.kind === "audioinput").map((device, index) => ({
4493
+ deviceId: device.deviceId,
4494
+ label: device.label || `Microphone ${index + 1}`
4495
+ }));
4496
+ setInputDevices(audioInputs);
4497
+ return audioInputs;
4498
+ } catch (error) {
4499
+ callbacksRef.current.onError?.(errorMessage(error));
4500
+ return [];
4501
+ }
4502
+ }, []);
4503
+ const start = reactExports.useCallback(
4504
+ async (deviceId = "default") => {
4505
+ if (recordingRef.current || !configured) return;
4506
+ const asrSessionId = `asr_${Date.now()}_${Math.random().toString(36).slice(2)}`;
4507
+ asrSessionIdRef.current = asrSessionId;
4508
+ stoppedRef.current = false;
4509
+ stoppingRef.current = false;
4510
+ try {
4511
+ const stream = await navigator.mediaDevices.getUserMedia({
4512
+ audio: {
4513
+ channelCount: 1,
4514
+ echoCancellation: true,
4515
+ noiseSuppression: true,
4516
+ ...deviceId !== "default" ? { deviceId: { exact: deviceId } } : {}
4517
+ }
4518
+ });
4519
+ streamRef.current = stream;
4520
+ const audioContext = new AudioContext();
4521
+ audioContextRef.current = audioContext;
4522
+ await audioContext.resume();
4523
+ const workletUrl = URL.createObjectURL(
4524
+ new Blob([workletSource], { type: "application/javascript" })
4525
+ );
4526
+ try {
4527
+ await audioContext.audioWorklet.addModule(workletUrl);
4528
+ } finally {
4529
+ URL.revokeObjectURL(workletUrl);
4530
+ }
4531
+ const source = audioContext.createMediaStreamSource(stream);
4532
+ const node = new AudioWorkletNode(audioContext, "fello-pcm-processor");
4533
+ const silentGain = audioContext.createGain();
4534
+ silentGain.gain.value = 0;
4535
+ node.port.onmessage = (message) => {
4536
+ if (stoppedRef.current || asrSessionIdRef.current !== asrSessionId) return;
4537
+ updateAudioLevel(message.data);
4538
+ void request.sendRealtimeAsrFrame({
4539
+ clientId,
4540
+ asrSessionId,
4541
+ audioB64: arrayBufferToBase64(message.data)
4542
+ }).catch((error) => callbacksRef.current.onError?.(errorMessage(error)));
4543
+ };
4544
+ sourceRef.current = source;
4545
+ nodeRef.current = node;
4546
+ await request.startRealtimeAsr({ clientId, asrSessionId });
4547
+ source.connect(node);
4548
+ node.connect(silentGain);
4549
+ silentGain.connect(audioContext.destination);
4550
+ setRecordingState(true);
4551
+ } catch (error) {
4552
+ await cleanupAudio();
4553
+ await request.stopRealtimeAsr({ clientId, asrSessionId }).catch(() => void 0);
4554
+ asrSessionIdRef.current = null;
4555
+ callbacksRef.current.onError?.(errorMessage(error));
4556
+ setRecordingState(false);
4557
+ }
4558
+ },
4559
+ [cleanupAudio, configured, setRecordingState, updateAudioLevel]
4560
+ );
4561
+ const stop = reactExports.useCallback(async () => {
4562
+ const asrSessionId = asrSessionIdRef.current;
4563
+ if (!asrSessionId) return;
4564
+ stoppedRef.current = true;
4565
+ stoppingRef.current = true;
4566
+ await cleanupAudio();
4567
+ await request.stopRealtimeAsr({ clientId, asrSessionId }).catch((error) => {
4568
+ callbacksRef.current.onError?.(errorMessage(error));
4569
+ });
4570
+ asrSessionIdRef.current = null;
4571
+ stoppingRef.current = false;
4572
+ setRecordingState(false);
4573
+ }, [cleanupAudio, setRecordingState]);
4574
+ stopRef.current = stop;
4575
+ reactExports.useEffect(() => {
4576
+ return () => {
4577
+ stoppedRef.current = true;
4578
+ void stopRef.current();
4579
+ };
4580
+ }, []);
4581
+ const toggle = reactExports.useCallback(() => {
4582
+ void (recordingRef.current ? stop() : start());
4583
+ }, [start, stop]);
4584
+ return {
4585
+ recording,
4586
+ audioLevel,
4587
+ configured,
4588
+ inputDevices,
4589
+ refreshInputDevices,
4590
+ start,
4591
+ stop,
4592
+ toggle
4593
+ };
4594
+ }
4595
+ const ASR_WAVEFORM_BARS = [0.35, 0.65, 0.95, 0.55, 0.8, 0.45, 0.9, 0.6, 0.38];
4596
+ const DEFAULT_MAX_DURATION = 5 * 60 * 1e3;
4597
+ const ALT_DOUBLE_PRESS_WINDOW = 350;
4598
+ const VOICE_INPUT_DEVICE_STORAGE_KEY = "fello.voice-input.device-id";
4599
+ let activeVoiceRecording = null;
4600
+ let voiceRecordingStartChain = Promise.resolve();
4601
+ function getInputCursor(input) {
4602
+ return input.selectionStart ?? input.value.length;
4603
+ }
4604
+ function VoiceInputButton({
4605
+ inputRef,
4606
+ disabled = false,
4607
+ className,
4608
+ maxDuration = DEFAULT_MAX_DURATION,
4609
+ ref
4610
+ }) {
4611
+ const { t } = useTranslation();
4612
+ const { toast } = useMessage();
4613
+ const altDoublePressEnabled = useAppStore((state) => state.voiceInput.altDoublePress);
4614
+ const [instanceId] = reactExports.useState(() => /* @__PURE__ */ Symbol("VoiceInputButton"));
4615
+ const stopAsrRef = reactExports.useRef(() => Promise.resolve());
4616
+ const asrApplyingRef = reactExports.useRef(false);
4617
+ const asrSegmentsRef = reactExports.useRef(/* @__PURE__ */ new Map());
4618
+ const asrTailRef = reactExports.useRef(0);
4619
+ const asrCurrentKeyRef = reactExports.useRef(null);
4620
+ const asrSequenceRef = reactExports.useRef(0);
4621
+ const lastAltTapAtRef = reactExports.useRef(null);
4622
+ const suppressAltUntilRef = reactExports.useRef(0);
4623
+ const freezeAsrSegments = reactExports.useCallback(() => {
4624
+ asrSegmentsRef.current.clear();
4625
+ asrCurrentKeyRef.current = null;
4626
+ const textarea = inputRef.current;
4627
+ if (textarea) asrTailRef.current = getInputCursor(textarea);
4628
+ }, [inputRef]);
4629
+ const applyAsrTranscript = reactExports.useCallback(
4630
+ (transcript) => {
4631
+ const textarea = inputRef.current;
4632
+ if (!textarea) return;
4633
+ const key = transcript.id ? `id:${transcript.id}` : transcript.index !== void 0 ? `index:${transcript.index}` : asrCurrentKeyRef.current ?? `stream:${asrSequenceRef.current++}`;
4634
+ let segment = asrSegmentsRef.current.get(key);
4635
+ if (!segment) {
4636
+ segment = { start: asrTailRef.current, length: 0 };
4637
+ asrSegmentsRef.current.set(key, segment);
4638
+ asrCurrentKeyRef.current = key;
4639
+ }
4640
+ textarea.focus();
4641
+ textarea.setSelectionRange(segment.start, segment.start + segment.length);
4642
+ asrApplyingRef.current = true;
4643
+ try {
4644
+ document.execCommand("insertText", false, transcript.text);
4645
+ } finally {
4646
+ asrApplyingRef.current = false;
4647
+ }
4648
+ textarea.ownerDocument.dispatchEvent(new Event("selectionchange"));
4649
+ segment.length = transcript.text.length;
4650
+ asrTailRef.current = segment.start + segment.length;
4651
+ if (transcript.isFinal) {
4652
+ asrSegmentsRef.current.delete(key);
4653
+ asrCurrentKeyRef.current = null;
4654
+ }
4655
+ },
4656
+ [inputRef]
4657
+ );
4658
+ const handleRecordingChange = reactExports.useCallback(
4659
+ (recording) => {
4660
+ if (recording) {
4661
+ activeVoiceRecording = {
4662
+ owner: instanceId,
4663
+ stop: () => stopAsrRef.current()
4664
+ };
4665
+ } else {
4666
+ freezeAsrSegments();
4667
+ if (activeVoiceRecording?.owner === instanceId) {
4668
+ activeVoiceRecording = null;
4669
+ }
4670
+ }
4671
+ },
4672
+ [freezeAsrSegments, instanceId]
4673
+ );
4674
+ const asr = useRealtimeAsr({
4675
+ onTranscript: applyAsrTranscript,
4676
+ onError: (message) => toast.error(message),
4677
+ onRecordingChange: handleRecordingChange
4678
+ });
4679
+ const { start: startAsr, stop: stopAsr, refreshInputDevices } = asr;
4680
+ const handleStop = reactExports.useCallback(async () => {
4681
+ await stopAsr();
4682
+ }, [stopAsr]);
4683
+ stopAsrRef.current = handleStop;
4684
+ reactExports.useImperativeHandle(
4685
+ ref,
4686
+ () => ({
4687
+ stop: handleStop
4688
+ }),
4689
+ [handleStop]
4690
+ );
4691
+ const handleStart = reactExports.useCallback(
4692
+ async (deviceId) => {
4693
+ const startOperation = voiceRecordingStartChain.then(async () => {
4694
+ await activeVoiceRecording?.stop();
4695
+ const textarea = inputRef.current;
4696
+ if (!textarea) return;
4697
+ textarea.focus();
4698
+ const cursorPosition = getInputCursor(textarea);
4699
+ const before = textarea.value.slice(0, cursorPosition);
4700
+ if (before.length > 0 && !/\s$/.test(before)) {
4701
+ asrApplyingRef.current = true;
4702
+ try {
4703
+ document.execCommand("insertText", false, " ");
4704
+ } finally {
4705
+ asrApplyingRef.current = false;
4706
+ }
4707
+ }
4708
+ asrSegmentsRef.current.clear();
4709
+ asrCurrentKeyRef.current = null;
4710
+ asrTailRef.current = getInputCursor(textarea);
4711
+ await startAsr(deviceId);
4712
+ });
4713
+ voiceRecordingStartChain = startOperation.catch(() => void 0);
4714
+ await startOperation;
4715
+ },
4716
+ [startAsr, inputRef]
4717
+ );
4718
+ const [savedDeviceId, setSavedDeviceId] = reactExports.useState(() => {
4719
+ try {
4720
+ return localStorage.getItem(VOICE_INPUT_DEVICE_STORAGE_KEY);
4721
+ } catch {
4722
+ return null;
4723
+ }
4724
+ });
4725
+ const [deviceMenuOpen, setDeviceMenuOpen] = reactExports.useState(false);
4726
+ const [starting, setStarting] = reactExports.useState(false);
4727
+ const rememberDevice = reactExports.useCallback((deviceId) => {
4728
+ setSavedDeviceId(deviceId);
4729
+ try {
4730
+ localStorage.setItem(VOICE_INPUT_DEVICE_STORAGE_KEY, deviceId);
4731
+ } catch {
4732
+ }
4733
+ }, []);
4734
+ const handleSelectDevice = reactExports.useCallback(
4735
+ (deviceId) => {
4736
+ rememberDevice(deviceId);
4737
+ setDeviceMenuOpen(false);
4738
+ },
4739
+ [rememberDevice]
4740
+ );
4741
+ const handleMicClick = reactExports.useCallback(async () => {
4742
+ if (disabled || !asr.configured || starting) return;
4743
+ setStarting(true);
4744
+ try {
4745
+ const devices = await refreshInputDevices();
4746
+ const hasSavedDevice = savedDeviceId != null && devices.some((device) => device.deviceId === savedDeviceId);
4747
+ if (hasSavedDevice) {
4748
+ await handleStart(savedDeviceId);
4749
+ } else {
4750
+ setDeviceMenuOpen(true);
4751
+ }
4752
+ } finally {
4753
+ setStarting(false);
4754
+ }
4755
+ }, [asr.configured, disabled, handleStart, refreshInputDevices, savedDeviceId, starting]);
4756
+ const handleShortcutToggle = reactExports.useCallback(async () => {
4757
+ if (disabled || !asr.configured || starting) return;
4758
+ if (asr.recording) {
4759
+ await handleStop();
4760
+ return;
4761
+ }
4762
+ setStarting(true);
4763
+ try {
4764
+ const devices = await refreshInputDevices();
4765
+ const hasSavedDevice = savedDeviceId != null && devices.some((device) => device.deviceId === savedDeviceId);
4766
+ await handleStart(hasSavedDevice ? savedDeviceId : void 0);
4767
+ } finally {
4768
+ setStarting(false);
4769
+ }
4770
+ }, [
4771
+ asr.configured,
4772
+ asr.recording,
4773
+ disabled,
4774
+ handleStart,
4775
+ handleStop,
4776
+ refreshInputDevices,
4777
+ savedDeviceId,
4778
+ starting
4779
+ ]);
4780
+ const handleDeviceMenuChange = reactExports.useCallback(
4781
+ (open) => {
4782
+ setDeviceMenuOpen(open);
4783
+ if (open) void refreshInputDevices();
4784
+ },
4785
+ [refreshInputDevices]
4786
+ );
4787
+ reactExports.useEffect(() => {
4788
+ lastAltTapAtRef.current = null;
4789
+ const handleKeyDown = (event) => {
4790
+ const input = inputRef.current;
4791
+ if (!input || document.activeElement !== input) {
4792
+ lastAltTapAtRef.current = null;
4793
+ return;
4794
+ }
4795
+ if (event.key === "Escape") {
4796
+ lastAltTapAtRef.current = null;
4797
+ suppressAltUntilRef.current = 0;
4798
+ if (!event.repeat && !event.ctrlKey && !event.metaKey && !event.shiftKey && asr.recording) {
4799
+ event.preventDefault();
4800
+ void handleStop();
4801
+ }
4802
+ return;
4803
+ }
4804
+ if (event.key !== "Alt") {
4805
+ lastAltTapAtRef.current = null;
4806
+ return;
4807
+ }
4808
+ if (!altDoublePressEnabled || event.repeat || event.ctrlKey || event.metaKey || event.shiftKey) {
4809
+ lastAltTapAtRef.current = null;
4810
+ return;
4811
+ }
4812
+ const now = Date.now();
4813
+ if (now < suppressAltUntilRef.current) {
4814
+ lastAltTapAtRef.current = null;
4815
+ return;
4816
+ }
4817
+ if (asr.recording) {
4818
+ lastAltTapAtRef.current = null;
4819
+ suppressAltUntilRef.current = now + ALT_DOUBLE_PRESS_WINDOW;
4820
+ event.preventDefault();
4821
+ void handleStop();
4822
+ return;
4823
+ }
4824
+ const lastTapAt = lastAltTapAtRef.current;
4825
+ if (lastTapAt !== null && now - lastTapAt <= ALT_DOUBLE_PRESS_WINDOW) {
4826
+ lastAltTapAtRef.current = null;
4827
+ event.preventDefault();
4828
+ void handleShortcutToggle();
4829
+ }
4830
+ };
4831
+ const handleKeyUp = (event) => {
4832
+ if (event.key !== "Alt") return;
4833
+ const input = inputRef.current;
4834
+ if (!input || document.activeElement !== input) {
4835
+ lastAltTapAtRef.current = null;
4836
+ return;
4837
+ }
4838
+ if (Date.now() < suppressAltUntilRef.current) {
4839
+ lastAltTapAtRef.current = null;
4840
+ return;
4841
+ }
4842
+ lastAltTapAtRef.current = Date.now();
4843
+ };
4844
+ const resetAltTap = () => {
4845
+ lastAltTapAtRef.current = null;
4846
+ suppressAltUntilRef.current = 0;
4847
+ };
4848
+ window.addEventListener("keydown", handleKeyDown);
4849
+ window.addEventListener("keyup", handleKeyUp);
4850
+ window.addEventListener("blur", resetAltTap);
4851
+ return () => {
4852
+ window.removeEventListener("keydown", handleKeyDown);
4853
+ window.removeEventListener("keyup", handleKeyUp);
4854
+ window.removeEventListener("blur", resetAltTap);
4855
+ };
4856
+ }, [altDoublePressEnabled, asr.recording, handleShortcutToggle, handleStop, inputRef]);
4857
+ reactExports.useEffect(() => {
4858
+ const textarea = inputRef.current;
4859
+ if (!textarea) return;
4860
+ const handleInput = () => {
4861
+ if (!asrApplyingRef.current && asr.recording) freezeAsrSegments();
4862
+ };
4863
+ textarea.addEventListener("input", handleInput);
4864
+ return () => textarea.removeEventListener("input", handleInput);
4865
+ }, [asr.recording, freezeAsrSegments, inputRef]);
4866
+ reactExports.useEffect(() => {
4867
+ if (disabled && asr.recording) void handleStop();
4868
+ }, [asr.recording, disabled, handleStop]);
4869
+ reactExports.useEffect(() => {
4870
+ if (!asr.recording || maxDuration <= 0 || !Number.isFinite(maxDuration)) return;
4871
+ const timer = setTimeout(() => void handleStop(), maxDuration);
4872
+ return () => clearTimeout(timer);
4873
+ }, [asr.recording, handleStop, maxDuration]);
4874
+ if (!asr.configured) return null;
4875
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4876
+ "div",
4877
+ {
4878
+ className: cn$1(
4879
+ "relative inline-flex h-7 transition-[width] duration-300 ease-out flex-none",
4880
+ asr.recording ? "w-18" : "w-12.5",
4881
+ className
4882
+ ),
4883
+ children: [
4884
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4885
+ "div",
4886
+ {
4887
+ className: cn$1(
4888
+ "absolute inset-0 transition-[opacity,transform,visibility] duration-300 ease-out",
4889
+ asr.recording ? "visible scale-100 opacity-100" : "pointer-events-none invisible scale-90 opacity-0"
4890
+ ),
4891
+ "aria-hidden": !asr.recording,
4892
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
4893
+ Button,
4894
+ {
4895
+ variant: "default",
4896
+ size: "sm",
4897
+ className: "h-full w-full rounded-lg bg-muted hover:bg-input border border-input text-amber-300/50 hover:text-amber-200",
4898
+ onClick: () => void handleStop(),
4899
+ "aria-label": t("chatInput.stopVoiceInput", "Stop voice input"),
4900
+ "aria-pressed": "true",
4901
+ title: t("chatInput.stopVoiceInput", "Stop voice input"),
4902
+ children: [
4903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { className: "size-3.5 shrink-0 animate-pulse" }),
4904
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4905
+ "span",
4906
+ {
4907
+ className: "flex h-4 flex-1 items-center justify-center gap-0.5 overflow-hidden",
4908
+ "aria-hidden": "true",
4909
+ children: ASR_WAVEFORM_BARS.map((scale, index) => /* @__PURE__ */ jsxRuntimeExports.jsx(
4910
+ "span",
4911
+ {
4912
+ className: "border-l rounded-full bg-amber-300 transition-[height] duration-75",
4913
+ style: {
4914
+ height: `${Math.max(2, Math.round(2 + asr.audioLevel * 16 * scale))}px`
4915
+ }
4916
+ },
4917
+ index
4918
+ ))
4919
+ }
4920
+ )
4921
+ ]
4922
+ }
4923
+ )
4924
+ }
4925
+ ),
4926
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4927
+ "div",
4928
+ {
4929
+ className: cn$1(
4930
+ "absolute inset-0 transition-[opacity,transform,visibility] duration-300 ease-out",
4931
+ asr.recording ? "pointer-events-none invisible scale-90 opacity-0" : "visible scale-100 opacity-100"
4932
+ ),
4933
+ "aria-hidden": asr.recording,
4934
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { open: deviceMenuOpen, onOpenChange: handleDeviceMenuChange, children: [
4935
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full items-stretch", children: [
4936
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4937
+ Button,
4938
+ {
4939
+ size: "sm",
4940
+ className: "h-full w-8 rounded-r-none border-r-0 bg-muted hover:bg-input border border-input text-amber-300/50 hover:text-amber-200",
4941
+ onClick: () => void handleMicClick(),
4942
+ disabled: disabled || !asr.configured || starting,
4943
+ "aria-busy": starting,
4944
+ "aria-label": starting ? t("chatInput.startingVoiceInput", "Starting voice input") : t("chatInput.voiceInput", "Voice input"),
4945
+ title: starting ? t("chatInput.startingVoiceInput", "Starting voice input") : asr.configured ? t("chatInput.voiceInput", "Voice input") : t("chatInput.voiceInputNotConfigured", "Configure voice input in Settings"),
4946
+ children: starting ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { className: "size-3.5" })
4947
+ }
4948
+ ),
4949
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4950
+ DropdownMenuTrigger,
4951
+ {
4952
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
4953
+ Button,
4954
+ {
4955
+ size: "icon",
4956
+ className: "h-full w-4.5 rounded-l-none bg-muted hover:bg-input border border-input border-l-0 px-0 text-amber-300/50 hover:text-amber-200",
4957
+ disabled: disabled || !asr.configured || starting,
4958
+ "aria-label": t("chatInput.selectVoiceInputDevice", "Select microphone"),
4959
+ title: t("chatInput.selectVoiceInputDevice", "Select microphone")
4960
+ }
4961
+ ),
4962
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" })
4963
+ }
4964
+ )
4965
+ ] }),
4966
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4967
+ DropdownMenuContent,
4968
+ {
4969
+ side: "top",
4970
+ align: "center",
4971
+ className: "w-auto! max-w-72 min-w-(--anchor-width)",
4972
+ children: asr.inputDevices.length > 0 ? asr.inputDevices.map((device) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
4973
+ DropdownMenuItem,
4974
+ {
4975
+ onClick: () => handleSelectDevice(device.deviceId),
4976
+ className: "gap-2",
4977
+ children: [
4978
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4979
+ Check,
4980
+ {
4981
+ className: cn$1(
4982
+ "size-3 shrink-0",
4983
+ device.deviceId === savedDeviceId ? "opacity-100" : "opacity-0"
4984
+ )
4985
+ }
4986
+ ),
4987
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-w-0 flex-col gap-0.5 pr-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: device.label }) })
4988
+ ]
4989
+ },
4990
+ device.deviceId
4991
+ )) : /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { disabled: true, children: t("chatInput.noAudioInputDevices", "No audio input devices found") })
4992
+ }
4993
+ )
4994
+ ] })
4995
+ }
4996
+ )
4997
+ ]
4998
+ }
4999
+ );
5000
+ }
4317
5001
  function readFileAsBase64(file) {
4318
5002
  return new Promise((resolve, reject) => {
4319
5003
  const reader = new FileReader();
@@ -4447,10 +5131,16 @@ function ChatInput({ session }) {
4447
5131
  const promptCapabilities = initializeInfo?.agentCapabilities?.promptCapabilities;
4448
5132
  const supportsImage = promptCapabilities?.image ?? false;
4449
5133
  const supportsEmbedded = promptCapabilities?.embeddedContext ?? false;
4450
- const getTextarea = reactExports.useCallback(
4451
- () => containerRef.current?.querySelector("textarea"),
4452
- []
4453
- );
5134
+ const textareaRef = reactExports.useRef(null);
5135
+ const voiceInputRef = reactExports.useRef(null);
5136
+ const getTextarea = reactExports.useCallback(() => textareaRef.current, []);
5137
+ const focusInput = reactExports.useCallback(() => {
5138
+ const textarea = getTextarea();
5139
+ if (!textarea || textarea.disabled) return false;
5140
+ textarea.focus({ preventScroll: true });
5141
+ return document.activeElement === textarea;
5142
+ }, [getTextarea]);
5143
+ useFocusTarget("chat-input", focusInput);
4454
5144
  const insertTriggerChar = reactExports.useCallback(
4455
5145
  (char) => {
4456
5146
  const textarea = getTextarea();
@@ -4617,6 +5307,7 @@ function ChatInput({ session }) {
4617
5307
  [localInput, availableCommands]
4618
5308
  );
4619
5309
  const handleSubmit = reactExports.useCallback(async () => {
5310
+ await voiceInputRef.current?.stop();
4620
5311
  v$2();
4621
5312
  const state = useAppStore.getState().getSessionState(session.id);
4622
5313
  const currentAttachments = state.draftAttachments;
@@ -4911,7 +5602,10 @@ function ChatInput({ session }) {
4911
5602
  MentionsInput$1,
4912
5603
  {
4913
5604
  value: localInput,
4914
- onChange: (_e3, newValue) => setLocalInput(newValue),
5605
+ inputRef: textareaRef,
5606
+ onChange: (_e3, newValue) => {
5607
+ setLocalInput(newValue);
5608
+ },
4915
5609
  onBlur: handleBlur,
4916
5610
  onKeyDown: handleKeyDown,
4917
5611
  placeholder: disabled ? t("chatInput.placeholderDisabled") : t("chatInput.placeholderActive"),
@@ -5005,14 +5699,14 @@ function ChatInput({ session }) {
5005
5699
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
5006
5700
  "div",
5007
5701
  {
5008
- className: "flex cursor-text items-center justify-between gap-2 px-2 pb-2 overflow-hidden",
5702
+ className: "flex cursor-text items-center justify-between gap-2 -mt-3 overflow-hidden",
5009
5703
  onClick: (e) => {
5010
5704
  const target = e.target;
5011
5705
  if (target.closest("button, select, [role='combobox']")) return;
5012
5706
  getTextarea()?.focus();
5013
5707
  },
5014
5708
  children: [
5015
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 overflow-hidden", children: [
5709
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 p-2 overflow-hidden -mr-4", children: [
5016
5710
  availableModes.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
5017
5711
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
5018
5712
  DropdownMenuTrigger,
@@ -5022,7 +5716,7 @@ function ChatInput({ session }) {
5022
5716
  {
5023
5717
  variant: "outline",
5024
5718
  size: "sm",
5025
- className: "h-7 text-xs text-muted-foreground hover:text-foreground gap-2 max-w-48 shrink overflow-hidden"
5719
+ className: "h-7 min-w-16 text-xs text-muted-foreground hover:text-foreground gap-2 max-w-48 shrink overflow-hidden"
5026
5720
  }
5027
5721
  ),
5028
5722
  children: [
@@ -5144,7 +5838,7 @@ function ChatInput({ session }) {
5144
5838
  ] })
5145
5839
  ] })
5146
5840
  ] }),
5147
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 overflow-hidden", children: [
5841
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 p-2 overflow-hidden", children: [
5148
5842
  availableModels.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
5149
5843
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
5150
5844
  DropdownMenuTrigger,
@@ -5154,7 +5848,7 @@ function ChatInput({ session }) {
5154
5848
  {
5155
5849
  variant: "outline",
5156
5850
  size: "sm",
5157
- className: "h-7 text-xs text-muted-foreground hover:text-foreground gap-2 max-w-48 shrink overflow-hidden"
5851
+ className: "h-7 min-w-16 text-xs text-muted-foreground hover:text-foreground gap-2 max-w-48 shrink overflow-hidden"
5158
5852
  }
5159
5853
  ),
5160
5854
  children: [
@@ -5290,6 +5984,7 @@ function ChatInput({ session }) {
5290
5984
  }
5291
5985
  )
5292
5986
  ] }),
5987
+ /* @__PURE__ */ jsxRuntimeExports.jsx(VoiceInputButton, { ref: voiceInputRef, inputRef: textareaRef, disabled }),
5293
5988
  isStreaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(
5294
5989
  Button,
5295
5990
  {
@@ -5333,14 +6028,14 @@ const mentionsInputStyle = {
5333
6028
  highlighter: {
5334
6029
  padding: "12px 16px 8px",
5335
6030
  border: "none",
5336
- maxHeight: 200
6031
+ maxHeight: "80vh"
5337
6032
  },
5338
6033
  input: {
5339
6034
  padding: "12px 16px 8px",
5340
6035
  border: "none",
5341
6036
  outline: "none",
5342
6037
  overflow: "auto",
5343
- maxHeight: 200,
6038
+ maxHeight: "80vh",
5344
6039
  color: "var(--foreground)",
5345
6040
  fontSize: 13,
5346
6041
  lineHeight: "1.5",
@@ -5433,12 +6128,15 @@ function AskUserDialog({ sessionId }) {
5433
6128
  "div",
5434
6129
  {
5435
6130
  ref: cardRef,
6131
+ role: "dialog",
6132
+ "aria-labelledby": "ask-user-dialog-title",
6133
+ "aria-describedby": currentRequest.description ? "ask-user-dialog-description" : void 0,
5436
6134
  onClick: () => setCollapsed(false),
5437
6135
  className: "grid grid-rows-[auto_1fr_auto] rounded-xl border border-border bg-card p-4 shadow-lg shadow-primary/5 max-h-[90vh]",
5438
6136
  children: [
5439
6137
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-3 min-h-0", children: [
5440
6138
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "size-4.5 shrink-0 text-sky-500" }),
5441
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium leading-snug truncate", children: currentRequest.title || t("askUser.title", "Request") }),
6139
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { id: "ask-user-dialog-title", className: "text-sm font-medium leading-snug truncate", children: currentRequest.title || t("askUser.title", "Request") }),
5442
6140
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center ml-auto gap-1", children: [
5443
6141
  currentRequest.timeoutAt != null && /* @__PURE__ */ jsxRuntimeExports.jsx(AskUserCountdown, { timeoutAt: currentRequest.timeoutAt }),
5444
6142
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -5452,13 +6150,14 @@ function AskUserDialog({ sessionId }) {
5452
6150
  setCollapsed((c) => !c);
5453
6151
  },
5454
6152
  "aria-label": collapsed ? t("askUser.expand", "Expand") : t("askUser.collapse", "Collapse"),
6153
+ "aria-expanded": !collapsed,
5455
6154
  title: collapsed ? t("askUser.expand", "Expand") : t("askUser.collapse", "Collapse"),
5456
6155
  children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
5457
6156
  }
5458
6157
  )
5459
6158
  ] })
5460
6159
  ] }),
5461
- currentRequest.description && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "h-full rounded-md bg-muted/40", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-3 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-muted-foreground leading-relaxed whitespace-pre-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: formatDescription(currentRequest.description) }) }) }) }) }),
6160
+ currentRequest.description && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { id: "ask-user-dialog-description", className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "h-full rounded-md bg-muted/40", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-3 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-muted-foreground leading-relaxed whitespace-pre-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: formatDescription(currentRequest.description) }) }) }) }) }),
5462
6161
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("pt-3 min-h-0", collapsed ? "pointer-events-none" : ""), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5463
6162
  AskUserOptions,
5464
6163
  {
@@ -5522,13 +6221,21 @@ function AskUserOptions({
5522
6221
  const [highlightedIndex, setHighlightedIndex] = reactExports.useState(null);
5523
6222
  const [isDragOver, setIsDragOver] = reactExports.useState(false);
5524
6223
  const inputContainerRef = reactExports.useRef(null);
6224
+ const optionRefs = reactExports.useRef([]);
6225
+ const otherButtonRef = reactExports.useRef(null);
6226
+ const textareaRef = reactExports.useRef(null);
6227
+ const voiceInputRef = reactExports.useRef(null);
5525
6228
  const navigate = useNavigate();
5526
6229
  const snippets = useAppStore((s15) => s15.snippets);
5527
6230
  const fileInputRef = reactExports.useRef(null);
5528
- const getTextarea = reactExports.useCallback(
5529
- () => inputContainerRef.current?.querySelector("textarea"),
5530
- []
5531
- );
6231
+ const getTextarea = reactExports.useCallback(() => textareaRef.current, []);
6232
+ const focusAskUser = reactExports.useCallback(() => {
6233
+ const target = mode === "input" ? textareaRef.current : optionRefs.current[0];
6234
+ if (!target || target.disabled) return false;
6235
+ target.focus({ preventScroll: true });
6236
+ return document.activeElement === target;
6237
+ }, [mode]);
6238
+ useFocusTarget("ask-user-dialog", focusAskUser);
5532
6239
  const insertTriggerChar = reactExports.useCallback(
5533
6240
  (char) => {
5534
6241
  const textarea = getTextarea();
@@ -5593,7 +6300,7 @@ function AskUserOptions({
5593
6300
  }
5594
6301
  if (paths.length === 0) return;
5595
6302
  void (async () => {
5596
- const textarea = inputContainerRef.current?.querySelector("textarea");
6303
+ const textarea = textareaRef.current;
5597
6304
  if (!textarea) return;
5598
6305
  textarea.focus();
5599
6306
  const tags = await Promise.all(
@@ -5679,7 +6386,8 @@ function AskUserOptions({
5679
6386
  window.addEventListener("keydown", handler);
5680
6387
  return () => window.removeEventListener("keydown", handler);
5681
6388
  }, [mode, hasOptions, request$1.options, handleSelectOption]);
5682
- const handleSubmitInput = () => {
6389
+ const handleSubmitInput = async () => {
6390
+ await voiceInputRef.current?.stop();
5683
6391
  const trimmed = resolveMentions(inputValue).trim();
5684
6392
  request.respondAskUser({
5685
6393
  sessionId: request$1.sessionId,
@@ -5693,35 +6401,57 @@ function AskUserOptions({
5693
6401
  if (e.nativeEvent.isComposing) return;
5694
6402
  if (e.key === "Enter" && !e.shiftKey) {
5695
6403
  e.preventDefault();
5696
- handleSubmitInput();
6404
+ void handleSubmitInput();
5697
6405
  }
5698
6406
  };
5699
6407
  reactExports.useEffect(() => {
5700
- if (mode === "input") {
5701
- inputContainerRef.current?.querySelector("textarea")?.focus();
5702
- }
6408
+ if (mode !== "input") return;
6409
+ textareaRef.current?.focus();
5703
6410
  }, [mode]);
6411
+ reactExports.useEffect(() => {
6412
+ if (mode !== "options" || !hasOptions) return;
6413
+ const frame = window.requestAnimationFrame(() => optionRefs.current[0]?.focus());
6414
+ return () => window.cancelAnimationFrame(frame);
6415
+ }, [hasOptions, mode]);
6416
+ const handleOptionKeyDown = (event, index) => {
6417
+ const { key } = event;
6418
+ if (key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End") {
6419
+ event.preventDefault();
6420
+ event.stopPropagation();
6421
+ if (key === "Home") {
6422
+ optionRefs.current[0]?.focus();
6423
+ } else if (key === "End") {
6424
+ if (showOther) otherButtonRef.current?.focus();
6425
+ else optionRefs.current[request$1.options.length - 1]?.focus();
6426
+ } else if (key === "ArrowDown") {
6427
+ if (index < request$1.options.length - 1) {
6428
+ optionRefs.current[index + 1]?.focus();
6429
+ } else if (showOther) {
6430
+ otherButtonRef.current?.focus();
6431
+ }
6432
+ } else if (index > 0) {
6433
+ optionRefs.current[index - 1]?.focus();
6434
+ }
6435
+ }
6436
+ };
5704
6437
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5705
6438
  mode === "options" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
5706
6439
  request$1.options.map((option, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
5707
- "div",
6440
+ "button",
5708
6441
  {
5709
- role: "button",
5710
- tabIndex: 0,
5711
- className: `relative flex w-full min-h-8 py-2 px-2 text-xs text-left rounded-lg border transition-all select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:translate-y-px ${highlightedIndex === index ? "ring-1 ring-sky-500 bg-sky-500/10 border-sky-500/30" : option.danger ? "bg-destructive/10 text-destructive hover:bg-destructive/20 border-destructive/20" : "bg-secondary/50 hover:bg-secondary hover:text-foreground"}`,
5712
- onClick: () => handleSelectOption(option.value),
5713
- onKeyDown: (e) => {
5714
- if (e.key === "Enter" || e.key === " ") {
5715
- e.preventDefault();
5716
- handleSelectOption(option.value);
5717
- }
6442
+ ref: (element) => {
6443
+ optionRefs.current[index] = element;
5718
6444
  },
6445
+ type: "button",
6446
+ className: `relative flex w-full min-h-8 py-2 px-2 text-xs text-left rounded-lg border transition-all select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:translate-y-px ${highlightedIndex === index ? "ring-1 ring-sky-500 bg-sky-500/10 border-sky-500/30" : option.danger ? "bg-destructive/10 text-destructive hover:bg-destructive/20 border-destructive/20" : "bg-secondary/50 hover:bg-secondary hover:text-foreground"}`,
6447
+ onClick: () => handleSelectOption(option.value),
6448
+ onKeyDown: (event) => handleOptionKeyDown(event, index),
5719
6449
  children: [
5720
6450
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-start gap-1.5 w-full", children: [
5721
6451
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex size-5 items-center justify-center rounded bg-muted-foreground/10 text-[10px] font-mono shrink-0 self-start mt-0.5", children: index + 1 }),
5722
6452
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 whitespace-normal self-start py-1", children: option.label })
5723
6453
  ] }),
5724
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0.5 right-1 text-[9px] leading-none text-muted-foreground/30 shrink-0 self-start font-mono", children: option.priority })
6454
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute top-0.5 right-1 text-[9px] leading-none text-muted-foreground/30 shrink-0 self-start font-mono", children: option.priority })
5725
6455
  ]
5726
6456
  },
5727
6457
  option.value
@@ -5729,10 +6459,22 @@ function AskUserOptions({
5729
6459
  showOther && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5730
6460
  Button,
5731
6461
  {
6462
+ ref: otherButtonRef,
5732
6463
  variant: "ghost",
5733
6464
  size: "sm",
5734
6465
  className: "justify-start h-8 px-2 text-xs text-muted-foreground",
5735
6466
  onClick: () => setMode("input"),
6467
+ onKeyDown: (event) => {
6468
+ if (event.key === "ArrowUp") {
6469
+ event.preventDefault();
6470
+ event.stopPropagation();
6471
+ optionRefs.current[request$1.options.length - 1]?.focus();
6472
+ } else if (event.key === "Home") {
6473
+ event.preventDefault();
6474
+ event.stopPropagation();
6475
+ optionRefs.current[0]?.focus();
6476
+ }
6477
+ },
5736
6478
  children: [
5737
6479
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("askUser.other", "Custom reply") }),
5738
6480
  /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { className: "size-3" })
@@ -5767,6 +6509,7 @@ function AskUserOptions({
5767
6509
  MentionsInput$1,
5768
6510
  {
5769
6511
  value: inputValue,
6512
+ inputRef: textareaRef,
5770
6513
  onChange: (_e3, newValue) => setInputValue(newValue),
5771
6514
  onKeyDown: handleKeyDown,
5772
6515
  placeholder: t("askUser.inputPlaceholder", "Type your response... (Enter to send)"),
@@ -5907,16 +6650,19 @@ function AskUserOptions({
5907
6650
  )) : /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { onClick: () => navigate("/settings/snippets"), children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: t("chatInput.snippetsEmpty", "No snippets. Click to add in Settings.") }) }) })
5908
6651
  ] })
5909
6652
  ] }),
5910
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5911
- Button,
5912
- {
5913
- size: "icon",
5914
- className: "size-7 rounded-lg",
5915
- onClick: handleSubmitInput,
5916
- "aria-label": t("askUser.submit", "Submit"),
5917
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { className: "size-3.5" })
5918
- }
5919
- )
6653
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
6654
+ /* @__PURE__ */ jsxRuntimeExports.jsx(VoiceInputButton, { ref: voiceInputRef, inputRef: textareaRef }),
6655
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6656
+ Button,
6657
+ {
6658
+ size: "icon",
6659
+ className: "size-7 rounded-lg",
6660
+ onClick: handleSubmitInput,
6661
+ "aria-label": t("askUser.submit", "Submit"),
6662
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { className: "size-3.5" })
6663
+ }
6664
+ )
6665
+ ] })
5920
6666
  ] })
5921
6667
  ]
5922
6668
  }
@@ -6128,7 +6874,7 @@ function useFile(projectId, file, options = {}) {
6128
6874
  };
6129
6875
  }
6130
6876
  const CodeEditor = reactExports.lazy(
6131
- () => __vitePreload(() => import("./code-editor-SuOcN-5B.js"), true ? __vite__mapDeps([0,1,2,3,4]) : void 0, import.meta.url).then((m) => ({ default: m.CodeEditor }))
6877
+ () => __vitePreload(() => import("./code-editor-tm0AYOLz.js"), true ? __vite__mapDeps([0,1,2,3,4]) : void 0, import.meta.url).then((m) => ({ default: m.CodeEditor }))
6132
6878
  );
6133
6879
  const MAX_EDIT_SIZE = 1024 * 1024;
6134
6880
  const EditPanel = reactExports.forwardRef(function EditPanel2({ projectId, filePath, content, loading = false, onCancel, onSaved }, ref) {
@@ -16699,6 +17445,9 @@ const GIT_SUMMARY_BADGES = [
16699
17445
  { key: "C", color: "text-yellow-500" },
16700
17446
  { key: "D", color: "text-red-500" }
16701
17447
  ];
17448
+ function fileNavigationItemKey(item) {
17449
+ return item.type === "action" ? `action:${item.action}` : `tree:${item.id}`;
17450
+ }
16702
17451
  function TreeItem({
16703
17452
  node,
16704
17453
  depth,
@@ -16712,7 +17461,10 @@ function TreeItem({
16712
17461
  onEditChange,
16713
17462
  onEditSubmit,
16714
17463
  onEditCancel,
16715
- actions
17464
+ actions,
17465
+ onKeyDown: onKeyDown2,
17466
+ registerRef,
17467
+ onFocus
16716
17468
  }) {
16717
17469
  const { t } = useTranslation();
16718
17470
  const isOpen = openFolders.has(node.id);
@@ -16723,6 +17475,15 @@ function TreeItem({
16723
17475
  reactExports.useEffect(() => {
16724
17476
  if (isEditing) setTimeout(() => inputRef.current?.focus(), 0);
16725
17477
  }, [isEditing]);
17478
+ const handleView = (e) => {
17479
+ if (node.isFolder) {
17480
+ actions.toggle(node);
17481
+ actions.select(node.id, e);
17482
+ } else {
17483
+ actions.previewFile(node.id);
17484
+ actions.select(node.id, e);
17485
+ }
17486
+ };
16726
17487
  const childProps = {
16727
17488
  selectedIds,
16728
17489
  openFolders,
@@ -16733,7 +17494,10 @@ function TreeItem({
16733
17494
  onEditChange,
16734
17495
  onEditSubmit,
16735
17496
  onEditCancel,
16736
- actions
17497
+ actions,
17498
+ onKeyDown: onKeyDown2,
17499
+ registerRef,
17500
+ onFocus
16737
17501
  };
16738
17502
  const status = gitStatusMap.get(node.id);
16739
17503
  let statusText = "";
@@ -16770,7 +17534,22 @@ function TreeItem({
16770
17534
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
16771
17535
  ContextMenuTrigger,
16772
17536
  {
16773
- render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
17537
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
17538
+ "div",
17539
+ {
17540
+ ref: (element) => registerRef(node.id, element),
17541
+ tabIndex: -1,
17542
+ role: "treeitem",
17543
+ "aria-level": depth + 1,
17544
+ "aria-expanded": node.isFolder ? isOpen : void 0,
17545
+ "aria-selected": isSelected,
17546
+ className: "outline-0 focus-visible:inset-ring-1 focus-visible:inset-ring-ring/80 rounded-md",
17547
+ onKeyDown: (e) => {
17548
+ if (e.target === e.currentTarget) onKeyDown2(e, node);
17549
+ },
17550
+ onFocus: () => onFocus(node.id)
17551
+ }
17552
+ ),
16774
17553
  draggable: !isEditing,
16775
17554
  onDragStart: (e) => {
16776
17555
  e.stopPropagation();
@@ -16788,7 +17567,6 @@ function TreeItem({
16788
17567
  },
16789
17568
  className: cn$1(
16790
17569
  "flex h-6 cursor-default select-none items-center gap-1.5 px-1.5 text-sx leading-none text-foreground/60",
16791
- node.ignored && "opacity-50",
16792
17570
  node.id === previewId ? "bg-primary/8 hover:bg-primary/10" : "hover:bg-primary/5 hover:text-foreground",
16793
17571
  isSelected ? "text-foreground bg-primary/6" : "",
16794
17572
  isDragOver && "relative ring-1 ring-primary bg-primary/5"
@@ -16800,13 +17578,7 @@ function TreeItem({
16800
17578
  actions.select(node.id, e);
16801
17579
  return;
16802
17580
  }
16803
- if (node.isFolder) {
16804
- actions.toggle(node);
16805
- actions.select(node.id, e);
16806
- } else {
16807
- actions.previewFile(node.id);
16808
- actions.select(node.id, e);
16809
- }
17581
+ handleView(e);
16810
17582
  },
16811
17583
  children: [
16812
17584
  node.isFolder ? /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -16814,10 +17586,11 @@ function TreeItem({
16814
17586
  {
16815
17587
  className: cn$1(
16816
17588
  "size-3.5 shrink-0 text-muted-foreground scale-110 transition-transform",
16817
- isOpen && "rotate-90"
17589
+ isOpen && "rotate-90",
17590
+ node.ignored && "opacity-50"
16818
17591
  )
16819
17592
  }
16820
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(FileIcon, { name: node.name, className: "size-3.5 shrink-0 text-muted-foreground/90" }),
17593
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(FileIcon, { name: node.name, className: cn$1("size-3.5 shrink-0 text-muted-foreground/90", node.ignored && "opacity-50") }),
16821
17594
  isEditing ? /* @__PURE__ */ jsxRuntimeExports.jsx(
16822
17595
  "input",
16823
17596
  {
@@ -16838,7 +17611,8 @@ function TreeItem({
16838
17611
  {
16839
17612
  className: cn$1(
16840
17613
  "flex-1 truncate leading-normal",
16841
- statusText && !node.isFolder && statusColor
17614
+ statusText && !node.isFolder && statusColor,
17615
+ node.ignored && "opacity-50"
16842
17616
  ),
16843
17617
  children: node.name
16844
17618
  }
@@ -16858,6 +17632,11 @@ function TreeItem({
16858
17632
  }
16859
17633
  ),
16860
17634
  /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { children: [
17635
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: handleView, children: [
17636
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, {}),
17637
+ t("filePanel.view")
17638
+ ] }),
17639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
16861
17640
  node.isFolder ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
16862
17641
  /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => actions.createIn(node.id, false), children: [
16863
17642
  /* @__PURE__ */ jsxRuntimeExports.jsx(FilePlus, {}),
@@ -16997,7 +17776,13 @@ const FilePanel = reactExports.memo(function FilePanel2({
16997
17776
  onPreviewFile
16998
17777
  }) {
16999
17778
  const { t } = useTranslation();
17000
- const containerRef = reactExports.useRef(null);
17779
+ const treeRef = reactExports.useRef(null);
17780
+ const treeItemRefs = reactExports.useRef(/* @__PURE__ */ new Map());
17781
+ const navigationActionRefs = reactExports.useRef(/* @__PURE__ */ new Map());
17782
+ const lastFocusedActionRef = reactExports.useRef(null);
17783
+ const focusedTreeItemIdRef = reactExports.useRef(null);
17784
+ const pendingFocusIdRef = reactExports.useRef(null);
17785
+ const pendingFocusRestoreRef = reactExports.useRef(false);
17001
17786
  const [data, setData] = reactExports.useState([]);
17002
17787
  const [loading, setLoading] = reactExports.useState(false);
17003
17788
  const [showLoading, setShowLoading] = reactExports.useState(false);
@@ -17208,6 +17993,144 @@ const FilePanel = reactExports.memo(function FilePanel2({
17208
17993
  },
17209
17994
  [openFolders]
17210
17995
  );
17996
+ const registerTreeItemRef = reactExports.useCallback((id, element) => {
17997
+ if (element) {
17998
+ treeItemRefs.current.set(id, element);
17999
+ } else {
18000
+ treeItemRefs.current.delete(id);
18001
+ }
18002
+ }, []);
18003
+ const registerNavigationActionRef = reactExports.useCallback(
18004
+ (action, element) => {
18005
+ if (element) {
18006
+ navigationActionRefs.current.set(action, element);
18007
+ } else {
18008
+ navigationActionRefs.current.delete(action);
18009
+ }
18010
+ },
18011
+ []
18012
+ );
18013
+ const handleTreeItemFocus = reactExports.useCallback((id) => {
18014
+ focusedTreeItemIdRef.current = id;
18015
+ }, []);
18016
+ const focusTreeItem = reactExports.useCallback(
18017
+ (id) => {
18018
+ let element = treeItemRefs.current.get(id);
18019
+ if (!element) {
18020
+ const visibleIds = flattenTree(data);
18021
+ const itemIndex = visibleIds.indexOf(id);
18022
+ if (itemIndex !== -1) {
18023
+ element = treeRef.current?.querySelectorAll('[role="treeitem"]')[itemIndex];
18024
+ }
18025
+ }
18026
+ if (!element) return;
18027
+ element.focus({ preventScroll: true });
18028
+ element.scrollIntoView({ block: "nearest" });
18029
+ },
18030
+ [data, flattenTree]
18031
+ );
18032
+ const fileNavigationItems = reactExports.useMemo(() => {
18033
+ const items = [];
18034
+ if (!isWebUI && cwd) {
18035
+ items.push({ type: "action", action: "reveal" });
18036
+ items.push({ type: "action", action: "editor" });
18037
+ }
18038
+ if (!loading) {
18039
+ items.push(
18040
+ { type: "action", action: "new-file" },
18041
+ { type: "action", action: "new-folder" },
18042
+ { type: "action", action: "refresh" },
18043
+ { type: "action", action: "collapse" }
18044
+ );
18045
+ }
18046
+ items.push(...flattenTree(data).map((id) => ({ type: "tree", id })));
18047
+ return items;
18048
+ }, [cwd, data, flattenTree, loading]);
18049
+ const fileActionItems = reactExports.useMemo(
18050
+ () => fileNavigationItems.filter(
18051
+ (item) => item.type === "action"
18052
+ ),
18053
+ [fileNavigationItems]
18054
+ );
18055
+ const treeNavigationItems = reactExports.useMemo(
18056
+ () => fileNavigationItems.filter(
18057
+ (item) => item.type === "tree"
18058
+ ),
18059
+ [fileNavigationItems]
18060
+ );
18061
+ const focusFileNavigationItem = reactExports.useCallback(
18062
+ (item) => {
18063
+ if (item.type === "action") {
18064
+ const element = navigationActionRefs.current.get(item.action);
18065
+ if (!element) return;
18066
+ element.focus({ preventScroll: true });
18067
+ return;
18068
+ }
18069
+ focusTreeItem(item.id);
18070
+ },
18071
+ [focusTreeItem]
18072
+ );
18073
+ const handleFileNavigationKeyDown = reactExports.useCallback(
18074
+ (event, items, item) => {
18075
+ event.preventDefault();
18076
+ event.stopPropagation();
18077
+ if (items.length === 0) return;
18078
+ const currentIndex = items.findIndex(
18079
+ (navigationItem) => fileNavigationItemKey(navigationItem) === fileNavigationItemKey(item)
18080
+ );
18081
+ const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : Math.max(
18082
+ 0,
18083
+ Math.min(
18084
+ items.length - 1,
18085
+ currentIndex + (event.key === "ArrowRight" || event.key === "ArrowDown" ? 1 : -1)
18086
+ )
18087
+ );
18088
+ const nextItem = items[nextIndex];
18089
+ if (nextItem) focusFileNavigationItem(nextItem);
18090
+ },
18091
+ [focusFileNavigationItem]
18092
+ );
18093
+ const focusFileTree = reactExports.useCallback(() => {
18094
+ const visibleIds = flattenTree(data);
18095
+ const targetId = lastSelectedId && visibleIds.includes(lastSelectedId) && lastSelectedId || visibleIds.find((id) => selectedIds.has(id)) || visibleIds[0];
18096
+ if (targetId) {
18097
+ focusTreeItem(targetId);
18098
+ } else if (fileActionItems[0]) {
18099
+ focusFileNavigationItem(fileActionItems[0]);
18100
+ }
18101
+ }, [
18102
+ data,
18103
+ fileActionItems,
18104
+ flattenTree,
18105
+ focusFileNavigationItem,
18106
+ focusTreeItem,
18107
+ lastSelectedId,
18108
+ selectedIds
18109
+ ]);
18110
+ useFocusTarget("file-tree-content", focusFileTree);
18111
+ reactExports.useEffect(() => {
18112
+ if (!pendingFocusRestoreRef.current || loading) return;
18113
+ const visibleIds = flattenTree(data);
18114
+ const pendingId = pendingFocusIdRef.current;
18115
+ const targetId = pendingId && visibleIds.includes(pendingId) ? pendingId : visibleIds[0];
18116
+ const fallbackAction = fileActionItems[0];
18117
+ pendingFocusRestoreRef.current = false;
18118
+ pendingFocusIdRef.current = null;
18119
+ window.requestAnimationFrame(() => {
18120
+ if (targetId) {
18121
+ focusTreeItem(targetId);
18122
+ } else if (fallbackAction) {
18123
+ focusFileNavigationItem(fallbackAction);
18124
+ }
18125
+ });
18126
+ }, [
18127
+ data,
18128
+ fileActionItems,
18129
+ flattenTree,
18130
+ focusFileNavigationItem,
18131
+ focusTreeItem,
18132
+ loading
18133
+ ]);
17211
18134
  reactExports.useEffect(() => {
17212
18135
  useAppStore.getState().updateProjectState(projectId, () => ({
17213
18136
  openFolders: Array.from(openFolders)
@@ -17259,6 +18182,86 @@ const FilePanel = reactExports.memo(function FilePanel2({
17259
18182
  },
17260
18183
  [activeProjectId]
17261
18184
  );
18185
+ const handleTreeKeyDown = reactExports.useCallback(
18186
+ (event, node) => {
18187
+ const { key } = event;
18188
+ const isVerticalNavigationKey = key === "ArrowUp" || key === "ArrowDown" || key === "Home" || key === "End";
18189
+ if (isVerticalNavigationKey) {
18190
+ const treeItem = { type: "tree", id: node.id };
18191
+ const currentIndex = treeNavigationItems.findIndex((item) => item.id === node.id);
18192
+ if (key === "ArrowUp" && currentIndex === 0) {
18193
+ const actionItem = fileActionItems.find((item) => item.action === lastFocusedActionRef.current) ?? fileActionItems[0];
18194
+ if (actionItem) {
18195
+ event.preventDefault();
18196
+ event.stopPropagation();
18197
+ focusFileNavigationItem(actionItem);
18198
+ return;
18199
+ }
18200
+ }
18201
+ handleFileNavigationKeyDown(event, treeNavigationItems, treeItem);
18202
+ return;
18203
+ }
18204
+ if (key === "/") {
18205
+ event.preventDefault();
18206
+ event.stopPropagation();
18207
+ const target = event.currentTarget;
18208
+ const rect = target.getBoundingClientRect();
18209
+ target.dispatchEvent(
18210
+ new MouseEvent("contextmenu", {
18211
+ bubbles: true,
18212
+ cancelable: true,
18213
+ view: window,
18214
+ button: 2,
18215
+ buttons: 2,
18216
+ clientX: rect.left + rect.width / 2,
18217
+ clientY: rect.top + rect.height / 2
18218
+ })
18219
+ );
18220
+ return;
18221
+ }
18222
+ if (key === "ArrowLeft" || key === "ArrowRight") {
18223
+ event.preventDefault();
18224
+ event.stopPropagation();
18225
+ if (key === "ArrowRight" && node.isFolder) {
18226
+ if (!openFolders.has(node.id)) {
18227
+ toggle(node);
18228
+ } else {
18229
+ const firstChild = node.children?.[0];
18230
+ if (firstChild) focusTreeItem(firstChild.id);
18231
+ }
18232
+ } else if (key === "ArrowLeft") {
18233
+ if (node.isFolder && openFolders.has(node.id)) {
18234
+ toggle(node);
18235
+ } else {
18236
+ const parentId = node.id.slice(0, node.id.lastIndexOf("/"));
18237
+ if (parentId) focusTreeItem(parentId);
18238
+ }
18239
+ }
18240
+ return;
18241
+ }
18242
+ if (key === "Enter" || key === " " || key === "Spacebar") {
18243
+ event.preventDefault();
18244
+ event.stopPropagation();
18245
+ setSelectedIds(/* @__PURE__ */ new Set([node.id]));
18246
+ setLastSelectedId(node.id);
18247
+ if (node.isFolder) {
18248
+ toggle(node);
18249
+ } else {
18250
+ onPreviewFile(node.id);
18251
+ }
18252
+ }
18253
+ },
18254
+ [
18255
+ fileActionItems,
18256
+ focusFileNavigationItem,
18257
+ focusTreeItem,
18258
+ handleFileNavigationKeyDown,
18259
+ onPreviewFile,
18260
+ openFolders,
18261
+ toggle,
18262
+ treeNavigationItems
18263
+ ]
18264
+ );
17262
18265
  const collapseAll = reactExports.useCallback(() => {
17263
18266
  setOpenFolders(/* @__PURE__ */ new Set());
17264
18267
  openFoldersRef.current = /* @__PURE__ */ new Set();
@@ -17397,6 +18400,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17397
18400
  request.getPlatform().then((p) => setOsPlatform(p));
17398
18401
  }, []);
17399
18402
  const trashLabel = osPlatform === "darwin" ? t("filePanel.moveToTrash") : osPlatform === "win32" ? t("filePanel.moveToRecycleBin") : t("filePanel.moveToTrash");
18403
+ const prepareFocusAfterDelete = (ids) => {
18404
+ const isDeletedId = (id) => ids.some((deletedId) => id === deletedId || id.startsWith(`${deletedId}/`));
18405
+ const referenceId = (focusedTreeItemIdRef.current && isDeletedId(focusedTreeItemIdRef.current) ? focusedTreeItemIdRef.current : null) ?? (lastSelectedId && isDeletedId(lastSelectedId) ? lastSelectedId : null) ?? ids.find(isDeletedId);
18406
+ if (!referenceId) return;
18407
+ const visibleIds = flattenTree(data);
18408
+ const referenceIndex = visibleIds.indexOf(referenceId);
18409
+ const targetId = (referenceIndex !== -1 ? visibleIds.slice(referenceIndex + 1).find((id) => !isDeletedId(id)) : void 0) ?? (referenceIndex !== -1 ? [...visibleIds.slice(0, referenceIndex)].reverse().find((id) => !isDeletedId(id)) : void 0) ?? visibleIds.find((id) => !isDeletedId(id));
18410
+ pendingFocusIdRef.current = targetId ?? null;
18411
+ pendingFocusRestoreRef.current = true;
18412
+ };
17400
18413
  const deleteNode = async (ids) => {
17401
18414
  if (ids.length === 0 || !activeProjectId) return;
17402
18415
  await confirm2({
@@ -17425,7 +18438,9 @@ const FilePanel = reactExports.memo(function FilePanel2({
17425
18438
  } catch (err) {
17426
18439
  console.error("Delete failed:", extractErrorMessage(err));
17427
18440
  }
18441
+ prepareFocusAfterDelete(ids);
17428
18442
  setSelectedIds(/* @__PURE__ */ new Set());
18443
+ setLastSelectedId(null);
17429
18444
  refresh();
17430
18445
  return "trashed";
17431
18446
  }
@@ -17443,7 +18458,9 @@ const FilePanel = reactExports.memo(function FilePanel2({
17443
18458
  } catch (err) {
17444
18459
  console.error("Delete failed:", extractErrorMessage(err));
17445
18460
  }
18461
+ prepareFocusAfterDelete(ids);
17446
18462
  setSelectedIds(/* @__PURE__ */ new Set());
18463
+ setLastSelectedId(null);
17447
18464
  refresh();
17448
18465
  return "deleted";
17449
18466
  }
@@ -17884,6 +18901,47 @@ const FilePanel = reactExports.memo(function FilePanel2({
17884
18901
  }
17885
18902
  return null;
17886
18903
  };
18904
+ const handleFileActionKeyDown = (event, action) => {
18905
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "Home" || event.key === "End") {
18906
+ handleFileNavigationKeyDown(event, fileActionItems, { type: "action", action });
18907
+ return;
18908
+ }
18909
+ if (event.key === "ArrowDown") {
18910
+ event.preventDefault();
18911
+ event.stopPropagation();
18912
+ const firstTreeItem = treeNavigationItems[0];
18913
+ if (firstTreeItem) focusFileNavigationItem(firstTreeItem);
18914
+ return;
18915
+ }
18916
+ if (event.key !== "Enter" && event.key !== " " && event.key !== "Spacebar") return;
18917
+ event.preventDefault();
18918
+ event.stopPropagation();
18919
+ switch (action) {
18920
+ case "reveal":
18921
+ if (cwd) void revealInFinder(cwd);
18922
+ break;
18923
+ case "editor":
18924
+ if (cwd) {
18925
+ const editorName = useAppStore.getState().editor.name;
18926
+ void electron.openInEditor(cwd, editorName).catch((err) => {
18927
+ console.error("openInEditor failed:", extractErrorMessage(err));
18928
+ });
18929
+ }
18930
+ break;
18931
+ case "new-file":
18932
+ createIn(getSelectedFolder(), false);
18933
+ break;
18934
+ case "new-folder":
18935
+ createIn(getSelectedFolder(), true);
18936
+ break;
18937
+ case "refresh":
18938
+ refresh();
18939
+ break;
18940
+ case "collapse":
18941
+ collapseAll();
18942
+ break;
18943
+ }
18944
+ };
17887
18945
  if (!projectId) {
17888
18946
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center text-xs text-muted-foreground", children: t("filePanel.noProject", "No project selected") });
17889
18947
  }
@@ -17903,9 +18961,12 @@ const FilePanel = reactExports.memo(function FilePanel2({
17903
18961
  onEditChange: setEditingValue,
17904
18962
  onEditSubmit: submitEdit,
17905
18963
  onEditCancel: () => setEditingId(null),
17906
- actions
18964
+ actions,
18965
+ onKeyDown: handleTreeKeyDown,
18966
+ registerRef: registerTreeItemRef,
18967
+ onFocus: handleTreeItemFocus
17907
18968
  };
17908
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "flex h-full min-h-0 flex-col text-xs w-full", children: [
18969
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full min-h-0 flex-col text-xs w-full", children: [
17909
18970
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-10 items-center gap-0.5 border-b border-border", children: [
17910
18971
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex text-muted-foreground items-center gap-1 px-3", children: [
17911
18972
  /* @__PURE__ */ jsxRuntimeExports.jsx(Folders, { className: "size-3.5" }),
@@ -17916,10 +18977,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17916
18977
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17917
18978
  Button,
17918
18979
  {
18980
+ ref: (element) => registerNavigationActionRef("reveal", element),
17919
18981
  variant: "ghost",
17920
18982
  size: "icon",
18983
+ tabIndex: -1,
17921
18984
  className: "size-6 text-muted-foreground hover:text-foreground",
17922
18985
  disabled: !cwd,
18986
+ onKeyDown: (event) => handleFileActionKeyDown(event, "reveal"),
18987
+ onFocus: () => {
18988
+ lastFocusedActionRef.current = "reveal";
18989
+ },
17923
18990
  onClick: () => revealInFinder(cwd),
17924
18991
  title: t("filePanel.revealInFinder"),
17925
18992
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-3.5" })
@@ -17928,10 +18995,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17928
18995
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17929
18996
  Button,
17930
18997
  {
18998
+ ref: (element) => registerNavigationActionRef("editor", element),
17931
18999
  variant: "ghost",
17932
19000
  size: "icon",
19001
+ tabIndex: -1,
17933
19002
  className: "size-6 text-muted-foreground hover:text-foreground",
17934
19003
  disabled: !cwd,
19004
+ onKeyDown: (event) => handleFileActionKeyDown(event, "editor"),
19005
+ onFocus: () => {
19006
+ lastFocusedActionRef.current = "editor";
19007
+ },
17935
19008
  onClick: () => {
17936
19009
  if (!cwd) return;
17937
19010
  const editorName = useAppStore.getState().editor.name;
@@ -17949,10 +19022,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17949
19022
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17950
19023
  Button,
17951
19024
  {
19025
+ ref: (element) => registerNavigationActionRef("new-file", element),
17952
19026
  variant: "ghost",
17953
19027
  size: "icon",
19028
+ tabIndex: -1,
17954
19029
  className: "size-6 text-muted-foreground hover:text-foreground",
17955
19030
  disabled: loading,
19031
+ onKeyDown: (event) => handleFileActionKeyDown(event, "new-file"),
19032
+ onFocus: () => {
19033
+ lastFocusedActionRef.current = "new-file";
19034
+ },
17956
19035
  onClick: () => createIn(getSelectedFolder(), false),
17957
19036
  title: t("filePanel.newFile"),
17958
19037
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(FilePlus, { className: "size-3.5" })
@@ -17961,10 +19040,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17961
19040
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17962
19041
  Button,
17963
19042
  {
19043
+ ref: (element) => registerNavigationActionRef("new-folder", element),
17964
19044
  variant: "ghost",
17965
19045
  size: "icon",
19046
+ tabIndex: -1,
17966
19047
  className: "size-6 text-muted-foreground hover:text-foreground",
17967
19048
  disabled: loading,
19049
+ onKeyDown: (event) => handleFileActionKeyDown(event, "new-folder"),
19050
+ onFocus: () => {
19051
+ lastFocusedActionRef.current = "new-folder";
19052
+ },
17968
19053
  onClick: () => createIn(getSelectedFolder(), true),
17969
19054
  title: t("filePanel.newFolder"),
17970
19055
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderPlus, { className: "size-3.5" })
@@ -17973,10 +19058,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17973
19058
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17974
19059
  Button,
17975
19060
  {
19061
+ ref: (element) => registerNavigationActionRef("refresh", element),
17976
19062
  variant: "ghost",
17977
19063
  size: "icon",
19064
+ tabIndex: -1,
17978
19065
  className: "size-6 text-muted-foreground hover:text-foreground",
17979
19066
  disabled: loading,
19067
+ onKeyDown: (event) => handleFileActionKeyDown(event, "refresh"),
19068
+ onFocus: () => {
19069
+ lastFocusedActionRef.current = "refresh";
19070
+ },
17980
19071
  onClick: refresh,
17981
19072
  title: t("filePanel.refresh", "Refresh"),
17982
19073
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5" })
@@ -17985,10 +19076,16 @@ const FilePanel = reactExports.memo(function FilePanel2({
17985
19076
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17986
19077
  Button,
17987
19078
  {
19079
+ ref: (element) => registerNavigationActionRef("collapse", element),
17988
19080
  variant: "ghost",
17989
19081
  size: "icon",
19082
+ tabIndex: -1,
17990
19083
  className: "size-6 text-muted-foreground hover:text-foreground",
17991
19084
  disabled: loading,
19085
+ onKeyDown: (event) => handleFileActionKeyDown(event, "collapse"),
19086
+ onFocus: () => {
19087
+ lastFocusedActionRef.current = "collapse";
19088
+ },
17992
19089
  onClick: collapseAll,
17993
19090
  title: t("filePanel.collapseFolders"),
17994
19091
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ListChevronsDownUp, { className: "size-3.5" })
@@ -18010,11 +19107,18 @@ const FilePanel = reactExports.memo(function FilePanel2({
18010
19107
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground/70", children: t("filePanel.emptyDirectory") })
18011
19108
  ]
18012
19109
  }
18013
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1 pl-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenu, { children: [
19110
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1 px-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenu, { children: [
18014
19111
  /* @__PURE__ */ jsxRuntimeExports.jsx(
18015
19112
  ContextMenuTrigger,
18016
19113
  {
18017
- render: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
19114
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
19115
+ "div",
19116
+ {
19117
+ ref: treeRef,
19118
+ role: "tree",
19119
+ "aria-label": t("filePanel.title", "Files")
19120
+ }
19121
+ ),
18018
19122
  className: "min-h-full py-1",
18019
19123
  onClick: (e) => {
18020
19124
  if (e.target === e.currentTarget) clearSelection();
@@ -18142,6 +19246,10 @@ function findNode(node, id) {
18142
19246
  }
18143
19247
  return null;
18144
19248
  }
19249
+ const ADD_TERMINAL_NAVIGATION_ID = "__add_terminal__";
19250
+ function terminalNavigationItemKey(item) {
19251
+ return item.type === "add" ? ADD_TERMINAL_NAVIGATION_ID : item.id;
19252
+ }
18145
19253
  function TerminalPanel({
18146
19254
  projectId,
18147
19255
  activeTerminalId,
@@ -18151,7 +19259,51 @@ function TerminalPanel({
18151
19259
  const projectState = useProjectState(projectId);
18152
19260
  const terminals = projectState.terminals;
18153
19261
  const updateProjectState = useAppStore((s15) => s15.updateProjectState);
18154
- async function createTerminal() {
19262
+ const navigationItemRefs = reactExports.useRef(/* @__PURE__ */ new Map());
19263
+ const focusedNavigationItemIdRef = reactExports.useRef(null);
19264
+ const navigationItems = reactExports.useMemo(() => {
19265
+ const items = [{ type: "add" }];
19266
+ items.push(
19267
+ ...terminals.map(
19268
+ (terminal) => ({ type: "terminal", id: terminal.id })
19269
+ )
19270
+ );
19271
+ return items;
19272
+ }, [terminals]);
19273
+ const terminalItems = reactExports.useMemo(
19274
+ () => navigationItems.filter(
19275
+ (item) => item.type === "terminal"
19276
+ ),
19277
+ [navigationItems]
19278
+ );
19279
+ const registerNavigationItemRef = reactExports.useCallback((id, element) => {
19280
+ if (element) {
19281
+ navigationItemRefs.current.set(id, element);
19282
+ } else {
19283
+ navigationItemRefs.current.delete(id);
19284
+ }
19285
+ }, []);
19286
+ const focusNavigationItem = reactExports.useCallback((item) => {
19287
+ const element = navigationItemRefs.current.get(terminalNavigationItemKey(item));
19288
+ if (!element) return;
19289
+ element.focus({ preventScroll: true });
19290
+ element.scrollIntoView({ block: "nearest" });
19291
+ }, []);
19292
+ const focusTerminalList = reactExports.useCallback(() => {
19293
+ const activeItem = activeTerminalId ? navigationItems.find(
19294
+ (item) => item.type === "terminal" && item.id === activeTerminalId
19295
+ ) : void 0;
19296
+ focusNavigationItem(activeItem ?? navigationItems[0]);
19297
+ }, [activeTerminalId, focusNavigationItem, navigationItems]);
19298
+ useFocusTarget("terminal-list-content", focusTerminalList);
19299
+ const selectTerminal = reactExports.useCallback(
19300
+ (terminalId) => {
19301
+ updateProjectState(projectId, () => ({ activeTerminalId: terminalId }));
19302
+ onSelectTerminal(terminalId);
19303
+ },
19304
+ [onSelectTerminal, projectId, updateProjectState]
19305
+ );
19306
+ const createTerminal = reactExports.useCallback(async () => {
18155
19307
  if (!projectId) return;
18156
19308
  const { terminalId } = await request.createTerminal({ projectId, cwd: "", clientId });
18157
19309
  updateProjectState(projectId, (prev) => ({
@@ -18159,9 +19311,14 @@ function TerminalPanel({
18159
19311
  activeTerminalId: terminalId
18160
19312
  }));
18161
19313
  onSelectTerminal(terminalId);
18162
- }
19314
+ }, [onSelectTerminal, projectId, updateProjectState]);
18163
19315
  async function deleteTerminal(terminalId, e) {
18164
- e.stopPropagation();
19316
+ e?.stopPropagation();
19317
+ const shouldRestoreFocus = focusedNavigationItemIdRef.current === terminalId;
19318
+ const deletedIndex = navigationItems.findIndex(
19319
+ (item) => item.type === "terminal" && item.id === terminalId
19320
+ );
19321
+ const focusTarget = deletedIndex === -1 ? void 0 : navigationItems[deletedIndex + 1] ?? navigationItems[deletedIndex - 1] ?? navigationItems[0];
18165
19322
  destroyTerminalInstance(terminalId);
18166
19323
  await request.killTerminal({ terminalId });
18167
19324
  updateProjectState(projectId, (prev) => {
@@ -18171,7 +19328,81 @@ function TerminalPanel({
18171
19328
  activeTerminalId: prev.activeTerminalId === terminalId ? nextList[nextList.length - 1]?.id ?? null : prev.activeTerminalId
18172
19329
  };
18173
19330
  });
19331
+ if (shouldRestoreFocus && focusTarget) {
19332
+ window.requestAnimationFrame(() => focusNavigationItem(focusTarget));
19333
+ }
18174
19334
  }
19335
+ const handleNavigationKeyDown = reactExports.useCallback(
19336
+ (event, item) => {
19337
+ const { key } = event;
19338
+ if (item.type === "add") {
19339
+ if (key === "ArrowLeft" || key === "ArrowRight" || key === "Home" || key === "End") {
19340
+ event.preventDefault();
19341
+ event.stopPropagation();
19342
+ focusNavigationItem(item);
19343
+ return;
19344
+ }
19345
+ if (key === "ArrowDown") {
19346
+ event.preventDefault();
19347
+ event.stopPropagation();
19348
+ const firstTerminal = terminalItems[0];
19349
+ if (firstTerminal) focusNavigationItem(firstTerminal);
19350
+ return;
19351
+ }
19352
+ if (key === "Enter" || key === " " || key === "Spacebar") {
19353
+ event.preventDefault();
19354
+ event.stopPropagation();
19355
+ void createTerminal();
19356
+ }
19357
+ return;
19358
+ }
19359
+ const isVerticalNavigationKey = key === "ArrowUp" || key === "ArrowDown" || key === "Home" || key === "End";
19360
+ if (isVerticalNavigationKey) {
19361
+ event.preventDefault();
19362
+ event.stopPropagation();
19363
+ const currentIndex = terminalItems.findIndex((terminal) => terminal.id === item.id);
19364
+ if (key === "ArrowUp" && currentIndex === 0) {
19365
+ focusNavigationItem({ type: "add" });
19366
+ return;
19367
+ }
19368
+ if (terminalItems.length === 0) return;
19369
+ const nextIndex = key === "Home" ? 0 : key === "End" ? terminalItems.length - 1 : Math.max(
19370
+ 0,
19371
+ Math.min(
19372
+ terminalItems.length - 1,
19373
+ currentIndex + (key === "ArrowDown" ? 1 : -1)
19374
+ )
19375
+ );
19376
+ const nextTerminal = terminalItems[nextIndex];
19377
+ if (nextTerminal) focusNavigationItem(nextTerminal);
19378
+ return;
19379
+ }
19380
+ if (key === "/") {
19381
+ event.preventDefault();
19382
+ event.stopPropagation();
19383
+ const target = event.currentTarget;
19384
+ const rect = target.getBoundingClientRect();
19385
+ target.dispatchEvent(
19386
+ new MouseEvent("contextmenu", {
19387
+ bubbles: true,
19388
+ cancelable: true,
19389
+ view: window,
19390
+ button: 2,
19391
+ buttons: 2,
19392
+ clientX: rect.left + rect.width / 2,
19393
+ clientY: rect.top + rect.height / 2
19394
+ })
19395
+ );
19396
+ return;
19397
+ }
19398
+ if (key === "Enter" || key === " " || key === "Spacebar") {
19399
+ event.preventDefault();
19400
+ event.stopPropagation();
19401
+ selectTerminal(item.id);
19402
+ }
19403
+ },
19404
+ [createTerminal, focusNavigationItem, selectTerminal, terminalItems]
19405
+ );
18175
19406
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col", children: [
18176
19407
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-10 items-center gap-0.5 border-b border-border", children: [
18177
19408
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex text-muted-foreground items-center gap-1 px-3", children: [
@@ -18181,10 +19412,16 @@ function TerminalPanel({
18181
19412
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-auto mr-2 flex items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
18182
19413
  Button,
18183
19414
  {
19415
+ ref: (element) => registerNavigationItemRef(ADD_TERMINAL_NAVIGATION_ID, element),
18184
19416
  variant: "ghost",
18185
19417
  size: "icon",
19418
+ tabIndex: -1,
18186
19419
  className: "size-6 text-muted-foreground hover:text-foreground",
18187
19420
  onClick: () => void createTerminal(),
19421
+ onKeyDown: (event) => handleNavigationKeyDown(event, { type: "add" }),
19422
+ onFocus: () => {
19423
+ focusedNavigationItemIdRef.current = ADD_TERMINAL_NAVIGATION_ID;
19424
+ },
18188
19425
  "aria-label": t("terminalPanel.addTerminal", "Add terminal"),
18189
19426
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3.5" })
18190
19427
  }
@@ -18193,41 +19430,70 @@ function TerminalPanel({
18193
19430
  terminals.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center text-muted-foreground", children: [
18194
19431
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-10 rounded-full bg-muted flex items-center justify-center mb-3 -mt-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SquareTerminal, { className: "size-5 text-muted-foreground/60" }) }),
18195
19432
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground/70", children: t("terminalPanel.noTerminalSelected", "No terminals") })
18196
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0.5 py-1 px-1", children: terminals.map((terminal) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
18197
- "button",
18198
- {
18199
- type: "button",
18200
- onClick: () => {
18201
- updateProjectState(projectId, () => ({ activeTerminalId: terminal.id }));
18202
- onSelectTerminal(terminal.id);
18203
- },
18204
- className: cn$1(
18205
- "flex h-8 items-center gap-2 rounded-md pl-2 pr-1.5 text-xs w-full transition-colors",
18206
- terminal.id === activeTerminalId ? "bg-accent text-accent-foreground" : "text-foreground/70 hover:bg-accent/50 hover:text-foreground"
18207
- ),
18208
- children: [
18209
- /* @__PURE__ */ jsxRuntimeExports.jsx(
18210
- Circle,
19433
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0.5 py-1 px-1", children: terminals.map((terminal) => /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenu, { children: [
19434
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19435
+ ContextMenuTrigger,
19436
+ {
19437
+ render: /* @__PURE__ */ jsxRuntimeExports.jsx(
19438
+ "button",
18211
19439
  {
18212
- className: cn$1(
18213
- "size-2 shrink-0",
18214
- terminal.running ? "fill-emerald-400 text-emerald-400" : "text-muted-foreground"
18215
- )
19440
+ ref: (element) => registerNavigationItemRef(terminal.id, element),
19441
+ type: "button",
19442
+ tabIndex: -1,
19443
+ className: "outline-0 focus-visible:inset-ring-1 focus-visible:inset-ring-ring/80 rounded-md",
19444
+ "aria-current": terminal.id === activeTerminalId ? "true" : void 0,
19445
+ onKeyDown: (event) => handleNavigationKeyDown(event, { type: "terminal", id: terminal.id }),
19446
+ onFocus: () => {
19447
+ focusedNavigationItemIdRef.current = terminal.id;
19448
+ }
18216
19449
  }
18217
19450
  ),
18218
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-left", children: terminal.id.slice(0, 8) }),
18219
- /* @__PURE__ */ jsxRuntimeExports.jsx(
18220
- "span",
18221
- {
18222
- className: "flex size-5 shrink-0 items-center justify-center rounded hover:bg-background/70",
18223
- onClick: (e) => void deleteTerminal(terminal.id, e),
18224
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { className: "size-3 text-muted-foreground" })
18225
- }
18226
- )
18227
- ]
18228
- },
18229
- terminal.id
18230
- )) }) })
19451
+ onClick: () => selectTerminal(terminal.id),
19452
+ className: cn$1(
19453
+ "flex h-8 items-center gap-2 rounded-md pl-2 pr-1.5 text-xs w-full transition-colors",
19454
+ terminal.id === activeTerminalId ? "bg-accent text-accent-foreground" : "text-foreground/70 hover:bg-accent/50 hover:text-foreground"
19455
+ ),
19456
+ children: [
19457
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19458
+ Circle,
19459
+ {
19460
+ className: cn$1(
19461
+ "size-2 shrink-0",
19462
+ terminal.running ? "fill-emerald-400 text-emerald-400" : "text-muted-foreground"
19463
+ )
19464
+ }
19465
+ ),
19466
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-left", children: terminal.id.slice(0, 8) }),
19467
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19468
+ "span",
19469
+ {
19470
+ className: "flex size-5 shrink-0 items-center justify-center rounded hover:bg-background/70",
19471
+ onClick: (e) => void deleteTerminal(terminal.id, e),
19472
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { className: "size-3 text-muted-foreground" })
19473
+ }
19474
+ )
19475
+ ]
19476
+ }
19477
+ ),
19478
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuContent, { children: [
19479
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuItem, { onClick: () => selectTerminal(terminal.id), children: [
19480
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, {}),
19481
+ t("terminalPanel.view", "View")
19482
+ ] }),
19483
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuSeparator, {}),
19484
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19485
+ ContextMenuItem,
19486
+ {
19487
+ variant: "destructive",
19488
+ onClick: () => void deleteTerminal(terminal.id),
19489
+ children: [
19490
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, {}),
19491
+ t("terminalPanel.delete", "Delete")
19492
+ ]
19493
+ }
19494
+ )
19495
+ ] })
19496
+ ] }, terminal.id)) }) })
18231
19497
  ] });
18232
19498
  }
18233
19499
  function Panel({
@@ -18240,6 +19506,27 @@ function Panel({
18240
19506
  onSelectTerminal
18241
19507
  }) {
18242
19508
  const { t } = useTranslation();
19509
+ const { focus } = useFocusTargetRegistry();
19510
+ const focusFileTree = reactExports.useCallback(() => {
19511
+ const focusFileTreeContent = () => focus("file-tree-content");
19512
+ if (tab === "files") {
19513
+ focusFileTreeContent();
19514
+ return;
19515
+ }
19516
+ onTabChange("files");
19517
+ window.requestAnimationFrame(focusFileTreeContent);
19518
+ }, [focus, onTabChange, tab]);
19519
+ useFocusTarget("file-tree", focusFileTree, Boolean(projectId));
19520
+ const focusTerminalList = reactExports.useCallback(() => {
19521
+ const focusTerminalListContent = () => focus("terminal-list-content");
19522
+ if (tab === "terminal") {
19523
+ focusTerminalListContent();
19524
+ return;
19525
+ }
19526
+ onTabChange("terminal");
19527
+ window.requestAnimationFrame(focusTerminalListContent);
19528
+ }, [focus, onTabChange, tab]);
19529
+ useFocusTarget("terminal-list", focusTerminalList, Boolean(projectId));
18243
19530
  if (!projectId) {
18244
19531
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center text-xs text-muted-foreground border-l border-border bg-background", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("panel.noProjectSelected", "No project selected") }) });
18245
19532
  }
@@ -18260,7 +19547,7 @@ function Panel({
18260
19547
  t("panel.terminal", "Terminal")
18261
19548
  ] })
18262
19549
  ] }) }),
18263
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "files", className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
19550
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "files", keepMounted: true, className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
18264
19551
  FilePanel,
18265
19552
  {
18266
19553
  projectId,
@@ -18268,7 +19555,7 @@ function Panel({
18268
19555
  onPreviewFile
18269
19556
  }
18270
19557
  ) }),
18271
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "terminal", className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
19558
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "terminal", keepMounted: true, className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
18272
19559
  TerminalPanel,
18273
19560
  {
18274
19561
  projectId,
@@ -18350,61 +19637,21 @@ function Session({ session }) {
18350
19637
  const [connectionError, setConnectionError] = reactExports.useState(false);
18351
19638
  reactExports.useEffect(() => {
18352
19639
  setConnectionError(false);
18353
- request.loadSession({ sessionId }).catch((err) => {
19640
+ const sessionState = useAppStore.getState().getSessionState(sessionId);
19641
+ const shouldLoadHistory = sessionState.messages.length === 0 && !isCreatingSession && sessionState.loadedAt === null;
19642
+ if (shouldLoadHistory && fetchingRef.current === sessionId) return;
19643
+ if (shouldLoadHistory) fetchingRef.current = sessionId;
19644
+ void loadSession(sessionId, { loadHistory: shouldLoadHistory }).catch((err) => {
19645
+ if (err instanceof SessionLifecycleBusyError) return;
18354
19646
  setConnectionError(true);
18355
19647
  toast.error(err instanceof Error ? err.message : String(err));
19648
+ }).finally(() => {
19649
+ if (fetchingRef.current === sessionId) fetchingRef.current = null;
18356
19650
  });
18357
- const sessionState = useAppStore.getState().getSessionState(sessionId);
18358
- if (sessionState.messages.length > 0 || isCreatingSession || sessionState.loadedAt !== null) {
18359
- return;
18360
- }
18361
- if (fetchingRef.current === sessionId) return;
18362
- fetchingRef.current = sessionId;
18363
- async function fetchHistory() {
18364
- useAppStore.getState().updateSessionState(sessionId, (prev) => ({
18365
- ...reduceFlushStreaming(prev),
18366
- isLoading: true
18367
- }));
18368
- try {
18369
- const result = await request.getSessionHistory({ sessionId });
18370
- let state = useAppStore.getState().getSessionState(sessionId);
18371
- state = { ...state, messages: [], activeToolCalls: /* @__PURE__ */ new Map(), activeSubagents: /* @__PURE__ */ new Map() };
18372
- const displayIds = /* @__PURE__ */ new Set();
18373
- for (const notification of result.messages) {
18374
- const displayId = notification?.update?._meta?.fello?.displayId;
18375
- if (displayId) {
18376
- displayIds.add(displayId);
18377
- }
18378
- if (!notification?.update) continue;
18379
- state = reduceSessionNotification(sessionId, state, notification);
18380
- }
18381
- for (const notification of state.pendingNotifications) {
18382
- const did = notification.update._meta?.fello?.displayId;
18383
- if (did && displayIds.has(did)) {
18384
- continue;
18385
- }
18386
- state = reduceSessionNotification(sessionId, state, notification);
18387
- }
18388
- state = reduceFlushStreaming(state);
18389
- state = {
18390
- ...state,
18391
- isLoading: false,
18392
- pendingNotifications: [],
18393
- loadedAt: Date.now()
18394
- };
18395
- useAppStore.getState().updateSessionState(sessionId, () => state);
18396
- } catch (err) {
18397
- console.error("Failed to fetch session history", err);
18398
- useAppStore.getState().updateSessionState(sessionId, (prev) => ({ ...prev, isLoading: false }));
18399
- } finally {
18400
- fetchingRef.current = null;
18401
- }
18402
- }
18403
- void fetchHistory();
18404
19651
  }, [sessionId, isCreatingSession, toast]);
18405
19652
  return /* @__PURE__ */ jsxRuntimeExports.jsx("main", { ref: setMainEl, className: "flex min-w-0 flex-1 flex-col relative overflow-hidden", children: isLoading || sessionConnected !== "connected" || isCreatingSession ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 relative", children: [
18406
19653
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 top-0 right-0 h-12", style: { WebkitAppRegion: "drag" } }),
18407
- connectionError && sessionConnected !== "connected" && !isCreatingSession ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19654
+ connectionError && !isCreatingSession ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
18408
19655
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-normal text-muted-foreground", children: t("session.connectionFailed") }),
18409
19656
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
18410
19657
  Button,
@@ -18413,7 +19660,8 @@ function Session({ session }) {
18413
19660
  size: "sm",
18414
19661
  onClick: () => {
18415
19662
  setConnectionError(false);
18416
- request.loadSession({ sessionId, force: true }).catch((err) => {
19663
+ void loadSession(sessionId, { force: true, loadHistory: true }).catch((err) => {
19664
+ if (err instanceof SessionLifecycleBusyError) return;
18417
19665
  setConnectionError(true);
18418
19666
  toast.error(err instanceof Error ? err.message : String(err));
18419
19667
  });