@band-app/server 0.16.8 → 0.16.9

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 (172) hide show
  1. package/dist/client/assets/DockviewBrowserContainer-CCBAkU1p.js +5 -0
  2. package/dist/client/assets/DockviewTerminalContainer-B9hdDoMz.js +2 -0
  3. package/dist/client/assets/{TerminalPanel-C6VJtwdn.js → TerminalPanel-BKfWmj6W.js} +1 -1
  4. package/dist/client/assets/_basePickBy-uVqvgsJc.js +1 -0
  5. package/dist/client/assets/{_baseUniq-K3DMbXu0.js → _baseUniq-7i7L1uNE.js} +1 -1
  6. package/dist/client/assets/{arc-BkgOQsVG.js → arc-DiYEQGs3.js} +1 -1
  7. package/dist/client/assets/{architectureDiagram-VXUJARFQ-CD44Dllc.js → architectureDiagram-VXUJARFQ-C5z8L1iK.js} +1 -1
  8. package/dist/client/assets/{blockDiagram-VD42YOAC-BKiU7fF0.js → blockDiagram-VD42YOAC-CXQSSz3O.js} +1 -1
  9. package/dist/client/assets/{c4Diagram-YG6GDRKO-CYISxlyF.js → c4Diagram-YG6GDRKO-DplRXCR2.js} +1 -1
  10. package/dist/client/assets/channel-BnMJuhcI.js +1 -0
  11. package/dist/client/assets/{chunk-4BX2VUAB-Bphc00nU.js → chunk-4BX2VUAB-B4Mk_d7A.js} +1 -1
  12. package/dist/client/assets/{chunk-55IACEB6-CfN6hb2c.js → chunk-55IACEB6-B0hf8Eje.js} +1 -1
  13. package/dist/client/assets/{chunk-B4BG7PRW-DltWViHS.js → chunk-B4BG7PRW-DlN9tUxh.js} +1 -1
  14. package/dist/client/assets/{chunk-DI55MBZ5-51JXB972.js → chunk-DI55MBZ5-a11zyhq4.js} +1 -1
  15. package/dist/client/assets/{chunk-FMBD7UC4-CynepQnx.js → chunk-FMBD7UC4-aGGVV1yn.js} +1 -1
  16. package/dist/client/assets/{chunk-QN33PNHL-B45qeM-p.js → chunk-QN33PNHL-Dit4S-yJ.js} +1 -1
  17. package/dist/client/assets/{chunk-QZHKN3VN-1ObdmeeM.js → chunk-QZHKN3VN-L-h1VSi0.js} +1 -1
  18. package/dist/client/assets/{chunk-TZMSLE5B-CWtn_mgw.js → chunk-TZMSLE5B-DpK-hWaC.js} +1 -1
  19. package/dist/client/assets/classDiagram-2ON5EDUG-D89oCSXI.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-WZHVMYZB-D89oCSXI.js +1 -0
  21. package/dist/client/assets/clone-X7FA7kE2.js +1 -0
  22. package/dist/client/assets/{cose-bilkent-S5V4N54A-B4IAbafd.js → cose-bilkent-S5V4N54A-CzoIK-3W.js} +1 -1
  23. package/dist/client/assets/{dagre-6UL2VRFP-BGoRZDH9.js → dagre-6UL2VRFP-BhfXeb7k.js} +1 -1
  24. package/dist/client/assets/{diagram-PSM6KHXK-DIkN7d-V.js → diagram-PSM6KHXK-BwFVwR1b.js} +1 -1
  25. package/dist/client/assets/{diagram-QEK2KX5R-B-hPXzou.js → diagram-QEK2KX5R-BO-CEopy.js} +1 -1
  26. package/dist/client/assets/{diagram-S2PKOQOG-BlUeis_2.js → diagram-S2PKOQOG-BxiH83lN.js} +1 -1
  27. package/dist/client/assets/{erDiagram-Q2GNP2WA-DZUjamhk.js → erDiagram-Q2GNP2WA-kUd-gmjz.js} +1 -1
  28. package/dist/client/assets/{flowDiagram-NV44I4VS-oIHer-h-.js → flowDiagram-NV44I4VS-CHbLqCKB.js} +1 -1
  29. package/dist/client/assets/{ganttDiagram-JELNMOA3-D8LfLthv.js → ganttDiagram-JELNMOA3-B7gBcAFl.js} +4 -4
  30. package/dist/client/assets/{gitGraphDiagram-V2S2FVAM-BgFUKaMK.js → gitGraphDiagram-V2S2FVAM-gca_jqKQ.js} +1 -1
  31. package/dist/client/assets/{graph-r-V7AF54.js → graph-CSi_akmB.js} +1 -1
  32. package/dist/client/assets/{highlighted-body-B3W2YXNL-BHkeGyHR.js → highlighted-body-B3W2YXNL-C2a-lsk8.js} +1 -1
  33. package/dist/client/assets/{index-fbwZk1fH.js → index-2F-slvqo.js} +1 -1
  34. package/dist/client/assets/{index-BDQWeqNk.js → index-5X7JRvnK.js} +1 -1
  35. package/dist/client/assets/{index-u_2V_RF4.js → index-B0ZcEU4c.js} +1 -1
  36. package/dist/client/assets/{index-CYvgC-U1.js → index-BJP1qdPz.js} +1 -1
  37. package/dist/client/assets/{index-W1CPMGYX.js → index-BPf6S9f1.js} +1 -1
  38. package/dist/client/assets/{index-BiUUT09K.js → index-C-3PAt1d.js} +1 -1
  39. package/dist/client/assets/{index-DQixQE5Y.js → index-C-6dsPog.js} +1 -1
  40. package/dist/client/assets/{index-DLFTwdhm.js → index-C4PISrIW.js} +1 -1
  41. package/dist/client/assets/{index-foc9a7eE.js → index-C4ee_PQ0.js} +1 -1
  42. package/dist/client/assets/{index-BvWkKnfY.js → index-CA1NuCd_.js} +1 -1
  43. package/dist/client/assets/{index-NMjGSNmU.js → index-CNQHGhCu.js} +1 -1
  44. package/dist/client/assets/{index-CXHZbHRT.js → index-CiKTedrx.js} +1 -1
  45. package/dist/client/assets/{index-CNerXIiJ.js → index-DNrK0iIn.js} +1 -1
  46. package/dist/client/assets/{index-DhTLiwux.js → index-DfHcm4JM.js} +1 -1
  47. package/dist/client/assets/{index-CdJ3WH3f.js → index-T_1b5WRg.js} +1 -1
  48. package/dist/client/assets/{index-CFEu-Cf9.js → index-oolizINO.js} +1 -1
  49. package/dist/client/assets/{index-y0IIofTa.js → index-spwNnd85.js} +1 -1
  50. package/dist/client/assets/{index-DAMgQMyn.js → index-udv0M-ix.js} +1 -1
  51. package/dist/client/assets/{infoDiagram-HS3SLOUP-xB0m1ffH.js → infoDiagram-HS3SLOUP-BK3MmhkJ.js} +1 -1
  52. package/dist/client/assets/{journeyDiagram-XKPGCS4Q-E4OGYZo3.js → journeyDiagram-XKPGCS4Q-DIY8FE8Z.js} +1 -1
  53. package/dist/client/assets/{kanban-definition-3W4ZIXB7-DDOW42i6.js → kanban-definition-3W4ZIXB7-Cd_JJ8FN.js} +1 -1
  54. package/dist/client/assets/{layout-BU7n9QQq.js → layout-Bhb5FQCr.js} +1 -1
  55. package/dist/client/assets/{linear-CFFUeqRn.js → linear-Cmk2RyH_.js} +1 -1
  56. package/dist/client/assets/{main-onb8WwfN.js → main-BlRcPsmZ.js} +231 -235
  57. package/dist/client/assets/{mindmap-definition-VGOIOE7T-BQlhTP7t.js → mindmap-definition-VGOIOE7T-B-WhBVeY.js} +1 -1
  58. package/dist/client/assets/{pieDiagram-ADFJNKIX-khlEdX5z.js → pieDiagram-ADFJNKIX-CYoHsaM9.js} +1 -1
  59. package/dist/client/assets/{quadrantDiagram-AYHSOK5B-D7D_64Ur.js → quadrantDiagram-AYHSOK5B-DHBoMUCQ.js} +1 -1
  60. package/dist/client/assets/{requirementDiagram-UZGBJVZJ-Dd-Qxkau.js → requirementDiagram-UZGBJVZJ-DjS5RONh.js} +1 -1
  61. package/dist/client/assets/{sankeyDiagram-TZEHDZUN-BxF-L0mm.js → sankeyDiagram-TZEHDZUN-DmhEbBY-.js} +1 -1
  62. package/dist/client/assets/{sequenceDiagram-WL72ISMW-UH13bSlf.js → sequenceDiagram-WL72ISMW-CzqbRfb_.js} +1 -1
  63. package/dist/client/assets/{square-terminal-BgnPkQhG.js → square-terminal-CNHJa6OY.js} +1 -1
  64. package/dist/client/assets/{stateDiagram-FKZM4ZOC-DJXW-Mwx.js → stateDiagram-FKZM4ZOC-CafvdTb3.js} +1 -1
  65. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BkKHK-CQ.js +1 -0
  66. package/dist/client/assets/{timeline-definition-IT6M3QCI-DoLvii7e.js → timeline-definition-IT6M3QCI-CNKMKDwq.js} +1 -1
  67. package/dist/client/assets/{treemap-GDKQZRPO-C6cooeby.js → treemap-GDKQZRPO-DCEPrZh7.js} +1 -1
  68. package/dist/client/assets/{useSessionListContext-DJKT08Su.js → useSessionListContext-xer-Wcmf.js} +1 -1
  69. package/dist/client/assets/{workspace._workspaceId-BZyIo0Fq.js → workspace._workspaceId-CgG1n_1x.js} +1 -1
  70. package/dist/client/assets/{workspace._workspaceId.changes-WzxDrDHA.js → workspace._workspaceId.changes-BKX7lUhX.js} +1 -1
  71. package/dist/client/assets/workspace._workspaceId.code-BcnxRdZi.js +1 -0
  72. package/dist/client/assets/{workspace._workspaceId.code._-DjAaAaSY.js → workspace._workspaceId.code._-BbOt6tOD.js} +1 -1
  73. package/dist/client/assets/{workspace._workspaceId.code.index-D0PytT4F.js → workspace._workspaceId.code.index-DC2535x-.js} +1 -1
  74. package/dist/client/assets/workspace._workspaceId.index-CNjq1nDf.js +1 -0
  75. package/dist/client/assets/{workspace._workspaceId.terminal-BeG77FAq.js → workspace._workspaceId.terminal-DhYn8I5t.js} +2 -2
  76. package/dist/client/assets/{xychartDiagram-PRI3JC2R-DjUMVf0W.js → xychartDiagram-PRI3JC2R-Cr18I3A3.js} +1 -1
  77. package/dist/openapi.json +17 -12
  78. package/dist/server/assets/DockviewBrowserContainer-Bm7ARaqd.js +1934 -0
  79. package/dist/server/assets/{DockviewTerminalContainer-Dl-H-7q5.js → DockviewTerminalContainer-BbMZgBJ8.js} +32 -3
  80. package/dist/server/assets/{TerminalPanel-Nx4CmvlW.js → TerminalPanel-BmhYCf33.js} +1 -1
  81. package/dist/server/assets/{_basePickBy-BG2OhlJc.js → _basePickBy-C7CNOUDi.js} +2 -2
  82. package/dist/server/assets/{_baseUniq-DB6mvOCI.js → _baseUniq-DeF47vVO.js} +1 -1
  83. package/dist/server/assets/{_tanstack-start-manifest_v-2XlZMknZ.js → _tanstack-start-manifest_v-BNCQ-izx.js} +1 -1
  84. package/dist/server/assets/{arc-DaVwrLrM.js → arc-P5FY7IYH.js} +1 -1
  85. package/dist/server/assets/{architecture-7HQA4BMR-DIi4TM4E.js → architecture-7HQA4BMR-LvprLHYS.js} +6 -6
  86. package/dist/server/assets/{architectureDiagram-VXUJARFQ-DBRmPh1t.js → architectureDiagram-VXUJARFQ-Br4iiUzH.js} +6 -6
  87. package/dist/server/assets/{blockDiagram-VD42YOAC-CFdPLnD-.js → blockDiagram-VD42YOAC-BahODDhX.js} +6 -6
  88. package/dist/server/assets/{c4Diagram-YG6GDRKO-BJi6gbIt.js → c4Diagram-YG6GDRKO-CQcZX6cr.js} +2 -2
  89. package/dist/server/assets/{channel-DkntTxx0.js → channel-BmAB_ZKU.js} +1 -1
  90. package/dist/server/assets/{chunk-4BX2VUAB-D2FiW3aP.js → chunk-4BX2VUAB-YUvszr4w.js} +1 -1
  91. package/dist/server/assets/{chunk-55IACEB6-jZmh1Z5I.js → chunk-55IACEB6-BUgBPiJd.js} +1 -1
  92. package/dist/server/assets/{chunk-B4BG7PRW-CNZgnxgA.js → chunk-B4BG7PRW-BxuWeZlV.js} +4 -4
  93. package/dist/server/assets/{chunk-DI55MBZ5-q0pODS0a.js → chunk-DI55MBZ5-DuGkdvte.js} +3 -3
  94. package/dist/server/assets/{chunk-FMBD7UC4-C96aN_Us.js → chunk-FMBD7UC4-qY-BVG34.js} +1 -1
  95. package/dist/server/assets/{chunk-QN33PNHL-Dam5gy0m.js → chunk-QN33PNHL-BM_oi8-e.js} +1 -1
  96. package/dist/server/assets/{chunk-QZHKN3VN-CXCmBbbu.js → chunk-QZHKN3VN-BL6_-at8.js} +1 -1
  97. package/dist/server/assets/{chunk-TZMSLE5B-CbK3gIHb.js → chunk-TZMSLE5B-38NhX5at.js} +1 -1
  98. package/dist/server/assets/{classDiagram-v2-WZHVMYZB-BHB2vhDs.js → classDiagram-2ON5EDUG-CwIsZ53p.js} +5 -5
  99. package/dist/server/assets/{classDiagram-2ON5EDUG-BHB2vhDs.js → classDiagram-v2-WZHVMYZB-CwIsZ53p.js} +5 -5
  100. package/dist/server/assets/{clone-CN5lfg2Z.js → clone-BXHuGM8i.js} +1 -1
  101. package/dist/server/assets/{cose-bilkent-S5V4N54A-Bq-O80sc.js → cose-bilkent-S5V4N54A-D1CdgGvI.js} +1 -1
  102. package/dist/server/assets/{dagre-6UL2VRFP-U80sNC8f.js → dagre-6UL2VRFP-UMvdMLG3.js} +6 -6
  103. package/dist/server/assets/{diagram-PSM6KHXK-5ksUDb2Z.js → diagram-PSM6KHXK-DtOD93m7.js} +7 -7
  104. package/dist/server/assets/{diagram-QEK2KX5R-COmUOoRU.js → diagram-QEK2KX5R-DViyYq-y.js} +6 -6
  105. package/dist/server/assets/{diagram-S2PKOQOG-BOTznhFu.js → diagram-S2PKOQOG-C42mRVEn.js} +6 -6
  106. package/dist/server/assets/{erDiagram-Q2GNP2WA-CZOlCzBi.js → erDiagram-Q2GNP2WA-D14z9IvB.js} +4 -4
  107. package/dist/server/assets/{flowDiagram-NV44I4VS-ObYcdqyZ.js → flowDiagram-NV44I4VS-BYBNOZwM.js} +5 -5
  108. package/dist/server/assets/{ganttDiagram-JELNMOA3-6c9A3Tfv.js → ganttDiagram-JELNMOA3-CaG3uoK-.js} +2 -2
  109. package/dist/server/assets/{gitGraph-G5XIXVHT-Q_zXZbiJ.js → gitGraph-G5XIXVHT-Dsx3dV0R.js} +6 -6
  110. package/dist/server/assets/{gitGraphDiagram-V2S2FVAM-C-GqaejI.js → gitGraphDiagram-V2S2FVAM-CrSfx7b3.js} +7 -7
  111. package/dist/server/assets/{graph-CGXsNa6T.js → graph-gBFInK6H.js} +2 -2
  112. package/dist/server/assets/{highlighted-body-B3W2YXNL-CjyCfwez.js → highlighted-body-B3W2YXNL-DjTWk17l.js} +1 -1
  113. package/dist/server/assets/{index-BaYLnq50.js → index-2gWBdIrR.js} +2 -2
  114. package/dist/server/assets/{index-CAfRymUE.js → index-3wtbr7va.js} +3 -3
  115. package/dist/server/assets/{index-C0kihLnk.js → index-5N2Gh0ba.js} +2 -2
  116. package/dist/server/assets/{index-DIdSfU9-.js → index-6Zof9vm8.js} +1 -1
  117. package/dist/server/assets/{index-DIahp6rS.js → index-B84Urg23.js} +2 -2
  118. package/dist/server/assets/{index-D_0vKELK.js → index-Bd5jTryJ.js} +2 -2
  119. package/dist/server/assets/{index-BBvVnzgd.js → index-Bs6a6-xI.js} +5 -5
  120. package/dist/server/assets/{index-DnWmaxai.js → index-Bw42FDwO.js} +2 -2
  121. package/dist/server/assets/{index-DKtCHZcS.js → index-C245EJn1.js} +2 -2
  122. package/dist/server/assets/{index-C-7vS9m8.js → index-C3N_Nx6h.js} +2 -2
  123. package/dist/server/assets/{index-DU_XVqOC.js → index-CD97pPmc.js} +1 -1
  124. package/dist/server/assets/{index-BfyUYDbk.js → index-CwQ5qWPC.js} +5 -5
  125. package/dist/server/assets/{index-C71uG8NB.js → index-DXgP0Lsu.js} +2 -2
  126. package/dist/server/assets/{index-CxSyZwen.js → index-DZZ6NiTf.js} +3 -3
  127. package/dist/server/assets/{index-DejplVdQ.js → index-DsGPaWHg.js} +2 -2
  128. package/dist/server/assets/{index-73YfcfUk.js → index-Dv-16VYI.js} +2 -2
  129. package/dist/server/assets/{index-B3U9PRLr.js → index-QxgXX0fb.js} +2 -2
  130. package/dist/server/assets/{index-ukALXr65.js → index-egmIrLx2.js} +4 -4
  131. package/dist/server/assets/{info-VBDWY6EO-BsWgc-Ym.js → info-VBDWY6EO-CueOsYFZ.js} +6 -6
  132. package/dist/server/assets/{infoDiagram-HS3SLOUP-C04udjz-.js → infoDiagram-HS3SLOUP-CeyMzohL.js} +5 -5
  133. package/dist/server/assets/{journeyDiagram-XKPGCS4Q-D3MBfJwF.js → journeyDiagram-XKPGCS4Q-ANqeM_TM.js} +4 -4
  134. package/dist/server/assets/{kanban-definition-3W4ZIXB7-Czuz4I_Q.js → kanban-definition-3W4ZIXB7-B0RkH45q.js} +2 -2
  135. package/dist/server/assets/{layout-D6n2z2ff.js → layout-JjP5jdu6.js} +4 -4
  136. package/dist/server/assets/{linear-BDP9KC1O.js → linear-BJgoq5J4.js} +1 -1
  137. package/dist/server/assets/{mermaid-3ZIDBTTL-oS_2f_Id.js → mermaid-3ZIDBTTL-CFPuUseQ.js} +1 -1
  138. package/dist/server/assets/{mermaid-parser.core-DRwka5gl.js → mermaid-parser.core-CKKZ1VFr.js} +11 -11
  139. package/dist/server/assets/{mindmap-definition-VGOIOE7T-BPhhfr5z.js → mindmap-definition-VGOIOE7T-DlEXF5z8.js} +3 -3
  140. package/dist/server/assets/{packet-DYOGHKS2-XJIQk327.js → packet-DYOGHKS2-CvyxHTRM.js} +6 -6
  141. package/dist/server/assets/{pie-VRWISCQL-BEbH9jgO.js → pie-VRWISCQL-DfpVZlpC.js} +6 -6
  142. package/dist/server/assets/{pieDiagram-ADFJNKIX-oLEFnRFT.js → pieDiagram-ADFJNKIX-CWkrHsNk.js} +7 -7
  143. package/dist/server/assets/{quadrantDiagram-AYHSOK5B-nsrtyikJ.js → quadrantDiagram-AYHSOK5B-C25p2xz7.js} +2 -2
  144. package/dist/server/assets/{radar-ZZBFDIW7-XJaR0sVv.js → radar-ZZBFDIW7-DWqPsDMi.js} +6 -6
  145. package/dist/server/assets/{requirementDiagram-UZGBJVZJ-CHkWIv3O.js → requirementDiagram-UZGBJVZJ-ACRVXQGO.js} +3 -3
  146. package/dist/server/assets/{router-DqGeMR0F.js → router-GhGSLKfP.js} +415 -2225
  147. package/dist/server/assets/{sankeyDiagram-TZEHDZUN-XynupQoy.js → sankeyDiagram-TZEHDZUN-uzNSHudY.js} +1 -1
  148. package/dist/server/assets/{sequenceDiagram-WL72ISMW-Cnkmy2xq.js → sequenceDiagram-WL72ISMW-C_wFtq8b.js} +3 -3
  149. package/dist/server/assets/{square-terminal-D7AxF9nB.js → square-terminal-cghvb5Il.js} +1 -1
  150. package/dist/server/assets/{stateDiagram-FKZM4ZOC-Chk4nsf_.js → stateDiagram-FKZM4ZOC-B9MGwsuS.js} +8 -8
  151. package/dist/server/assets/{stateDiagram-v2-4FDKWEC3-DNIM2ODr.js → stateDiagram-v2-4FDKWEC3-CLHd8oUa.js} +4 -4
  152. package/dist/server/assets/{timeline-definition-IT6M3QCI-Fz2nlxYj.js → timeline-definition-IT6M3QCI-CqKot5RT.js} +2 -2
  153. package/dist/server/assets/{treemap-GDKQZRPO-DNxd_7q0.js → treemap-GDKQZRPO-mMRojqRQ.js} +6 -6
  154. package/dist/server/assets/{workspace._workspaceId-KoR0msx0.js → workspace._workspaceId-DwsRTXCg.js} +2 -2
  155. package/dist/server/assets/{workspace._workspaceId.changes-BJoSmFXW.js → workspace._workspaceId.changes-C6vwSbUh.js} +1 -1
  156. package/dist/server/assets/{workspace._workspaceId.code._-D3Ifnqrr.js → workspace._workspaceId.code._-BJdg1-Gs.js} +1 -1
  157. package/dist/server/assets/{workspace._workspaceId.code.index-DZZZQ6sT.js → workspace._workspaceId.code.index-CE_CMD_I.js} +1 -1
  158. package/dist/server/assets/{workspace._workspaceId.index-D1ULJxZJ.js → workspace._workspaceId.index-BPhXMwl8.js} +18 -2
  159. package/dist/server/assets/{workspace._workspaceId.terminal-BaKW7ZGQ.js → workspace._workspaceId.terminal-BdVDaWNH.js} +2 -2
  160. package/dist/server/assets/{xychartDiagram-PRI3JC2R-DtoxEUbW.js → xychartDiagram-PRI3JC2R-j8PckQEj.js} +2 -2
  161. package/dist/server/server.js +2 -2
  162. package/dist/start-server.mjs +211 -61
  163. package/package.json +4 -4
  164. package/dist/client/assets/DockviewTerminalContainer-CfWqm1iB.js +0 -2
  165. package/dist/client/assets/_basePickBy-CIXRHkYC.js +0 -1
  166. package/dist/client/assets/channel-DjN8EGMy.js +0 -1
  167. package/dist/client/assets/classDiagram-2ON5EDUG-C3N3Omm0.js +0 -1
  168. package/dist/client/assets/classDiagram-v2-WZHVMYZB-C3N3Omm0.js +0 -1
  169. package/dist/client/assets/clone-Co6N-TQN.js +0 -1
  170. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BCtCzm2C.js +0 -1
  171. package/dist/client/assets/workspace._workspaceId.code-DGeXE4KO.js +0 -1
  172. package/dist/client/assets/workspace._workspaceId.index-DUzba27e.js +0 -1
@@ -0,0 +1,1934 @@
1
+ import { r as reactExports, j as jsxRuntimeExports, R as React } from "../server.js";
2
+ import { E as createLucideIcon, i as isDesktop, ch as invoke, ci as listen, t as trpc, cj as reactDomExports, ck as Globe, cc as SearchBar, cl as Popover, cm as PopoverTrigger, cn as PopoverContent, co as Search, cp as Trash2, cq as DropdownMenu, cr as DropdownMenuTrigger, cs as ChevronDown, ct as DropdownMenuContent, cu as DropdownMenuItem, cv as TriangleAlert, bk as ArrowLeft, c5 as ArrowRight, X, cw as RotateCw, F as useAdapter, G as useQueryClient, H as useQuery, J as selectNeighbourBeforeRemove, K as DockviewReact, S as cycleTabsInActiveGroup, U as cycleGridGroups, M as Columns2, O as Rows2, Q as Plus } from "./router-GhGSLKfP.js";
3
+ import "node:async_hooks";
4
+ import "node:stream";
5
+ import "util";
6
+ import "crypto";
7
+ import "async_hooks";
8
+ import "stream";
9
+ import "node:stream/web";
10
+ import "node:process";
11
+ import "node:path";
12
+ import "node:url";
13
+ const __iconNode$1 = [
14
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
15
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }],
16
+ ["path", { d: "M12 7v5l4 2", key: "1fdv2h" }]
17
+ ];
18
+ const History = createLucideIcon("history", __iconNode$1);
19
+ const __iconNode = [
20
+ [
21
+ "path",
22
+ {
23
+ d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z",
24
+ key: "1ngwbx"
25
+ }
26
+ ]
27
+ ];
28
+ const Wrench = createLucideIcon("wrench", __iconNode);
29
+ function injectInitialUrls(layout, urls) {
30
+ if (!urls || urls.size === 0) return layout;
31
+ if (typeof layout !== "object" || layout === null) return layout;
32
+ const panels = layout.panels;
33
+ if (typeof panels !== "object" || panels === null) return layout;
34
+ let changed = false;
35
+ const nextPanels = {};
36
+ for (const [id, panel] of Object.entries(panels)) {
37
+ const url = urls.get(id);
38
+ if (url && typeof panel === "object" && panel !== null && typeof panel.params === "object" && panel.params !== null) {
39
+ const params = panel.params;
40
+ if (!params.initialUrl) {
41
+ nextPanels[id] = { ...panel, params: { ...params, initialUrl: url } };
42
+ changed = true;
43
+ continue;
44
+ }
45
+ }
46
+ nextPanels[id] = panel;
47
+ }
48
+ if (!changed) return layout;
49
+ return { ...layout, panels: nextPanels };
50
+ }
51
+ let domOverlayCount = 0;
52
+ let manualHoldCount = 0;
53
+ const listeners$1 = /* @__PURE__ */ new Set();
54
+ let lastEmitted = false;
55
+ function isFrozen() {
56
+ return domOverlayCount + manualHoldCount > 0;
57
+ }
58
+ function emitIfChanged() {
59
+ const next = isFrozen();
60
+ if (next === lastEmitted) return;
61
+ lastEmitted = next;
62
+ for (const l of listeners$1) l();
63
+ }
64
+ function subscribe$1(l) {
65
+ listeners$1.add(l);
66
+ return () => {
67
+ listeners$1.delete(l);
68
+ };
69
+ }
70
+ function getSnapshot$1() {
71
+ return lastEmitted;
72
+ }
73
+ function useBrowserPaneFrozen() {
74
+ return reactExports.useSyncExternalStore(subscribe$1, getSnapshot$1, getSnapshot$1);
75
+ }
76
+ function acquireFreezeHold() {
77
+ manualHoldCount += 1;
78
+ emitIfChanged();
79
+ let released = false;
80
+ return () => {
81
+ if (released) return;
82
+ released = true;
83
+ manualHoldCount = Math.max(0, manualHoldCount - 1);
84
+ emitIfChanged();
85
+ };
86
+ }
87
+ function useFreezeWhile(open) {
88
+ reactExports.useEffect(() => {
89
+ if (!open) return;
90
+ return acquireFreezeHold();
91
+ }, [open]);
92
+ }
93
+ const DEFAULT_OPTIONS = {
94
+ caseSensitive: false,
95
+ wholeWord: false,
96
+ regex: false
97
+ };
98
+ function useBrowserFindInPage({
99
+ key,
100
+ keyName
101
+ }) {
102
+ const [isOpen, setIsOpen] = reactExports.useState(false);
103
+ const [query, setQuery] = reactExports.useState("");
104
+ const [options, setOptions] = reactExports.useState(DEFAULT_OPTIONS);
105
+ const [matchInfo, setMatchInfo] = reactExports.useState(null);
106
+ const searchBarRef = reactExports.useRef(null);
107
+ const keyRef = reactExports.useRef(key);
108
+ keyRef.current = key;
109
+ const activeRequestIdRef = reactExports.useRef(null);
110
+ const payloadKey = reactExports.useCallback(
111
+ (payload) => keyName === "browserId" ? payload.browser_id : payload.workspace_id,
112
+ [keyName]
113
+ );
114
+ const buildArgs = reactExports.useCallback(
115
+ (extra = {}) => ({ [keyName]: keyRef.current, ...extra }),
116
+ [keyName]
117
+ );
118
+ const focusInput = reactExports.useCallback(() => {
119
+ requestAnimationFrame(() => {
120
+ searchBarRef.current?.focus();
121
+ searchBarRef.current?.select();
122
+ });
123
+ }, []);
124
+ const issueFind = reactExports.useCallback(
125
+ async (text, opts = {}) => {
126
+ if (!isDesktop) return;
127
+ if (!text) {
128
+ setMatchInfo(null);
129
+ activeRequestIdRef.current = null;
130
+ try {
131
+ await invoke("browser_stop_find_in_page", buildArgs({ action: "clearSelection" }));
132
+ } catch {
133
+ }
134
+ return;
135
+ }
136
+ if (!(opts.findNext ?? false)) {
137
+ setMatchInfo(null);
138
+ activeRequestIdRef.current = null;
139
+ }
140
+ try {
141
+ const reqId = await invoke(
142
+ "browser_find_in_page",
143
+ buildArgs({
144
+ text,
145
+ options: {
146
+ matchCase: options.caseSensitive,
147
+ // First search for a query → omit findNext so Chromium
148
+ // rescans. Stepping → set findNext: true and toggle forward.
149
+ findNext: opts.findNext ?? false,
150
+ forward: opts.forward ?? true
151
+ }
152
+ })
153
+ );
154
+ if (typeof reqId === "number") activeRequestIdRef.current = reqId;
155
+ } catch (e) {
156
+ console.error("browser_find_in_page failed:", e);
157
+ }
158
+ },
159
+ [buildArgs, options.caseSensitive]
160
+ );
161
+ const close = reactExports.useCallback(() => {
162
+ setIsOpen(false);
163
+ setQuery("");
164
+ setMatchInfo(null);
165
+ activeRequestIdRef.current = null;
166
+ if (!isDesktop) return;
167
+ invoke("browser_stop_find_in_page", buildArgs({ action: "clearSelection" })).catch(
168
+ () => {
169
+ }
170
+ );
171
+ }, [buildArgs]);
172
+ const open = reactExports.useCallback(() => {
173
+ setIsOpen(true);
174
+ focusInput();
175
+ }, [focusInput]);
176
+ reactExports.useEffect(() => {
177
+ if (!isOpen) return;
178
+ void issueFind(query);
179
+ }, [query, isOpen, issueFind]);
180
+ reactExports.useEffect(() => {
181
+ if (!isDesktop) return;
182
+ let unlisten;
183
+ void (async () => {
184
+ unlisten = await listen("browser-found-in-page", (event) => {
185
+ if (payloadKey(event.payload) !== keyRef.current) return;
186
+ if (activeRequestIdRef.current !== null && event.payload.request_id !== activeRequestIdRef.current) {
187
+ return;
188
+ }
189
+ setMatchInfo({
190
+ total: event.payload.matches,
191
+ // Chromium reports `0` while a query is being typed and the
192
+ // scan is mid-flight; promote to 0 of N so the UI shows
193
+ // "No results" until a match is selected.
194
+ current: event.payload.active_match_ordinal
195
+ });
196
+ });
197
+ })();
198
+ return () => unlisten?.();
199
+ }, [payloadKey]);
200
+ reactExports.useEffect(() => {
201
+ if (!isDesktop) return;
202
+ let unlisten;
203
+ void (async () => {
204
+ unlisten = await listen("browser-find-shortcut", (event) => {
205
+ if (payloadKey(event.payload) !== keyRef.current) return;
206
+ setIsOpen(true);
207
+ focusInput();
208
+ });
209
+ })();
210
+ return () => unlisten?.();
211
+ }, [focusInput, payloadKey]);
212
+ reactExports.useEffect(() => {
213
+ if (!isDesktop) return;
214
+ let unlisten;
215
+ void (async () => {
216
+ unlisten = await listen("browser-url-changed", (event) => {
217
+ if (payloadKey(event.payload) !== keyRef.current) return;
218
+ if (event.payload.loading) {
219
+ close();
220
+ }
221
+ });
222
+ })();
223
+ return () => unlisten?.();
224
+ }, [close, payloadKey]);
225
+ const findNext = reactExports.useCallback(() => {
226
+ if (!query) return;
227
+ void issueFind(query, { findNext: true, forward: true });
228
+ }, [query, issueFind]);
229
+ const findPrevious = reactExports.useCallback(() => {
230
+ if (!query) return;
231
+ void issueFind(query, { findNext: true, forward: false });
232
+ }, [query, issueFind]);
233
+ return {
234
+ isOpen,
235
+ open,
236
+ close,
237
+ query,
238
+ setQuery,
239
+ options,
240
+ setOptions,
241
+ matchInfo,
242
+ findNext,
243
+ findPrevious,
244
+ searchBarRef
245
+ };
246
+ }
247
+ const AUTOCOMPLETE_DEBOUNCE_MS = 50;
248
+ const AUTOCOMPLETE_BLUR_CLOSE_MS = 100;
249
+ function useBrowserPaneControls(args) {
250
+ const { key, keyName, workspaceId, currentUrlRef, setInputUrl, inputUrl, onNavigate } = args;
251
+ const find = useBrowserFindInPage({ key, keyName });
252
+ const addressInputFocusedRef = reactExports.useRef(false);
253
+ const [autocompleteItems, setAutocompleteItems] = reactExports.useState([]);
254
+ const [autocompleteIsOpen, setAutocompleteIsOpen] = reactExports.useState(false);
255
+ const [autocompleteSelectedIndex, setAutocompleteSelectedIndex] = reactExports.useState(0);
256
+ useFreezeWhile(autocompleteIsOpen);
257
+ const closeAutocomplete = reactExports.useCallback(() => {
258
+ setAutocompleteIsOpen(false);
259
+ setAutocompleteItems([]);
260
+ setAutocompleteSelectedIndex(0);
261
+ }, []);
262
+ reactExports.useEffect(() => {
263
+ if (!addressInputFocusedRef.current) return;
264
+ const trimmed = inputUrl.trim();
265
+ if (trimmed === "") {
266
+ closeAutocomplete();
267
+ return;
268
+ }
269
+ if (trimmed === currentUrlRef.current.trim()) {
270
+ closeAutocomplete();
271
+ return;
272
+ }
273
+ let cancelled = false;
274
+ const handle = setTimeout(() => {
275
+ trpc.history.search.query({ workspaceId, query: trimmed, limit: 8 }).then((result) => {
276
+ if (cancelled) return;
277
+ if (result.entries.length === 0) {
278
+ closeAutocomplete();
279
+ return;
280
+ }
281
+ setAutocompleteItems(result.entries);
282
+ setAutocompleteIsOpen(true);
283
+ setAutocompleteSelectedIndex(0);
284
+ }).catch(() => {
285
+ if (!cancelled) closeAutocomplete();
286
+ });
287
+ }, AUTOCOMPLETE_DEBOUNCE_MS);
288
+ return () => {
289
+ cancelled = true;
290
+ clearTimeout(handle);
291
+ };
292
+ }, [inputUrl, workspaceId, closeAutocomplete, currentUrlRef]);
293
+ const handleAddressFocus = reactExports.useCallback((e) => {
294
+ addressInputFocusedRef.current = true;
295
+ e.target.select();
296
+ }, []);
297
+ const handleAddressBlur = reactExports.useCallback(() => {
298
+ addressInputFocusedRef.current = false;
299
+ setInputUrl(currentUrlRef.current);
300
+ setTimeout(() => {
301
+ if (!addressInputFocusedRef.current) closeAutocomplete();
302
+ }, AUTOCOMPLETE_BLUR_CLOSE_MS);
303
+ }, [currentUrlRef, setInputUrl, closeAutocomplete]);
304
+ const handleAddressKeyDown = reactExports.useCallback(
305
+ (e) => {
306
+ if (autocompleteIsOpen) {
307
+ if (e.key === "ArrowDown") {
308
+ e.preventDefault();
309
+ setAutocompleteSelectedIndex((i) => Math.min(i + 1, autocompleteItems.length - 1));
310
+ return;
311
+ }
312
+ if (e.key === "ArrowUp") {
313
+ e.preventDefault();
314
+ setAutocompleteSelectedIndex((i) => Math.max(i - 1, 0));
315
+ return;
316
+ }
317
+ if (e.key === "Enter") {
318
+ const selected = autocompleteItems[autocompleteSelectedIndex];
319
+ if (selected) {
320
+ e.preventDefault();
321
+ closeAutocomplete();
322
+ onNavigate(selected.url);
323
+ return;
324
+ }
325
+ }
326
+ if (e.key === "Escape") {
327
+ e.preventDefault();
328
+ closeAutocomplete();
329
+ return;
330
+ }
331
+ }
332
+ if (e.key === "Enter") {
333
+ e.preventDefault();
334
+ closeAutocomplete();
335
+ onNavigate(inputUrl);
336
+ return;
337
+ }
338
+ if (e.key === "Escape") {
339
+ e.preventDefault();
340
+ setInputUrl(currentUrlRef.current);
341
+ const input = e.currentTarget;
342
+ requestAnimationFrame(() => {
343
+ if (input.isConnected) input.select();
344
+ });
345
+ }
346
+ },
347
+ [
348
+ inputUrl,
349
+ onNavigate,
350
+ setInputUrl,
351
+ currentUrlRef,
352
+ autocompleteIsOpen,
353
+ autocompleteItems,
354
+ autocompleteSelectedIndex,
355
+ closeAutocomplete
356
+ ]
357
+ );
358
+ const handlePaneKeyDown = reactExports.useCallback(
359
+ (e) => {
360
+ if (e.key.toLowerCase() !== "f") return;
361
+ if (e.shiftKey || e.altKey) return;
362
+ const isMac = /mac/i.test(navigator.userAgent);
363
+ const wantsFind = isMac ? e.metaKey && !e.ctrlKey : e.ctrlKey && !e.metaKey;
364
+ if (!wantsFind) return;
365
+ e.preventDefault();
366
+ e.stopPropagation();
367
+ find.open();
368
+ },
369
+ [find]
370
+ );
371
+ const handleToggleDevTools = reactExports.useCallback(async () => {
372
+ if (!isDesktop) return;
373
+ try {
374
+ await invoke("browser_toggle_dev_tools", { [keyName]: key });
375
+ } catch (e) {
376
+ console.error("browser_toggle_dev_tools failed:", e);
377
+ }
378
+ }, [key, keyName]);
379
+ return {
380
+ find,
381
+ addressInputFocusedRef,
382
+ handleAddressFocus,
383
+ handleAddressBlur,
384
+ handleAddressKeyDown,
385
+ handlePaneKeyDown,
386
+ handleToggleDevTools,
387
+ autocomplete: {
388
+ isOpen: autocompleteIsOpen,
389
+ items: autocompleteItems,
390
+ selectedIndex: autocompleteSelectedIndex,
391
+ close: closeAutocomplete,
392
+ setSelectedIndex: setAutocompleteSelectedIndex
393
+ },
394
+ paneDataAttrs: {
395
+ "data-band-browser-pane": "",
396
+ "data-band-browser-pane-key": key,
397
+ "data-band-browser-pane-keyname": keyName
398
+ }
399
+ };
400
+ }
401
+ function waitForPaint() {
402
+ return new Promise((resolve) => {
403
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
404
+ });
405
+ }
406
+ function useBrowserPaneFreeze(args) {
407
+ const { created, visible, ipcKeyRef } = args;
408
+ const frozen = useBrowserPaneFrozen();
409
+ const [snapshot, setSnapshot] = reactExports.useState(null);
410
+ const freezeAppliedRef = reactExports.useRef(false);
411
+ reactExports.useEffect(() => {
412
+ if (!isDesktop || !created) return;
413
+ let cancelled = false;
414
+ if (frozen) {
415
+ if (!visible) return;
416
+ const ipcKey = ipcKeyRef.current;
417
+ (async () => {
418
+ try {
419
+ const dataUrl = await invoke("browser_capture_page", ipcKey);
420
+ if (!cancelled && dataUrl) {
421
+ reactDomExports.flushSync(() => setSnapshot(dataUrl));
422
+ await waitForPaint();
423
+ }
424
+ } catch {
425
+ }
426
+ if (cancelled) return;
427
+ freezeAppliedRef.current = true;
428
+ invoke("browser_pause_media", ipcKey).catch(() => {
429
+ });
430
+ invoke("browser_hide", ipcKey).catch(() => {
431
+ });
432
+ })();
433
+ } else {
434
+ if (freezeAppliedRef.current) {
435
+ freezeAppliedRef.current = false;
436
+ const ipcKey = ipcKeyRef.current;
437
+ invoke("browser_show", ipcKey).catch(() => {
438
+ });
439
+ invoke("browser_resume_media", ipcKey).catch(() => {
440
+ });
441
+ (async () => {
442
+ await waitForPaint();
443
+ if (!cancelled) setSnapshot(null);
444
+ })();
445
+ } else {
446
+ setSnapshot(null);
447
+ }
448
+ }
449
+ return () => {
450
+ cancelled = true;
451
+ };
452
+ }, [frozen, created, visible, ipcKeyRef]);
453
+ return { snapshot };
454
+ }
455
+ let hosts = /* @__PURE__ */ new Set();
456
+ const listeners = /* @__PURE__ */ new Set();
457
+ let bootstrapped = false;
458
+ function setHosts(updater) {
459
+ const next = updater(hosts);
460
+ if (next === hosts) return;
461
+ hosts = next;
462
+ for (const cb of listeners) cb();
463
+ }
464
+ function addHost(raw) {
465
+ const h = raw.toLowerCase();
466
+ if (!h || hosts.has(h)) return;
467
+ setHosts((prev) => {
468
+ const next = new Set(prev);
469
+ next.add(h);
470
+ return next;
471
+ });
472
+ }
473
+ function bootstrap() {
474
+ if (bootstrapped) return;
475
+ if (!isDesktop) return;
476
+ bootstrapped = true;
477
+ void listen("browser-host-overridden", (event) => {
478
+ if (event.payload.host) addHost(event.payload.host);
479
+ }).catch((err) => {
480
+ console.error("browser-host-overridden listen failed:", err);
481
+ bootstrapped = false;
482
+ });
483
+ invoke("browser_get_overridden_hosts").then((list) => {
484
+ if (!Array.isArray(list)) return;
485
+ for (const h of list) addHost(h);
486
+ }).catch((err) => {
487
+ console.error("browser_get_overridden_hosts failed:", err);
488
+ });
489
+ }
490
+ function subscribe(cb) {
491
+ bootstrap();
492
+ listeners.add(cb);
493
+ return () => {
494
+ listeners.delete(cb);
495
+ };
496
+ }
497
+ function getSnapshot() {
498
+ return hosts;
499
+ }
500
+ function useOverriddenHosts() {
501
+ const currentHosts = reactExports.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
502
+ const isOverriddenHost = reactExports.useCallback(
503
+ (currentUrl) => {
504
+ if (!currentUrl) return false;
505
+ let host;
506
+ try {
507
+ host = new URL(currentUrl).hostname.toLowerCase();
508
+ } catch {
509
+ return false;
510
+ }
511
+ return currentHosts.has(host);
512
+ },
513
+ [currentHosts]
514
+ );
515
+ return { isOverriddenHost };
516
+ }
517
+ function AddressBarAutocomplete({ state, onSelect }) {
518
+ if (!state.isOpen || state.items.length === 0) return null;
519
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
520
+ "div",
521
+ {
522
+ className: "absolute top-full left-0 right-0 z-20 mx-2 mt-1 overflow-hidden rounded-md border border-border bg-popover shadow-md",
523
+ onMouseDown: (e) => {
524
+ e.preventDefault();
525
+ },
526
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "max-h-80 overflow-y-auto py-1", children: state.items.map((item, index) => {
527
+ const selected = index === state.selectedIndex;
528
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
529
+ "button",
530
+ {
531
+ type: "button",
532
+ onMouseDown: (e) => {
533
+ e.preventDefault();
534
+ onSelect(item.url);
535
+ state.close();
536
+ },
537
+ onMouseEnter: () => state.setSelectedIndex(index),
538
+ className: `flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm transition-colors ${selected ? "bg-accent text-foreground" : "text-foreground hover:bg-accent/60"}`,
539
+ children: [
540
+ item.faviconUrl ? /* @__PURE__ */ jsxRuntimeExports.jsx(
541
+ "img",
542
+ {
543
+ src: item.faviconUrl,
544
+ alt: "",
545
+ className: "size-4 shrink-0 rounded-sm",
546
+ onError: (e) => {
547
+ e.currentTarget.style.display = "none";
548
+ }
549
+ }
550
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "size-4 shrink-0 text-muted-foreground" }),
551
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
552
+ item.title ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate font-medium leading-tight", children: item.title }) : null,
553
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
554
+ "span",
555
+ {
556
+ className: `truncate text-xs leading-tight ${item.title ? "text-muted-foreground" : "text-foreground"}`,
557
+ children: item.url
558
+ }
559
+ )
560
+ ] })
561
+ ]
562
+ }
563
+ ) }, item.id);
564
+ }) })
565
+ }
566
+ );
567
+ }
568
+ function BrowserFindBar({ find }) {
569
+ if (!find.isOpen) return null;
570
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
571
+ SearchBar,
572
+ {
573
+ ref: find.searchBarRef,
574
+ query: find.query,
575
+ onQueryChange: find.setQuery,
576
+ options: find.options,
577
+ onOptionsChange: find.setOptions,
578
+ placeholder: "Find in page",
579
+ matchInfo: find.matchInfo ?? void 0,
580
+ onNext: find.findNext,
581
+ onPrevious: find.findPrevious,
582
+ onClose: find.close,
583
+ visibleOptions: ["caseSensitive"]
584
+ }
585
+ );
586
+ }
587
+ const SEARCH_DEBOUNCE_MS = 150;
588
+ const LIST_LIMIT = 200;
589
+ function groupByDay(entries) {
590
+ const groups = /* @__PURE__ */ new Map();
591
+ const order = [];
592
+ for (const entry of entries) {
593
+ const dayKey = dayKeyFor(entry.lastVisitedAt);
594
+ let group = groups.get(dayKey);
595
+ if (!group) {
596
+ group = { label: labelFor(dayKey), entries: [] };
597
+ groups.set(dayKey, group);
598
+ order.push(dayKey);
599
+ }
600
+ group.entries.push(entry);
601
+ }
602
+ return order.map((k) => groups.get(k));
603
+ }
604
+ function dayKeyFor(ts) {
605
+ const d = new Date(ts);
606
+ const y = d.getFullYear();
607
+ const m = String(d.getMonth() + 1).padStart(2, "0");
608
+ const day = String(d.getDate()).padStart(2, "0");
609
+ return `${y}-${m}-${day}`;
610
+ }
611
+ function labelFor(dayKey) {
612
+ const now = /* @__PURE__ */ new Date();
613
+ const today = dayKeyFor(now.getTime());
614
+ const yesterdayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
615
+ const yesterday = dayKeyFor(yesterdayDate.getTime());
616
+ if (dayKey === today) return "Today";
617
+ if (dayKey === yesterday) return "Yesterday";
618
+ const [y, m, d] = dayKey.split("-").map(Number);
619
+ const date = new Date(y, m - 1, d);
620
+ const daysSince = Math.floor((Date.now() - date.getTime()) / (24 * 60 * 60 * 1e3));
621
+ if (daysSince <= 7) {
622
+ return date.toLocaleDateString(void 0, { weekday: "long", month: "short", day: "numeric" });
623
+ }
624
+ return date.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
625
+ }
626
+ function timeFor(ts) {
627
+ return new Date(ts).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
628
+ }
629
+ function HistoryPopover({ workspaceId, onNavigate }) {
630
+ const searchId = reactExports.useId();
631
+ const [open, setOpen] = reactExports.useState(false);
632
+ const [query, setQuery] = reactExports.useState("");
633
+ const [entries, setEntries] = reactExports.useState([]);
634
+ const [loading, setLoading] = reactExports.useState(false);
635
+ const [reloadKey, setReloadKey] = reactExports.useState(0);
636
+ const refresh = reactExports.useCallback(() => {
637
+ setReloadKey((k) => k + 1);
638
+ }, []);
639
+ reactExports.useEffect(() => {
640
+ if (!open) return;
641
+ let cancelled = false;
642
+ setLoading(true);
643
+ const trimmed = query.trim();
644
+ const fire = () => {
645
+ const promise = trimmed ? trpc.history.search.query({ workspaceId, query: trimmed, limit: 50 }) : trpc.history.list.query({ workspaceId, limit: LIST_LIMIT });
646
+ promise.then((result) => {
647
+ if (cancelled) return;
648
+ setEntries(result.entries);
649
+ }).catch(() => {
650
+ if (!cancelled) setEntries([]);
651
+ }).finally(() => {
652
+ if (!cancelled) setLoading(false);
653
+ });
654
+ };
655
+ const delay = trimmed ? SEARCH_DEBOUNCE_MS : 0;
656
+ const handle = setTimeout(fire, delay);
657
+ return () => {
658
+ cancelled = true;
659
+ clearTimeout(handle);
660
+ };
661
+ }, [open, query, workspaceId, reloadKey]);
662
+ const groups = reactExports.useMemo(() => groupByDay(entries), [entries]);
663
+ const handleNavigate = reactExports.useCallback(
664
+ (url) => {
665
+ onNavigate(url);
666
+ setOpen(false);
667
+ },
668
+ [onNavigate]
669
+ );
670
+ const handleDelete = reactExports.useCallback(
671
+ async (id) => {
672
+ await trpc.history.delete.mutate({ id, workspaceId }).catch(() => {
673
+ });
674
+ refresh();
675
+ },
676
+ [refresh, workspaceId]
677
+ );
678
+ const handleClear = reactExports.useCallback(
679
+ async (range) => {
680
+ await trpc.history.clear.mutate({ workspaceId, range }).catch(() => {
681
+ });
682
+ refresh();
683
+ },
684
+ [refresh, workspaceId]
685
+ );
686
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { open, onOpenChange: setOpen, children: [
687
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
688
+ "button",
689
+ {
690
+ type: "button",
691
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
692
+ title: "History",
693
+ "aria-label": "Browser history",
694
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(History, { className: "size-4" })
695
+ }
696
+ ) }),
697
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
698
+ PopoverContent,
699
+ {
700
+ align: "end",
701
+ sideOffset: 6,
702
+ className: "w-[440px] p-0",
703
+ onOpenAutoFocus: (e) => {
704
+ e.preventDefault();
705
+ const input = document.getElementById(searchId);
706
+ if (input) input.focus();
707
+ },
708
+ children: [
709
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
710
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "size-4 shrink-0 text-muted-foreground" }),
711
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
712
+ "input",
713
+ {
714
+ id: searchId,
715
+ type: "text",
716
+ value: query,
717
+ onChange: (e) => setQuery(e.target.value),
718
+ placeholder: "Search history",
719
+ className: "min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
720
+ }
721
+ )
722
+ ] }),
723
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-[420px] overflow-y-auto", children: loading && entries.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "Loading…" }) : groups.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: query ? "No matching history" : "No history yet" }) : groups.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
724
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sticky top-0 z-10 border-b border-border bg-popover px-3 py-1 text-xs font-medium text-muted-foreground", children: group.label }),
725
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { children: group.entries.map((entry) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
726
+ "li",
727
+ {
728
+ className: "group relative flex min-w-0 items-center gap-2 py-1.5 pl-3 pr-2 hover:bg-accent/60",
729
+ children: [
730
+ entry.faviconUrl ? /* @__PURE__ */ jsxRuntimeExports.jsx(
731
+ "img",
732
+ {
733
+ src: entry.faviconUrl,
734
+ alt: "",
735
+ className: "size-4 shrink-0 rounded-sm",
736
+ onError: (e) => {
737
+ e.currentTarget.style.display = "none";
738
+ }
739
+ }
740
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "size-4 shrink-0 text-muted-foreground" }),
741
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
742
+ "button",
743
+ {
744
+ type: "button",
745
+ onClick: () => handleNavigate(entry.url),
746
+ className: "flex min-w-0 flex-1 flex-col items-stretch gap-0.5 overflow-hidden pr-6 text-left",
747
+ children: [
748
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate text-sm font-medium leading-tight text-foreground", children: entry.title || entry.url }),
749
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 leading-tight", children: [
750
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs text-muted-foreground", children: entry.url }),
751
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground tabular-nums", children: timeFor(entry.lastVisitedAt) })
752
+ ] })
753
+ ]
754
+ }
755
+ ),
756
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
757
+ "button",
758
+ {
759
+ type: "button",
760
+ onClick: () => handleDelete(entry.id),
761
+ className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100",
762
+ title: "Delete entry",
763
+ "aria-label": "Delete entry",
764
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" })
765
+ }
766
+ )
767
+ ]
768
+ },
769
+ entry.id
770
+ )) })
771
+ ] }, group.label)) }),
772
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-end border-t border-border px-3 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
773
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
774
+ "button",
775
+ {
776
+ type: "button",
777
+ className: "flex items-center gap-1 rounded px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
778
+ children: [
779
+ "Clear ",
780
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" })
781
+ ]
782
+ }
783
+ ) }),
784
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuContent, { align: "end", children: [
785
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { onSelect: () => handleClear("hour"), children: "Last hour" }),
786
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { onSelect: () => handleClear("day"), children: "Last 24 hours" }),
787
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { onSelect: () => handleClear("week"), children: "Last week" }),
788
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { onSelect: () => handleClear("all"), children: "All time" })
789
+ ] })
790
+ ] }) })
791
+ ]
792
+ }
793
+ )
794
+ ] });
795
+ }
796
+ function NotSecureBadge() {
797
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
798
+ "span",
799
+ {
800
+ className: "inline-flex shrink-0 items-center gap-1 rounded bg-destructive/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-destructive",
801
+ title: "The certificate for this site was overridden for the current session. Use with caution.",
802
+ "data-testid": "cert-not-secure-badge",
803
+ children: [
804
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-hidden": true }),
805
+ "Not secure"
806
+ ]
807
+ }
808
+ );
809
+ }
810
+ const DEFAULT_URL = "";
811
+ const BLANK_URL = "about:blank";
812
+ const faviconMap = /* @__PURE__ */ new Map();
813
+ const faviconListeners = /* @__PURE__ */ new Set();
814
+ function setFaviconUrl(browserId, url) {
815
+ if (faviconMap.get(browserId) !== url) {
816
+ faviconMap.set(browserId, url);
817
+ for (const listener of faviconListeners) listener();
818
+ }
819
+ }
820
+ function subscribeFavicons(cb) {
821
+ faviconListeners.add(cb);
822
+ return () => {
823
+ faviconListeners.delete(cb);
824
+ };
825
+ }
826
+ function useFavicon(browserId) {
827
+ return reactExports.useSyncExternalStore(subscribeFavicons, () => faviconMap.get(browserId));
828
+ }
829
+ function BrowserPaneComponent({
830
+ params,
831
+ api
832
+ }) {
833
+ const { browserId, initialUrl, workspaceId: workspaceIdParam } = params;
834
+ const [currentUrl, setCurrentUrl] = reactExports.useState(() => initialUrl ?? DEFAULT_URL);
835
+ const [inputUrl, setInputUrl] = reactExports.useState(() => initialUrl ?? DEFAULT_URL);
836
+ const [loading, setLoading] = reactExports.useState(false);
837
+ const [created, setCreated] = reactExports.useState(false);
838
+ const createdRef = reactExports.useRef(false);
839
+ const placeholderRef = reactExports.useRef(null);
840
+ const creatingRef = reactExports.useRef(false);
841
+ const pendingNavRef = reactExports.useRef(null);
842
+ const browserIdRef = reactExports.useRef(browserId);
843
+ browserIdRef.current = browserId;
844
+ const currentUrlRef = reactExports.useRef(currentUrl);
845
+ currentUrlRef.current = currentUrl;
846
+ const [workspaceId, setWorkspaceId] = reactExports.useState(workspaceIdParam ?? "");
847
+ const workspaceIdRef = reactExports.useRef(workspaceId);
848
+ workspaceIdRef.current = workspaceId;
849
+ const ipcKeyRef = reactExports.useRef({ browserId });
850
+ ipcKeyRef.current = { browserId };
851
+ const { snapshot } = useBrowserPaneFreeze({
852
+ created,
853
+ visible: api.isVisible && params.wsActive !== false,
854
+ ipcKeyRef
855
+ });
856
+ const { isOverriddenHost } = useOverriddenHosts();
857
+ const getBounds = reactExports.useCallback(() => {
858
+ const el = placeholderRef.current;
859
+ if (!el) return null;
860
+ const rect = el.getBoundingClientRect();
861
+ return {
862
+ x: rect.left + 1,
863
+ y: rect.top,
864
+ width: Math.max(0, rect.width - 2),
865
+ height: rect.height
866
+ };
867
+ }, []);
868
+ const invoke$1 = reactExports.useCallback(async (cmd, args) => {
869
+ if (!isDesktop) return;
870
+ return invoke(cmd, args);
871
+ }, []);
872
+ reactExports.useEffect(() => {
873
+ if (!browserId || initialUrl) return;
874
+ let cancelled = false;
875
+ trpc.browsers.get.query({ browserId }).then((result) => {
876
+ if (cancelled) return;
877
+ const ws = result.browser?.workspaceId;
878
+ if (ws && !workspaceIdRef.current) {
879
+ setWorkspaceId(ws);
880
+ }
881
+ const url = result.browser?.url;
882
+ if (!url || url === "" || url === BLANK_URL) return;
883
+ setCurrentUrl(url);
884
+ setInputUrl(url);
885
+ if (createdRef.current) {
886
+ invoke$1("browser_navigate", { browserId, url }).catch(() => {
887
+ });
888
+ } else {
889
+ pendingNavRef.current = url;
890
+ }
891
+ }).catch(() => {
892
+ });
893
+ return () => {
894
+ cancelled = true;
895
+ };
896
+ }, [browserId, initialUrl, invoke$1]);
897
+ reactExports.useEffect(() => {
898
+ if (!isDesktop || created || creatingRef.current) return;
899
+ const el = placeholderRef.current;
900
+ if (!el) return;
901
+ let cancelled = false;
902
+ const tryCreate = async () => {
903
+ if (cancelled || createdRef.current || creatingRef.current) return;
904
+ const bounds = getBounds();
905
+ if (!bounds || bounds.width === 0 || bounds.height === 0) return;
906
+ observer.disconnect();
907
+ creatingRef.current = true;
908
+ try {
909
+ await invoke$1("browser_create", {
910
+ browserId,
911
+ ...bounds,
912
+ url: currentUrlRef.current || BLANK_URL
913
+ });
914
+ createdRef.current = true;
915
+ setCreated(true);
916
+ const pending = pendingNavRef.current;
917
+ if (pending) {
918
+ pendingNavRef.current = null;
919
+ await invoke$1("browser_navigate", {
920
+ browserId: browserIdRef.current,
921
+ url: pending
922
+ });
923
+ }
924
+ } catch (e) {
925
+ console.error("Failed to create browser webview:", e);
926
+ } finally {
927
+ creatingRef.current = false;
928
+ }
929
+ };
930
+ const observer = new ResizeObserver(() => {
931
+ tryCreate();
932
+ });
933
+ observer.observe(el);
934
+ const timer = setTimeout(tryCreate, 50);
935
+ return () => {
936
+ cancelled = true;
937
+ clearTimeout(timer);
938
+ observer.disconnect();
939
+ };
940
+ }, [created, getBounds, invoke$1, browserId]);
941
+ const urlPersistTimer = reactExports.useRef(null);
942
+ reactExports.useEffect(() => {
943
+ if (!isDesktop) return;
944
+ let unlistenUrl;
945
+ let unlistenTitle;
946
+ (async () => {
947
+ unlistenUrl = await listen("browser-url-changed", (event) => {
948
+ if (event.payload.browser_id !== browserIdRef.current) return;
949
+ const url = event.payload.url;
950
+ setLoading(event.payload.loading);
951
+ if (url === BLANK_URL) return;
952
+ setCurrentUrl(url);
953
+ if (!addressInputFocusedRef.current) {
954
+ setInputUrl(url);
955
+ }
956
+ if (urlPersistTimer.current) clearTimeout(urlPersistTimer.current);
957
+ urlPersistTimer.current = setTimeout(() => {
958
+ trpc.browsers.navigate.mutate({ browserId: browserIdRef.current, url }).catch(() => {
959
+ });
960
+ }, 500);
961
+ let faviconForHistory;
962
+ try {
963
+ const origin = new URL(url).origin;
964
+ faviconForHistory = `${origin}/favicon.ico`;
965
+ setFaviconUrl(browserIdRef.current, faviconForHistory);
966
+ } catch {
967
+ }
968
+ if (!event.payload.loading && workspaceIdRef.current) {
969
+ trpc.history.record.mutate({
970
+ workspaceId: workspaceIdRef.current,
971
+ url,
972
+ faviconUrl: faviconForHistory
973
+ }).catch(() => {
974
+ });
975
+ }
976
+ });
977
+ unlistenTitle = await listen(
978
+ "browser-title-changed",
979
+ (event) => {
980
+ if (event.payload.browser_id !== browserIdRef.current) return;
981
+ if (event.payload.title) {
982
+ api.setTitle(event.payload.title);
983
+ const url = currentUrlRef.current;
984
+ if (url && url !== BLANK_URL && workspaceIdRef.current) {
985
+ trpc.history.updateMeta.mutate({
986
+ workspaceId: workspaceIdRef.current,
987
+ url,
988
+ title: event.payload.title
989
+ }).catch(() => {
990
+ });
991
+ }
992
+ }
993
+ }
994
+ );
995
+ })();
996
+ return () => {
997
+ unlistenUrl?.();
998
+ unlistenTitle?.();
999
+ if (urlPersistTimer.current) {
1000
+ clearTimeout(urlPersistTimer.current);
1001
+ urlPersistTimer.current = null;
1002
+ const finalUrl = currentUrlRef.current;
1003
+ if (finalUrl && finalUrl !== BLANK_URL) {
1004
+ trpc.browsers.navigate.mutate({ browserId: browserIdRef.current, url: finalUrl }).catch(() => {
1005
+ });
1006
+ }
1007
+ }
1008
+ };
1009
+ }, [api]);
1010
+ reactExports.useEffect(() => {
1011
+ if (!isDesktop || !created) return;
1012
+ const logFail = (cmd) => (err) => console.error(`[BrowserPane] ${cmd} failed`, err);
1013
+ const showWebview = () => {
1014
+ const bounds = getBounds();
1015
+ if (bounds && bounds.width > 0 && bounds.height > 0) {
1016
+ invoke$1("browser_set_bounds", { browserId, ...bounds }).catch(logFail("browser_set_bounds"));
1017
+ }
1018
+ invoke$1("browser_show", { browserId }).catch(logFail("browser_show"));
1019
+ };
1020
+ const hideWebview = () => {
1021
+ invoke$1("browser_hide", { browserId }).catch(logFail("browser_hide"));
1022
+ };
1023
+ const d = api.onDidVisibilityChange((e) => {
1024
+ if (e.isVisible) {
1025
+ showWebview();
1026
+ } else {
1027
+ hideWebview();
1028
+ }
1029
+ });
1030
+ return () => {
1031
+ d.dispose();
1032
+ };
1033
+ }, [api, created, getBounds, invoke$1, browserId]);
1034
+ reactExports.useEffect(() => {
1035
+ if (!isDesktop || !created) return;
1036
+ const wsActive = params.wsActive !== false;
1037
+ const logFail = (cmd) => (err) => console.error(`[BrowserPane] ${cmd} failed`, err);
1038
+ if (!wsActive) {
1039
+ invoke$1("browser_hide", { browserId }).catch(logFail("browser_hide"));
1040
+ } else if (api.isVisible) {
1041
+ const bounds = getBounds();
1042
+ if (bounds && bounds.width > 0 && bounds.height > 0) {
1043
+ invoke$1("browser_set_bounds", { browserId, ...bounds }).catch(logFail("browser_set_bounds"));
1044
+ }
1045
+ invoke$1("browser_show", { browserId }).catch(logFail("browser_show"));
1046
+ }
1047
+ }, [params.wsActive, api, created, getBounds, invoke$1, browserId]);
1048
+ reactExports.useEffect(() => {
1049
+ if (!isDesktop || !created) return;
1050
+ const el = placeholderRef.current;
1051
+ if (!el) return;
1052
+ const observer = new ResizeObserver(() => {
1053
+ const bounds = getBounds();
1054
+ if (!bounds || bounds.width === 0 || bounds.height === 0) return;
1055
+ invoke$1("browser_set_bounds", { browserId, ...bounds }).catch(() => {
1056
+ });
1057
+ });
1058
+ observer.observe(el);
1059
+ return () => observer.disconnect();
1060
+ }, [created, getBounds, invoke$1, browserId]);
1061
+ reactExports.useEffect(() => {
1062
+ return () => {
1063
+ if (isDesktop) {
1064
+ const bId = browserIdRef.current;
1065
+ invoke("browser_destroy", { browserId: bId }).catch(() => {
1066
+ });
1067
+ }
1068
+ };
1069
+ }, []);
1070
+ const handleNavigate = reactExports.useCallback(
1071
+ async (rawUrl) => {
1072
+ let normalized = rawUrl.trim();
1073
+ if (!normalized) {
1074
+ setCurrentUrl("");
1075
+ setInputUrl("");
1076
+ setLoading(false);
1077
+ if (createdRef.current) {
1078
+ try {
1079
+ await invoke$1("browser_navigate", { browserId, url: BLANK_URL });
1080
+ } catch (e) {
1081
+ console.error("browser_navigate failed:", e);
1082
+ }
1083
+ } else {
1084
+ pendingNavRef.current = BLANK_URL;
1085
+ }
1086
+ return;
1087
+ }
1088
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
1089
+ if (normalized.includes(".") && !normalized.includes(" ")) {
1090
+ normalized = `https://${normalized}`;
1091
+ } else {
1092
+ normalized = `https://www.google.com/search?q=${encodeURIComponent(normalized)}`;
1093
+ }
1094
+ }
1095
+ setCurrentUrl(normalized);
1096
+ setInputUrl(normalized);
1097
+ setLoading(true);
1098
+ if (createdRef.current) {
1099
+ try {
1100
+ await invoke$1("browser_navigate", { browserId, url: normalized });
1101
+ } catch (e) {
1102
+ console.error("browser_navigate failed:", e);
1103
+ }
1104
+ } else {
1105
+ pendingNavRef.current = normalized;
1106
+ }
1107
+ },
1108
+ [invoke$1, browserId]
1109
+ );
1110
+ const handleBack = reactExports.useCallback(async () => {
1111
+ try {
1112
+ await invoke$1("browser_go_back", { browserId });
1113
+ } catch (e) {
1114
+ console.error("browser_go_back failed:", e);
1115
+ }
1116
+ }, [invoke$1, browserId]);
1117
+ const handleForward = reactExports.useCallback(async () => {
1118
+ try {
1119
+ await invoke$1("browser_go_forward", { browserId });
1120
+ } catch (e) {
1121
+ console.error("browser_go_forward failed:", e);
1122
+ }
1123
+ }, [invoke$1, browserId]);
1124
+ const handleReload = reactExports.useCallback(async () => {
1125
+ try {
1126
+ setLoading(true);
1127
+ await invoke$1("browser_reload", { browserId });
1128
+ } catch (e) {
1129
+ console.error("browser_reload failed:", e);
1130
+ }
1131
+ }, [invoke$1, browserId]);
1132
+ const handleStop = reactExports.useCallback(async () => {
1133
+ try {
1134
+ await invoke$1("browser_eval", { browserId, js: "window.stop()" });
1135
+ setLoading(false);
1136
+ } catch {
1137
+ setLoading(false);
1138
+ }
1139
+ }, [invoke$1, browserId]);
1140
+ const {
1141
+ find,
1142
+ addressInputFocusedRef,
1143
+ handleAddressFocus,
1144
+ handleAddressBlur,
1145
+ handleAddressKeyDown,
1146
+ handlePaneKeyDown,
1147
+ handleToggleDevTools,
1148
+ autocomplete,
1149
+ paneDataAttrs
1150
+ } = useBrowserPaneControls({
1151
+ key: browserId,
1152
+ keyName: "browserId",
1153
+ workspaceId,
1154
+ currentUrlRef,
1155
+ setInputUrl,
1156
+ inputUrl,
1157
+ onNavigate: handleNavigate
1158
+ });
1159
+ if (!browserId) return null;
1160
+ if (!isDesktop) {
1161
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center text-muted-foreground", children: "Browser panel is only available in the desktop app" });
1162
+ }
1163
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full w-full flex-col", onKeyDown: handlePaneKeyDown, ...paneDataAttrs, children: [
1164
+ /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `@keyframes browser-bar-slide {
1165
+ 0% { transform: translateX(-100%); }
1166
+ 50% { transform: translateX(200%); }
1167
+ 100% { transform: translateX(-100%); }
1168
+ }` }),
1169
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex h-10 shrink-0 items-center gap-1 border-b border-border bg-background px-2", children: [
1170
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1171
+ "button",
1172
+ {
1173
+ type: "button",
1174
+ onClick: handleBack,
1175
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1176
+ title: "Back",
1177
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-4" })
1178
+ }
1179
+ ),
1180
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1181
+ "button",
1182
+ {
1183
+ type: "button",
1184
+ onClick: handleForward,
1185
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1186
+ title: "Forward",
1187
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { className: "size-4" })
1188
+ }
1189
+ ),
1190
+ loading ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1191
+ "button",
1192
+ {
1193
+ type: "button",
1194
+ onClick: handleStop,
1195
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1196
+ title: "Stop",
1197
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
1198
+ }
1199
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
1200
+ "button",
1201
+ {
1202
+ type: "button",
1203
+ onClick: handleReload,
1204
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1205
+ title: "Reload",
1206
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: "size-4" })
1207
+ }
1208
+ ),
1209
+ isOverriddenHost(currentUrl) ? /* @__PURE__ */ jsxRuntimeExports.jsx(NotSecureBadge, {}) : null,
1210
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1211
+ "input",
1212
+ {
1213
+ type: "text",
1214
+ value: inputUrl,
1215
+ onChange: (e) => setInputUrl(e.target.value),
1216
+ onKeyDown: handleAddressKeyDown,
1217
+ onFocus: handleAddressFocus,
1218
+ onBlur: handleAddressBlur,
1219
+ className: "min-w-0 flex-1 rounded border border-transparent bg-muted/50 px-3 py-1.5 text-sm text-foreground outline-none transition-colors focus:border-border",
1220
+ placeholder: "Enter URL or search...",
1221
+ "data-band-address-input": ""
1222
+ }
1223
+ ),
1224
+ workspaceId ? /* @__PURE__ */ jsxRuntimeExports.jsx(HistoryPopover, { workspaceId, onNavigate: handleNavigate }) : null,
1225
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1226
+ "button",
1227
+ {
1228
+ type: "button",
1229
+ onClick: handleToggleDevTools,
1230
+ className: "flex items-center justify-center rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1231
+ title: "Toggle DevTools",
1232
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-4" })
1233
+ }
1234
+ ),
1235
+ loading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-x-0 bottom-0 h-0.5 overflow-hidden bg-blue-500/10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1236
+ "div",
1237
+ {
1238
+ className: "h-full w-2/5 rounded-full bg-blue-500",
1239
+ style: {
1240
+ animation: "browser-bar-slide 1.4s ease-in-out infinite"
1241
+ }
1242
+ }
1243
+ ) }),
1244
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AddressBarAutocomplete, { state: autocomplete, onSelect: handleNavigate })
1245
+ ] }),
1246
+ /* @__PURE__ */ jsxRuntimeExports.jsx(BrowserFindBar, { find }),
1247
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: placeholderRef, className: "relative min-h-0 flex-1", children: snapshot ? (
1248
+ // `object-contain object-top` — see the identical block in
1249
+ // `BrowserPanelComponent` for the DevTools-aware rationale.
1250
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1251
+ "img",
1252
+ {
1253
+ src: snapshot,
1254
+ alt: "",
1255
+ className: "pointer-events-none absolute inset-0 size-full object-contain object-top",
1256
+ draggable: false
1257
+ }
1258
+ )
1259
+ ) : null })
1260
+ ] });
1261
+ }
1262
+ const freshBrowserIds = /* @__PURE__ */ new Set();
1263
+ function markBrowserFresh(browserId) {
1264
+ freshBrowserIds.add(browserId);
1265
+ }
1266
+ function uuid() {
1267
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1268
+ return crypto.randomUUID();
1269
+ }
1270
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
1271
+ bytes[6] = bytes[6] & 15 | 64;
1272
+ bytes[8] = bytes[8] & 63 | 128;
1273
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1274
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1275
+ }
1276
+ function newBrowserId() {
1277
+ return `browser_${uuid()}`;
1278
+ }
1279
+ function browserLayoutKey(workspaceId) {
1280
+ return ["browserLayout", workspaceId];
1281
+ }
1282
+ const saveTimers = /* @__PURE__ */ new Map();
1283
+ function panelIdsFromLayout(layout) {
1284
+ if (typeof layout === "object" && layout !== null) {
1285
+ const panels = layout.panels;
1286
+ if (typeof panels === "object" && panels !== null) {
1287
+ return new Set(Object.keys(panels));
1288
+ }
1289
+ }
1290
+ return /* @__PURE__ */ new Set();
1291
+ }
1292
+ function persistToServer(workspaceId, layout, opts) {
1293
+ if (opts?.queryClient) {
1294
+ const prev = opts.queryClient.getQueryData(browserLayoutKey(workspaceId));
1295
+ opts.queryClient.setQueryData(browserLayoutKey(workspaceId), {
1296
+ layout,
1297
+ browserIds: panelIdsFromLayout(layout),
1298
+ urls: prev?.urls ?? /* @__PURE__ */ new Map()
1299
+ });
1300
+ }
1301
+ const existing = saveTimers.get(workspaceId);
1302
+ if (existing) clearTimeout(existing);
1303
+ saveTimers.set(
1304
+ workspaceId,
1305
+ setTimeout(() => {
1306
+ saveTimers.delete(workspaceId);
1307
+ trpc.browserLayout.save.mutate({ workspaceId, tree: layout }).catch((err) => {
1308
+ console.error("[DockviewBrowserContainer] failed to persist layout:", err);
1309
+ });
1310
+ }, 500)
1311
+ );
1312
+ }
1313
+ function isDockviewLayout(obj) {
1314
+ if (typeof obj !== "object" || obj === null) return false;
1315
+ const o = obj;
1316
+ return typeof o.grid === "object" && typeof o.panels === "object";
1317
+ }
1318
+ const browserTabTheme = {
1319
+ name: "band",
1320
+ className: "dockview-theme-band dockview-browser-tabs"
1321
+ };
1322
+ const BrowserVisibilityContext = reactExports.createContext({ visible: true, wsActive: true });
1323
+ function BrowserTabPanel({ params, api }) {
1324
+ const { visible } = reactExports.useContext(BrowserVisibilityContext);
1325
+ if (!params.workspaceId || !params.browserId) return null;
1326
+ const paneParams = {
1327
+ workspaceId: params.workspaceId,
1328
+ browserId: params.browserId,
1329
+ wsActive: visible,
1330
+ initialUrl: params.initialUrl
1331
+ };
1332
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full w-full flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1333
+ BrowserPaneComponent,
1334
+ {
1335
+ params: paneParams,
1336
+ api,
1337
+ ...{}
1338
+ }
1339
+ ) });
1340
+ }
1341
+ function BrowserTab(props) {
1342
+ const [title, setTitle] = reactExports.useState(props.api.title ?? "New Tab");
1343
+ const [panelCount, setPanelCount] = reactExports.useState(props.containerApi.panels.length);
1344
+ const [faviconError, setFaviconError] = reactExports.useState(false);
1345
+ const browserId = props.params.browserId;
1346
+ const faviconUrl = useFavicon(browserId);
1347
+ const prevFaviconRef = reactExports.useRef(faviconUrl);
1348
+ if (faviconUrl !== prevFaviconRef.current) {
1349
+ prevFaviconRef.current = faviconUrl;
1350
+ if (faviconError) setFaviconError(false);
1351
+ }
1352
+ reactExports.useEffect(() => {
1353
+ const d = props.api.onDidTitleChange(() => setTitle(props.api.title ?? "New Tab"));
1354
+ return () => d.dispose();
1355
+ }, [props.api]);
1356
+ reactExports.useEffect(() => {
1357
+ const cApi = props.containerApi;
1358
+ const update = () => setPanelCount(cApi.panels.length);
1359
+ const d1 = cApi.onDidAddPanel(update);
1360
+ const d2 = cApi.onDidRemovePanel(update);
1361
+ return () => {
1362
+ d1.dispose();
1363
+ d2.dispose();
1364
+ };
1365
+ }, [props.containerApi]);
1366
+ const handleClose = reactExports.useCallback(
1367
+ (e) => {
1368
+ e.stopPropagation();
1369
+ closeTabRef.current?.(browserId);
1370
+ },
1371
+ [browserId]
1372
+ );
1373
+ const showClose = panelCount > 1;
1374
+ const showFavicon = faviconUrl && !faviconError;
1375
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dv-default-tab", children: [
1376
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
1377
+ showFavicon ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1378
+ "img",
1379
+ {
1380
+ src: faviconUrl,
1381
+ alt: "",
1382
+ className: "size-3.5 shrink-0",
1383
+ onError: () => setFaviconError(true)
1384
+ }
1385
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "size-3.5 shrink-0 text-muted-foreground" }),
1386
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: title })
1387
+ ] }),
1388
+ showClose && /* @__PURE__ */ jsxRuntimeExports.jsx(
1389
+ "button",
1390
+ {
1391
+ type: "button",
1392
+ className: "ml-1 inline-flex size-4 items-center justify-center rounded-sm opacity-60 hover:opacity-100 hover:bg-accent transition-colors",
1393
+ onClick: handleClose,
1394
+ title: "Close tab",
1395
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3" })
1396
+ }
1397
+ )
1398
+ ] });
1399
+ }
1400
+ const addTabRef = {
1401
+ current: { onAdd: () => {
1402
+ }, onSplit: () => {
1403
+ } }
1404
+ };
1405
+ const closeTabRef = {
1406
+ current: null
1407
+ };
1408
+ const RightHeaderActions = React.memo(function RightHeaderActions2(props) {
1409
+ const groupId = props.group.id;
1410
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full items-center", children: [
1411
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1412
+ "button",
1413
+ {
1414
+ type: "button",
1415
+ className: "inline-flex size-8 items-center justify-center text-muted-foreground hover:text-foreground hover:bg-accent rounded transition-colors",
1416
+ onClick: () => addTabRef.current.onSplit(groupId, "right"),
1417
+ title: "Split right",
1418
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Columns2, { className: "size-3.5" })
1419
+ }
1420
+ ),
1421
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1422
+ "button",
1423
+ {
1424
+ type: "button",
1425
+ className: "inline-flex size-8 items-center justify-center text-muted-foreground hover:text-foreground hover:bg-accent rounded transition-colors",
1426
+ onClick: () => addTabRef.current.onSplit(groupId, "below"),
1427
+ title: "Split down",
1428
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Rows2, { className: "size-3.5" })
1429
+ }
1430
+ ),
1431
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1432
+ "button",
1433
+ {
1434
+ type: "button",
1435
+ className: "inline-flex size-8 items-center justify-center text-muted-foreground hover:text-foreground hover:bg-accent rounded transition-colors",
1436
+ onClick: () => addTabRef.current.onAdd(groupId),
1437
+ title: "New browser tab",
1438
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" })
1439
+ }
1440
+ )
1441
+ ] });
1442
+ });
1443
+ const browserPanelComponents = {
1444
+ browserTab: BrowserTabPanel
1445
+ };
1446
+ const browserTabComponents = {
1447
+ browserTab: BrowserTab
1448
+ };
1449
+ function DockviewBrowserContainer({
1450
+ workspaceId,
1451
+ visible,
1452
+ wsActive
1453
+ }) {
1454
+ const adapter = useAdapter();
1455
+ const queryClient = useQueryClient();
1456
+ const apiRef = reactExports.useRef(null);
1457
+ const isRestoringRef = reactExports.useRef(false);
1458
+ const containerRef = reactExports.useRef(null);
1459
+ const { data: initialData } = useQuery({
1460
+ queryKey: browserLayoutKey(workspaceId),
1461
+ queryFn: async () => {
1462
+ const [{ tree }, { browsers }] = await Promise.all([
1463
+ trpc.browserLayout.get.query({ workspaceId }).catch(() => ({ tree: null })),
1464
+ trpc.browsers.list.query({ workspaceId }).catch(() => ({ browsers: [] }))
1465
+ ]);
1466
+ const urls = /* @__PURE__ */ new Map();
1467
+ for (const b of browsers) {
1468
+ if (b.url && b.url !== "about:blank") urls.set(b.id, b.url);
1469
+ }
1470
+ return {
1471
+ layout: tree,
1472
+ browserIds: new Set(browsers.map((b) => b.id)),
1473
+ urls
1474
+ };
1475
+ },
1476
+ staleTime: Number.POSITIVE_INFINITY
1477
+ // never auto-refetch — we manage persistence ourselves
1478
+ });
1479
+ const queryClientRef = reactExports.useRef(queryClient);
1480
+ queryClientRef.current = queryClient;
1481
+ const schedulePersist = reactExports.useCallback(() => {
1482
+ if (isRestoringRef.current) return;
1483
+ const api = apiRef.current;
1484
+ if (!api) return;
1485
+ persistToServer(workspaceId, api.toJSON(), { queryClient: queryClientRef.current });
1486
+ }, [workspaceId]);
1487
+ const handleAddTab = reactExports.useCallback(
1488
+ async (groupId, addOptions) => {
1489
+ const api = apiRef.current;
1490
+ if (!api) return;
1491
+ const browserId = newBrowserId();
1492
+ markBrowserFresh(browserId);
1493
+ const initialUrl = addOptions?.initialUrl;
1494
+ const options = {
1495
+ id: browserId,
1496
+ component: "browserTab",
1497
+ tabComponent: "browserTab",
1498
+ title: "New Tab",
1499
+ params: {
1500
+ workspaceId,
1501
+ browserId,
1502
+ ...initialUrl ? { initialUrl } : {}
1503
+ }
1504
+ };
1505
+ if (groupId) {
1506
+ options.position = {
1507
+ referenceGroup: groupId
1508
+ };
1509
+ }
1510
+ api.addPanel(options);
1511
+ try {
1512
+ await trpc.browsers.create.mutate({
1513
+ workspaceId,
1514
+ id: browserId,
1515
+ ...initialUrl ? { url: initialUrl } : {}
1516
+ });
1517
+ } catch (err) {
1518
+ console.error("[DockviewBrowserContainer] error pre-creating browser:", err);
1519
+ }
1520
+ },
1521
+ [workspaceId]
1522
+ );
1523
+ const handleSplit = reactExports.useCallback(
1524
+ async (groupId, direction) => {
1525
+ const api = apiRef.current;
1526
+ if (!api) return;
1527
+ const browserId = newBrowserId();
1528
+ markBrowserFresh(browserId);
1529
+ try {
1530
+ api.addPanel({
1531
+ id: browserId,
1532
+ component: "browserTab",
1533
+ tabComponent: "browserTab",
1534
+ title: "New Tab",
1535
+ params: {
1536
+ workspaceId,
1537
+ browserId
1538
+ },
1539
+ position: {
1540
+ referenceGroup: groupId,
1541
+ direction
1542
+ }
1543
+ });
1544
+ } catch (err) {
1545
+ console.error("[DockviewBrowserContainer] split addPanel threw:", err);
1546
+ return;
1547
+ }
1548
+ try {
1549
+ await trpc.browsers.create.mutate({ workspaceId, id: browserId });
1550
+ } catch (err) {
1551
+ console.error("[DockviewBrowserContainer] error creating split browser:", err);
1552
+ }
1553
+ },
1554
+ [workspaceId]
1555
+ );
1556
+ const closeTab = reactExports.useCallback((browserId) => {
1557
+ const api = apiRef.current;
1558
+ if (!api || api.panels.length <= 1) return;
1559
+ selectNeighbourBeforeRemove(api, browserId);
1560
+ const panel = api.getPanel(browserId);
1561
+ if (panel) {
1562
+ api.removePanel(panel);
1563
+ }
1564
+ requestAnimationFrame(() => {
1565
+ const activePanel = api.activePanel;
1566
+ if (!activePanel) return;
1567
+ activePanel.view.content.element.querySelector("[data-band-address-input]")?.focus();
1568
+ });
1569
+ trpc.browsers.remove.mutate({ browserId }).catch((err) => {
1570
+ console.error("[DockviewBrowserContainer] failed to remove browser:", err);
1571
+ });
1572
+ }, []);
1573
+ reactExports.useEffect(() => {
1574
+ if (!visible) return;
1575
+ const refocusAddressBar = () => {
1576
+ const panel = apiRef.current?.activePanel;
1577
+ if (!panel) return;
1578
+ panel.view.content.element.querySelector("[data-band-address-input]")?.focus();
1579
+ };
1580
+ const cycleTabs = (direction) => {
1581
+ cycleTabsInActiveGroup(apiRef.current, direction, () => {
1582
+ requestAnimationFrame(refocusAddressBar);
1583
+ });
1584
+ };
1585
+ const cycleGroups = (direction) => {
1586
+ cycleGridGroups(apiRef.current, direction, () => {
1587
+ requestAnimationFrame(refocusAddressBar);
1588
+ });
1589
+ };
1590
+ const handler = (e) => {
1591
+ if (!containerRef.current?.contains(document.activeElement)) return;
1592
+ const key = e.key.toLowerCase();
1593
+ if (e.ctrlKey && !e.metaKey && key === "tab") {
1594
+ e.preventDefault();
1595
+ e.stopPropagation();
1596
+ cycleTabs(e.shiftKey ? -1 : 1);
1597
+ return;
1598
+ }
1599
+ const mod = e.metaKey || e.ctrlKey;
1600
+ if (!mod) return;
1601
+ if (e.shiftKey && (key === "[" || key === "]")) {
1602
+ e.preventDefault();
1603
+ e.stopPropagation();
1604
+ cycleTabs(key === "]" ? 1 : -1);
1605
+ return;
1606
+ }
1607
+ if (!e.shiftKey && (key === "[" || key === "]")) {
1608
+ e.preventDefault();
1609
+ e.stopPropagation();
1610
+ cycleGroups(key === "]" ? 1 : -1);
1611
+ return;
1612
+ }
1613
+ if (key === "t" && !e.shiftKey) {
1614
+ e.preventDefault();
1615
+ e.stopPropagation();
1616
+ handleAddTab().then(() => {
1617
+ requestAnimationFrame(() => {
1618
+ const panel = apiRef.current?.activePanel;
1619
+ if (!panel) return;
1620
+ panel.view.content.element.querySelector("[data-band-address-input]")?.focus();
1621
+ });
1622
+ });
1623
+ } else if (key === "w" && !e.shiftKey) {
1624
+ const api = apiRef.current;
1625
+ if (!api || api.panels.length <= 1) return;
1626
+ e.preventDefault();
1627
+ e.stopPropagation();
1628
+ const active = api.activePanel;
1629
+ if (active) {
1630
+ closeTab(active.id);
1631
+ }
1632
+ } else if (key === "d") {
1633
+ e.preventDefault();
1634
+ e.stopPropagation();
1635
+ const api = apiRef.current;
1636
+ if (!api) return;
1637
+ const activeGroup = api.activeGroup;
1638
+ if (!activeGroup) return;
1639
+ const direction = e.shiftKey ? "below" : "right";
1640
+ handleSplit(activeGroup.id, direction);
1641
+ } else if (key === "r" && !e.shiftKey && isDesktop) {
1642
+ const api = apiRef.current;
1643
+ if (!api) return;
1644
+ const active = api.activePanel;
1645
+ const browserId = active?.params?.browserId;
1646
+ if (!browserId) return;
1647
+ e.preventDefault();
1648
+ e.stopPropagation();
1649
+ invoke("browser_reload", { browserId }).catch((err) => {
1650
+ console.error("[DockviewBrowserContainer] browser_reload failed:", err);
1651
+ });
1652
+ }
1653
+ };
1654
+ window.addEventListener("keydown", handler, true);
1655
+ return () => window.removeEventListener("keydown", handler, true);
1656
+ }, [visible, closeTab, handleSplit, handleAddTab]);
1657
+ reactExports.useLayoutEffect(() => {
1658
+ if (!visible) return;
1659
+ const api = apiRef.current;
1660
+ const container = containerRef.current;
1661
+ if (!api || !container) return;
1662
+ const rect = container.getBoundingClientRect();
1663
+ if (rect.width > 0 && rect.height > 0) {
1664
+ api.layout(Math.round(rect.width), Math.round(rect.height), true);
1665
+ return;
1666
+ }
1667
+ const ro = new ResizeObserver((entries) => {
1668
+ const entry = entries[0];
1669
+ if (!entry) return;
1670
+ const { width, height } = entry.contentRect;
1671
+ if (width <= 0 || height <= 0) return;
1672
+ ro.disconnect();
1673
+ api.layout(Math.round(width), Math.round(height), true);
1674
+ });
1675
+ ro.observe(container);
1676
+ return () => ro.disconnect();
1677
+ }, [visible]);
1678
+ reactExports.useEffect(() => {
1679
+ if (!visible) return;
1680
+ const id = requestAnimationFrame(() => {
1681
+ const panel = apiRef.current?.activePanel;
1682
+ if (!panel) return;
1683
+ panel.view.content.element.querySelector("[data-band-address-input]")?.focus();
1684
+ });
1685
+ return () => cancelAnimationFrame(id);
1686
+ }, [visible]);
1687
+ reactExports.useEffect(() => {
1688
+ if (!isDesktop) return;
1689
+ let unlisten;
1690
+ void (async () => {
1691
+ unlisten = await listen(
1692
+ "browser-new-tab-shortcut",
1693
+ (event) => {
1694
+ const api = apiRef.current;
1695
+ if (!api) return;
1696
+ const sourceId = event.payload.browser_id;
1697
+ if (!sourceId || !api.getPanel(sourceId)) return;
1698
+ void handleAddTab().then(() => {
1699
+ requestAnimationFrame(() => {
1700
+ const panel = apiRef.current?.activePanel;
1701
+ panel?.view.content.element.querySelector("[data-band-address-input]")?.focus();
1702
+ });
1703
+ });
1704
+ }
1705
+ );
1706
+ })();
1707
+ return () => unlisten?.();
1708
+ }, [handleAddTab]);
1709
+ reactExports.useEffect(() => {
1710
+ if (!isDesktop) return;
1711
+ let unlisten;
1712
+ let cancelled = false;
1713
+ void (async () => {
1714
+ const fn = await listen("browser-open-window", (event) => {
1715
+ const api = apiRef.current;
1716
+ if (!api) return;
1717
+ const sourceId = event.payload.browser_id;
1718
+ const sourcePanel = sourceId ? api.getPanel(sourceId) : void 0;
1719
+ if (!sourcePanel) return;
1720
+ const groupId = sourcePanel.group?.id;
1721
+ const url = event.payload.url;
1722
+ if (!url) return;
1723
+ void handleAddTab(groupId, { initialUrl: url }).then(() => {
1724
+ requestAnimationFrame(() => {
1725
+ const panel = apiRef.current?.activePanel;
1726
+ panel?.view.content.element.querySelector("[data-band-address-input]")?.focus();
1727
+ });
1728
+ }).catch((err) => {
1729
+ console.error("[DockviewBrowserContainer] browser-open-window add-tab failed:", err);
1730
+ });
1731
+ });
1732
+ if (cancelled) fn();
1733
+ else unlisten = fn;
1734
+ })();
1735
+ return () => {
1736
+ cancelled = true;
1737
+ unlisten?.();
1738
+ };
1739
+ }, [handleAddTab]);
1740
+ reactExports.useEffect(() => {
1741
+ if (!isDesktop) return;
1742
+ const unlisteners = [];
1743
+ let cancelled = false;
1744
+ const refocusAddressBar = () => {
1745
+ const panel = apiRef.current?.activePanel;
1746
+ panel?.view.content.element.querySelector("[data-band-address-input]")?.focus();
1747
+ };
1748
+ void (async () => {
1749
+ const fns = await Promise.all([
1750
+ listen("browser-split-shortcut", (event) => {
1751
+ const api = apiRef.current;
1752
+ if (!api) return;
1753
+ const sourcePanel = api.getPanel(event.payload.browser_id);
1754
+ if (!sourcePanel?.group) return;
1755
+ void handleSplit(sourcePanel.group.id, event.payload.direction);
1756
+ }),
1757
+ listen("browser-close-shortcut", (event) => {
1758
+ const api = apiRef.current;
1759
+ if (!api) return;
1760
+ const sourceId = event.payload.browser_id;
1761
+ if (!api.getPanel(sourceId)) return;
1762
+ closeTab(sourceId);
1763
+ }),
1764
+ listen("browser-cycle-shortcut", (event) => {
1765
+ const api = apiRef.current;
1766
+ if (!api) return;
1767
+ const sourcePanel = api.getPanel(event.payload.browser_id);
1768
+ if (!sourcePanel) return;
1769
+ sourcePanel.api.setActive();
1770
+ const refocus = () => requestAnimationFrame(refocusAddressBar);
1771
+ if (event.payload.target === "tabs") {
1772
+ cycleTabsInActiveGroup(apiRef.current, event.payload.direction, refocus);
1773
+ } else {
1774
+ cycleGridGroups(apiRef.current, event.payload.direction, refocus);
1775
+ }
1776
+ })
1777
+ ]);
1778
+ if (cancelled) {
1779
+ for (const fn of fns) fn();
1780
+ } else {
1781
+ unlisteners.push(...fns);
1782
+ }
1783
+ })();
1784
+ return () => {
1785
+ cancelled = true;
1786
+ for (const unlisten of unlisteners) unlisten();
1787
+ };
1788
+ }, [handleSplit, closeTab]);
1789
+ reactExports.useEffect(() => {
1790
+ const handler = () => {
1791
+ const root = containerRef.current;
1792
+ if (!root || root.offsetParent === null) return;
1793
+ const inputs = root.querySelectorAll("[data-band-address-input]");
1794
+ for (const input of inputs) {
1795
+ if (input.offsetParent !== null) {
1796
+ input.focus({ preventScroll: true });
1797
+ return;
1798
+ }
1799
+ }
1800
+ };
1801
+ window.addEventListener("band:focus-browser", handler);
1802
+ return () => window.removeEventListener("band:focus-browser", handler);
1803
+ }, []);
1804
+ reactExports.useEffect(() => {
1805
+ return adapter.subscribeStatusEvents((event) => {
1806
+ if (event.workspaceId !== workspaceId) return;
1807
+ const api = apiRef.current;
1808
+ if (!api) return;
1809
+ if (event.kind === "browser-created" && typeof event.browserId === "string") {
1810
+ if (api.getPanel(event.browserId)) return;
1811
+ api.addPanel({
1812
+ id: event.browserId,
1813
+ component: "browserTab",
1814
+ tabComponent: "browserTab",
1815
+ title: "New Tab",
1816
+ params: { workspaceId, browserId: event.browserId }
1817
+ });
1818
+ } else if (event.kind === "browser-removed" && typeof event.browserId === "string") {
1819
+ const panel = api.getPanel(event.browserId);
1820
+ if (panel) {
1821
+ api.removePanel(panel);
1822
+ if (api.panels.length === 0) {
1823
+ createDefaultPanel(api, workspaceId);
1824
+ }
1825
+ }
1826
+ }
1827
+ });
1828
+ }, [adapter, workspaceId]);
1829
+ addTabRef.current = { onAdd: handleAddTab, onSplit: handleSplit };
1830
+ closeTabRef.current = closeTab;
1831
+ const initialLayoutRef = reactExports.useRef(null);
1832
+ initialLayoutRef.current = initialData?.layout ?? null;
1833
+ const initialBrowserIdsRef = reactExports.useRef(null);
1834
+ initialBrowserIdsRef.current = initialData?.browserIds ?? null;
1835
+ const initialUrlsRef = reactExports.useRef(null);
1836
+ initialUrlsRef.current = initialData?.urls ?? null;
1837
+ const visibleRef = reactExports.useRef(visible);
1838
+ visibleRef.current = visible;
1839
+ const onReady = reactExports.useCallback(
1840
+ (event) => {
1841
+ apiRef.current = event.api;
1842
+ const savedLayout = initialLayoutRef.current;
1843
+ const knownBrowserIds = initialBrowserIdsRef.current;
1844
+ const knownUrls = initialUrlsRef.current;
1845
+ if (savedLayout && isDockviewLayout(savedLayout)) {
1846
+ isRestoringRef.current = true;
1847
+ const layoutToRestore = injectInitialUrls(savedLayout, knownUrls);
1848
+ try {
1849
+ event.api.fromJSON(layoutToRestore);
1850
+ } catch (err) {
1851
+ console.error("[DockviewBrowserContainer] fromJSON failed, creating default:", err);
1852
+ createDefaultPanel(event.api, workspaceId);
1853
+ }
1854
+ let dropped = 0;
1855
+ if (knownBrowserIds) {
1856
+ const orphans = event.api.panels.filter((p) => !knownBrowserIds.has(p.id));
1857
+ for (const orphan of orphans) {
1858
+ event.api.removePanel(orphan);
1859
+ dropped++;
1860
+ }
1861
+ if (event.api.panels.length === 0) {
1862
+ createDefaultPanel(event.api, workspaceId);
1863
+ dropped++;
1864
+ }
1865
+ }
1866
+ setTimeout(() => {
1867
+ isRestoringRef.current = false;
1868
+ }, 0);
1869
+ if (dropped > 0) {
1870
+ persistToServer(workspaceId, event.api.toJSON(), {
1871
+ queryClient: queryClientRef.current
1872
+ });
1873
+ }
1874
+ } else {
1875
+ createDefaultPanel(event.api, workspaceId);
1876
+ persistToServer(workspaceId, event.api.toJSON(), { queryClient: queryClientRef.current });
1877
+ }
1878
+ const persist = () => schedulePersist();
1879
+ event.api.onDidLayoutChange(persist);
1880
+ event.api.onDidAddPanel(persist);
1881
+ event.api.onDidRemovePanel(persist);
1882
+ event.api.onDidActivePanelChange(persist);
1883
+ event.api.onDidAddGroup(persist);
1884
+ event.api.onDidRemoveGroup(persist);
1885
+ if (visibleRef.current && containerRef.current) {
1886
+ const rect = containerRef.current.getBoundingClientRect();
1887
+ if (rect.width > 0 && rect.height > 0) {
1888
+ event.api.layout(Math.round(rect.width), Math.round(rect.height), true);
1889
+ }
1890
+ }
1891
+ },
1892
+ [workspaceId, schedulePersist]
1893
+ );
1894
+ const visibilityValue = reactExports.useMemo(
1895
+ () => ({ visible: visible && wsActive !== false, wsActive: wsActive !== false }),
1896
+ [visible, wsActive]
1897
+ );
1898
+ if (!initialData) {
1899
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full w-full items-center justify-center" });
1900
+ }
1901
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: containerRef, className: "flex h-full w-full flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(BrowserVisibilityContext.Provider, { value: visibilityValue, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1902
+ DockviewReact,
1903
+ {
1904
+ theme: browserTabTheme,
1905
+ className: "h-full",
1906
+ components: browserPanelComponents,
1907
+ tabComponents: browserTabComponents,
1908
+ defaultTabComponent: BrowserTab,
1909
+ onReady,
1910
+ rightHeaderActionsComponent: RightHeaderActions
1911
+ }
1912
+ ) }) });
1913
+ }
1914
+ function createDefaultPanel(api, workspaceId) {
1915
+ const browserId = newBrowserId();
1916
+ trpc.browsers.create.mutate({ workspaceId, id: browserId }).catch((err) => {
1917
+ console.error("[DockviewBrowserContainer] error creating default browser:", err);
1918
+ });
1919
+ api.addPanel({
1920
+ id: browserId,
1921
+ component: "browserTab",
1922
+ tabComponent: "browserTab",
1923
+ title: "Browser",
1924
+ params: {
1925
+ workspaceId,
1926
+ browserId
1927
+ }
1928
+ });
1929
+ }
1930
+ export {
1931
+ DockviewBrowserContainer,
1932
+ markBrowserFresh,
1933
+ newBrowserId
1934
+ };