@dylanpiercey/work 0.0.6 → 0.0.7

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 (156) hide show
  1. package/bin/work.mjs +25 -2
  2. package/dist/index.mjs +992 -688
  3. package/dist/public/assets/{_lO429-VP.js → _B-_0cKbG.js} +2 -2
  4. package/dist/public/assets/{_lO429-VP.js.map → _B-_0cKbG.js.map} +1 -1
  5. package/dist/public/assets/{_kf1lUDoU2.js → _B1F2GQHq2.js} +2 -2
  6. package/dist/public/assets/{_kf1lUDoU2.js.map → _B1F2GQHq2.js.map} +1 -1
  7. package/dist/public/assets/{_D1AvypB0.js → _B49hrT7F.js} +2 -2
  8. package/dist/public/assets/{_D1AvypB0.js.map → _B49hrT7F.js.map} +1 -1
  9. package/dist/public/assets/{_lENTdKos.js → _B4R8SNvy.js} +2 -2
  10. package/dist/public/assets/{_lENTdKos.js.map → _B4R8SNvy.js.map} +1 -1
  11. package/dist/public/assets/{_T-HpFRXH.js → _B7K5PePe.js} +2 -2
  12. package/dist/public/assets/{_T-HpFRXH.js.map → _B7K5PePe.js.map} +1 -1
  13. package/dist/public/assets/{_BqB9YKHu.js → _B8mEgNvE.js} +2 -2
  14. package/dist/public/assets/{_BqB9YKHu.js.map → _B8mEgNvE.js.map} +1 -1
  15. package/dist/public/assets/{_BSe5zjTJ2.js → _BBbcogWL2.js} +2 -2
  16. package/dist/public/assets/{_BSe5zjTJ2.js.map → _BBbcogWL2.js.map} +1 -1
  17. package/dist/public/assets/{_CUCwB86v.js → _BDxD__Ca.js} +2 -2
  18. package/dist/public/assets/{_CUCwB86v.js.map → _BDxD__Ca.js.map} +1 -1
  19. package/dist/public/assets/{_D8RDRYsV.js → _BE5Ltp0D.js} +2 -2
  20. package/dist/public/assets/{_D8RDRYsV.js.map → _BE5Ltp0D.js.map} +1 -1
  21. package/dist/public/assets/{_BvkUWaoR.js → _BE8gA6TO.js} +2 -2
  22. package/dist/public/assets/{_BvkUWaoR.js.map → _BE8gA6TO.js.map} +1 -1
  23. package/dist/public/assets/{_Bt1hNExm.js → _BEY9qyEj.js} +2 -2
  24. package/dist/public/assets/{_Bt1hNExm.js.map → _BEY9qyEj.js.map} +1 -1
  25. package/dist/public/assets/{_kN8jmSEy2.js → _BFPTVBh82.js} +2 -2
  26. package/dist/public/assets/{_kN8jmSEy2.js.map → _BFPTVBh82.js.map} +1 -1
  27. package/dist/public/assets/{_DcCF4p4b2.js → _BKNwgsJI2.js} +2 -2
  28. package/dist/public/assets/{_DcCF4p4b2.js.map → _BKNwgsJI2.js.map} +1 -1
  29. package/dist/public/assets/{_B7WJMKdz.js → _BLuo0bJF.js} +2 -2
  30. package/dist/public/assets/{_B7WJMKdz.js.map → _BLuo0bJF.js.map} +1 -1
  31. package/dist/public/assets/{_D4Z_UM_B2.js → _BMbg6Q_32.js} +2 -2
  32. package/dist/public/assets/{_D4Z_UM_B2.js.map → _BMbg6Q_32.js.map} +1 -1
  33. package/dist/public/assets/{_ChbGErmQ.js → _BQqn5S5j.js} +2 -2
  34. package/dist/public/assets/{_ChbGErmQ.js.map → _BQqn5S5j.js.map} +1 -1
  35. package/dist/public/assets/{_BGjK3KLI.js → _BTJ_-CIz.js} +2 -2
  36. package/dist/public/assets/{_BGjK3KLI.js.map → _BTJ_-CIz.js.map} +1 -1
  37. package/dist/public/assets/{_D7AQAhlo.js → _BTnhYWUT.js} +2 -2
  38. package/dist/public/assets/{_D7AQAhlo.js.map → _BTnhYWUT.js.map} +1 -1
  39. package/dist/public/assets/{_DOeCLKRd.js → _B_U5SPso.js} +2 -2
  40. package/dist/public/assets/{_DOeCLKRd.js.map → _B_U5SPso.js.map} +1 -1
  41. package/dist/public/assets/{_38qjx0VE.js → _BfxQEfor.js} +2 -2
  42. package/dist/public/assets/{_38qjx0VE.js.map → _BfxQEfor.js.map} +1 -1
  43. package/dist/public/assets/{_CeDXSlq_2.js → _Bhn-MKXG2.js} +2 -2
  44. package/dist/public/assets/{_CeDXSlq_2.js.map → _Bhn-MKXG2.js.map} +1 -1
  45. package/dist/public/assets/{_17ixsFjl2.js → _BkEOzqHA2.js} +2 -2
  46. package/dist/public/assets/{_17ixsFjl2.js.map → _BkEOzqHA2.js.map} +1 -1
  47. package/dist/public/assets/{_D9oMqxHc.js → _BqeWVi6c.js} +2 -2
  48. package/dist/public/assets/{_D9oMqxHc.js.map → _BqeWVi6c.js.map} +1 -1
  49. package/dist/public/assets/{_Dfi8Atag.js → _Bx6coq7v.js} +2 -2
  50. package/dist/public/assets/{_Dfi8Atag.js.map → _Bx6coq7v.js.map} +1 -1
  51. package/dist/public/assets/{_CjoA9dbQ.js → _BxWguH11.js} +2 -2
  52. package/dist/public/assets/{_CjoA9dbQ.js.map → _BxWguH11.js.map} +1 -1
  53. package/dist/public/assets/{_C52df4172.js → _C2TX6Jrr2.js} +2 -2
  54. package/dist/public/assets/{_C52df4172.js.map → _C2TX6Jrr2.js.map} +1 -1
  55. package/dist/public/assets/{_B01vNcBH.js → _C8Lw1Tmb.js} +2 -2
  56. package/dist/public/assets/{_B01vNcBH.js.map → _C8Lw1Tmb.js.map} +1 -1
  57. package/dist/public/assets/{_Czy_Rmr8.js → _CDtlFxZX.js} +2 -2
  58. package/dist/public/assets/{_Czy_Rmr8.js.map → _CDtlFxZX.js.map} +1 -1
  59. package/dist/public/assets/{_xikqPUw52.js → _CF3V7ZyO2.js} +2 -2
  60. package/dist/public/assets/{_xikqPUw52.js.map → _CF3V7ZyO2.js.map} +1 -1
  61. package/dist/public/assets/{_CgK4NAc5.js → _CIUf_Jmq.js} +2 -2
  62. package/dist/public/assets/{_CgK4NAc5.js.map → _CIUf_Jmq.js.map} +1 -1
  63. package/dist/public/assets/{_Dg2FAYj_2.js → _CJlF2h3E2.js} +2 -2
  64. package/dist/public/assets/{_Dg2FAYj_2.js.map → _CJlF2h3E2.js.map} +1 -1
  65. package/dist/public/assets/{_CPMq9qKD.js → _CNDjHC1Q.js} +2 -2
  66. package/dist/public/assets/{_CPMq9qKD.js.map → _CNDjHC1Q.js.map} +1 -1
  67. package/dist/public/assets/{_D-u1ssE0.js → _CTkR0S8t.js} +2 -2
  68. package/dist/public/assets/{_D-u1ssE0.js.map → _CTkR0S8t.js.map} +1 -1
  69. package/dist/public/assets/{_DpRvij_q.js → _C_K-IZx7.js} +2 -2
  70. package/dist/public/assets/{_DpRvij_q.js.map → _C_K-IZx7.js.map} +1 -1
  71. package/dist/public/assets/{_rDdCnzAf.js → _CawcEo7A.js} +2 -2
  72. package/dist/public/assets/{_rDdCnzAf.js.map → _CawcEo7A.js.map} +1 -1
  73. package/dist/public/assets/{_DtkPfG0P2.js → _Ch0uVmAz2.js} +2 -2
  74. package/dist/public/assets/{_DtkPfG0P2.js.map → _Ch0uVmAz2.js.map} +1 -1
  75. package/dist/public/assets/{_DWSSCCOX.js → _Ck30GMoL.js} +2 -2
  76. package/dist/public/assets/{_DWSSCCOX.js.map → _Ck30GMoL.js.map} +1 -1
  77. package/dist/public/assets/{_BjlFBd9j.js → _Cpqbvj5d.js} +2 -2
  78. package/dist/public/assets/{_BjlFBd9j.js.map → _Cpqbvj5d.js.map} +1 -1
  79. package/dist/public/assets/{_BCmJwCZ4.js → _CqNVh0h5.js} +2 -2
  80. package/dist/public/assets/{_BCmJwCZ4.js.map → _CqNVh0h5.js.map} +1 -1
  81. package/dist/public/assets/{_CUdHfuKf2.js → _Cx_br0wh2.js} +2 -2
  82. package/dist/public/assets/{_CUdHfuKf2.js.map → _Cx_br0wh2.js.map} +1 -1
  83. package/dist/public/assets/{_nv0goT4Z.js → _CzBadWXq.js} +2 -2
  84. package/dist/public/assets/{_nv0goT4Z.js.map → _CzBadWXq.js.map} +1 -1
  85. package/dist/public/assets/{_DDDACu2e.js → _D0Dk2eMz.js} +2 -2
  86. package/dist/public/assets/{_DDDACu2e.js.map → _D0Dk2eMz.js.map} +1 -1
  87. package/dist/public/assets/{_nfuFU9Un2.js → _D0PyqJyr2.js} +2 -2
  88. package/dist/public/assets/{_nfuFU9Un2.js.map → _D0PyqJyr2.js.map} +1 -1
  89. package/dist/public/assets/{_CscsJi7i.js → _D409hg3n.js} +2 -2
  90. package/dist/public/assets/{_CscsJi7i.js.map → _D409hg3n.js.map} +1 -1
  91. package/dist/public/assets/{_DtpDQy9c.js → _D4uduNrb.js} +2 -2
  92. package/dist/public/assets/{_DtpDQy9c.js.map → _D4uduNrb.js.map} +1 -1
  93. package/dist/public/assets/{_Dlbc7SgI.js → _DBj-L0Ww.js} +2 -2
  94. package/dist/public/assets/{_Dlbc7SgI.js.map → _DBj-L0Ww.js.map} +1 -1
  95. package/dist/public/assets/{_DsK9qp5z2.js → _DC9uMckf2.js} +2 -2
  96. package/dist/public/assets/{_DsK9qp5z2.js.map → _DC9uMckf2.js.map} +1 -1
  97. package/dist/public/assets/{_DpdFHjlT.js → _DHsfzMP_.js} +2 -2
  98. package/dist/public/assets/{_DpdFHjlT.js.map → _DHsfzMP_.js.map} +1 -1
  99. package/dist/public/assets/{_C0fM-yEd.js → _DPDjFNn3.js} +2 -2
  100. package/dist/public/assets/{_C0fM-yEd.js.map → _DPDjFNn3.js.map} +1 -1
  101. package/dist/public/assets/{_vd_LQrD6.js → _DS1r4Da7.js} +2 -2
  102. package/dist/public/assets/{_vd_LQrD6.js.map → _DS1r4Da7.js.map} +1 -1
  103. package/dist/public/assets/{_BGfZ-AIc.js → _DZp-Xvf0.js} +2 -2
  104. package/dist/public/assets/{_BGfZ-AIc.js.map → _DZp-Xvf0.js.map} +1 -1
  105. package/dist/public/assets/{_Cm0oW23H.js → _DbP8pES9.js} +2 -2
  106. package/dist/public/assets/{_Cm0oW23H.js.map → _DbP8pES9.js.map} +1 -1
  107. package/dist/public/assets/{_Bj_P-is9.js → _DhqQ2yva.js} +2 -2
  108. package/dist/public/assets/{_Bj_P-is9.js.map → _DhqQ2yva.js.map} +1 -1
  109. package/dist/public/assets/{_tf4220G9.js → _Din9HEMs.js} +2 -2
  110. package/dist/public/assets/{_tf4220G9.js.map → _Din9HEMs.js.map} +1 -1
  111. package/dist/public/assets/{_kzd-9LxV.js → _Dk-i8IAv.js} +2 -2
  112. package/dist/public/assets/{_kzd-9LxV.js.map → _Dk-i8IAv.js.map} +1 -1
  113. package/dist/public/assets/{_CIE37fGr.js → _DvdWf5o-.js} +2 -2
  114. package/dist/public/assets/{_CIE37fGr.js.map → _DvdWf5o-.js.map} +1 -1
  115. package/dist/public/assets/{_U-btoBCb2.js → _DwmbSeOR2.js} +2 -2
  116. package/dist/public/assets/{_U-btoBCb2.js.map → _DwmbSeOR2.js.map} +1 -1
  117. package/dist/public/assets/{_DoTuVtdo.js → _Dz57V0my.js} +2 -2
  118. package/dist/public/assets/{_DoTuVtdo.js.map → _Dz57V0my.js.map} +1 -1
  119. package/dist/public/assets/{_CATT-8QU2.js → _JDPB-L9T2.js} +2 -2
  120. package/dist/public/assets/{_CATT-8QU2.js.map → _JDPB-L9T2.js.map} +1 -1
  121. package/dist/public/assets/{_PBOdFezG2.js → _T3Aahgg12.js} +2 -2
  122. package/dist/public/assets/{_PBOdFezG2.js.map → _T3Aahgg12.js.map} +1 -1
  123. package/dist/public/assets/{_Q7i-xAMZ.js → _Tbp9N0Pq.js} +2 -2
  124. package/dist/public/assets/{_Q7i-xAMZ.js.map → _Tbp9N0Pq.js.map} +1 -1
  125. package/dist/public/assets/{_Bs-d5a142.js → _Tqwd1-8Z2.js} +2 -2
  126. package/dist/public/assets/{_Bs-d5a142.js.map → _Tqwd1-8Z2.js.map} +1 -1
  127. package/dist/public/assets/{_uhGymT-L.js → _UPkRAhR-.js} +2 -2
  128. package/dist/public/assets/{_uhGymT-L.js.map → _UPkRAhR-.js.map} +1 -1
  129. package/dist/public/assets/{_CVvMxylB2.js → _WO2um4ce2.js} +2 -2
  130. package/dist/public/assets/{_CVvMxylB2.js.map → _WO2um4ce2.js.map} +1 -1
  131. package/dist/public/assets/{_BV7Qzzwy2.js → _WXstblxD2.js} +2 -2
  132. package/dist/public/assets/{_BV7Qzzwy2.js.map → _WXstblxD2.js.map} +1 -1
  133. package/dist/public/assets/{_DFLlSEDh2.js → _Wq5Lwig62.js} +2 -2
  134. package/dist/public/assets/{_DFLlSEDh2.js.map → _Wq5Lwig62.js.map} +1 -1
  135. package/dist/public/assets/{_VVnpWsa-2.js → _YMDMk2XX2.js} +2 -2
  136. package/dist/public/assets/{_VVnpWsa-2.js.map → _YMDMk2XX2.js.map} +1 -1
  137. package/dist/public/assets/{_CLjg6iH5.js → _b9uzSPzx.js} +2 -2
  138. package/dist/public/assets/{_CLjg6iH5.js.map → _b9uzSPzx.js.map} +1 -1
  139. package/dist/public/assets/{_BO1auSkL2.js → _dfWRmwy82.js} +2 -2
  140. package/dist/public/assets/{_BO1auSkL2.js.map → _dfWRmwy82.js.map} +1 -1
  141. package/dist/public/assets/{_DvigaXuY.js → _ehtlhcCR.js} +2 -2
  142. package/dist/public/assets/{_DvigaXuY.js.map → _ehtlhcCR.js.map} +1 -1
  143. package/dist/public/assets/{_CDUUTPIa.js → _fZl_Qerv.js} +2 -2
  144. package/dist/public/assets/{_CDUUTPIa.js.map → _fZl_Qerv.js.map} +1 -1
  145. package/dist/public/assets/{_BZgEX-SR.js → _js4WyaBT.js} +2 -2
  146. package/dist/public/assets/{_BZgEX-SR.js.map → _js4WyaBT.js.map} +1 -1
  147. package/dist/public/assets/{_B5_K4U35.js → _okHqOb61.js} +2 -2
  148. package/dist/public/assets/{_B5_K4U35.js.map → _okHqOb61.js.map} +1 -1
  149. package/dist/public/assets/{_o_JLlSoU2.js → _rMU5-5Oc2.js} +2 -2
  150. package/dist/public/assets/{_o_JLlSoU2.js.map → _rMU5-5Oc2.js.map} +1 -1
  151. package/dist/public/assets/{index-BwlA-lgI.js → index-Bx9UtyrE.js} +63 -61
  152. package/dist/public/assets/index-Bx9UtyrE.js.map +1 -0
  153. package/dist/public/assets/{index-KIy0rCx1.css → index-C_YqjXeW.css} +1 -1
  154. package/package.json +1 -1
  155. package/scripts/migrate.mjs +141 -0
  156. package/dist/public/assets/index-BwlA-lgI.js.map +0 -1
package/dist/index.mjs CHANGED
@@ -3567,17 +3567,17 @@ var palette_default = _template("h", (input) => {
3567
3567
  _html(`<span class=row-name><span${_attr_class(["dot", phaseOf$1(st)])}></span>${_el_resume($scope3_id, "a")}<span class=row-name-text>${_escape(row.ws.name)}${_el_resume($scope3_id, "b")}</span>`);
3568
3568
  _if(() => {
3569
3569
  if (row.ws.id === input.selectedId) {
3570
- const $scope15_id = _scope_id();
3570
+ const $scope14_id = _scope_id();
3571
3571
  _html("<span class=\"chip open\">open</span>");
3572
- writeScope($scope15_id, {});
3572
+ writeScope($scope14_id, {});
3573
3573
  return 0;
3574
3574
  }
3575
3575
  }, $scope3_id, "c", 1, 1, 1, 0, 1);
3576
3576
  _if(() => {
3577
3577
  if (row.archived) {
3578
- const $scope16_id = _scope_id();
3578
+ const $scope15_id = _scope_id();
3579
3579
  _html("<span class=chip>archived</span>");
3580
- writeScope($scope16_id, {});
3580
+ writeScope($scope15_id, {});
3581
3581
  return 0;
3582
3582
  }
3583
3583
  }, $scope3_id, "d", 1, 1, 1, 0, 1);
@@ -3590,44 +3590,35 @@ var palette_default = _template("h", (input) => {
3590
3590
  return 0;
3591
3591
  }
3592
3592
  }, $scope3_id, "e", 1, 1, 1, 0, 1);
3593
- _if(() => {
3594
- if (st?.changes) {
3595
- const $scope12_id = _scope_id();
3596
- _html(`<span class="chip stat changes"${_attr("title", `${st.changes} changed file${st.changes === 1 ? "" : "s"}`)}>±<!>${_escape(st.changes)}${_el_resume($scope12_id, "b")}</span>${_el_resume($scope12_id, "a")}`);
3597
- writeScope($scope12_id, {});
3598
- return 0;
3599
- }
3600
- }, $scope3_id, "f", 1, 1, 1, 0, 1);
3601
3593
  _if(() => {
3602
3594
  if (rowPrs.length) {
3603
- const $scope14_id = _scope_id();
3604
- _html(`<span class="chip stat"${_attr("title", `${rowPrs.length} open PR${rowPrs.length === 1 ? "" : "s"}`)}>${_escape(rowPrs.length)}${_el_resume($scope14_id, "b")} PR<!>${rowPrs.length === 1 ? "" : "s"}${_el_resume($scope14_id, "c")}</span>${_el_resume($scope14_id, "a")}`);
3605
- writeScope($scope14_id, {});
3595
+ const $scope13_id = _scope_id();
3596
+ _html(`<span class="chip stat"${_attr("title", `${rowPrs.length} open PR${rowPrs.length === 1 ? "" : "s"}`)}>${_escape(rowPrs.length)}${_el_resume($scope13_id, "b")} PR<!>${rowPrs.length === 1 ? "" : "s"}${_el_resume($scope13_id, "c")}</span>${_el_resume($scope13_id, "a")}`);
3597
+ writeScope($scope13_id, {});
3606
3598
  return 0;
3607
3599
  }
3608
- }, $scope3_id, "g", 1, 1, 1, 0, 1);
3600
+ }, $scope3_id, "f", 1, 1, 1, 0, 1);
3609
3601
  _if(() => {
3610
3602
  if ((st?.ports || []).length) {
3611
- const $scope13_id = _scope_id();
3612
- _html(`<span class="chip stat"${_attr("title", st.ports.map((p) => `:${p.port} ${p.cmd}`).join("\n"))}>:<!>${_escape(st.ports.length)}${_el_resume($scope13_id, "b")}</span>${_el_resume($scope13_id, "a")}`);
3613
- writeScope($scope13_id, {});
3603
+ const $scope12_id = _scope_id();
3604
+ _html(`<span class="chip stat"${_attr("title", st.ports.map((p) => `:${p.port} ${p.cmd}`).join("\n"))}>:<!>${_escape(st.ports.length)}${_el_resume($scope12_id, "b")}</span>${_el_resume($scope12_id, "a")}`);
3605
+ writeScope($scope12_id, {});
3614
3606
  return 0;
3615
3607
  }
3616
- }, $scope3_id, "h", 1, 1, 1, 0, 1);
3608
+ }, $scope3_id, "g", 1, 1, 1, 0, 1);
3617
3609
  _html("</span></span><span class=row-repos>");
3618
3610
  _for_of(st?.repos || row.ws.repos, (repo) => {
3619
- const $scope17_id = _scope_id();
3620
- _html(`<span class=mono>${_escape(repo.worktree)}${_el_resume($scope17_id, "a")}</span>`);
3621
- writeScope($scope17_id, {});
3622
- }, "full_name", $scope3_id, "i", 1, 1, 1, "</span>", 1);
3611
+ const $scope16_id = _scope_id();
3612
+ _html(`<span class=mono>${_escape(repo.worktree)}${_el_resume($scope16_id, "a")}</span>`);
3613
+ writeScope($scope16_id, {});
3614
+ }, "full_name", $scope3_id, "h", 1, 1, 1, "</span>", 1);
3623
3615
  _subscribe($si__input_status && $input_status__closures, _subscribe($si__input_selectedId && $input_selectedId__closures, writeScope($scope3_id, {
3624
- m: st?.agents,
3625
- n: st?.working,
3626
- o: st?.waiting,
3627
- p: st?.changes,
3628
- q: st?.ports,
3629
- r: st?.ports?.length,
3630
- t: rowPrs?.length
3616
+ l: st?.agents,
3617
+ m: st?.working,
3618
+ n: st?.waiting,
3619
+ o: st?.ports,
3620
+ p: st?.ports?.length,
3621
+ r: rowPrs?.length
3631
3622
  })));
3632
3623
  return 2;
3633
3624
  }
@@ -3709,9 +3700,9 @@ var palette_default = _template("h", (input) => {
3709
3700
  }, "h1", $scope6_id), () => {
3710
3701
  _html("<select aria-label=\"Preferred agent\">");
3711
3702
  _for_of(input.settings.agents, (agent) => {
3712
- const $scope18_id = _scope_id();
3713
- _html(`<option${_attr_option_value(agent)}>${_escape(agent)}${_el_resume($scope18_id, "b", $sg__input_settings_agents)}</option>${_el_resume($scope18_id, "a", $sg__input_settings_agents)}`);
3714
- $si__input_settings_agents && writeScope($scope18_id, {});
3703
+ const $scope17_id = _scope_id();
3704
+ _html(`<option${_attr_option_value(agent)}>${_escape(agent)}${_el_resume($scope17_id, "b", $sg__input_settings_agents)}</option>${_el_resume($scope17_id, "a", $sg__input_settings_agents)}`);
3705
+ $si__input_settings_agents && writeScope($scope17_id, {});
3715
3706
  }, (a) => a, $scope6_id, "a", $sg__input_settings_agents, 1, $sg__input_settings_agents, "</select>", 1);
3716
3707
  });
3717
3708
  _html("</label>");
@@ -4682,6 +4673,9 @@ function setWorkspaceTab(workspaceId, sessionId) {
4682
4673
  function diffTreeOpen() {
4683
4674
  return get$1("diff-tree") !== "0";
4684
4675
  }
4676
+ function diffUncommitted() {
4677
+ return get$1("diff-uncommitted") === "1";
4678
+ }
4685
4679
  //#endregion
4686
4680
  //#region src/tags/icons/icon-edit.marko
4687
4681
  var icon_edit_default = _template("w", (input) => {
@@ -4737,7 +4731,6 @@ var side_nav_item_default = _template("k", (input) => {
4737
4731
  _if(() => {
4738
4732
  if (ws) {
4739
4733
  const $scope1_id = _scope_id();
4740
- const $if_content__st_changes__closures = /* @__PURE__ */ new Set();
4741
4734
  const $if_content__st_repos__closures = /* @__PURE__ */ new Set();
4742
4735
  const $if_content__st_ports__closures = /* @__PURE__ */ new Set();
4743
4736
  const st = input.status[input.workspaceId];
@@ -4828,7 +4821,7 @@ var side_nav_item_default = _template("k", (input) => {
4828
4821
  })));
4829
4822
  }, "id", $scope3_id, "i", $sg__input_sessions, $sg__input_sessions, $sg__input_sessions, 0, 1);
4830
4823
  _if(() => {
4831
- if ((st?.repos || []).some((r) => (r.prs || []).length) || (st?.ports || []).length || st?.changes) {
4824
+ if ((st?.repos || []).some((r) => (r.prs || []).length) || (st?.ports || []).length) {
4832
4825
  const $scope8_id = _scope_id();
4833
4826
  const $if_content5__repoLabel__closures = /* @__PURE__ */ new Set();
4834
4827
  const repoLabel = _resume((repo) => {
@@ -4838,48 +4831,40 @@ var side_nav_item_default = _template("k", (input) => {
4838
4831
  return repos.some((r) => r.full_name !== repo.full_name && r.full_name.split("/")[1] === name) ? repo.full_name : name;
4839
4832
  }, "k1", $scope8_id);
4840
4833
  _html("<div class=side-chips>");
4841
- _if(() => {
4842
- if (st?.changes) {
4843
- const $scope9_id = _scope_id();
4844
- _html(`<button class="chip changes"${_attr("title", `${st.changes} changed file${st.changes === 1 ? "" : "s"} — review`)}>±${_sep($sg__input_workspaceId__OR__input_status)}${_escape(st.changes)}${_el_resume($scope9_id, "b", $sg__input_workspaceId__OR__input_status)}</button>${_el_resume($scope9_id, "a")}`);
4845
- _script($scope9_id, "k7");
4846
- _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_changes__closures, writeScope($scope9_id, {
4847
- _: _scope_with_id($scope8_id),
4848
- Cj: $si__input_workspaceId__OR__input_status && 1
4849
- }));
4850
- return 0;
4851
- }
4852
- }, $scope8_id, "a", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, 0, 1);
4853
4834
  _for_of(st?.repos || [], (repo) => {
4854
- const $scope11_id = _scope_id();
4835
+ const $scope9_id = _scope_id();
4855
4836
  _for_of(repo.prs || [], (pr) => {
4856
- const $scope12_id = _scope_id();
4857
- _html(`<a class=${$class$2[(pr.isDraft ? 1 : 0) + (pr.reviewDecision === "CHANGES_REQUESTED" ? 2 : 0)]}${_attr("href", pr.url)} target=_blank rel=noopener${_attr("title", `${repo.worktree} — ${pr.title}${pr.reviewDecision === "APPROVED" ? " (approved)" : pr.reviewDecision === "CHANGES_REQUESTED" ? " (changes requested)" : ""}`)}>${_escape(repoLabel(repo))}${_el_resume($scope12_id, "b", $sg__input_workspaceId__OR__input_status)}#${_sep($sg__input_workspaceId__OR__input_status)}${_escape(pr.number)}${_el_resume($scope12_id, "c", $sg__input_workspaceId__OR__input_status)}${_sep($sg__input_workspaceId__OR__input_status)}${pr.reviewDecision === "APPROVED" ? " ✓" : pr.reviewDecision === "CHANGES_REQUESTED" ? " ✗" : ""}${_el_resume($scope12_id, "d", $sg__input_workspaceId__OR__input_status)}</a>${_el_resume($scope12_id, "a", $sg__input_workspaceId__OR__input_status)}`);
4858
- $si__input_workspaceId__OR__input_status && _subscribe($if_content5__repoLabel__closures, writeScope($scope12_id, { _: _scope_with_id($scope11_id) }));
4859
- }, "number", $scope11_id, "a", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, 0, 1);
4860
- $si__input_workspaceId__OR__input_status && writeScope($scope11_id, {
4861
- c: repo,
4837
+ const $scope10_id = _scope_id();
4838
+ _html(`<button class=${$class$2[(pr.isDraft ? 1 : 0) + (pr.reviewDecision === "CHANGES_REQUESTED" ? 2 : 0)]}${_attr("title", `${repo.worktree} — ${pr.title}${pr.reviewDecision === "APPROVED" ? " (approved)" : pr.reviewDecision === "CHANGES_REQUESTED" ? " (changes requested)" : ""}`)}>${_escape(repoLabel(repo))}${_el_resume($scope10_id, "b", $sg__input_workspaceId__OR__input_status)}#${_sep($sg__input_workspaceId__OR__input_status)}${_escape(pr.number)}${_el_resume($scope10_id, "c", $sg__input_workspaceId__OR__input_status)}${_sep($sg__input_workspaceId__OR__input_status)}${pr.reviewDecision === "APPROVED" ? " ✓" : pr.reviewDecision === "CHANGES_REQUESTED" ? " ✗" : ""}${_el_resume($scope10_id, "d", $sg__input_workspaceId__OR__input_status)}</button>${_el_resume($scope10_id, "a")}`);
4839
+ _script($scope10_id, "k7");
4840
+ _subscribe($si__input_workspaceId__OR__input_status && $if_content5__repoLabel__closures, writeScope($scope10_id, {
4841
+ l: pr?.number,
4842
+ _: _scope_with_id($scope9_id)
4843
+ }));
4844
+ }, "number", $scope9_id, "a", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, 0, 1);
4845
+ writeScope($scope9_id, {
4846
+ c: $si__input_workspaceId__OR__input_status && repo,
4862
4847
  e: repo?.worktree,
4863
4848
  _: _scope_with_id($scope8_id)
4864
4849
  });
4865
- }, "full_name", $scope8_id, "b", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status);
4850
+ }, "full_name", $scope8_id, "a", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status);
4866
4851
  _for_of(st?.ports || [], (p) => {
4867
- const $scope10_id = _scope_id();
4868
- _html(`<span class="chip port-chip"${_attr("title", p.cmd)}><button class=port-open${_attr("title", `Open http://${typeof location === "undefined" ? "localhost" : location.hostname}:${p.port}`)}>:${_sep($sg__input_workspaceId__OR__input_status)}${_escape(p.port)}${_el_resume($scope10_id, "c", $sg__input_workspaceId__OR__input_status)}</button>${_el_resume($scope10_id, "b")}<button class=${armedPid === p.pid ? "\"port-kill armed\"" : "port-kill"}${armedPid === p.pid ? _attr("title", `Really kill ${p.cmd}?`) : _attr("title", `Kill ${p.cmd} (pid ${p.pid})`)}>×</button>${_el_resume($scope10_id, "d")}</span>${_el_resume($scope10_id, "a", $sg__input_workspaceId__OR__input_status)}`);
4869
- _script($scope10_id, "k8");
4870
- _subscribe($armedPid__closures, writeScope($scope10_id, {
4852
+ const $scope11_id = _scope_id();
4853
+ _html(`<span class="chip port-chip"${_attr("title", p.cmd)}><button class=port-open${_attr("title", `Open http://${typeof location === "undefined" ? "localhost" : location.hostname}:${p.port}`)}>:${_sep($sg__input_workspaceId__OR__input_status)}${_escape(p.port)}${_el_resume($scope11_id, "c", $sg__input_workspaceId__OR__input_status)}</button>${_el_resume($scope11_id, "b")}<button class=${armedPid === p.pid ? "\"port-kill armed\"" : "port-kill"}${armedPid === p.pid ? _attr("title", `Really kill ${p.cmd}?`) : _attr("title", `Kill ${p.cmd} (pid ${p.pid})`)}>×</button>${_el_resume($scope11_id, "d")}</span>${_el_resume($scope11_id, "a", $sg__input_workspaceId__OR__input_status)}`);
4854
+ _script($scope11_id, "k8");
4855
+ _subscribe($armedPid__closures, writeScope($scope11_id, {
4871
4856
  g: p?.cmd,
4872
4857
  h: p?.port,
4873
4858
  i: p?.pid,
4874
4859
  _: _scope_with_id($scope8_id)
4875
4860
  }));
4876
- }, "port", $scope8_id, "c", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, 0, 1);
4861
+ }, "port", $scope8_id, "b", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, 0, 1);
4877
4862
  _html("</div>");
4878
- _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_changes__closures, _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_ports__closures, _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_repos__closures, writeScope($scope8_id, {
4879
- d: $si__input_workspaceId__OR__input_status && repoLabel,
4863
+ _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_ports__closures, _subscribe($si__input_workspaceId__OR__input_status && $if_content__st_repos__closures, writeScope($scope8_id, {
4864
+ c: $si__input_workspaceId__OR__input_status && repoLabel,
4880
4865
  _: _scope_with_id($scope3_id),
4881
- e: $si__input_workspaceId__OR__input_status && $if_content5__repoLabel__closures
4882
- }))));
4866
+ d: $si__input_workspaceId__OR__input_status && $if_content5__repoLabel__closures
4867
+ })));
4883
4868
  return 0;
4884
4869
  }
4885
4870
  }, $scope3_id, "j", $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status, $sg__input_workspaceId__OR__input_status__OR__input_onReview__OR__input_onNewSession, 0, 1);
@@ -4887,8 +4872,8 @@ var side_nav_item_default = _template("k", (input) => {
4887
4872
  _script($scope3_id, "k9");
4888
4873
  _subscribe($si__input_workspaces__OR__input_workspaceId && $ws_name__closures, _subscribe($si__input_sessions && $input_sessions__closures, _subscribe($si__input_onNewSession && $input_onNewSession__closures, _subscribe($si__input_onReview && $input_onReview__closures, _subscribe($si__input_current && $input_current__closures, writeScope($scope3_id, {
4889
4874
  _: _scope_with_id($scope1_id),
4890
- Ca4: $si__input_current && 1,
4891
- Cad: $si__input_workspaces__OR__input_workspaceId && 1
4875
+ Ca5: $si__input_current && 1,
4876
+ Cae: $si__input_workspaces__OR__input_workspaceId && 1
4892
4877
  }))))));
4893
4878
  return 1;
4894
4879
  }
@@ -4896,12 +4881,10 @@ var side_nav_item_default = _template("k", (input) => {
4896
4881
  writeScope($scope1_id, {
4897
4882
  c: $si__input_workspaceId__OR__input_status__OR__input_rail && st?.repos,
4898
4883
  d: $si__input_workspaceId__OR__input_status__OR__input_rail && st?.ports,
4899
- e: $si__input_workspaceId__OR__input_status__OR__input_rail && st?.changes,
4900
- f: _serialize_if($scope0_reason, 11) && dot,
4884
+ e: _serialize_if($scope0_reason, 11) && dot,
4901
4885
  _: _scope_with_id($scope0_id),
4902
- j: $si__input_workspaceId__OR__input_status && $if_content__st_changes__closures,
4903
- h: $si__input_workspaceId__OR__input_status && $if_content__st_repos__closures,
4904
- i: $si__input_workspaceId__OR__input_status && $if_content__st_ports__closures
4886
+ g: $si__input_workspaceId__OR__input_status && $if_content__st_repos__closures,
4887
+ h: $si__input_workspaceId__OR__input_status && $if_content__st_ports__closures
4905
4888
  });
4906
4889
  return 0;
4907
4890
  }
@@ -4923,21 +4906,22 @@ var side_nav_item_default = _template("k", (input) => {
4923
4906
  r: input.onOpenTab,
4924
4907
  s: input.onRenameSession,
4925
4908
  t: input.onCloseSession,
4926
- u: armedKill,
4927
- v: armedPid,
4928
- x: armBox.timer,
4929
- y: arm,
4930
- a0: $si__input_workspaces__OR__input_workspaceId__OR__input_rail && ws?.name,
4931
- a4: $si__input_current && $input_current__closures,
4932
- a5: $si__input_index && $input_index__closures,
4933
- ad: $si__input_workspaces__OR__input_workspaceId && $ws_name__closures,
4934
- a8: $si__input_agents && $input_agents__closures,
4935
- aa: $si__input_currentTab && $input_currentTab__closures,
4936
- ab: $armedKill__closures,
4937
- ac: $armedPid__closures,
4938
- a6: $si__input_onReview && $input_onReview__closures,
4939
- a7: $si__input_onNewSession && $input_onNewSession__closures,
4940
- a9: $si__input_sessions && $input_sessions__closures
4909
+ u: input.onReviewPr,
4910
+ v: armedKill,
4911
+ w: armedPid,
4912
+ y: armBox.timer,
4913
+ z: arm,
4914
+ a1: $si__input_workspaces__OR__input_workspaceId__OR__input_rail && ws?.name,
4915
+ a5: $si__input_current && $input_current__closures,
4916
+ a6: $si__input_index && $input_index__closures,
4917
+ ae: $si__input_workspaces__OR__input_workspaceId && $ws_name__closures,
4918
+ a9: $si__input_agents && $input_agents__closures,
4919
+ ab: $si__input_currentTab && $input_currentTab__closures,
4920
+ ac: $armedKill__closures,
4921
+ ad: $armedPid__closures,
4922
+ a7: $si__input_onReview && $input_onReview__closures,
4923
+ a8: $si__input_onNewSession && $input_onNewSession__closures,
4924
+ aa: $si__input_sessions && $input_sessions__closures
4941
4925
  });
4942
4926
  _resume_branch($scope0_id);
4943
4927
  });
@@ -5039,7 +5023,7 @@ var $class$1 = [
5039
5023
  "\"side-nav closed open\""
5040
5024
  ];
5041
5025
  var side_nav_default = _template("l", (input) => {
5042
- const $scope0_reason = _scope_reason(), $sg__input_workspace__OR__input_allWorkspaces__OR__input_allStatus = _serialize_guard($scope0_reason, 1), $sg__input_workspace__OR__input_allWorkspaces = _serialize_guard($scope0_reason, 0), $sg__input_workspace__OR__input_allWorkspaces__OR__input_onNewSession = _serialize_guard($scope0_reason, 2), $sg__input_workspace__OR__input_allWorkspaces__OR__input_allStatus__OR__input_sessions = _serialize_guard($scope0_reason, 3), $sg__input_agents = _serialize_guard($scope0_reason, 14), $si__input_workspace = _serialize_if($scope0_reason, 4), $si__input_workspace__OR__input_allWorkspaces = _serialize_if($scope0_reason, 0), $si__input_allStatus = _serialize_if($scope0_reason, 12), $si__input_onSelectWorkspace = _serialize_if($scope0_reason, 13), $si__input_agents = _serialize_if($scope0_reason, 14), $si__input_onNewSession = _serialize_if($scope0_reason, 15), $si__input_sessions = _serialize_if($scope0_reason, 16), $si__input_onRenameSession = _serialize_if($scope0_reason, 17), $si__input_onCloseSession = _serialize_if($scope0_reason, 18);
5026
+ const $scope0_reason = _scope_reason(), $sg__input_workspace__OR__input_allWorkspaces__OR__input_allStatus = _serialize_guard($scope0_reason, 1), $sg__input_workspace__OR__input_allWorkspaces = _serialize_guard($scope0_reason, 0), $sg__input_workspace__OR__input_allWorkspaces__OR__input_onNewSession = _serialize_guard($scope0_reason, 2), $sg__input_workspace__OR__input_allWorkspaces__OR__input_allStatus__OR__input_sessions = _serialize_guard($scope0_reason, 3), $sg__input_agents = _serialize_guard($scope0_reason, 14), $si__input_workspace = _serialize_if($scope0_reason, 4), $si__input_workspace__OR__input_allWorkspaces = _serialize_if($scope0_reason, 0), $si__input_allStatus = _serialize_if($scope0_reason, 12), $si__input_onSelectWorkspace = _serialize_if($scope0_reason, 13), $si__input_agents = _serialize_if($scope0_reason, 14), $si__input_onNewSession = _serialize_if($scope0_reason, 15), $si__input_onReviewOpened = _serialize_if($scope0_reason, 16), $si__input_sessions = _serialize_if($scope0_reason, 17), $si__input_onRenameSession = _serialize_if($scope0_reason, 18), $si__input_onCloseSession = _serialize_if($scope0_reason, 19);
5043
5027
  const $scope0_id = _scope_id();
5044
5028
  const $ws_id__closures = /* @__PURE__ */ new Set();
5045
5029
  const $workspaces__closures = /* @__PURE__ */ new Set();
@@ -5047,6 +5031,7 @@ var side_nav_default = _template("l", (input) => {
5047
5031
  const $input_onSelectWorkspace__closures = /* @__PURE__ */ new Set();
5048
5032
  const $input_agents__closures = /* @__PURE__ */ new Set();
5049
5033
  const $input_onNewSession__closures = /* @__PURE__ */ new Set();
5034
+ const $input_onReviewOpened__closures = /* @__PURE__ */ new Set();
5050
5035
  const $input_sessions__closures = /* @__PURE__ */ new Set();
5051
5036
  const $input_onRenameSession__closures = /* @__PURE__ */ new Set();
5052
5037
  const $input_onCloseSession__closures = /* @__PURE__ */ new Set();
@@ -5080,7 +5065,7 @@ var side_nav_default = _template("l", (input) => {
5080
5065
  if (drawerOpen) {
5081
5066
  const $scope5_id = _scope_id();
5082
5067
  _html(`<div class=drawer-backdrop></div>${_el_resume($scope5_id, "a")}`);
5083
- _script($scope5_id, "l14");
5068
+ _script($scope5_id, "l16");
5084
5069
  writeScope($scope5_id, {});
5085
5070
  return 0;
5086
5071
  }
@@ -5130,12 +5115,25 @@ var side_nav_default = _template("l", (input) => {
5130
5115
  }, "l3", $scope1_id),
5131
5116
  onReview: _resume(function() {
5132
5117
  drawerOpen = false;
5133
- if (wsId === ws.id) view = view === "diff" ? "term" : "diff";
5134
- else {
5118
+ if (wsId === ws.id) {
5119
+ view = view === "diff" ? "term" : "diff";
5120
+ if (view === "diff") input.onReviewOpened?.();
5121
+ } else {
5135
5122
  navIntent$1.requestDiff(wsId);
5136
5123
  input.onSelectWorkspace?.(wsId);
5137
5124
  }
5138
5125
  }, "l4", $scope1_id),
5126
+ onReviewPr: _resume(function(worktree, number) {
5127
+ drawerOpen = false;
5128
+ const pr = `${worktree}/${number}`;
5129
+ if (wsId === ws.id) {
5130
+ view = "diff";
5131
+ input.onReviewOpened?.(pr);
5132
+ } else {
5133
+ navIntent$1.requestDiff(wsId, pr);
5134
+ input.onSelectWorkspace?.(wsId);
5135
+ }
5136
+ }, "l5", $scope1_id),
5139
5137
  sessions: wsId === ws.id && input.sessions ? input.sessions : allStatus[wsId]?.sessions || [],
5140
5138
  currentTab: wsId === ws.id && view === "term" ? tab : null,
5141
5139
  onOpenTab: _resume(function(sessionId) {
@@ -5144,7 +5142,7 @@ var side_nav_default = _template("l", (input) => {
5144
5142
  tab = sessionId;
5145
5143
  view = "term";
5146
5144
  } else input.onOpenSession?.(wsId, sessionId);
5147
- }, "l5", $scope1_id),
5145
+ }, "l6", $scope1_id),
5148
5146
  onRenameSession: input.onRenameSession,
5149
5147
  onCloseSession: input.onCloseSession
5150
5148
  });
@@ -5159,13 +5157,13 @@ var side_nav_default = _template("l", (input) => {
5159
5157
  const $scope2_id = _scope_id();
5160
5158
  let railHover = null;
5161
5159
  const railHoverBox = { timer: 0 };
5162
- const railKeep = _resume(() => clearTimeout(railHoverBox.timer), "l6", $scope2_id);
5160
+ const railKeep = _resume(() => clearTimeout(railHoverBox.timer), "l7", $scope2_id);
5163
5161
  const railHide = _resume(() => {
5164
5162
  clearTimeout(railHoverBox.timer);
5165
5163
  railHoverBox.timer = setTimeout(() => {
5166
5164
  railHover = null;
5167
5165
  }, 150);
5168
- }, "l7", $scope2_id);
5166
+ }, "l8", $scope2_id);
5169
5167
  _html("<div class=side-rail>");
5170
5168
  _for_of(itemIds, (wsId, i) => {
5171
5169
  const $scope3_id = _scope_id();
@@ -5194,10 +5192,10 @@ var side_nav_default = _template("l", (input) => {
5194
5192
  current: wsId === ws.id,
5195
5193
  onSelect: _resume(function() {
5196
5194
  if (wsId !== ws.id) input.onSelectWorkspace?.(wsId);
5197
- }, "l8", $scope3_id)
5195
+ }, "l9", $scope3_id)
5198
5196
  });
5199
5197
  _html(`</span>${_el_resume($scope3_id, "a")}`);
5200
- _script($scope3_id, "l15");
5198
+ _script($scope3_id, "l17");
5201
5199
  _subscribe($si__input_allStatus && $allStatus__closures, _subscribe($si__input_workspace__OR__input_allWorkspaces && $workspaces__closures, _subscribe($si__input_workspace && $ws_id__closures, writeScope($scope3_id, {
5202
5200
  d: wsId,
5203
5201
  _: _scope_with_id($scope2_id),
@@ -5237,24 +5235,37 @@ var side_nav_default = _template("l", (input) => {
5237
5235
  onSelect: _resume(function() {
5238
5236
  railHover = null;
5239
5237
  if (hoverId !== ws.id) input.onSelectWorkspace?.(hoverId);
5240
- }, "l9", $scope4_id),
5238
+ }, "l10", $scope4_id),
5241
5239
  onManage: _resume(function() {
5242
5240
  railHover = null;
5243
5241
  input.onManage?.(hoverId);
5244
- }, "l10", $scope4_id),
5242
+ }, "l11", $scope4_id),
5245
5243
  agents: input.agents,
5246
5244
  onNewSession: _resume(function(agent) {
5247
5245
  railHover = null;
5248
5246
  input.onNewSession(hoverId, agent);
5249
- }, "l11", $scope4_id),
5247
+ }, "l12", $scope4_id),
5250
5248
  onReview: _resume(function() {
5251
5249
  railHover = null;
5252
- if (hoverId === ws.id) view = view === "diff" ? "term" : "diff";
5253
- else {
5250
+ if (hoverId === ws.id) {
5251
+ view = view === "diff" ? "term" : "diff";
5252
+ if (view === "diff") input.onReviewOpened?.();
5253
+ } else {
5254
5254
  navIntent$1.requestDiff(hoverId);
5255
5255
  input.onSelectWorkspace?.(hoverId);
5256
5256
  }
5257
- }, "l12", $scope4_id),
5257
+ }, "l13", $scope4_id),
5258
+ onReviewPr: _resume(function(worktree, number) {
5259
+ railHover = null;
5260
+ const pr = `${worktree}/${number}`;
5261
+ if (hoverId === ws.id) {
5262
+ view = "diff";
5263
+ input.onReviewOpened?.(pr);
5264
+ } else {
5265
+ navIntent$1.requestDiff(hoverId, pr);
5266
+ input.onSelectWorkspace?.(hoverId);
5267
+ }
5268
+ }, "l14", $scope4_id),
5258
5269
  sessions: hoverId === ws.id && input.sessions ? input.sessions : allStatus[hoverId]?.sessions || [],
5259
5270
  currentTab: hoverId === ws.id && view === "term" ? tab : null,
5260
5271
  onOpenTab: _resume(function(sessionId) {
@@ -5263,19 +5274,19 @@ var side_nav_default = _template("l", (input) => {
5263
5274
  tab = sessionId;
5264
5275
  view = "term";
5265
5276
  } else input.onOpenSession?.(hoverId, sessionId);
5266
- }, "l13", $scope4_id),
5277
+ }, "l15", $scope4_id),
5267
5278
  onRenameSession: input.onRenameSession,
5268
5279
  onCloseSession: input.onCloseSession
5269
5280
  });
5270
5281
  _html(`</div>${_el_resume($scope4_id, "a")}`);
5271
- _script($scope4_id, "l16");
5272
- _subscribe($si__input_allStatus && $allStatus__closures, _subscribe($si__input_workspace__OR__input_allWorkspaces && $workspaces__closures, _subscribe($view__closures, _subscribe($tab__closures, _subscribe($si__input_workspace && $ws_id__closures, _subscribe($si__input_onCloseSession && $input_onCloseSession__closures, _subscribe($si__input_onRenameSession && $input_onRenameSession__closures, _subscribe($si__input_sessions && $input_sessions__closures, _subscribe($si__input_onNewSession && $input_onNewSession__closures, _subscribe($si__input_agents && $input_agents__closures, _subscribe($si__input_onSelectWorkspace && $input_onSelectWorkspace__closures, writeScope($scope4_id, {
5282
+ _script($scope4_id, "l18");
5283
+ _subscribe($si__input_allStatus && $allStatus__closures, _subscribe($si__input_workspace__OR__input_allWorkspaces && $workspaces__closures, _subscribe($view__closures, _subscribe($tab__closures, _subscribe($si__input_workspace && $ws_id__closures, _subscribe($si__input_onCloseSession && $input_onCloseSession__closures, _subscribe($si__input_onRenameSession && $input_onRenameSession__closures, _subscribe($si__input_sessions && $input_sessions__closures, _subscribe($si__input_onReviewOpened && $input_onReviewOpened__closures, _subscribe($si__input_onNewSession && $input_onNewSession__closures, _subscribe($si__input_agents && $input_agents__closures, _subscribe($si__input_onSelectWorkspace && $input_onSelectWorkspace__closures, writeScope($scope4_id, {
5273
5284
  c: hoverId,
5274
5285
  b: _existing_scope($childScope3),
5275
- Caz: $si__input_workspace && 1,
5276
- Cb4: $si__input_workspace__OR__input_allWorkspaces && 1,
5277
- Cb5: $si__input_allStatus && 1
5278
- }))))))))))));
5286
+ Cb1: $si__input_workspace && 1,
5287
+ Cb6: $si__input_workspace__OR__input_allWorkspaces && 1,
5288
+ Cb7: $si__input_allStatus && 1
5289
+ })))))))))))));
5279
5290
  return 0;
5280
5291
  }
5281
5292
  }, $scope2_id, "b", 1, 1, 1, 0, 1);
@@ -5311,7 +5322,7 @@ var side_nav_default = _template("l", (input) => {
5311
5322
  _html(`<button class=${notifyOn ? "\"icon-btn notify-btn on\"" : "\"icon-btn notify-btn\""} title=${notifyOn ? "\"Disable agent-waiting notifications\"" : "\"Notify when an agent is waiting\""}>`);
5312
5323
  icon_bell_default({ size: 15 });
5313
5324
  _html(`</button>${_el_resume($scope6_id, "a")}`);
5314
- _script($scope6_id, "l17");
5325
+ _script($scope6_id, "l19");
5315
5326
  writeScope($scope6_id, { _: _scope_with_id($scope0_id) });
5316
5327
  return 0;
5317
5328
  }
@@ -5321,8 +5332,8 @@ var side_nav_default = _template("l", (input) => {
5321
5332
  _html(`</button>${_el_resume($scope0_id, "g")}<button class=icon-btn title="New workspace">`);
5322
5333
  icon_folder_plus_default({ size: 16 });
5323
5334
  _html(`</button>${_el_resume($scope0_id, "i")}</div></aside>${_el_resume($scope0_id, "b")}`);
5324
- _script($scope0_id, "l18");
5325
- _script($scope0_id, "l19");
5335
+ _script($scope0_id, "l20");
5336
+ _script($scope0_id, "l21");
5326
5337
  writeScope($scope0_id, {
5327
5338
  n: _serialize_if($scope0_reason, 6) && input.tab,
5328
5339
  o: _serialize_if($scope0_reason, 5) && input.tabChange,
@@ -5337,61 +5348,111 @@ var side_nav_default = _template("l", (input) => {
5337
5348
  a1: input.onManage,
5338
5349
  a2: input.agents,
5339
5350
  a3: input.onNewSession,
5340
- a4: input.sessions,
5341
- a5: input.onOpenSession,
5342
- a6: input.onRenameSession,
5343
- a7: input.onCloseSession,
5344
- a8: input.onUsage,
5345
- a9: input.onNew,
5346
- aa: _serialize_if($scope0_reason, 11) && ws,
5347
- ac: ws?.id,
5348
- ae: tab,
5349
- ag: view,
5350
- ai: drawerOpen,
5351
- ak: navOpen,
5352
- am: notifyOn,
5353
- ao: toggleNotify,
5354
- ap: itemIds,
5355
- aq: workspaces,
5356
- ar: allStatus,
5357
- af: input.tabChange || void 0,
5358
- ah: input.viewChange || void 0,
5359
- aj: input.drawerOpenChange || void 0,
5360
- az: $si__input_workspace && $ws_id__closures,
5361
- b4: $si__input_workspace__OR__input_allWorkspaces && $workspaces__closures,
5362
- b5: $si__input_allStatus && $allStatus__closures,
5363
- at: $si__input_onSelectWorkspace && $input_onSelectWorkspace__closures,
5364
- au: $si__input_agents && $input_agents__closures,
5365
- av: $si__input_onNewSession && $input_onNewSession__closures,
5366
- aw: $si__input_sessions && $input_sessions__closures,
5367
- ax: $si__input_onRenameSession && $input_onRenameSession__closures,
5368
- ay: $si__input_onCloseSession && $input_onCloseSession__closures,
5369
- b0: $tab__closures,
5370
- b1: $view__closures
5351
+ a4: input.onReviewOpened,
5352
+ a5: input.sessions,
5353
+ a6: input.onOpenSession,
5354
+ a7: input.onRenameSession,
5355
+ a8: input.onCloseSession,
5356
+ a9: input.onUsage,
5357
+ aa: input.onNew,
5358
+ ab: _serialize_if($scope0_reason, 11) && ws,
5359
+ ad: ws?.id,
5360
+ af: tab,
5361
+ ah: view,
5362
+ aj: drawerOpen,
5363
+ al: navOpen,
5364
+ an: notifyOn,
5365
+ ap: toggleNotify,
5366
+ aq: itemIds,
5367
+ ar: workspaces,
5368
+ as: allStatus,
5369
+ ag: input.tabChange || void 0,
5370
+ ai: input.viewChange || void 0,
5371
+ ak: input.drawerOpenChange || void 0,
5372
+ b1: $si__input_workspace && $ws_id__closures,
5373
+ b6: $si__input_workspace__OR__input_allWorkspaces && $workspaces__closures,
5374
+ b7: $si__input_allStatus && $allStatus__closures,
5375
+ au: $si__input_onSelectWorkspace && $input_onSelectWorkspace__closures,
5376
+ av: $si__input_agents && $input_agents__closures,
5377
+ aw: $si__input_onNewSession && $input_onNewSession__closures,
5378
+ ax: $si__input_onReviewOpened && $input_onReviewOpened__closures,
5379
+ ay: $si__input_sessions && $input_sessions__closures,
5380
+ az: $si__input_onRenameSession && $input_onRenameSession__closures,
5381
+ b0: $si__input_onCloseSession && $input_onCloseSession__closures,
5382
+ b2: $tab__closures,
5383
+ b3: $view__closures
5371
5384
  });
5372
5385
  _resume_branch($scope0_id);
5373
5386
  });
5374
5387
  //#endregion
5375
5388
  //#region src/client/review.ts
5376
- /** Render review comments as markdown, quoting the commented lines. */
5389
+ var CONTEXT = 3;
5390
+ /** Render review comments as markdown: per-repo base/head so the agent
5391
+ * can see if it has moved on, plus ±3 lines of numbered patch context
5392
+ * so the snippet is still locatable after a small edit. */
5377
5393
  function commentMarkdown(comments, repos) {
5378
- const byFile = /* @__PURE__ */ new Map();
5379
- for (const c of comments) {
5380
- const key = `${c.worktree}/${c.path}`;
5381
- byFile.set(key, [...byFile.get(key) || [], c]);
5382
- }
5394
+ const byRepo = /* @__PURE__ */ new Map();
5395
+ for (const c of comments) byRepo.set(c.worktree, [...byRepo.get(c.worktree) || [], c]);
5383
5396
  const chunks = ["Review feedback:"];
5384
- for (const [key, list] of byFile) for (const c of list) {
5385
- const file = (repos || []).find((r) => r.worktree === c.worktree)?.files.find((f) => f.path === c.path);
5386
- const source = c.side === "deletions" ? file?.old : file?.new;
5387
- const lines = source ? source.split("\n").slice(c.line_start - 1, c.line_end).join("\n") : null;
5388
- const range = c.line_start === c.line_end ? `L${c.line_start}` : `L${c.line_start}-L${c.line_end}`;
5389
- chunks.push(`\n\`${key}\` ${range}:`);
5390
- if (lines != null) chunks.push("```\n" + lines + "\n```");
5391
- chunks.push(c.text);
5397
+ let n = 0;
5398
+ for (const [worktree, list] of byRepo) {
5399
+ const repo = (repos || []).find((r) => r.worktree === worktree);
5400
+ chunks.push("");
5401
+ chunks.push(repoHeader(worktree, repo));
5402
+ for (const c of list) {
5403
+ n++;
5404
+ const file = repo?.files.find((f) => f.path === c.path);
5405
+ const source = c.side === "deletions" ? file?.old : file?.new;
5406
+ const side = c.side === "deletions" ? "Old" : "New";
5407
+ const range = c.line_start === c.line_end ? `line ${c.line_start}` : `lines ${c.line_start}-${c.line_end}`;
5408
+ chunks.push("");
5409
+ chunks.push(`${n}. **${c.path}** (${side} ${range})`);
5410
+ if (source != null) {
5411
+ const snippet = quoteRange(source, c.line_start, c.line_end, c.side);
5412
+ const fence = markdownFence(snippet);
5413
+ chunks.push("");
5414
+ chunks.push(indent(`${fence}diff\n${snippet}\n${fence}`));
5415
+ }
5416
+ chunks.push("");
5417
+ chunks.push(indent(c.text));
5418
+ }
5392
5419
  }
5393
5420
  return chunks.join("\n");
5394
5421
  }
5422
+ function repoHeader(worktree, repo) {
5423
+ if (!repo?.head) return `${worktree}/`;
5424
+ const head = shortSha(repo.head);
5425
+ const base = shortSha(repo.baseSha);
5426
+ const branch = repo.pr?.headRefName || repo.branch || "HEAD";
5427
+ return `${worktree}/${repo.pr ? ` #${repo.pr.number}` : ""} — \`${branch}\` ${head} vs \`${repo.base}\` ${base}`;
5428
+ }
5429
+ function shortSha(sha, n = 12) {
5430
+ return sha.slice(0, n);
5431
+ }
5432
+ /** ±CONTEXT rows around the commented range, with +/- prefixes and
5433
+ * line numbers — same shape as codiff's review payload. */
5434
+ function quoteRange(source, start, end, side) {
5435
+ const lines = source.split("\n");
5436
+ const from = Math.max(1, start - CONTEXT);
5437
+ const to = Math.min(lines.length, end + CONTEXT);
5438
+ const mark = side === "deletions" ? "-" : "+";
5439
+ const rows = [];
5440
+ for (let i = from; i <= to; i++) {
5441
+ const prefix = i >= start && i <= end ? mark : " ";
5442
+ rows.push(`${prefix}${String(i).padStart(4)} | ${lines[i - 1] ?? ""}`);
5443
+ }
5444
+ return rows.join("\n");
5445
+ }
5446
+ function markdownFence(body) {
5447
+ let ticks = 3;
5448
+ const re = /`+/g;
5449
+ let m;
5450
+ while (m = re.exec(body)) ticks = Math.max(ticks, m[0].length + 1);
5451
+ return "`".repeat(ticks);
5452
+ }
5453
+ function indent(text) {
5454
+ return text.split("\n").map((line) => line ? ` ${line}` : line).join("\n");
5455
+ }
5395
5456
  /** Build hierarchical rows from a sorted file list: a path trie with
5396
5457
  * single-child directory chains compressed into one label (GitHub-style
5397
5458
  * "src/util" segments), each level indented by depth — labels stay short
@@ -5486,77 +5547,11 @@ _template("c", (input) => {
5486
5547
  _resume_branch($scope0_id);
5487
5548
  });
5488
5549
  //#endregion
5489
- //#region src/tags/prompt-dialog.marko
5490
- var prompt_dialog_default = _template("i", (input) => {
5491
- const $scope0_reason = _scope_reason(), $sg__input_label = _serialize_guard($scope0_reason, 1), $sg__input_onCancel = _serialize_guard($scope0_reason, 2), $si__input_submitLabel = _serialize_if($scope0_reason, 3), $si__input_label = _serialize_if($scope0_reason, 1), $si__input_placeholder = _serialize_if($scope0_reason, 4);
5492
- const $scope0_id = _scope_id();
5493
- const $modalview_content__subscribers = /* @__PURE__ */ new Set();
5494
- const $input_submitLabel__closures = /* @__PURE__ */ new Set();
5495
- const $value__closures = /* @__PURE__ */ new Set();
5496
- const $input_label__closures = /* @__PURE__ */ new Set();
5497
- const $input_placeholder__closures = /* @__PURE__ */ new Set();
5498
- let value = input.value || "";
5499
- const submit = _resume(() => {
5500
- const v = value.trim();
5501
- if (v) input.onSubmit(v);
5502
- }, "i1", $scope0_id);
5503
- _set_serialize_reason({
5504
- 0: $sg__input_onCancel,
5505
- 1: $sg__input_label,
5506
- 2: $sg__input_onCancel
5507
- });
5508
- const $childScope = _peek_scope_id();
5509
- modal_view_default({
5510
- label: input.label,
5511
- class: "prompt-dlg",
5512
- onClose: _resume(function() {
5513
- input.onCancel();
5514
- }, "i2", $scope0_id),
5515
- footer: attrTag({ content: _content_resume("i5", () => {
5516
- _scope_reason();
5517
- const $scope1_id = _scope_id();
5518
- _html(`<button class="btn primary"${_attr("disabled", !value.trim())}>${_escape(input.submitLabel || "OK")}${_el_resume($scope1_id, "b", _serialize_guard($scope0_reason, 3))}</button>${_el_resume($scope1_id, "a")}<div class=spacer></div><button class=btn>Cancel</button>${_el_resume($scope1_id, "c")}`);
5519
- _script($scope1_id, "i3");
5520
- _subscribe($value__closures, _subscribe($si__input_submitLabel && $input_submitLabel__closures, writeScope($scope1_id, { _: _scope_with_id($scope0_id) })));
5521
- _resume_branch($scope1_id);
5522
- }, $scope0_id) }),
5523
- content: _content("i6", () => {
5524
- _scope_reason();
5525
- const $scope2_id = _scope_id();
5526
- _html(`<h2 class=prompt-title>${_escape(input.label)}${_el_resume($scope2_id, "a", $sg__input_label)}</h2><input${_attr_input_value($scope2_id, "b", value, _resume((_new_value) => {
5527
- value = _new_value;
5528
- }, "i0", $scope2_id))}${_attr("placeholder", input.placeholder)}>${_el_resume($scope2_id, "b")}`);
5529
- _script($scope2_id, "i4");
5530
- _subscribe($value__closures, _subscribe($si__input_placeholder && $input_placeholder__closures, _subscribe($si__input_label && $input_label__closures, _subscribe($modalview_content__subscribers, writeScope($scope2_id, {
5531
- _: _scope_with_id($scope0_id),
5532
- Cp: 1
5533
- })))));
5534
- _resume_branch($scope2_id);
5535
- })
5536
- });
5537
- _script($scope0_id, "i7");
5538
- writeScope($scope0_id, {
5539
- e: input.onSubmit,
5540
- g: input.onCancel,
5541
- h: input.submitLabel,
5542
- j: value,
5543
- l: submit,
5544
- B2: $modalview_content__subscribers,
5545
- n: $si__input_submitLabel && $input_submitLabel__closures,
5546
- p: $value__closures,
5547
- m: $si__input_label && $input_label__closures,
5548
- o: $si__input_placeholder && $input_placeholder__closures,
5549
- a: _serialize_if($scope0_reason, 0) && _existing_scope($childScope)
5550
- });
5551
- _resume_branch($scope0_id);
5552
- });
5553
- //#endregion
5554
5550
  //#region src/tags/diff-view.marko
5555
5551
  var api$3;
5556
5552
  var copyText;
5557
5553
  var getTermDims;
5558
5554
  var makeFileFilter;
5559
- var CUSTOM = "__custom";
5560
5555
  var diff_view_default = _template("d", (input) => {
5561
5556
  const $scope0_reason = _scope_reason(), $sg__input_onExit = _serialize_guard($scope0_reason, 1), $sg__input_sessions = _serialize_guard($scope0_reason, 0), $si__input_sessions = _serialize_if($scope0_reason, 0), $si__input_sessions_0_title = _serialize_if($scope0_reason, 2), $si__input_sessions_0_agent = _serialize_if($scope0_reason, 3);
5562
5557
  const $scope0_id = _scope_id();
@@ -5585,16 +5580,15 @@ var diff_view_default = _template("d", (input) => {
5585
5580
  let sending = false;
5586
5581
  let sendTarget = null;
5587
5582
  let fileFilter = "";
5588
- let baseRef = "";
5583
+ let uncommitted = diffUncommitted();
5589
5584
  let treeOpen = diffTreeOpen();
5590
- let askBase = false;
5591
5585
  let collapsedDirs = /* @__PURE__ */ new Set();
5592
5586
  let collapsedRepos = /* @__PURE__ */ new Set();
5593
5587
  const toggleRepo = _resume((worktree) => {
5594
5588
  const next = new Set(collapsedRepos);
5595
5589
  if (!next.delete(worktree)) next.add(worktree);
5596
5590
  collapsedRepos = next;
5597
- }, "d4", $scope0_id);
5591
+ }, "d3", $scope0_id);
5598
5592
  const nearBox = {
5599
5593
  io: null,
5600
5594
  cbs: null
@@ -5609,12 +5603,15 @@ var diff_view_default = _template("d", (input) => {
5609
5603
  }, { rootMargin: "600px 0px" });
5610
5604
  nearBox.cbs.set(el, cb);
5611
5605
  nearBox.io.observe(el);
5612
- }, "d5", $scope0_id);
5613
- const load = _resume(async (base = baseRef) => {
5606
+ }, "d4", $scope0_id);
5607
+ const load = _resume(async (dirty = uncommitted) => {
5614
5608
  loading = true;
5615
5609
  error = null;
5616
5610
  try {
5617
- const qs = base ? `?base=${encodeURIComponent(base)}` : "";
5611
+ const q = new URLSearchParams();
5612
+ if (dirty) q.set("uncommitted", "1");
5613
+ if (input.focusPr) q.set("pr", input.focusPr);
5614
+ const qs = q.size ? `?${q}` : "";
5618
5615
  const [diffData, reviewData] = await Promise.all([api$3(`/api/workspaces/${input.workspaceId}/diff${qs}`), api$3(`/api/workspaces/${input.workspaceId}/review`)]);
5619
5616
  repos = diffData;
5620
5617
  comments = reviewData.comments || [];
@@ -5623,23 +5620,23 @@ var diff_view_default = _template("d", (input) => {
5623
5620
  error = String(err.message || err);
5624
5621
  }
5625
5622
  loading = false;
5626
- }, "d6", $scope0_id);
5623
+ }, "d5", $scope0_id);
5627
5624
  const loadReview = _resume(async () => {
5628
5625
  try {
5629
5626
  const reviewData = await api$3(`/api/workspaces/${input.workspaceId}/review`);
5630
5627
  comments = reviewData.comments || [];
5631
5628
  viewed = new Set(reviewData.viewed || []);
5632
5629
  } catch {}
5633
- }, "d7", $scope0_id);
5630
+ }, "d6", $scope0_id);
5634
5631
  const repoList = repos || [];
5635
5632
  const allFiles = repoList.flatMap((r) => r.files.map((f) => ({
5636
5633
  ...f,
5637
- repo: r.worktree,
5634
+ repo: r.id,
5638
5635
  key: `${r.worktree}/${f.path}`
5639
5636
  })));
5640
5637
  const fileMatch = makeFileFilter(fileFilter);
5641
5638
  const shownFiles = allFiles.filter((f) => fileMatch(f.key));
5642
- const filesByRepo = new Map(repoList.map((r) => [r.worktree, shownFiles.filter((f) => f.repo === r.worktree)]));
5639
+ const filesByRepo = new Map(repoList.map((r) => [r.id, shownFiles.filter((f) => f.repo === r.id)]));
5643
5640
  const commentsByFile = (() => {
5644
5641
  const map = /* @__PURE__ */ new Map();
5645
5642
  for (const c of comments) {
@@ -5648,12 +5645,8 @@ var diff_view_default = _template("d", (input) => {
5648
5645
  }
5649
5646
  return map;
5650
5647
  })();
5651
- const defaultBranches = [...new Set(repoList.map((r) => r.default_branch).filter(Boolean))];
5652
5648
  const sendPick = (input.sessions || []).some((s) => s.id === sendTarget) ? sendTarget : input.sessions?.[0]?.id ?? null;
5653
- const setBase = _resume((ref) => {
5654
- baseRef = ref;
5655
- load(ref);
5656
- }, "d8", $scope0_id);
5649
+ const localDiffers = _resume((repo) => !!repo.pr && !!repo.localHead && (repo.localHead !== repo.head || repo.branch !== repo.pr.headRefName), "d7");
5657
5650
  const toggleViewed = _resume((key) => {
5658
5651
  const next = new Set(viewed);
5659
5652
  const isViewed = !next.has(key);
@@ -5671,7 +5664,7 @@ var diff_view_default = _template("d", (input) => {
5671
5664
  }).catch(() => {
5672
5665
  error = "Saving viewed state failed — it may reset on reload.";
5673
5666
  });
5674
- }, "d9", $scope0_id);
5667
+ }, "d8", $scope0_id);
5675
5668
  const saveComment = _resume(async (file, text) => {
5676
5669
  const range = draft;
5677
5670
  draft = null;
@@ -5693,11 +5686,11 @@ var diff_view_default = _template("d", (input) => {
5693
5686
  } catch (err) {
5694
5687
  error = String(err.message || err);
5695
5688
  }
5696
- }, "d10", $scope0_id);
5689
+ }, "d9", $scope0_id);
5697
5690
  const removeComment = _resume(async (id) => {
5698
5691
  comments = comments.filter((c) => c.id !== id);
5699
5692
  await fetch(`/api/review-comments/${id}`, { method: "DELETE" });
5700
- }, "d11", $scope0_id);
5693
+ }, "d10", $scope0_id);
5701
5694
  const clearComments = _resume(async () => {
5702
5695
  comments = [];
5703
5696
  draft = null;
@@ -5706,7 +5699,7 @@ var diff_view_default = _template("d", (input) => {
5706
5699
  headers: { "content-type": "application/json" },
5707
5700
  body: JSON.stringify({ action: "clear" })
5708
5701
  });
5709
- }, "d12", $scope0_id);
5702
+ }, "d11", $scope0_id);
5710
5703
  const copyMarkdown = _resume(async () => {
5711
5704
  if (!await copyText(commentMarkdown(comments, repos))) {
5712
5705
  error = "Copy failed — clipboard needs HTTPS on this browser.";
@@ -5717,7 +5710,7 @@ var diff_view_default = _template("d", (input) => {
5717
5710
  copied = false;
5718
5711
  }, 1500);
5719
5712
  await clearComments();
5720
- }, "d13", $scope0_id);
5713
+ }, "d12", $scope0_id);
5721
5714
  const sendTo = _resume(async (sessionId) => {
5722
5715
  if (!sessionId) return;
5723
5716
  sending = true;
@@ -5737,57 +5730,57 @@ var diff_view_default = _template("d", (input) => {
5737
5730
  error = String(err.message || err);
5738
5731
  sending = false;
5739
5732
  }
5740
- }, "d14", $scope0_id);
5733
+ }, "d13", $scope0_id);
5741
5734
  const jumpTo = _resume((key) => {
5742
5735
  const el = ((scrollEl) => scrollEl())(_el_read_error).querySelector(`details[data-key="${CSS.escape(key)}"]`);
5743
5736
  if (el) {
5744
5737
  el.open = true;
5745
5738
  el.scrollIntoView({ block: "start" });
5746
5739
  }
5747
- }, "d15", $scope0_id);
5740
+ }, "d14", $scope0_id);
5748
5741
  _html(`<div class=diff-view><aside class=${!treeOpen ? "\"tree closed\"" : "tree"}>`);
5749
- _for_of(repoList.filter((r) => filesByRepo.get(r.worktree)?.length), (repo) => {
5742
+ _for_of(repoList.filter((r) => filesByRepo.get(r.id)?.length), (repo) => {
5750
5743
  const $scope5_id = _scope_id();
5751
- _html(`<div class=tree-repo><button class=${collapsedRepos.has(repo.worktree) ? "\"tree-repo-name mono closed\"" : "\"tree-repo-name mono\""}><span class=tree-chev>${collapsedRepos.has(repo.worktree) ? "▸" : "▾"}${_el_resume($scope5_id, "b")}</span><!>${_escape(repo.worktree)}${_el_resume($scope5_id, "c")}</button>${_el_resume($scope5_id, "a")}`);
5744
+ _html(`<div class=tree-repo><button class=${collapsedRepos.has(repo.id) ? "\"tree-repo-name mono closed\"" : "\"tree-repo-name mono\""}><span class=tree-chev>${collapsedRepos.has(repo.id) ? "▸" : "▾"}${_el_resume($scope5_id, "b")}</span><!>${repo.pr ? `${_escape(`${repo.worktree}`)} #${_escape(`${repo.pr.number}`)}` : _escape(repo.worktree)}${_el_resume($scope5_id, "c")}</button>${_el_resume($scope5_id, "a")}`);
5752
5745
  _if(() => {
5753
- if (!collapsedRepos.has(repo.worktree)) {
5746
+ if (!collapsedRepos.has(repo.id)) {
5754
5747
  const $scope6_id = _scope_id();
5755
5748
  const $if_content4__dirKey__closures = /* @__PURE__ */ new Set();
5756
- const repoRows = treeRows(filesByRepo.get(repo.worktree) || []);
5757
- const dirKey = _resume((path) => `${repo.worktree}/${path}`, "d16", $scope6_id);
5758
- const rowDir = _resume((row) => row.kind === "dir" ? row.path : row.file.path.includes("/") ? row.file.path.slice(0, row.file.path.lastIndexOf("/")) : "", "d17");
5749
+ const repoRows = treeRows(filesByRepo.get(repo.id) || []);
5750
+ const dirKey = _resume((path) => `${repo.id}/${path}`, "d15", $scope6_id);
5751
+ const rowDir = _resume((row) => row.kind === "dir" ? row.path : row.file.path.includes("/") ? row.file.path.slice(0, row.file.path.lastIndexOf("/")) : "", "d16");
5759
5752
  const hiddenByCollapse = _resume((row) => {
5760
5753
  const dir = rowDir(row);
5761
5754
  for (const collapsed of collapsedDirs) {
5762
5755
  const [wt, path] = [collapsed.slice(0, collapsed.indexOf("/")), collapsed.slice(collapsed.indexOf("/") + 1)];
5763
- if (wt !== repo.worktree) continue;
5756
+ if (wt !== repo.id) continue;
5764
5757
  if (row.kind === "dir" && row.path === path) continue;
5765
5758
  if (dir === path || dir.startsWith(`${path}/`)) return true;
5766
5759
  }
5767
5760
  return false;
5768
- }, "d18", $scope6_id);
5761
+ }, "d17", $scope6_id);
5769
5762
  _for_of(repoRows, (row) => {
5770
5763
  const $scope7_id = _scope_id();
5771
5764
  _if(() => {
5772
5765
  if (!hiddenByCollapse(row) && row.kind === "dir") {
5773
5766
  const $scope22_id = _scope_id();
5774
5767
  _html(`<button class=${collapsedDirs.has(dirKey(row.path)) ? "\"tree-dir dim mono closed\"" : "\"tree-dir dim mono\""}${_attr("title", row.path)}${_attr_style(`padding-left:${.35 + row.depth * .75}rem`)}><span class=tree-chev>${collapsedDirs.has(dirKey(row.path)) ? "▸" : "▾"}${_el_resume($scope22_id, "b")}</span>${_escape(row.label)}${_el_resume($scope22_id, "c")}/</button>${_el_resume($scope22_id, "a")}`);
5775
- _script($scope22_id, "d26");
5776
- _subscribe($if_content4__dirKey__closures, _subscribe($collapsedDirs__closures, writeScope($scope22_id, { Cca: 1 })));
5768
+ _script($scope22_id, "d23");
5769
+ _subscribe($if_content4__dirKey__closures, _subscribe($collapsedDirs__closures, writeScope($scope22_id, { Cc7: 1 })));
5777
5770
  return 0;
5778
5771
  } else if (!hiddenByCollapse(row) && row.kind !== "dir") {
5779
5772
  const $scope8_id = _scope_id();
5780
5773
  _html(`<button class=${viewed.has(row.file.key) ? "\"tree-file mono seen\"" : "\"tree-file mono\""}${_attr("title", row.file.path)}${_attr_style(`padding-left:${.5 + row.depth * .75}rem`)}><span${_attr_class(["status", row.file.status])}>${_escape(row.file.status)}${_el_resume($scope8_id, "c")}</span>${_el_resume($scope8_id, "b")}<span class=tree-file-name>${_escape(row.name)}${_el_resume($scope8_id, "d")}</span>`);
5781
5774
  _if(() => {
5782
5775
  if (commentsByFile.has(row.file.key)) {
5783
- const $scope25_id = _scope_id();
5776
+ const $scope27_id = _scope_id();
5784
5777
  _html("<span class=tree-note>💬</span>");
5785
- writeScope($scope25_id, {});
5778
+ writeScope($scope27_id, {});
5786
5779
  return 0;
5787
5780
  }
5788
5781
  }, $scope8_id, "e", 1, 1, 1, 0, 1);
5789
5782
  _html(`</button>${_el_resume($scope8_id, "a")}`);
5790
- _script($scope8_id, "d27");
5783
+ _script($scope8_id, "d24");
5791
5784
  _subscribe($commentsByFile__closures, _subscribe($viewed__closures, writeScope($scope8_id, {})));
5792
5785
  return 1;
5793
5786
  }
@@ -5813,9 +5806,9 @@ var diff_view_default = _template("d", (input) => {
5813
5806
  }
5814
5807
  }, $scope5_id, "d");
5815
5808
  _html("</div>");
5816
- _script($scope5_id, "d28");
5817
- writeScope($scope5_id, { g: repo?.worktree });
5818
- }, "worktree", $scope0_id, "a", 1, 1, 1, "</aside>", 1);
5809
+ _script($scope5_id, "d25");
5810
+ writeScope($scope5_id, { g: repo?.id });
5811
+ }, "id", $scope0_id, "a", 1, 1, 1, "</aside>", 1);
5819
5812
  _html("<div class=diff-col><div class=diff-head>");
5820
5813
  _if(() => {
5821
5814
  if (input.onExit) {
@@ -5823,7 +5816,7 @@ var diff_view_default = _template("d", (input) => {
5823
5816
  _html("<button class=icon-btn title=\"Back to terminal\">");
5824
5817
  icon_back_default({ size: 14 });
5825
5818
  _html(`</button>${_el_resume($scope1_id, "a")}`);
5826
- _script($scope1_id, "d29");
5819
+ _script($scope1_id, "d26");
5827
5820
  writeScope($scope1_id, { _: _scope_with_id($scope0_id) });
5828
5821
  return 0;
5829
5822
  }
@@ -5837,7 +5830,7 @@ var diff_view_default = _template("d", (input) => {
5837
5830
  if ((input.sessions || []).length === 1) {
5838
5831
  const $scope3_id = _scope_id();
5839
5832
  _html(`<button class="btn primary"${_attr("disabled", sending)}>${sending ? "Sending…" : `Send to ${_escape(`${input.sessions[0].title || input.sessions[0].agent}`)}`}${_el_resume($scope3_id, "b")}</button>${_el_resume($scope3_id, "a")}`);
5840
- _script($scope3_id, "d30");
5833
+ _script($scope3_id, "d27");
5841
5834
  _subscribe($sending__closures, _subscribe($si__input_sessions_0_agent && $input_sessions_0_agent__closures, _subscribe($si__input_sessions_0_title && $input_sessions_0_title__closures, writeScope($scope3_id, { _: _scope_with_id($scope2_id) }))));
5842
5835
  return 0;
5843
5836
  } else if ((input.sessions || []).length) {
@@ -5848,30 +5841,30 @@ var diff_view_default = _template("d", (input) => {
5848
5841
  }, "d0", $scope4_id), () => {
5849
5842
  _html(`<select aria-label="Send comments to session"${_attr("disabled", sending)}>`);
5850
5843
  _for_of(input.sessions, (s) => {
5851
- const $scope26_id = _scope_id();
5852
- _html(`<option${_attr_option_value(s.id)}>${_escape(s.title || s.agent)}${_el_resume($scope26_id, "b", $sg__input_sessions)}</option>${_el_resume($scope26_id, "a", $sg__input_sessions)}`);
5853
- $si__input_sessions && writeScope($scope26_id, {});
5844
+ const $scope28_id = _scope_id();
5845
+ _html(`<option${_attr_option_value(s.id)}>${_escape(s.title || s.agent)}${_el_resume($scope28_id, "b", $sg__input_sessions)}</option>${_el_resume($scope28_id, "a", $sg__input_sessions)}`);
5846
+ $si__input_sessions && writeScope($scope28_id, {});
5854
5847
  }, "id", $scope4_id, "c", $sg__input_sessions, 1, $sg__input_sessions, "</select>", 1);
5855
5848
  });
5856
5849
  _html("</span>");
5857
- _script($scope4_id, "d31");
5850
+ _script($scope4_id, "d28");
5858
5851
  _subscribe($sendTarget__closures, _subscribe($sending__closures, _subscribe($si__input_sessions && $input_sessions__closures, writeScope($scope4_id, {
5859
5852
  _: _scope_with_id($scope2_id),
5860
- Cc6: 1
5853
+ Cc3: 1
5861
5854
  }))));
5862
5855
  return 1;
5863
5856
  }
5864
5857
  }, $scope2_id, "c", $sg__input_sessions, $sg__input_sessions, $sg__input_sessions, 0, 1);
5865
- _script($scope2_id, "d32");
5858
+ _script($scope2_id, "d29");
5866
5859
  writeScope($scope2_id, {});
5867
5860
  return 0;
5868
5861
  }
5869
5862
  }, $scope0_id, "g", 1, 1, 1, "</span>");
5870
5863
  _if(() => {
5871
5864
  if (loading) {
5872
- const $scope27_id = _scope_id();
5865
+ const $scope29_id = _scope_id();
5873
5866
  _html("<span class=\"refresh-chip dim\"><span class=spin></span>Refreshing…</span>");
5874
- writeScope($scope27_id, {});
5867
+ writeScope($scope29_id, {});
5875
5868
  return 0;
5876
5869
  }
5877
5870
  }, $scope0_id, "h", 1, 1, 1, 0, 1);
@@ -5881,55 +5874,49 @@ var diff_view_default = _template("d", (input) => {
5881
5874
  icon_refresh_default({ size: 14 });
5882
5875
  _html(`</button>${_el_resume($scope0_id, "k")}<button class="btn files-btn" title="Jump to file">Files (<!>${_escape(shownFiles.length)}${_el_resume($scope0_id, "n")})</button>${_el_resume($scope0_id, "m")}<input${_attr_input_value($scope0_id, "o", fileFilter, _resume((_new_fileFilter) => {
5883
5876
  fileFilter = _new_fileFilter;
5884
- }, "d1", $scope0_id))} class=file-filter placeholder="Filter files… (globs, !excludes)" aria-label="Filter files">${_el_resume($scope0_id, "o")}`);
5885
- _attr_select_value($scope0_id, "p", baseRef, _resume(function(v) {
5886
- if (v === CUSTOM) askBase = true;
5887
- else setBase(v);
5888
- }, "d2", $scope0_id), () => {
5889
- _html(`<select class=base-pick aria-label="Compare against"><option${_attr_option_value("")}>vs base</option>`);
5890
- _for_of(defaultBranches, (branch) => {
5891
- const $scope28_id = _scope_id();
5892
- _html(`<option${_attr_option_value(branch)}>vs <!>${_escape(branch)}${_el_resume($scope28_id, "b")}</option>${_el_resume($scope28_id, "a")}`);
5893
- writeScope($scope28_id, {});
5894
- }, (b) => b, $scope0_id, "q", 1, 1, 1, 0, 1);
5895
- _if(() => {
5896
- if (baseRef && !defaultBranches.includes(baseRef)) {
5897
- const $scope19_id = _scope_id();
5898
- _html(`<option${_attr_option_value(baseRef)}>vs <!>${_escape(baseRef)}${_el_resume($scope19_id, "b")}</option>${_el_resume($scope19_id, "a")}`);
5899
- writeScope($scope19_id, {});
5900
- return 0;
5901
- }
5902
- }, $scope0_id, "r", 1, 1, 1, 0, 1);
5903
- _html(`<option${_attr_option_value(CUSTOM)}>custom…</option></select>`);
5904
- });
5905
- _html(`${_el_resume($scope0_id, "p")}</div><div class=diff-main>`);
5877
+ }, "d1", $scope0_id))} class=file-filter placeholder="Filter files… (globs, !excludes)" aria-label="Filter files">${_el_resume($scope0_id, "o")}<label class=two-dot title="Include uncommitted and untracked files in the review. Off by default — the review is commit-to-commit."><input${_attr_input_checked($scope0_id, "p", uncommitted)} type=checkbox>${_el_resume($scope0_id, "p")} include uncommitted</label></div><div class=diff-main>`);
5906
5878
  _if(() => {
5907
5879
  if (error) {
5908
5880
  const $scope17_id = _scope_id();
5909
5881
  _html(`<p class=toast-error>${_escape(error)}${_el_resume($scope17_id, "a")} <button>×</button>${_el_resume($scope17_id, "b")}</p>`);
5910
- _script($scope17_id, "d33");
5882
+ _script($scope17_id, "d30");
5911
5883
  writeScope($scope17_id, {});
5912
5884
  return 0;
5913
5885
  }
5914
- }, $scope0_id, "u", 1, 1, 1, 0, 1);
5886
+ }, $scope0_id, "r", 1, 1, 1, 0, 1);
5915
5887
  _for_of(repoList.filter((r) => r.baseMissing), (repo) => {
5916
- const $scope29_id = _scope_id();
5917
- _html(`<p class=toast-error>${_escape(repo.worktree)}${_el_resume($scope29_id, "a")}: base <!>${_escape(repo.base)}${_el_resume($scope29_id, "b")} not found — showing uncommitted changes only.</p>`);
5918
- writeScope($scope29_id, {});
5919
- }, "worktree", $scope0_id, "v", 1, 1, 1, 0, 1);
5888
+ const $scope19_id = _scope_id();
5889
+ _html(`<p class=toast-error>${_escape(repo.worktree)}${_el_resume($scope19_id, "a")}: base <!>${_escape(repo.base)}${_el_resume($scope19_id, "b")} not found<!>${uncommitted ? " — showing uncommitted changes only" : ""}${_el_resume($scope19_id, "c")}.</p>`);
5890
+ writeScope($scope19_id, {});
5891
+ }, "id", $scope0_id, "s", 1, 1, 1, 0, 1);
5892
+ _for_of(repoList.filter((r) => r.contained && r.files.length), (repo) => {
5893
+ const $scope20_id = _scope_id();
5894
+ _html(`<p class=dim>${_escape(repo.worktree)}${_el_resume($scope20_id, "a")}<!>${repo.pr ? ` #${_escape(`${repo.pr.number}`)}` : ""}${_el_resume($scope20_id, "b")}: already in <!>${_escape(repo.base)}${_el_resume($scope20_id, "c")}<!>${uncommitted ? " — showing uncommitted changes only" : ""}${_el_resume($scope20_id, "d")}.</p>`);
5895
+ writeScope($scope20_id, {});
5896
+ }, "id", $scope0_id, "t", 1, 1, 1, 0, 1);
5897
+ _for_of(repoList.filter((r) => r.githubDrift), (repo) => {
5898
+ const $scope30_id = _scope_id();
5899
+ _html(`<p class=toast-error>${_escape(repo.worktree)}${_el_resume($scope30_id, "a")} #<!>${_escape(repo.pr.number)}${_el_resume($scope30_id, "b")}: origin/<!>${_escape(repo.pr.headRefName)}${_el_resume($scope30_id, "c")} is <!>${_escape(shortSha(repo.head))}${_el_resume($scope30_id, "d")}, GitHub last saw <!>${_escape(shortSha(repo.pr.headRefOid))}${_el_resume($scope30_id, "e")}.</p>`);
5900
+ writeScope($scope30_id, {});
5901
+ }, "id", $scope0_id, "u", 1, 1, 1, 0, 1);
5902
+ _for_of(repoList.filter(localDiffers), (repo) => {
5903
+ const $scope21_id = _scope_id();
5904
+ _html(`<p class=dim>${_escape(repo.worktree)}${_el_resume($scope21_id, "a")} #<!>${_escape(repo.pr.number)}${_el_resume($scope21_id, "b")}: this view is <!>${_escape(shortSha(repo.head))}${_el_resume($scope21_id, "c")} (<!>${_escape(repo.pr.headRefName)}${_el_resume($scope21_id, "d")}). Local is <!>${_escape(shortSha(repo.localHead))}${_el_resume($scope21_id, "e")} (<!>${_escape(repo.branch)}${_el_resume($scope21_id, "f")}<!>${uncommitted ? ", plus uncommitted" : ""}${_el_resume($scope21_id, "g")}).</p>`);
5905
+ writeScope($scope21_id, {});
5906
+ }, "id", $scope0_id, "v", 1, 1, 1, 0, 1);
5920
5907
  _if(() => {
5921
5908
  if (!repos && loading) {
5922
- const $scope30_id = _scope_id();
5909
+ const $scope31_id = _scope_id();
5923
5910
  _html("<div class=diff-skel><p class=dim><span class=spin></span>Loading diff…</p><div class=skel-row></div><div class=skel-row></div><div class=skel-row></div></div>");
5924
- writeScope($scope30_id, {});
5911
+ writeScope($scope31_id, {});
5925
5912
  return 0;
5926
5913
  }
5927
5914
  }, $scope0_id, "w", 1, 1, 1, 0, 1);
5928
5915
  _if(() => {
5929
5916
  if (repos && !allFiles.length) {
5930
- const $scope20_id = _scope_id();
5931
- _html(`<p class="dim center-pad">No changes vs <!>${_escape(baseRef || "base")}${_el_resume($scope20_id, "a")}.</p>`);
5932
- writeScope($scope20_id, {});
5917
+ const $scope25_id = _scope_id();
5918
+ _html(`<p class="dim center-pad">${repoList.some((r) => r.pr) ? "No file changes on these PRs." : "No changes vs base."}${_el_resume($scope25_id, "a")}</p>`);
5919
+ writeScope($scope25_id, {});
5933
5920
  return 0;
5934
5921
  } else if (repos && !shownFiles.length) {
5935
5922
  const $scope18_id = _scope_id();
@@ -5940,26 +5927,106 @@ var diff_view_default = _template("d", (input) => {
5940
5927
  }, $scope0_id, "x", 1, 1, 1, 0, 1);
5941
5928
  _for_of(repoList, (repo) => {
5942
5929
  const $scope9_id = _scope_id();
5943
- const $for_content3__repo_worktree__closures = /* @__PURE__ */ new Set();
5944
- const $for_content3__repo_full_name__closures = /* @__PURE__ */ new Set();
5930
+ const $for_content3__repo_pr_number__closures = /* @__PURE__ */ new Set();
5931
+ const $for_content3__repo_pr_url__closures = /* @__PURE__ */ new Set();
5932
+ const $for_content3__repo_pr_title__closures = /* @__PURE__ */ new Set();
5933
+ const $for_content3__repo_pr_headRefName__closures = /* @__PURE__ */ new Set();
5934
+ const $for_content3__repo_head__closures = /* @__PURE__ */ new Set();
5945
5935
  const $for_content3__repo_base__closures = /* @__PURE__ */ new Set();
5936
+ const $for_content3__repo_baseSha__closures = /* @__PURE__ */ new Set();
5937
+ const $for_content3__repo_full_name__closures = /* @__PURE__ */ new Set();
5938
+ const $for_content3__repo_branch__closures = /* @__PURE__ */ new Set();
5939
+ const $for_content3__repo_localHead__closures = /* @__PURE__ */ new Set();
5940
+ const $for_content3__repo__closures = /* @__PURE__ */ new Set();
5941
+ const $for_content3__repo_id__closures = /* @__PURE__ */ new Set();
5942
+ const $for_content3__repo_pr__closures = /* @__PURE__ */ new Set();
5943
+ const $for_content3__repo_worktree__closures = /* @__PURE__ */ new Set();
5946
5944
  const $for_content3__repoFiles__closures = /* @__PURE__ */ new Set();
5947
- const repoFiles = filesByRepo.get(repo.worktree) || [];
5945
+ const repoFiles = filesByRepo.get(repo.id) || [];
5948
5946
  _if(() => {
5949
5947
  if (repoFiles.length) {
5950
5948
  const $scope10_id = _scope_id();
5951
- _html("<section class=repo-section>");
5949
+ _html(`<section class=repo-section${repo.pr ? _attr("data-pr", `${repo.worktree}/${repo.pr.number}`) : ""}>`);
5952
5950
  _if(() => {
5953
5951
  if (repoList.length > 1) {
5954
5952
  const $scope23_id = _scope_id();
5955
- _html(`<button class=${collapsedRepos.has(repo.worktree) ? "\"repo-sep closed\"" : "repo-sep"}><span class=tree-chev>${collapsedRepos.has(repo.worktree) ? "▸" : "▾"}${_el_resume($scope23_id, "b")}</span><span class=mono>${_escape(repo.worktree)}${_el_resume($scope23_id, "c")}</span><span class=dim>${_escape(repo.full_name)}${_el_resume($scope23_id, "d")}</span><span class=chip>vs <!>${_escape(repo.base)}${_el_resume($scope23_id, "e")}</span></button>${_el_resume($scope23_id, "a")}`);
5956
- _script($scope23_id, "d34");
5957
- _subscribe($for_content3__repo_base__closures, _subscribe($for_content3__repo_full_name__closures, _subscribe($for_content3__repo_worktree__closures, _subscribe($collapsedRepos__closures, writeScope($scope23_id, { Ccb: 1 })))));
5953
+ _html(`<button class=${collapsedRepos.has(repo.id) ? "\"repo-sep closed\"" : "repo-sep"}><span class=tree-chev>${collapsedRepos.has(repo.id) ? "▸" : "▾"}${_el_resume($scope23_id, "b")}</span><span class=mono>${_escape(repo.worktree)}${_el_resume($scope23_id, "c")}</span>`);
5954
+ _if(() => {
5955
+ if (repo.pr) {
5956
+ const $scope32_id = _scope_id();
5957
+ _html(`<a class="chip pr"${_attr("href", `${repo.pr.url}/files`)} target=_blank rel=noopener${_attr("title", `${repo.pr.title} — ${repo.pr.headRefName} ${repo.head} vs ${repo.base} ${repo.baseSha}`)}>#<!>${_escape(repo.pr.number)}${_el_resume($scope32_id, "b")}</a>${_el_resume($scope32_id, "a")}<span class=dim>${_escape(repo.pr.title)}${_el_resume($scope32_id, "c")}</span>`);
5958
+ _script($scope32_id, "d31");
5959
+ _subscribe($for_content3__repo_baseSha__closures, _subscribe($for_content3__repo_base__closures, _subscribe($for_content3__repo_head__closures, _subscribe($for_content3__repo_pr_headRefName__closures, _subscribe($for_content3__repo_pr_title__closures, _subscribe($for_content3__repo_pr_url__closures, _subscribe($for_content3__repo_pr_number__closures, writeScope($scope32_id, {
5960
+ Ca1: 2,
5961
+ Ca2: 2,
5962
+ Ca3: 2
5963
+ }))))))));
5964
+ return 0;
5965
+ } else {
5966
+ const $scope33_id = _scope_id();
5967
+ _html(`<span class=dim>${_escape(repo.full_name)}${_el_resume($scope33_id, "a")}</span>`);
5968
+ _subscribe($for_content3__repo_full_name__closures, writeScope($scope33_id, {}));
5969
+ return 1;
5970
+ }
5971
+ }, $scope23_id, "d");
5972
+ _html(`<span class="chip lane"${_attr("title", `${repo.pr ? "PR head" : "checked out"} ${repo.pr ? repo.pr.headRefName : repo.branch} ${repo.head}`)}>on <!>${repo.pr ? _escape(repo.pr.headRefName) : _escape(repo.branch)}${_el_resume($scope23_id, "f")}</span>${_el_resume($scope23_id, "e")}<span class=chip${_attr("title", `${repo.base} ${repo.baseSha}`)}>vs <!>${_escape(repo.base)}${_el_resume($scope23_id, "h")}</span>${_el_resume($scope23_id, "g")}`);
5973
+ _if(() => {
5974
+ if (localDiffers(repo)) {
5975
+ const $scope34_id = _scope_id();
5976
+ _html(`<span class=chip${_attr("title", `checked out ${repo.branch} ${repo.localHead}`)}>local <!>${_escape(repo.branch)}${_el_resume($scope34_id, "b")}</span>${_el_resume($scope34_id, "a")}`);
5977
+ _subscribe($for_content3__repo_localHead__closures, _subscribe($for_content3__repo_branch__closures, writeScope($scope34_id, { Ca5: 2 })));
5978
+ return 0;
5979
+ }
5980
+ }, $scope23_id, "i", 1, 1, 1, 0, 1);
5981
+ _html(`</button>${_el_resume($scope23_id, "a")}`);
5982
+ _script($scope23_id, "d32");
5983
+ _subscribe($for_content3__repo_branch__closures, _subscribe($for_content3__repo_baseSha__closures, _subscribe($for_content3__repo_base__closures, _subscribe($for_content3__repo_head__closures, _subscribe($for_content3__repo_worktree__closures, _subscribe($for_content3__repo_pr__closures, _subscribe($for_content3__repo_id__closures, _subscribe($for_content3__repo__closures, _subscribe($collapsedRepos__closures, writeScope($scope23_id, { Cc8: 1 }))))))))));
5958
5984
  return 0;
5985
+ } else {
5986
+ const $scope26_id = _scope_id();
5987
+ _html("<p class=repo-meta>");
5988
+ _if(() => {
5989
+ if (repo.pr) {
5990
+ const $scope35_id = _scope_id();
5991
+ _html(`<a class="chip pr"${_attr("href", `${repo.pr.url}/files`)} target=_blank rel=noopener${_attr("title", `${repo.pr.title} — ${repo.pr.headRefName} ${repo.head} vs ${repo.base} ${repo.baseSha}`)}>#<!>${_escape(repo.pr.number)}${_el_resume($scope35_id, "b")}</a>${_el_resume($scope35_id, "a")}<span class=dim>${_escape(repo.pr.title)}${_el_resume($scope35_id, "c")}</span>`);
5992
+ _subscribe($for_content3__repo_baseSha__closures, _subscribe($for_content3__repo_base__closures, _subscribe($for_content3__repo_head__closures, _subscribe($for_content3__repo_pr_headRefName__closures, _subscribe($for_content3__repo_pr_title__closures, _subscribe($for_content3__repo_pr_url__closures, _subscribe($for_content3__repo_pr_number__closures, writeScope($scope35_id, {
5993
+ Cx: 1,
5994
+ Cy: 1,
5995
+ Cz: 1,
5996
+ Ca0: 1,
5997
+ Ca1: 3,
5998
+ Ca2: 3,
5999
+ Ca3: 3
6000
+ }))))))));
6001
+ return 0;
6002
+ }
6003
+ }, $scope26_id, "a");
6004
+ _html(`<span class="chip lane"${_attr("title", `${repo.pr ? "PR head" : "checked out"} ${repo.pr ? repo.pr.headRefName : repo.branch} ${repo.head}`)}>on <!>${repo.pr ? _escape(repo.pr.headRefName) : _escape(repo.branch)}${_el_resume($scope26_id, "c")}</span>${_el_resume($scope26_id, "b")}<span class=chip${_attr("title", `${repo.base} ${repo.baseSha}`)}>vs <!>${_escape(repo.base)}${_el_resume($scope26_id, "e")}</span>${_el_resume($scope26_id, "d")}`);
6005
+ _if(() => {
6006
+ if (localDiffers(repo)) {
6007
+ const $scope36_id = _scope_id();
6008
+ _html(`<span class=chip${_attr("title", `checked out ${repo.branch} ${repo.localHead}`)}>local <!>${_escape(repo.branch)}${_el_resume($scope36_id, "b")}</span>${_el_resume($scope36_id, "a")}`);
6009
+ _subscribe($for_content3__repo_localHead__closures, _subscribe($for_content3__repo_branch__closures, writeScope($scope36_id, {
6010
+ Ca5: 3,
6011
+ Ca6: 1
6012
+ })));
6013
+ return 0;
6014
+ }
6015
+ }, $scope26_id, "f", 1, 1, 1, 0, 1);
6016
+ _html("</p>");
6017
+ _subscribe($for_content3__repo_branch__closures, _subscribe($for_content3__repo_baseSha__closures, _subscribe($for_content3__repo_base__closures, _subscribe($for_content3__repo_head__closures, _subscribe($for_content3__repo_pr__closures, _subscribe($for_content3__repo__closures, writeScope($scope26_id, {
6018
+ Ct: 1,
6019
+ Cv: 1,
6020
+ Ca1: 1,
6021
+ Ca2: 1,
6022
+ Ca3: 1,
6023
+ Ca5: 1
6024
+ })))))));
6025
+ return 1;
5959
6026
  }
5960
- }, $scope10_id, "a", 1, 1, 1, 0, 1);
6027
+ }, $scope10_id, "b", 1, 1, 1, 0, 1);
5961
6028
  _if(() => {
5962
- if (!(repoList.length > 1 && collapsedRepos.has(repo.worktree))) {
6029
+ if (!(repoList.length > 1 && collapsedRepos.has(repo.id))) {
5963
6030
  const $scope11_id = _scope_id();
5964
6031
  _html("<div class=files>");
5965
6032
  _for_of(repoFiles, (file) => {
@@ -5972,30 +6039,30 @@ var diff_view_default = _template("d", (input) => {
5972
6039
  _html(`<details${_attr_details_or_dialog_open($scope12_id, "a", expanded)} class=file${_attr("data-key", file.key)}><summary><span${_attr_class(["status", file.status])}>${_escape(file.status)}${_el_resume($scope12_id, "c")}</span>${_el_resume($scope12_id, "b")}<span class=path${_attr("title", file.path)}><bdi>${_escape(file.path)}${_el_resume($scope12_id, "e")}</bdi></span>${_el_resume($scope12_id, "d")}`);
5973
6040
  _if(() => {
5974
6041
  if (fileComments.length) {
5975
- const $scope31_id = _scope_id();
5976
- _html(`<span class=chip>💬 <!>${_escape(fileComments.length)}${_el_resume($scope31_id, "a")}</span>`);
5977
- writeScope($scope31_id, {});
6042
+ const $scope37_id = _scope_id();
6043
+ _html(`<span class=chip>💬 <!>${_escape(fileComments.length)}${_el_resume($scope37_id, "a")}</span>`);
6044
+ writeScope($scope37_id, {});
5978
6045
  return 0;
5979
6046
  }
5980
6047
  }, $scope12_id, "f", 1, 1, 1, 0, 1);
5981
6048
  _html(`<label class=viewed><input${_attr_input_checked($scope12_id, "h", viewed.has(file.key), _resume(function() {
5982
6049
  toggleViewed(file.key);
5983
- }, "d3", $scope12_id))} type=checkbox>${_el_resume($scope12_id, "h")}<span class=dim>viewed</span></label>${_el_resume($scope12_id, "g")}</summary>`);
6050
+ }, "d2", $scope12_id))} type=checkbox>${_el_resume($scope12_id, "h")}<span class=dim>viewed</span></label>${_el_resume($scope12_id, "g")}</summary>`);
5984
6051
  _if(() => {
5985
6052
  if (file.binary) {
5986
- const $scope32_id = _scope_id();
6053
+ const $scope38_id = _scope_id();
5987
6054
  _html("<p class=\"dim diff-pad\">binary file</p>");
5988
- writeScope($scope32_id, {});
6055
+ writeScope($scope38_id, {});
5989
6056
  return 0;
5990
6057
  } else if (file.tooLarge) {
5991
- const $scope33_id = _scope_id();
6058
+ const $scope39_id = _scope_id();
5992
6059
  _html("<p class=\"dim diff-pad\">file too large to display</p>");
5993
- writeScope($scope33_id, {});
6060
+ writeScope($scope39_id, {});
5994
6061
  return 1;
5995
6062
  } else if (file.old == null && file.new == null) {
5996
- const $scope34_id = _scope_id();
6063
+ const $scope40_id = _scope_id();
5997
6064
  _html("<p class=\"dim diff-pad\">file could not be read</p>");
5998
- writeScope($scope34_id, {});
6065
+ writeScope($scope40_id, {});
5999
6066
  return 2;
6000
6067
  } else if (expanded) {
6001
6068
  const $scope15_id = _scope_id();
@@ -6003,7 +6070,7 @@ var diff_view_default = _template("d", (input) => {
6003
6070
  {
6004
6071
  const $scope24_id = _scope_id();
6005
6072
  _html(`<div class=diff-wait></div>${_el_resume($scope24_id, "a")}`);
6006
- _script($scope24_id, "d35");
6073
+ _script($scope24_id, "d33");
6007
6074
  writeScope($scope24_id, {});
6008
6075
  return 0;
6009
6076
  }
@@ -6013,7 +6080,7 @@ var diff_view_default = _template("d", (input) => {
6013
6080
  }
6014
6081
  }, $scope12_id, "i");
6015
6082
  _html(`</details>${_el_resume($scope12_id, "a")}`);
6016
- _script($scope12_id, "d36");
6083
+ _script($scope12_id, "d34");
6017
6084
  _subscribe($toggleViewed2__closures, _subscribe($commentsByFile__closures, _subscribe($viewed__closures, writeScope($scope12_id, {
6018
6085
  k: file,
6019
6086
  l: file?.key,
@@ -6027,57 +6094,59 @@ var diff_view_default = _template("d", (input) => {
6027
6094
  z: $for_content4__file__closures,
6028
6095
  a0: $for_content4__file_key__closures,
6029
6096
  a1: $for_content4__fileComments__closures,
6030
- Cc1: 1,
6031
- Cch: 1
6097
+ Cby: 1,
6098
+ Ccf: 1
6032
6099
  }))));
6033
6100
  }, "key", $scope11_id, "a", 1, 1, 1, "</div>", 1);
6034
6101
  _subscribe($for_content3__repoFiles__closures, writeScope($scope11_id, {}));
6035
6102
  return 0;
6036
6103
  }
6037
- }, $scope10_id, "b", 1, 1, 1, 0, 1);
6038
- _html("</section>");
6104
+ }, $scope10_id, "c", 1, 1, 1, 0, 1);
6105
+ _html(`</section>${_el_resume($scope10_id, "a")}`);
6039
6106
  _subscribe($repoList_length__closures, _subscribe($collapsedRepos__closures, writeScope($scope10_id, {})));
6040
6107
  return 0;
6041
6108
  }
6042
6109
  }, $scope9_id, "a", 1, 1, 1, 0, 1);
6043
6110
  writeScope($scope9_id, {
6044
- d: repo?.worktree,
6045
- f: repo?.full_name,
6046
- g: repo?.base,
6047
- h: repoFiles,
6048
- j: $for_content3__repo_worktree__closures,
6049
- k: $for_content3__repo_full_name__closures,
6050
- l: $for_content3__repo_base__closures,
6051
- m: $for_content3__repoFiles__closures
6111
+ c: repo,
6112
+ d: repo?.id,
6113
+ f: repo?.pr,
6114
+ g: repo?.worktree,
6115
+ h: repo?.pr?.number,
6116
+ i: repo?.pr?.url,
6117
+ j: repo?.pr?.title,
6118
+ k: repo?.pr?.headRefName,
6119
+ l: repo?.head,
6120
+ m: repo?.base,
6121
+ n: repo?.baseSha,
6122
+ o: repo?.full_name,
6123
+ p: repo?.branch,
6124
+ q: repo?.localHead,
6125
+ r: repoFiles,
6126
+ x: $for_content3__repo_pr_number__closures,
6127
+ y: $for_content3__repo_pr_url__closures,
6128
+ z: $for_content3__repo_pr_title__closures,
6129
+ a0: $for_content3__repo_pr_headRefName__closures,
6130
+ a1: $for_content3__repo_head__closures,
6131
+ a2: $for_content3__repo_base__closures,
6132
+ a3: $for_content3__repo_baseSha__closures,
6133
+ a4: $for_content3__repo_full_name__closures,
6134
+ a5: $for_content3__repo_branch__closures,
6135
+ a6: $for_content3__repo_localHead__closures,
6136
+ t: $for_content3__repo__closures,
6137
+ u: $for_content3__repo_id__closures,
6138
+ v: $for_content3__repo_pr__closures,
6139
+ w: $for_content3__repo_worktree__closures,
6140
+ a7: $for_content3__repoFiles__closures
6052
6141
  });
6053
- }, "worktree", $scope0_id, "y");
6054
- _html(`</div>${_el_resume($scope0_id, "t")}</div>`);
6142
+ }, "id", $scope0_id, "y");
6143
+ _html(`</div>${_el_resume($scope0_id, "q")}</div>`);
6055
6144
  _if(() => {}, $scope0_id, "z");
6056
6145
  _html("</div>");
6057
- _if(() => {
6058
- if (askBase) {
6059
- const $scope21_id = _scope_id();
6060
- const $childScope2 = _peek_scope_id();
6061
- prompt_dialog_default({
6062
- label: "Compare against ref",
6063
- placeholder: "branch, tag, or commit",
6064
- value: baseRef,
6065
- submitLabel: "Compare",
6066
- onSubmit: _resume(function(ref) {
6067
- askBase = false;
6068
- setBase(ref);
6069
- }, "d24", $scope21_id),
6070
- onCancel: _resume(function() {
6071
- askBase = false;
6072
- }, "d25", $scope21_id)
6073
- });
6074
- writeScope($scope21_id, { a: _existing_scope($childScope2) });
6075
- return 0;
6076
- }
6077
- }, $scope0_id, "a0");
6078
- _script($scope0_id, "d39");
6079
- _script($scope0_id, "d40");
6146
+ _script($scope0_id, "d37");
6147
+ _script($scope0_id, "d38");
6080
6148
  writeScope($scope0_id, {
6149
+ a2: input.focusPr,
6081
6150
  a3: input.workspaceId,
6082
6151
  a4: input.active,
6083
6152
  a5: input.sessions,
@@ -6096,49 +6165,48 @@ var diff_view_default = _template("d", (input) => {
6096
6165
  an: sending,
6097
6166
  ao: sendTarget,
6098
6167
  aq: fileFilter,
6099
- ar: baseRef,
6168
+ ar: uncommitted,
6100
6169
  at: treeOpen,
6101
- aw: collapsedDirs,
6102
- ax: collapsedRepos,
6103
- ay: toggleRepo,
6104
- b0: nearBox.cbs,
6105
- b1: nearBox.io,
6106
- b3: observeNear,
6107
- b4: load,
6108
- b5: loadReview,
6109
- b7: repoList,
6110
- b8: repoList.length,
6111
- b9: allFiles,
6112
- ba: allFiles?.length,
6113
- bb: fileMatch,
6114
- bd: shownFiles,
6115
- bh: filesByRepo,
6116
- bj: commentsByFile,
6117
- bk: defaultBranches,
6118
- bm: sendPick,
6119
- bn: setBase,
6120
- bp: toggleViewed,
6121
- bq: saveComment,
6122
- br: removeComment,
6123
- bs: clearComments,
6124
- bv: copyMarkdown,
6125
- bw: sendTo,
6126
- bx: jumpTo,
6127
- ca: $collapsedDirs__closures,
6128
- c1: $viewed__closures,
6129
- ch: $commentsByFile__closures,
6130
- cg: $filesByRepo__closures,
6131
- bz: $si__input_sessions_0_title && $input_sessions_0_title__closures,
6132
- c0: $si__input_sessions_0_agent && $input_sessions_0_agent__closures,
6133
- c6: $sending__closures,
6134
- by: $si__input_sessions && $input_sessions__closures,
6135
- c7: $sendTarget__closures,
6136
- cb: $collapsedRepos__closures,
6137
- c3: $draft__closures,
6138
- ck: $saveComment2__closures,
6139
- cl: $removeComment2__closures,
6140
- cj: $toggleViewed2__closures,
6141
- cd: $repoList_length__closures
6170
+ av: collapsedDirs,
6171
+ aw: collapsedRepos,
6172
+ ax: toggleRepo,
6173
+ az: nearBox.cbs,
6174
+ b0: nearBox.io,
6175
+ b2: observeNear,
6176
+ b3: load,
6177
+ b4: loadReview,
6178
+ b6: repoList,
6179
+ b7: repoList.length,
6180
+ b8: allFiles,
6181
+ b9: allFiles?.length,
6182
+ ba: fileMatch,
6183
+ bc: shownFiles,
6184
+ bg: filesByRepo,
6185
+ bi: commentsByFile,
6186
+ bj: sendPick,
6187
+ bk: localDiffers,
6188
+ bm: toggleViewed,
6189
+ bn: saveComment,
6190
+ bo: removeComment,
6191
+ bp: clearComments,
6192
+ bs: copyMarkdown,
6193
+ bt: sendTo,
6194
+ bu: jumpTo,
6195
+ c7: $collapsedDirs__closures,
6196
+ by: $viewed__closures,
6197
+ cf: $commentsByFile__closures,
6198
+ ce: $filesByRepo__closures,
6199
+ bw: $si__input_sessions_0_title && $input_sessions_0_title__closures,
6200
+ bx: $si__input_sessions_0_agent && $input_sessions_0_agent__closures,
6201
+ c3: $sending__closures,
6202
+ bv: $si__input_sessions && $input_sessions__closures,
6203
+ c4: $sendTarget__closures,
6204
+ c8: $collapsedRepos__closures,
6205
+ c0: $draft__closures,
6206
+ ci: $saveComment2__closures,
6207
+ cj: $removeComment2__closures,
6208
+ ch: $toggleViewed2__closures,
6209
+ cb: $repoList_length__closures
6142
6210
  });
6143
6211
  _resume_branch($scope0_id);
6144
6212
  });
@@ -6513,6 +6581,71 @@ var usage_stats_default = _template("n", (input) => {
6513
6581
  _resume_branch($scope0_id);
6514
6582
  });
6515
6583
  //#endregion
6584
+ //#region src/tags/prompt-dialog.marko
6585
+ var prompt_dialog_default = _template("i", (input) => {
6586
+ const $scope0_reason = _scope_reason(), $sg__input_label = _serialize_guard($scope0_reason, 1), $sg__input_onCancel = _serialize_guard($scope0_reason, 2), $si__input_submitLabel = _serialize_if($scope0_reason, 3), $si__input_label = _serialize_if($scope0_reason, 1), $si__input_placeholder = _serialize_if($scope0_reason, 4);
6587
+ const $scope0_id = _scope_id();
6588
+ const $modalview_content__subscribers = /* @__PURE__ */ new Set();
6589
+ const $input_submitLabel__closures = /* @__PURE__ */ new Set();
6590
+ const $value__closures = /* @__PURE__ */ new Set();
6591
+ const $input_label__closures = /* @__PURE__ */ new Set();
6592
+ const $input_placeholder__closures = /* @__PURE__ */ new Set();
6593
+ let value = input.value || "";
6594
+ const submit = _resume(() => {
6595
+ const v = value.trim();
6596
+ if (v) input.onSubmit(v);
6597
+ }, "i1", $scope0_id);
6598
+ _set_serialize_reason({
6599
+ 0: $sg__input_onCancel,
6600
+ 1: $sg__input_label,
6601
+ 2: $sg__input_onCancel
6602
+ });
6603
+ const $childScope = _peek_scope_id();
6604
+ modal_view_default({
6605
+ label: input.label,
6606
+ class: "prompt-dlg",
6607
+ onClose: _resume(function() {
6608
+ input.onCancel();
6609
+ }, "i2", $scope0_id),
6610
+ footer: attrTag({ content: _content_resume("i5", () => {
6611
+ _scope_reason();
6612
+ const $scope1_id = _scope_id();
6613
+ _html(`<button class="btn primary"${_attr("disabled", !value.trim())}>${_escape(input.submitLabel || "OK")}${_el_resume($scope1_id, "b", _serialize_guard($scope0_reason, 3))}</button>${_el_resume($scope1_id, "a")}<div class=spacer></div><button class=btn>Cancel</button>${_el_resume($scope1_id, "c")}`);
6614
+ _script($scope1_id, "i3");
6615
+ _subscribe($value__closures, _subscribe($si__input_submitLabel && $input_submitLabel__closures, writeScope($scope1_id, { _: _scope_with_id($scope0_id) })));
6616
+ _resume_branch($scope1_id);
6617
+ }, $scope0_id) }),
6618
+ content: _content("i6", () => {
6619
+ _scope_reason();
6620
+ const $scope2_id = _scope_id();
6621
+ _html(`<h2 class=prompt-title>${_escape(input.label)}${_el_resume($scope2_id, "a", $sg__input_label)}</h2><input${_attr_input_value($scope2_id, "b", value, _resume((_new_value) => {
6622
+ value = _new_value;
6623
+ }, "i0", $scope2_id))}${_attr("placeholder", input.placeholder)}>${_el_resume($scope2_id, "b")}`);
6624
+ _script($scope2_id, "i4");
6625
+ _subscribe($value__closures, _subscribe($si__input_placeholder && $input_placeholder__closures, _subscribe($si__input_label && $input_label__closures, _subscribe($modalview_content__subscribers, writeScope($scope2_id, {
6626
+ _: _scope_with_id($scope0_id),
6627
+ Cp: 1
6628
+ })))));
6629
+ _resume_branch($scope2_id);
6630
+ })
6631
+ });
6632
+ _script($scope0_id, "i7");
6633
+ writeScope($scope0_id, {
6634
+ e: input.onSubmit,
6635
+ g: input.onCancel,
6636
+ h: input.submitLabel,
6637
+ j: value,
6638
+ l: submit,
6639
+ B2: $modalview_content__subscribers,
6640
+ n: $si__input_submitLabel && $input_submitLabel__closures,
6641
+ p: $value__closures,
6642
+ m: $si__input_label && $input_label__closures,
6643
+ o: $si__input_placeholder && $input_placeholder__closures,
6644
+ a: _serialize_if($scope0_reason, 0) && _existing_scope($childScope)
6645
+ });
6646
+ _resume_branch($scope0_id);
6647
+ });
6648
+ //#endregion
6516
6649
  //#region src/tags/work-view.marko
6517
6650
  var api$1;
6518
6651
  var work_view_default = _template("o", (input) => {
@@ -6532,6 +6665,7 @@ var work_view_default = _template("o", (input) => {
6532
6665
  let sessions = null;
6533
6666
  let tab = null;
6534
6667
  let view = "term";
6668
+ let focusPr = "";
6535
6669
  let drawerOpen = false;
6536
6670
  let usageOpen = false;
6537
6671
  let error = null;
@@ -6648,7 +6782,7 @@ var work_view_default = _template("o", (input) => {
6648
6782
  13: _serialize_guard($scope0_reason, 6),
6649
6783
  14: $sg__input_agents,
6650
6784
  15: 1,
6651
- 16: 1
6785
+ 17: 1
6652
6786
  });
6653
6787
  const $childScope = _peek_scope_id();
6654
6788
  side_nav_default({
@@ -6688,14 +6822,17 @@ var work_view_default = _template("o", (input) => {
6688
6822
  }, "o13", $scope0_id),
6689
6823
  onRenameSession: _resume(function(s) {
6690
6824
  promptRename(s);
6691
- }, "o14", $scope0_id)
6825
+ }, "o14", $scope0_id),
6826
+ onReviewOpened: _resume(function(pr) {
6827
+ focusPr = pr || "";
6828
+ }, "o15", $scope0_id)
6692
6829
  });
6693
6830
  _html("<div class=main-col>");
6694
6831
  _if(() => {
6695
6832
  if (error) {
6696
6833
  const $scope5_id = _scope_id();
6697
6834
  _html(`<p class=toast-error>${_escape(error)}${_el_resume($scope5_id, "a")}<button title=Dismiss>×</button>${_el_resume($scope5_id, "b")}</p>`);
6698
- _script($scope5_id, "o28");
6835
+ _script($scope5_id, "o29");
6699
6836
  writeScope($scope5_id, {});
6700
6837
  return 0;
6701
6838
  }
@@ -6708,13 +6845,14 @@ var work_view_default = _template("o", (input) => {
6708
6845
  workspaceId: ws.id,
6709
6846
  sessions: shownSessions || [],
6710
6847
  active: input.active && view === "diff",
6848
+ focusPr,
6711
6849
  onSent: _resume(function(sessionId) {
6712
6850
  tab = sessionId;
6713
6851
  view = "term";
6714
- }, "o15", $scope0_id),
6852
+ }, "o16", $scope0_id),
6715
6853
  onExit: _resume(function() {
6716
6854
  view = "term";
6717
- }, "o16", $scope0_id)
6855
+ }, "o17", $scope0_id)
6718
6856
  });
6719
6857
  _show_end($scope0_id, "f", $show);
6720
6858
  _html(`<div class=${view === "diff" ? "\"term-area as-bar\"" : "term-area"}>`);
@@ -6753,31 +6891,32 @@ var work_view_default = _template("o", (input) => {
6753
6891
  sessions: shownSessions,
6754
6892
  onNewSession: _resume(function(agent) {
6755
6893
  newSession(ws.id, agent);
6756
- }, "o17", $scope2_id),
6894
+ }, "o18", $scope2_id),
6757
6895
  onOpenTab: _resume(function(id) {
6758
6896
  tab = id;
6759
- }, "o18", $scope2_id),
6897
+ }, "o19", $scope2_id),
6760
6898
  onCloseSession: _resume(function(id) {
6761
6899
  closeSession(id);
6762
- }, "o19", $scope2_id),
6900
+ }, "o20", $scope2_id),
6763
6901
  onRenameSession: _resume(function(id) {
6764
6902
  const target = (shownSessions || []).find((x) => x.id === id);
6765
6903
  if (target) renaming = target;
6766
- }, "o20", $scope2_id),
6904
+ }, "o21", $scope2_id),
6767
6905
  active: input.active && view === "term" && s.id === curTab,
6768
6906
  mode: view,
6769
6907
  onToggleDiff: _resume(function() {
6908
+ focusPr = "";
6770
6909
  view = "diff";
6771
- }, "o21", $scope2_id),
6910
+ }, "o22", $scope2_id),
6772
6911
  onExitReview: _resume(function() {
6773
6912
  view = "term";
6774
- }, "o22", $scope2_id),
6913
+ }, "o23", $scope2_id),
6775
6914
  onOpenNav: _resume(function() {
6776
6915
  drawerOpen = true;
6777
- }, "o23", $scope2_id),
6916
+ }, "o24", $scope2_id),
6778
6917
  onManage: _resume(function() {
6779
6918
  input.onManage?.(ws.id);
6780
- }, "o24", $scope2_id)
6919
+ }, "o25", $scope2_id)
6781
6920
  });
6782
6921
  _show_end($scope2_id, "d", $show2);
6783
6922
  _subscribe($newSession2__closures, _subscribe($closeSession2__closures, _subscribe($curTab__closures, _subscribe($shownSessions__closures, _subscribe($si__input_workspace && $ws_name__closures, _subscribe($si__input_workspace && $ws_id__closures, _subscribe($view__closures, _subscribe($si__input_agents && $input_agents__closures, _subscribe($si__input_active && $input_active__closures, writeScope($scope2_id, {
@@ -6789,7 +6928,7 @@ var work_view_default = _template("o", (input) => {
6789
6928
  if (!shownSessions.length) {
6790
6929
  const $scope3_id = _scope_id();
6791
6930
  _html(`<div class=empty-sess><p class=dim>No sessions in this workspace.</p><button class="btn primary"${_attr("disabled", spawning)}>${spawning ? "Starting…" : `Start ${_escape(`${input.preferred || "claude"}`)}`}${_el_resume($scope3_id, "b")}</button>${_el_resume($scope3_id, "a")}</div>`);
6792
- _script($scope3_id, "o29");
6931
+ _script($scope3_id, "o30");
6793
6932
  _subscribe($spawning__closures, _subscribe($si__input_preferred && $input_preferred__closures, writeScope($scope3_id, {})));
6794
6933
  return 0;
6795
6934
  }
@@ -6809,7 +6948,7 @@ var work_view_default = _template("o", (input) => {
6809
6948
  const $scope4_id = _scope_id();
6810
6949
  usage_stats_default({ onClose: _resume(function() {
6811
6950
  usageOpen = false;
6812
- }, "o25", $scope4_id) });
6951
+ }, "o26", $scope4_id) });
6813
6952
  writeScope($scope4_id, {});
6814
6953
  return 0;
6815
6954
  }
@@ -6825,21 +6964,21 @@ var work_view_default = _template("o", (input) => {
6825
6964
  onSubmit: _resume(function(title) {
6826
6965
  renameSession(renaming.id, title);
6827
6966
  renaming = null;
6828
- }, "o26", $scope6_id),
6967
+ }, "o27", $scope6_id),
6829
6968
  onCancel: _resume(function() {
6830
6969
  renaming = null;
6831
- }, "o27", $scope6_id)
6970
+ }, "o28", $scope6_id)
6832
6971
  });
6833
6972
  writeScope($scope6_id, { a: _existing_scope($childScope4) });
6834
6973
  return 0;
6835
6974
  }
6836
6975
  }, $scope0_id, "i");
6837
6976
  _html("</div>");
6838
- _script($scope0_id, "o30");
6839
6977
  _script($scope0_id, "o31");
6840
6978
  _script($scope0_id, "o32");
6841
6979
  _script($scope0_id, "o33");
6842
6980
  _script($scope0_id, "o34");
6981
+ _script($scope0_id, "o35");
6843
6982
  writeScope($scope0_id, {
6844
6983
  m: input.allStatus,
6845
6984
  n: input.active,
@@ -6850,40 +6989,40 @@ var work_view_default = _template("o", (input) => {
6850
6989
  v: sessions,
6851
6990
  w: tab,
6852
6991
  y: view,
6853
- a0: drawerOpen,
6854
- a2: error,
6855
- a3: spawning,
6856
- a5: renaming?.title,
6857
- a6: renaming?.agent,
6858
- a7: renaming?.id,
6859
- a9: ws?.id,
6860
- ae: ws?.status,
6861
- af: ws?.name,
6862
- ag: shownSessions,
6863
- ai: shownSessions?.length,
6864
- aj: curTab,
6865
- al: createSession,
6866
- ao: loadMem.wanted,
6867
- aq: loadMem.run,
6868
- ar: loadBody,
6869
- at: loadSessions,
6870
- av: closeSession,
6871
- aw: renameSession,
6872
- ax: newSession,
6873
- ay: promptRename,
6992
+ a1: drawerOpen,
6993
+ a3: error,
6994
+ a4: spawning,
6995
+ a6: renaming?.title,
6996
+ a7: renaming?.agent,
6997
+ a8: renaming?.id,
6998
+ aa: ws?.id,
6999
+ af: ws?.status,
7000
+ ag: ws?.name,
7001
+ ah: shownSessions,
7002
+ aj: shownSessions?.length,
7003
+ ak: curTab,
7004
+ am: createSession,
7005
+ ap: loadMem.wanted,
7006
+ ar: loadMem.run,
7007
+ as: loadBody,
7008
+ au: loadSessions,
7009
+ aw: closeSession,
7010
+ ax: renameSession,
7011
+ ay: newSession,
7012
+ az: promptRename,
6874
7013
  a: _existing_scope($childScope),
6875
7014
  d: _existing_scope($childScope2),
6876
- az: $si__input_active && $input_active__closures,
6877
- b1: $si__input_agents && $input_agents__closures,
6878
- b2: $view__closures,
6879
- b8: $si__input_workspace && $ws_id__closures,
6880
- b9: $si__input_workspace && $ws_name__closures,
6881
- ba: $shownSessions__closures,
6882
- bc: $curTab__closures,
6883
- bd: $closeSession2__closures,
6884
- bf: $newSession2__closures,
6885
- b0: $si__input_preferred && $input_preferred__closures,
6886
- b4: $spawning__closures
7015
+ b0: $si__input_active && $input_active__closures,
7016
+ b2: $si__input_agents && $input_agents__closures,
7017
+ b3: $view__closures,
7018
+ b9: $si__input_workspace && $ws_id__closures,
7019
+ ba: $si__input_workspace && $ws_name__closures,
7020
+ bb: $shownSessions__closures,
7021
+ bd: $curTab__closures,
7022
+ be: $closeSession2__closures,
7023
+ bg: $newSession2__closures,
7024
+ b1: $si__input_preferred && $input_preferred__closures,
7025
+ b5: $spawning__closures
6887
7026
  });
6888
7027
  _resume_branch($scope0_id);
6889
7028
  });
@@ -14022,6 +14161,12 @@ async function ensureBare(fullName) {
14022
14161
  async function bareDefaultBranch(bare) {
14023
14162
  return (await git(["symbolic-ref", "HEAD"], bare)).replace("refs/heads/", "");
14024
14163
  }
14164
+ /** `ref` is this workspace branch or a `-topic` lane of it. Another
14165
+ * workspace's branch can extend this prefix, so callers must also
14166
+ * subtract `otherWorkspaceBranches`. */
14167
+ function ownsBranch(ref, prefix) {
14168
+ return ref === prefix || ref.startsWith(`${prefix}-`);
14169
+ }
14025
14170
  /** Branches of every other workspace — slugs are user-chosen, so one
14026
14171
  * workspace's branch can be a strict prefix of another's; prefix matching
14027
14172
  * (cleanup, PR attribution) must exclude these. */
@@ -14032,13 +14177,11 @@ function otherWorkspaceBranches(id) {
14032
14177
  * minus any ref that actually belongs to another workspace whose branch
14033
14178
  * extends this one's (see otherWorkspaceBranches). */
14034
14179
  async function workspaceBranches(bare, branch, exclude) {
14035
- const out = await git([
14180
+ return (await git([
14036
14181
  "for-each-ref",
14037
14182
  "--format=%(refname:short)",
14038
14183
  "refs/heads/"
14039
- ], bare).catch(() => "");
14040
- const owns = (ref, b) => ref === b || ref.startsWith(`${b}-`);
14041
- return out.split("\n").filter((b) => owns(b, branch) && !exclude.some((x) => owns(b, x)));
14184
+ ], bare).catch(() => "")).split("\n").filter((b) => ownsBranch(b, branch) && !exclude.some((x) => ownsBranch(b, x)));
14042
14185
  }
14043
14186
  /** Directory/branch-safe prefix for a workspace name. Decomposing first keeps
14044
14187
  * accented Latin readable (é → e); a name with nothing ASCII left (another
@@ -14067,16 +14210,33 @@ function writeAgentsMd(dir, branch, repos) {
14067
14210
  Workspace branch: **\`${branch}\`** — every repo here is checked out on it, and
14068
14211
  it is the required **prefix** for every other branch you create.
14069
14212
 
14070
- - **Branches:** one PR? Stay on \`${branch}\`. Need more than one? Cut a lane per
14071
- PR \`git switch -c ${branch}-<topic> origin/<base>\` — and keep the prefix.
14072
- Tooling finds PRs by that prefix, so a branch named anything else is
14073
- invisible: never invent one.
14213
+ - **Branches:** stay on \`${branch}\` and keep pushing it — one open PR, more
14214
+ commits. After it lands, \`git fetch\` and reset \`${branch}\` to
14215
+ \`origin/<base>\` so the next change reuses this branch. Do not cut a
14216
+ \`${branch}-<topic>\` just to start the next piece of work.
14217
+ A topic lane is only for when \`${branch}\` must stay unmerged (a larger
14218
+ change) and you need a second PR on top of it. Then push every repo's
14219
+ workspace branch first, cut from the parent —
14220
+ \`git switch -c ${branch}-<topic> origin/${branch}\` — and keep the prefix.
14221
+ Only two levels: never \`${branch}-a-b\`. Tooling finds PRs by that prefix,
14222
+ so a branch named anything else is invisible: never invent one.
14074
14223
  - **Repos:**
14075
14224
  ${repos.map((r) => `- \`${r.worktree}/\` — ${r.full_name} (base: \`${r.base_branch}\`)`).join("\n")}
14225
+ - **No \`git stash\`:** every workspace using a repo shares one stash stack, so
14226
+ a pop can take another workspace's work. Commit as you go — uncommitted
14227
+ work isn't part of the review.
14076
14228
  - **Push:** \`git push -u origin HEAD\` in each repo, for review on GitHub.
14077
14229
  Never commit on a base branch.
14230
+ - **Rebase after merge:** when \`${branch}\` is still open and \`origin/<base>\`
14231
+ moves, rebase onto it — a squash-merge does not move the fork point, so the
14232
+ review keeps showing landed work until you do. On a topic lane, rebase onto
14233
+ \`${branch}\` when the parent moves; after the parent merges: \`git fetch
14234
+ --prune\`, rebase onto \`origin/<base>\`, and retarget (\`gh pr edit --base
14235
+ <base>\`).
14078
14236
  - **PRs:** the description covers only what is in the diff — a few sentences on
14079
- what changed and why. No verification logs or file-by-file detail.
14237
+ what changed and why. No verification logs or file-by-file detail. A topic
14238
+ lane's PR is \`gh pr create --base ${branch}\` — GitHub will otherwise target
14239
+ the repo default, and the local review will disagree with the files tab.
14080
14240
  `;
14081
14241
  fs.writeFileSync(path.join(dir, "AGENTS.md"), content);
14082
14242
  const claudeMd = path.join(dir, "CLAUDE.md");
@@ -15529,6 +15689,7 @@ function swr(ttlMs, load, seed) {
15529
15689
  }
15530
15690
  //#endregion
15531
15691
  //#region src/server/prs.ts
15692
+ var PR_JSON = "number,title,url,headRefName,baseRefName,headRefOid,isDraft,state,reviewDecision";
15532
15693
  var TTL_MS$3 = 12e4;
15533
15694
  /** PRs in every state for a repo, stale-while-revalidate cached. */
15534
15695
  var prs = swr(TTL_MS$3, fetchPrs);
@@ -15559,7 +15720,7 @@ var headPrs = swr(TTL_MS$3, async (key) => {
15559
15720
  "--limit",
15560
15721
  "20",
15561
15722
  "--json",
15562
- "number,title,url,headRefName,isDraft,state,reviewDecision"
15723
+ PR_JSON
15563
15724
  ]).catch(() => "[]");
15564
15725
  return JSON.parse(out);
15565
15726
  });
@@ -15582,7 +15743,7 @@ async function fetchPrs(fullName) {
15582
15743
  "--limit",
15583
15744
  "100",
15584
15745
  "--json",
15585
- "number,title,url,headRefName,isDraft,state,reviewDecision"
15746
+ PR_JSON
15586
15747
  ]).catch(() => "[]");
15587
15748
  return JSON.parse(out);
15588
15749
  }
@@ -15617,7 +15778,7 @@ async function refreshPrsBatch(fullNames, force = false) {
15617
15778
  "-f",
15618
15779
  `query=${"query {" + names.map((fullName, i) => {
15619
15780
  const [owner, name] = fullName.split("/");
15620
- return `r${i}: repository(owner: ${JSON.stringify(owner)}, name: ${JSON.stringify(name)}) { pullRequests(first: 50, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { number title url headRefName isDraft state reviewDecision } } }`;
15781
+ return `r${i}: repository(owner: ${JSON.stringify(owner)}, name: ${JSON.stringify(name)}) { pullRequests(first: 50, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { number title url headRefName baseRefName isDraft state reviewDecision headRef { oid } } } }`;
15621
15782
  }).join(" ") + "}"}`
15622
15783
  ]).catch((err) => {
15623
15784
  if (!err.stdout) throw err;
@@ -15628,7 +15789,10 @@ async function refreshPrsBatch(fullNames, force = false) {
15628
15789
  names.forEach((fullName, i) => {
15629
15790
  const repo = data[`r${i}`];
15630
15791
  if (!repo) return;
15631
- prs.set(fullName, repo.pullRequests.nodes);
15792
+ prs.set(fullName, repo.pullRequests.nodes.map((n) => ({
15793
+ ...n,
15794
+ headRefOid: n.headRefOid || n.headRef?.oid
15795
+ })));
15632
15796
  });
15633
15797
  return true;
15634
15798
  } catch (err) {
@@ -15646,211 +15810,6 @@ function withoutDismissed(workspaceId, fullName, list) {
15646
15810
  return list.filter((pr) => pr.state === "OPEN" || !dismissed.has(pr.number));
15647
15811
  }
15648
15812
  //#endregion
15649
- //#region src/server/diff.ts
15650
- var MAX_FILE_BYTES = 5e5;
15651
- /** Diffs compare against remote-tracking refs, which only move when this
15652
- * machine fetches or pushes — a PR merged on GitHub leaves them stale and
15653
- * the diff shows already-merged work until the next fetch. Opening the
15654
- * diff freshens the base refs itself (throttled per repo; each ref
15655
- * fetched independently since one missing ref fails a combined fetch,
15656
- * and capped so a slow remote can't hang the view — stale refs then
15657
- * self-correct on the next open). */
15658
- var FETCH_TTL_MS = 3e4;
15659
- var FETCH_WAIT_MS = 8e3;
15660
- var lastFetch = /* @__PURE__ */ new Map();
15661
- async function freshenBaseRefs(dir, fullName, refs) {
15662
- const now = Date.now();
15663
- if (now - (lastFetch.get(fullName) ?? 0) < FETCH_TTL_MS) return;
15664
- lastFetch.set(fullName, now);
15665
- const fetches = [...new Set(refs)].map((ref) => git([
15666
- "fetch",
15667
- "--quiet",
15668
- "--no-tags",
15669
- "origin",
15670
- ref
15671
- ], dir).catch(() => {}));
15672
- let timer;
15673
- await Promise.race([Promise.all(fetches), new Promise((resolve) => {
15674
- timer = setTimeout(resolve, FETCH_WAIT_MS);
15675
- })]);
15676
- clearTimeout(timer);
15677
- }
15678
- /** The ref the working tree is compared against by default, best first:
15679
- * the workspace branch's remote (a lane worktree then shows its whole
15680
- * delta vs the pushed workspace branch, and a worktree on the workspace
15681
- * branch shows just what isn't pushed), the current branch's own remote,
15682
- * then the configured base branch. An explicit `baseRef` (the UI's
15683
- * "vs …" picker) overrides all. */
15684
- async function resolveBase(dir, wsBranch, baseBranch, baseRef) {
15685
- const candidates = [];
15686
- if (baseRef) candidates.push([`origin/${baseRef}`, baseRef], [baseRef, baseRef]);
15687
- else {
15688
- candidates.push([`origin/${wsBranch}`, `origin/${wsBranch}`]);
15689
- const branch = await git([
15690
- "rev-parse",
15691
- "--abbrev-ref",
15692
- "HEAD"
15693
- ], dir).catch(() => "HEAD");
15694
- if (branch !== "HEAD" && branch !== wsBranch) candidates.push([`origin/${branch}`, `origin/${branch}`]);
15695
- candidates.push([`origin/${baseBranch}`, baseBranch]);
15696
- }
15697
- for (const [ref, label] of candidates) try {
15698
- return {
15699
- mergeBase: await git([
15700
- "merge-base",
15701
- ref,
15702
- "HEAD"
15703
- ], dir),
15704
- label
15705
- };
15706
- } catch {}
15707
- return {
15708
- mergeBase: "HEAD",
15709
- label: baseRef || baseBranch,
15710
- missing: true
15711
- };
15712
- }
15713
- /** Changed files per repo: merge-base of a base ref vs the working tree.
15714
- * `baseRef` overrides each repo's default base (see resolveBase). */
15715
- async function workspaceDiff(ws, baseRef) {
15716
- return Promise.all(ws.repos.map(async (repo) => {
15717
- const dir = path.join(ws.path, repo.worktree);
15718
- const branch = await git([
15719
- "rev-parse",
15720
- "--abbrev-ref",
15721
- "HEAD"
15722
- ], dir).catch(() => "HEAD");
15723
- await freshenBaseRefs(dir, repo.full_name, [
15724
- ws.branch,
15725
- ...branch !== "HEAD" ? [branch] : [],
15726
- ...baseRef ? [baseRef] : [repo.base_branch]
15727
- ]);
15728
- const { mergeBase, label, missing } = await resolveBase(dir, ws.branch, repo.base_branch, baseRef);
15729
- const [nameStatus, untracked] = await Promise.all([git([
15730
- "diff",
15731
- "--name-status",
15732
- "-z",
15733
- "--no-renames",
15734
- mergeBase
15735
- ], dir).catch(() => ""), git([
15736
- "ls-files",
15737
- "--others",
15738
- "--exclude-standard",
15739
- "-z"
15740
- ], dir).catch(() => "")]);
15741
- const entries = [];
15742
- const parts = nameStatus.split("\0").filter(Boolean);
15743
- for (let i = 0; i + 1 < parts.length; i += 2) entries.push({
15744
- path: parts[i + 1],
15745
- status: parts[i]
15746
- });
15747
- for (const file of untracked.split("\0").filter(Boolean)) entries.push({
15748
- path: file,
15749
- status: "A"
15750
- });
15751
- const olds = await batchOldContents(dir, mergeBase, entries.filter((e) => e.status !== "A").map((e) => e.path));
15752
- const files = await Promise.all(entries.map((e) => loadFile(dir, e.path, e.status, olds.get(e.path))));
15753
- files.sort((a, b) => a.path.localeCompare(b.path));
15754
- return {
15755
- worktree: repo.worktree,
15756
- full_name: repo.full_name,
15757
- base: label,
15758
- default_branch: repo.default_branch,
15759
- ...missing && { baseMissing: true },
15760
- files
15761
- };
15762
- }));
15763
- }
15764
- /** Count of changed files vs the default base's merge-base (incl.
15765
- * untracked) — same notion of "changed" as the diff view's default. */
15766
- async function changedFileCount(dir, wsBranch, baseBranch) {
15767
- const { mergeBase, missing } = await resolveBase(dir, wsBranch, baseBranch);
15768
- if (missing) return 0;
15769
- const [changed, untracked] = await Promise.all([git([
15770
- "diff",
15771
- "--name-only",
15772
- "-z",
15773
- "--no-renames",
15774
- mergeBase
15775
- ], dir).catch(() => ""), git([
15776
- "ls-files",
15777
- "--others",
15778
- "--exclude-standard",
15779
- "-z"
15780
- ], dir).catch(() => "")]);
15781
- return changed.split("\0").filter(Boolean).length + untracked.split("\0").filter(Boolean).length;
15782
- }
15783
- /** Base-side contents for many paths through a single `git cat-file --batch`
15784
- * process (instead of one `git show` per file). Blobs over MAX_FILE_BYTES
15785
- * are skipped and mapped to "tooLarge". */
15786
- async function batchOldContents(dir, mergeBase, paths) {
15787
- const result = /* @__PURE__ */ new Map();
15788
- if (!paths.length) return result;
15789
- await new Promise((resolve) => {
15790
- const proc = spawn("git", ["cat-file", "--batch"], { cwd: dir });
15791
- let buf = Buffer.alloc(0);
15792
- let index = 0;
15793
- let need = -1;
15794
- const parse = () => {
15795
- for (;;) if (need >= 0) {
15796
- if (buf.length < need + 1) return;
15797
- const content = buf.subarray(0, need);
15798
- buf = buf.subarray(need + 1);
15799
- need = -1;
15800
- const p = paths[index++];
15801
- if (content.length > MAX_FILE_BYTES) result.set(p, "tooLarge");
15802
- else result.set(p, content.toString("utf8"));
15803
- } else {
15804
- const nl = buf.indexOf(10);
15805
- if (nl < 0) return;
15806
- const header = buf.subarray(0, nl).toString();
15807
- buf = buf.subarray(nl + 1);
15808
- if (header.endsWith(" missing")) {
15809
- index++;
15810
- continue;
15811
- }
15812
- need = Number(header.split(" ")[2]) || 0;
15813
- }
15814
- };
15815
- proc.stdout.on("data", (chunk) => {
15816
- buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
15817
- parse();
15818
- });
15819
- proc.on("close", () => resolve());
15820
- proc.on("error", () => resolve());
15821
- proc.stdin.on("error", () => {});
15822
- proc.stdin.end(paths.map((p) => `${mergeBase}:${p}`).join("\n") + "\n");
15823
- });
15824
- return result;
15825
- }
15826
- async function loadFile(dir, filePath, status, old) {
15827
- const result = {
15828
- path: filePath,
15829
- status,
15830
- old: null,
15831
- new: null
15832
- };
15833
- if (old === "tooLarge") result.tooLarge = true;
15834
- else if (old !== void 0) result.old = old;
15835
- if (status !== "D" && !result.tooLarge) try {
15836
- const root = path.resolve(dir);
15837
- const full = path.resolve(dir, filePath);
15838
- if (full !== root && !full.startsWith(root + path.sep)) throw new Error(`path escapes worktree: ${filePath}`);
15839
- if ((await fsp.stat(full)).size > MAX_FILE_BYTES) result.tooLarge = true;
15840
- else result.new = await fsp.readFile(full, "utf8");
15841
- } catch {}
15842
- if (result.old?.includes("\0") || result.new?.includes("\0")) {
15843
- result.binary = true;
15844
- result.old = null;
15845
- result.new = null;
15846
- }
15847
- if (result.tooLarge) {
15848
- result.old = null;
15849
- result.new = null;
15850
- }
15851
- return result;
15852
- }
15853
- //#endregion
15854
15813
  //#region src/server/status.ts
15855
15814
  var TTL_MS$2 = 2e3;
15856
15815
  var snapshot = null;
@@ -15882,9 +15841,9 @@ function statusForAll() {
15882
15841
  }
15883
15842
  /** Push-based freshness: while any SSE client is connected, recompute the
15884
15843
  * (shared, deduped) snapshot on a slow tick and publish it only when it
15885
- * actually changed — ports appearing, git changes, PR/CI flips reach every
15886
- * client in ~15s instead of waiting out their 60s poll, and idle systems
15887
- * push nothing at all. */
15844
+ * actually changed — ports appearing, PR/CI flips reach every client in
15845
+ * ~15s instead of waiting out their 60s poll, and idle systems push
15846
+ * nothing at all. */
15888
15847
  var WATCH_MS = 15e3;
15889
15848
  /** Right after a local push PR state flips within seconds — tick fast so
15890
15849
  * the badge lands while it still feels immediate. */
@@ -15938,6 +15897,8 @@ async function statusForAllFresh() {
15938
15897
  if (await refreshPrsBatch(activeRepoNames(), true).catch(() => false)) invalidateSnapshot();
15939
15898
  return statusForAll();
15940
15899
  }
15900
+ /** RSS here is the Node + Marko baseline (~80 MB empty, ~115 MB loaded),
15901
+ * not a leak and not worth chasing — young-gen flags do not move it. */
15941
15902
  async function computeStatus() {
15942
15903
  const workspaces = listWorkspaces().filter((ws) => ws.status === "active");
15943
15904
  const paths = new Map(workspaces.map((ws) => [ws.id, ws.path]));
@@ -15951,23 +15912,18 @@ async function computeStatus() {
15951
15912
  const statuses = await Promise.all(ws.repos.map((repo) => repoStatus(ws, repo, foreign)));
15952
15913
  return [ws.id, {
15953
15914
  ports: ports.get(ws.id) || [],
15954
- repos: statuses.map((s) => s.status),
15955
- ...sessionCounts(ws.id, states),
15956
- changes: statuses.reduce((sum, s) => sum + s.changed, 0)
15915
+ repos: statuses,
15916
+ ...sessionCounts(ws.id, states)
15957
15917
  }];
15958
15918
  }));
15959
15919
  return Object.fromEntries(entries);
15960
15920
  }
15961
15921
  async function repoStatus(ws, repo, foreign) {
15962
- const dir = path.join(ws.path, repo.worktree);
15963
- const [repoPrs, changed] = await Promise.all([workspacePrs(repo.full_name, ws.branch, foreign).then((list) => list.filter((pr) => pr.state === "OPEN")), changedFileCount(dir, ws.branch, repo.base_branch)]);
15922
+ const repoPrs = await workspacePrs(repo.full_name, ws.branch, foreign).then((list) => list.filter((pr) => pr.state === "OPEN"));
15964
15923
  return {
15965
- status: {
15966
- full_name: repo.full_name,
15967
- worktree: repo.worktree,
15968
- prs: repoPrs
15969
- },
15970
- changed
15924
+ full_name: repo.full_name,
15925
+ worktree: repo.worktree,
15926
+ prs: repoPrs
15971
15927
  };
15972
15928
  }
15973
15929
  function sessionCounts(workspaceId, states) {
@@ -16568,8 +16524,356 @@ function post20(context) {
16568
16524
  return call(mware3, __mware22, context);
16569
16525
  }
16570
16526
  //#endregion
16527
+ //#region src/server/diff.ts
16528
+ var MAX_FILE_BYTES = 5e5;
16529
+ /** Diffs compare against remote-tracking refs, which only move when this
16530
+ * machine fetches or pushes — a PR merged on GitHub leaves them stale and
16531
+ * the diff shows already-merged work until the next fetch. Opening the
16532
+ * diff freshens the base refs itself (throttled per repo; each ref
16533
+ * fetched independently since one missing ref fails a combined fetch,
16534
+ * and capped so a slow remote can't hang the view — stale refs then
16535
+ * self-correct on the next open). */
16536
+ var FETCH_TTL_MS = 3e4;
16537
+ var FETCH_WAIT_MS = 8e3;
16538
+ var lastFetch = /* @__PURE__ */ new Map();
16539
+ async function freshenBaseRefs(dir, fullName, refs) {
16540
+ if (Date.now() - (lastFetch.get(fullName) ?? 0) < FETCH_TTL_MS) return;
16541
+ const fetches = [...new Set(refs)].map((ref) => git([
16542
+ "fetch",
16543
+ "--quiet",
16544
+ "--no-tags",
16545
+ "origin",
16546
+ ref
16547
+ ], dir).catch((err) => {
16548
+ if (missingRemoteRef(err)) return git([
16549
+ "update-ref",
16550
+ "-d",
16551
+ `refs/remotes/origin/${ref}`
16552
+ ], dir).catch(() => {});
16553
+ }));
16554
+ fetches.push(git([
16555
+ "remote",
16556
+ "prune",
16557
+ "origin"
16558
+ ], dir).catch(() => {}));
16559
+ let timer;
16560
+ await Promise.race([Promise.all(fetches), new Promise((resolve) => {
16561
+ timer = setTimeout(resolve, FETCH_WAIT_MS);
16562
+ })]);
16563
+ clearTimeout(timer);
16564
+ lastFetch.set(fullName, Date.now());
16565
+ }
16566
+ function missingRemoteRef(err) {
16567
+ const msg = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : String(err);
16568
+ return /couldn't find remote ref/.test(msg);
16569
+ }
16570
+ /** The ref the working tree is compared against by default, best first.
16571
+ * Workspace branch / detached / foreign → `origin/<base>`. Our `-topic`
16572
+ * lane → `origin/<ws-branch>` (only two levels; `<ws>-a-b` still parents
16573
+ * to the workspace branch). `foreign` is other workspaces' branches so a
16574
+ * longer prefix is not stolen. An explicit `baseRef` overrides all. */
16575
+ async function resolveBase(dir, wsBranch, baseBranch, headBranch, foreign, baseRef) {
16576
+ const ours = ownsBranch(headBranch, wsBranch) && !foreign.some((x) => ownsBranch(headBranch, x));
16577
+ const candidates = [];
16578
+ if (baseRef) candidates.push([`origin/${baseRef}`, baseRef], [baseRef, baseRef]);
16579
+ else if (headBranch === wsBranch || headBranch === "HEAD" || !ours) {
16580
+ candidates.push([`origin/${baseBranch}`, baseBranch]);
16581
+ candidates.push([`origin/${wsBranch}`, `origin/${wsBranch}`]);
16582
+ } else {
16583
+ candidates.push([`origin/${wsBranch}`, `origin/${wsBranch}`]);
16584
+ candidates.push([`origin/${headBranch}`, `origin/${headBranch}`]);
16585
+ candidates.push([`origin/${baseBranch}`, baseBranch]);
16586
+ }
16587
+ for (const [ref, label] of candidates) try {
16588
+ return {
16589
+ mergeBase: await git([
16590
+ "merge-base",
16591
+ ref,
16592
+ "HEAD"
16593
+ ], dir),
16594
+ ref,
16595
+ label
16596
+ };
16597
+ } catch {}
16598
+ return {
16599
+ mergeBase: "HEAD",
16600
+ ref: "HEAD",
16601
+ label: baseRef || baseBranch,
16602
+ missing: true
16603
+ };
16604
+ }
16605
+ /** True when merging `head` into `base` is a no-op (head's unique work
16606
+ * is already in `base` — the squash-merge case). A conflict is not
16607
+ * containment; the caller falls through to three-dot. */
16608
+ async function treeContainedIn(dir, base, head) {
16609
+ try {
16610
+ return (await git([
16611
+ "merge-tree",
16612
+ "--write-tree",
16613
+ base,
16614
+ head
16615
+ ], dir)).split("\n")[0] === await git(["rev-parse", `${base}^{tree}`], dir);
16616
+ } catch {
16617
+ return false;
16618
+ }
16619
+ }
16620
+ /** Open PRs as GitHub's files tab (remote head vs the PR's `--base`).
16621
+ * Repos with no open PR fall back to the local checkout vs the derived
16622
+ * base. `pr` (`worktree/number`) focuses one PR. `uncommitted` overlays
16623
+ * the working tree on the PR whose head is the current branch. */
16624
+ async function workspaceDiff(ws, opts = {}) {
16625
+ const { baseRef, uncommitted = false, pr: focus } = opts;
16626
+ const foreign = otherWorkspaceBranches(ws.id);
16627
+ return (await Promise.all(ws.repos.map(async (repo) => {
16628
+ const dir = path.join(ws.path, repo.worktree);
16629
+ const branch = await git([
16630
+ "rev-parse",
16631
+ "--abbrev-ref",
16632
+ "HEAD"
16633
+ ], dir).catch(() => "HEAD");
16634
+ const localHead = await git(["rev-parse", "HEAD"], dir).catch(() => "");
16635
+ const open = (await workspacePrs(repo.full_name, ws.branch, foreign)).filter((p) => p.state === "OPEN" && p.baseRefName && p.headRefName && (!focus || focus === `${repo.worktree}/${p.number}`));
16636
+ if (open.length) return Promise.all(open.map((p) => prDiff(dir, repo, p, {
16637
+ uncommitted: uncommitted && branch === p.headRefName,
16638
+ localHead,
16639
+ branch
16640
+ })));
16641
+ if (focus) return [];
16642
+ return [await localDiff(dir, ws, repo, branch, localHead, baseRef, uncommitted)];
16643
+ }))).flat();
16644
+ }
16645
+ async function prDiff(dir, repo, pr, ctx) {
16646
+ await freshenBaseRefs(dir, repo.full_name, [pr.baseRefName, pr.headRefName]);
16647
+ const headRef = `origin/${pr.headRefName}`;
16648
+ const baseRef = `origin/${pr.baseRefName}`;
16649
+ const head = await git(["rev-parse", headRef], dir).catch(() => "") || pr.headRefOid || "";
16650
+ const baseSha = await git(["rev-parse", baseRef], dir).catch(() => "");
16651
+ if (!head || !baseSha) return {
16652
+ id: `${repo.worktree}#${pr.number}`,
16653
+ worktree: repo.worktree,
16654
+ full_name: repo.full_name,
16655
+ base: pr.baseRefName,
16656
+ default_branch: repo.default_branch,
16657
+ branch: ctx.branch,
16658
+ head,
16659
+ baseSha,
16660
+ pr: prMeta(pr),
16661
+ localHead: ctx.localHead,
16662
+ baseMissing: true,
16663
+ files: []
16664
+ };
16665
+ const mergeBase = await git([
16666
+ "merge-base",
16667
+ baseRef,
16668
+ head
16669
+ ], dir).catch(() => "");
16670
+ const contained = await treeContainedIn(dir, baseRef, head);
16671
+ const files = await collectFiles(dir, contained ? head : mergeBase || baseRef, head, ctx.uncommitted);
16672
+ const githubDrift = !!pr.headRefOid && head !== pr.headRefOid;
16673
+ return {
16674
+ id: `${repo.worktree}#${pr.number}`,
16675
+ worktree: repo.worktree,
16676
+ full_name: repo.full_name,
16677
+ base: pr.baseRefName,
16678
+ default_branch: repo.default_branch,
16679
+ branch: ctx.branch,
16680
+ head,
16681
+ baseSha,
16682
+ pr: prMeta(pr),
16683
+ localHead: ctx.localHead,
16684
+ ...githubDrift && { githubDrift: true },
16685
+ ...contained && { contained: true },
16686
+ files
16687
+ };
16688
+ }
16689
+ async function localDiff(dir, ws, repo, branch, localHead, baseRef, uncommitted) {
16690
+ await freshenBaseRefs(dir, repo.full_name, [
16691
+ ws.branch,
16692
+ ...branch !== "HEAD" ? [branch] : [],
16693
+ ...baseRef ? [baseRef] : [repo.base_branch]
16694
+ ]);
16695
+ const { mergeBase, ref, label, missing } = await resolveBase(dir, ws.branch, repo.base_branch, branch, otherWorkspaceBranches(ws.id), baseRef);
16696
+ if (missing && !uncommitted) return {
16697
+ id: repo.worktree,
16698
+ worktree: repo.worktree,
16699
+ full_name: repo.full_name,
16700
+ base: label,
16701
+ default_branch: repo.default_branch,
16702
+ branch,
16703
+ head: localHead,
16704
+ baseSha: localHead,
16705
+ baseMissing: true,
16706
+ files: []
16707
+ };
16708
+ const contained = !missing && await treeContainedIn(dir, ref, "HEAD");
16709
+ const against = contained ? "HEAD" : mergeBase;
16710
+ const baseSha = missing ? localHead : await git(["rev-parse", ref], dir).catch(() => mergeBase);
16711
+ const files = await collectFiles(dir, against, "HEAD", uncommitted);
16712
+ return {
16713
+ id: repo.worktree,
16714
+ worktree: repo.worktree,
16715
+ full_name: repo.full_name,
16716
+ base: label,
16717
+ default_branch: repo.default_branch,
16718
+ branch,
16719
+ head: localHead,
16720
+ baseSha,
16721
+ ...missing && { baseMissing: true },
16722
+ ...contained && { contained: true },
16723
+ files
16724
+ };
16725
+ }
16726
+ function prMeta(pr) {
16727
+ return {
16728
+ number: pr.number,
16729
+ title: pr.title,
16730
+ url: pr.url,
16731
+ headRefName: pr.headRefName,
16732
+ baseRefName: pr.baseRefName,
16733
+ ...pr.headRefOid && { headRefOid: pr.headRefOid }
16734
+ };
16735
+ }
16736
+ async function collectFiles(dir, oldRev, newRev, uncommitted) {
16737
+ const diffArgs = uncommitted ? [
16738
+ "diff",
16739
+ "--name-status",
16740
+ "-z",
16741
+ "--no-renames",
16742
+ oldRev
16743
+ ] : [
16744
+ "diff",
16745
+ "--name-status",
16746
+ "-z",
16747
+ "--no-renames",
16748
+ oldRev,
16749
+ newRev
16750
+ ];
16751
+ const [nameStatus, untracked] = await Promise.all([git(diffArgs, dir).catch(() => ""), uncommitted ? git([
16752
+ "ls-files",
16753
+ "--others",
16754
+ "--exclude-standard",
16755
+ "-z"
16756
+ ], dir).catch(() => "") : Promise.resolve("")]);
16757
+ const entries = [];
16758
+ const parts = nameStatus.split("\0").filter(Boolean);
16759
+ for (let i = 0; i + 1 < parts.length; i += 2) entries.push({
16760
+ path: parts[i + 1],
16761
+ status: parts[i]
16762
+ });
16763
+ for (const file of untracked.split("\0").filter(Boolean)) entries.push({
16764
+ path: file,
16765
+ status: "A"
16766
+ });
16767
+ const olds = await batchContents(dir, oldRev, entries.filter((e) => e.status !== "A").map((e) => e.path));
16768
+ const news = uncommitted ? null : await batchContents(dir, newRev, entries.filter((e) => e.status !== "D").map((e) => e.path));
16769
+ const files = await Promise.all(entries.map((e) => news ? fromBlobs(e.path, e.status, olds.get(e.path), news.get(e.path)) : loadWorktreeFile(dir, e.path, e.status, olds.get(e.path))));
16770
+ files.sort((a, b) => a.path.localeCompare(b.path));
16771
+ return files;
16772
+ }
16773
+ /** Blob contents for many paths through a single `git cat-file --batch`
16774
+ * process (instead of one `git show` per file). Blobs over MAX_FILE_BYTES
16775
+ * are skipped and mapped to "tooLarge". */
16776
+ async function batchContents(dir, rev, paths) {
16777
+ const result = /* @__PURE__ */ new Map();
16778
+ if (!paths.length) return result;
16779
+ await new Promise((resolve) => {
16780
+ const proc = spawn("git", ["cat-file", "--batch"], { cwd: dir });
16781
+ let buf = Buffer.alloc(0);
16782
+ let index = 0;
16783
+ let need = -1;
16784
+ const parse = () => {
16785
+ for (;;) if (need >= 0) {
16786
+ if (buf.length < need + 1) return;
16787
+ const content = buf.subarray(0, need);
16788
+ buf = buf.subarray(need + 1);
16789
+ need = -1;
16790
+ const p = paths[index++];
16791
+ if (content.length > MAX_FILE_BYTES) result.set(p, "tooLarge");
16792
+ else result.set(p, content.toString("utf8"));
16793
+ } else {
16794
+ const nl = buf.indexOf(10);
16795
+ if (nl < 0) return;
16796
+ const header = buf.subarray(0, nl).toString();
16797
+ buf = buf.subarray(nl + 1);
16798
+ if (header.endsWith(" missing")) {
16799
+ index++;
16800
+ continue;
16801
+ }
16802
+ need = Number(header.split(" ")[2]) || 0;
16803
+ }
16804
+ };
16805
+ proc.stdout.on("data", (chunk) => {
16806
+ buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
16807
+ parse();
16808
+ });
16809
+ proc.on("close", () => resolve());
16810
+ proc.on("error", () => resolve());
16811
+ proc.stdin.on("error", () => {});
16812
+ proc.stdin.end(paths.map((p) => `${rev}:${p}`).join("\n") + "\n");
16813
+ });
16814
+ return result;
16815
+ }
16816
+ function fromBlobs(filePath, status, old, neu) {
16817
+ const result = {
16818
+ path: filePath,
16819
+ status,
16820
+ old: null,
16821
+ new: null
16822
+ };
16823
+ if (old === "tooLarge" || neu === "tooLarge") result.tooLarge = true;
16824
+ else {
16825
+ if (old !== void 0) result.old = old;
16826
+ if (neu !== void 0) result.new = neu;
16827
+ }
16828
+ if (result.old?.includes("\0") || result.new?.includes("\0")) {
16829
+ result.binary = true;
16830
+ result.old = null;
16831
+ result.new = null;
16832
+ }
16833
+ if (result.tooLarge) {
16834
+ result.old = null;
16835
+ result.new = null;
16836
+ }
16837
+ return result;
16838
+ }
16839
+ async function loadWorktreeFile(dir, filePath, status, old) {
16840
+ const result = {
16841
+ path: filePath,
16842
+ status,
16843
+ old: null,
16844
+ new: null
16845
+ };
16846
+ if (old === "tooLarge") result.tooLarge = true;
16847
+ else if (old !== void 0) result.old = old;
16848
+ if (status !== "D" && !result.tooLarge) try {
16849
+ const root = path.resolve(dir);
16850
+ const full = path.resolve(dir, filePath);
16851
+ if (full !== root && !full.startsWith(root + path.sep)) throw new Error(`path escapes worktree: ${filePath}`);
16852
+ if ((await fsp.stat(full)).size > MAX_FILE_BYTES) result.tooLarge = true;
16853
+ else result.new = await fsp.readFile(full, "utf8");
16854
+ } catch {}
16855
+ if (result.old?.includes("\0") || result.new?.includes("\0")) {
16856
+ result.binary = true;
16857
+ result.old = null;
16858
+ result.new = null;
16859
+ }
16860
+ if (result.tooLarge) {
16861
+ result.old = null;
16862
+ result.new = null;
16863
+ }
16864
+ return result;
16865
+ }
16866
+ //#endregion
16571
16867
  //#region __marko-run__api.workspaces.$.diff.js
16572
- var getHandler$4 = normalizeHandler(Run.GET({ search: (value) => ({ base: typeof value.base === "string" && value.base ? value.base : void 0 }) }, async (ctx) => Response.json(await workspaceDiff(ctx.data.workspace, ctx.search.base))), "GET");
16868
+ var getHandler$4 = normalizeHandler(Run.GET({ search: (value) => ({
16869
+ base: typeof value.base === "string" && value.base ? value.base : void 0,
16870
+ uncommitted: value.uncommitted === "1" || value.uncommitted === "true",
16871
+ pr: typeof value.pr === "string" && value.pr ? value.pr : void 0
16872
+ }) }, async (ctx) => Response.json(await workspaceDiff(ctx.data.workspace, {
16873
+ baseRef: ctx.search.base,
16874
+ uncommitted: ctx.search.uncommitted,
16875
+ pr: ctx.search.pr
16876
+ }))), "GET");
16573
16877
  var get21_options = mergeOptions(mware3, mware22, getHandler$4);
16574
16878
  var head21_options = mergeOptions(mware3, mware22);
16575
16879
  function get21(context) {
@@ -21166,4 +21470,4 @@ server.listen(port, () => {
21166
21470
  //#endregion
21167
21471
  export {};
21168
21472
 
21169
- ;var __MARKO_MANIFEST__={"esfPH6H":{"block":[2,"stylesheet#/assets/index-KIy0rCx1.css",6,"<link",0," rel=\"stylesheet\" crossorigin href=\"",1,"assets/index-KIy0rCx1.css\"",">"],"defer":[2,"/assets/index-BwlA-lgI.js",6,"<script",0," async type=\"module\" crossorigin src=\"",1,"assets/index-BwlA-lgI.js\"","></script>"]}};
21473
+ ;var __MARKO_MANIFEST__={"esfPH6H":{"block":[2,"stylesheet#/assets/index-C_YqjXeW.css",6,"<link",0," rel=\"stylesheet\" crossorigin href=\"",1,"assets/index-C_YqjXeW.css\"",">"],"defer":[2,"/assets/index-Bx9UtyrE.js",6,"<script",0," async type=\"module\" crossorigin src=\"",1,"assets/index-Bx9UtyrE.js\"","></script>"]}};