@dylanpiercey/work 0.0.6 → 0.0.8

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 +1067 -736
  3. package/dist/public/assets/{_D4Z_UM_B2.js → _1sfledaC2.js} +2 -2
  4. package/dist/public/assets/{_D4Z_UM_B2.js.map → _1sfledaC2.js.map} +1 -1
  5. package/dist/public/assets/{_DvigaXuY.js → _5FPBKXmV.js} +2 -2
  6. package/dist/public/assets/{_DvigaXuY.js.map → _5FPBKXmV.js.map} +1 -1
  7. package/dist/public/assets/{_Bs-d5a142.js → _8JbqtAcO2.js} +2 -2
  8. package/dist/public/assets/{_Bs-d5a142.js.map → _8JbqtAcO2.js.map} +1 -1
  9. package/dist/public/assets/{_DcCF4p4b2.js → _9T4KD6Cx2.js} +2 -2
  10. package/dist/public/assets/{_DcCF4p4b2.js.map → _9T4KD6Cx2.js.map} +1 -1
  11. package/dist/public/assets/{_VVnpWsa-2.js → _ArCUqa6q2.js} +2 -2
  12. package/dist/public/assets/{_VVnpWsa-2.js.map → _ArCUqa6q2.js.map} +1 -1
  13. package/dist/public/assets/{_DpdFHjlT.js → _B3wDsGH_.js} +2 -2
  14. package/dist/public/assets/{_DpdFHjlT.js.map → _B3wDsGH_.js.map} +1 -1
  15. package/dist/public/assets/{_38qjx0VE.js → _B6VcGcFS.js} +2 -2
  16. package/dist/public/assets/{_38qjx0VE.js.map → _B6VcGcFS.js.map} +1 -1
  17. package/dist/public/assets/{_DtkPfG0P2.js → _B8hRUvsg2.js} +2 -2
  18. package/dist/public/assets/{_DtkPfG0P2.js.map → _B8hRUvsg2.js.map} +1 -1
  19. package/dist/public/assets/{_CUdHfuKf2.js → _BA1Flvg32.js} +2 -2
  20. package/dist/public/assets/{_CUdHfuKf2.js.map → _BA1Flvg32.js.map} +1 -1
  21. package/dist/public/assets/{_Dfi8Atag.js → _BBbOvFnN.js} +2 -2
  22. package/dist/public/assets/{_Dfi8Atag.js.map → _BBbOvFnN.js.map} +1 -1
  23. package/dist/public/assets/{_o_JLlSoU2.js → _BENakU6F2.js} +2 -2
  24. package/dist/public/assets/{_o_JLlSoU2.js.map → _BENakU6F2.js.map} +1 -1
  25. package/dist/public/assets/{_CLjg6iH5.js → _BKZkyaFV.js} +2 -2
  26. package/dist/public/assets/{_CLjg6iH5.js.map → _BKZkyaFV.js.map} +1 -1
  27. package/dist/public/assets/{_D8RDRYsV.js → _BKayEvX0.js} +2 -2
  28. package/dist/public/assets/{_D8RDRYsV.js.map → _BKayEvX0.js.map} +1 -1
  29. package/dist/public/assets/{_U-btoBCb2.js → _BRmBVeT02.js} +2 -2
  30. package/dist/public/assets/{_U-btoBCb2.js.map → _BRmBVeT02.js.map} +1 -1
  31. package/dist/public/assets/{_BSe5zjTJ2.js → _BSK-jOsW2.js} +2 -2
  32. package/dist/public/assets/{_BSe5zjTJ2.js.map → _BSK-jOsW2.js.map} +1 -1
  33. package/dist/public/assets/{_DDDACu2e.js → _BYiCMSp_.js} +2 -2
  34. package/dist/public/assets/{_DDDACu2e.js.map → _BYiCMSp_.js.map} +1 -1
  35. package/dist/public/assets/{_xikqPUw52.js → _BZ1vsOtU2.js} +2 -2
  36. package/dist/public/assets/{_xikqPUw52.js.map → _BZ1vsOtU2.js.map} +1 -1
  37. package/dist/public/assets/{_Bt1hNExm.js → _BZj2u1P8.js} +2 -2
  38. package/dist/public/assets/{_Bt1hNExm.js.map → _BZj2u1P8.js.map} +1 -1
  39. package/dist/public/assets/{_DOeCLKRd.js → _BdjtVej2.js} +2 -2
  40. package/dist/public/assets/{_DOeCLKRd.js.map → _BdjtVej2.js.map} +1 -1
  41. package/dist/public/assets/{_Dg2FAYj_2.js → _BfklLvTH2.js} +2 -2
  42. package/dist/public/assets/{_Dg2FAYj_2.js.map → _BfklLvTH2.js.map} +1 -1
  43. package/dist/public/assets/{_Q7i-xAMZ.js → _ByVsRQi_.js} +2 -2
  44. package/dist/public/assets/{_Q7i-xAMZ.js.map → _ByVsRQi_.js.map} +1 -1
  45. package/dist/public/assets/{_uhGymT-L.js → _BzzAJ7NO.js} +2 -2
  46. package/dist/public/assets/{_uhGymT-L.js.map → _BzzAJ7NO.js.map} +1 -1
  47. package/dist/public/assets/{_DtpDQy9c.js → _C7N-1UWP.js} +2 -2
  48. package/dist/public/assets/{_DtpDQy9c.js.map → _C7N-1UWP.js.map} +1 -1
  49. package/dist/public/assets/{_T-HpFRXH.js → _C7qjJec5.js} +2 -2
  50. package/dist/public/assets/{_T-HpFRXH.js.map → _C7qjJec5.js.map} +1 -1
  51. package/dist/public/assets/{_CDUUTPIa.js → _CDqUkraW.js} +2 -2
  52. package/dist/public/assets/{_CDUUTPIa.js.map → _CDqUkraW.js.map} +1 -1
  53. package/dist/public/assets/{_CUCwB86v.js → _CEhaT_yw.js} +2 -2
  54. package/dist/public/assets/{_CUCwB86v.js.map → _CEhaT_yw.js.map} +1 -1
  55. package/dist/public/assets/{_DFLlSEDh2.js → _CGshnRGq2.js} +2 -2
  56. package/dist/public/assets/{_DFLlSEDh2.js.map → _CGshnRGq2.js.map} +1 -1
  57. package/dist/public/assets/{_D1AvypB0.js → _CHnVT46K.js} +2 -2
  58. package/dist/public/assets/{_D1AvypB0.js.map → _CHnVT46K.js.map} +1 -1
  59. package/dist/public/assets/{_Dlbc7SgI.js → _CJkEzRYS.js} +2 -2
  60. package/dist/public/assets/{_Dlbc7SgI.js.map → _CJkEzRYS.js.map} +1 -1
  61. package/dist/public/assets/{_DpRvij_q.js → _CLKEqaNz.js} +2 -2
  62. package/dist/public/assets/{_DpRvij_q.js.map → _CLKEqaNz.js.map} +1 -1
  63. package/dist/public/assets/{_BZgEX-SR.js → _CRzez65S.js} +2 -2
  64. package/dist/public/assets/{_BZgEX-SR.js.map → _CRzez65S.js.map} +1 -1
  65. package/dist/public/assets/{_tf4220G9.js → _CVTm837N.js} +2 -2
  66. package/dist/public/assets/{_tf4220G9.js.map → _CVTm837N.js.map} +1 -1
  67. package/dist/public/assets/{_CeDXSlq_2.js → _CfcBd2P72.js} +2 -2
  68. package/dist/public/assets/{_CeDXSlq_2.js.map → _CfcBd2P72.js.map} +1 -1
  69. package/dist/public/assets/{_BV7Qzzwy2.js → _CghRvWTz2.js} +2 -2
  70. package/dist/public/assets/{_BV7Qzzwy2.js.map → _CghRvWTz2.js.map} +1 -1
  71. package/dist/public/assets/{_D7AQAhlo.js → _CiLa9EZD.js} +2 -2
  72. package/dist/public/assets/{_D7AQAhlo.js.map → _CiLa9EZD.js.map} +1 -1
  73. package/dist/public/assets/{_CVvMxylB2.js → _ClFfFrAg2.js} +2 -2
  74. package/dist/public/assets/{_CVvMxylB2.js.map → _ClFfFrAg2.js.map} +1 -1
  75. package/dist/public/assets/{_DWSSCCOX.js → _ClP4oh7a.js} +2 -2
  76. package/dist/public/assets/{_DWSSCCOX.js.map → _ClP4oh7a.js.map} +1 -1
  77. package/dist/public/assets/{_BjlFBd9j.js → _CraDhcnw.js} +2 -2
  78. package/dist/public/assets/{_BjlFBd9j.js.map → _CraDhcnw.js.map} +1 -1
  79. package/dist/public/assets/{_PBOdFezG2.js → _Cu2ARJ6m2.js} +2 -2
  80. package/dist/public/assets/{_PBOdFezG2.js.map → _Cu2ARJ6m2.js.map} +1 -1
  81. package/dist/public/assets/{_vd_LQrD6.js → _Cuarrf-F.js} +2 -2
  82. package/dist/public/assets/{_vd_LQrD6.js.map → _Cuarrf-F.js.map} +1 -1
  83. package/dist/public/assets/{_kN8jmSEy2.js → _CxREVo2W2.js} +2 -2
  84. package/dist/public/assets/{_kN8jmSEy2.js.map → _CxREVo2W2.js.map} +1 -1
  85. package/dist/public/assets/{_D9oMqxHc.js → _DDkF3p8s.js} +2 -2
  86. package/dist/public/assets/{_D9oMqxHc.js.map → _DDkF3p8s.js.map} +1 -1
  87. package/dist/public/assets/{_ChbGErmQ.js → _DNJJM0AE.js} +2 -2
  88. package/dist/public/assets/{_ChbGErmQ.js.map → _DNJJM0AE.js.map} +1 -1
  89. package/dist/public/assets/{_kzd-9LxV.js → _DPOTu7jR.js} +2 -2
  90. package/dist/public/assets/{_kzd-9LxV.js.map → _DPOTu7jR.js.map} +1 -1
  91. package/dist/public/assets/{_CgK4NAc5.js → _DWpL0T8T.js} +2 -2
  92. package/dist/public/assets/{_CgK4NAc5.js.map → _DWpL0T8T.js.map} +1 -1
  93. package/dist/public/assets/{_BqB9YKHu.js → _DatWcE_l.js} +2 -2
  94. package/dist/public/assets/{_BqB9YKHu.js.map → _DatWcE_l.js.map} +1 -1
  95. package/dist/public/assets/{_CjoA9dbQ.js → _DbScTuHX.js} +2 -2
  96. package/dist/public/assets/{_CjoA9dbQ.js.map → _DbScTuHX.js.map} +1 -1
  97. package/dist/public/assets/{_BGfZ-AIc.js → _Dlqi-FuG.js} +2 -2
  98. package/dist/public/assets/{_BGfZ-AIc.js.map → _Dlqi-FuG.js.map} +1 -1
  99. package/dist/public/assets/{_CscsJi7i.js → _Dmvx0mCq.js} +2 -2
  100. package/dist/public/assets/{_CscsJi7i.js.map → _Dmvx0mCq.js.map} +1 -1
  101. package/dist/public/assets/{_nfuFU9Un2.js → _Doc3xk-E2.js} +2 -2
  102. package/dist/public/assets/{_nfuFU9Un2.js.map → _Doc3xk-E2.js.map} +1 -1
  103. package/dist/public/assets/{_BvkUWaoR.js → _DpCf_atU.js} +2 -2
  104. package/dist/public/assets/{_BvkUWaoR.js.map → _DpCf_atU.js.map} +1 -1
  105. package/dist/public/assets/{_BO1auSkL2.js → _DsdF3uU02.js} +2 -2
  106. package/dist/public/assets/{_BO1auSkL2.js.map → _DsdF3uU02.js.map} +1 -1
  107. package/dist/public/assets/{_DsK9qp5z2.js → _Du7L3DNJ2.js} +2 -2
  108. package/dist/public/assets/{_DsK9qp5z2.js.map → _Du7L3DNJ2.js.map} +1 -1
  109. package/dist/public/assets/{_CATT-8QU2.js → _DzcnHy3e2.js} +2 -2
  110. package/dist/public/assets/{_CATT-8QU2.js.map → _DzcnHy3e2.js.map} +1 -1
  111. package/dist/public/assets/{_nv0goT4Z.js → _FkD5nJ3p.js} +2 -2
  112. package/dist/public/assets/{_nv0goT4Z.js.map → _FkD5nJ3p.js.map} +1 -1
  113. package/dist/public/assets/{_D-u1ssE0.js → _KZaj47gf.js} +2 -2
  114. package/dist/public/assets/{_D-u1ssE0.js.map → _KZaj47gf.js.map} +1 -1
  115. package/dist/public/assets/{_B01vNcBH.js → _MY6ncoWy.js} +2 -2
  116. package/dist/public/assets/{_B01vNcBH.js.map → _MY6ncoWy.js.map} +1 -1
  117. package/dist/public/assets/{_lO429-VP.js → _PLe738he.js} +2 -2
  118. package/dist/public/assets/{_lO429-VP.js.map → _PLe738he.js.map} +1 -1
  119. package/dist/public/assets/{_DoTuVtdo.js → _UIRw8u-5.js} +2 -2
  120. package/dist/public/assets/{_DoTuVtdo.js.map → _UIRw8u-5.js.map} +1 -1
  121. package/dist/public/assets/{_Bj_P-is9.js → _VQFhNNlC.js} +2 -2
  122. package/dist/public/assets/{_Bj_P-is9.js.map → _VQFhNNlC.js.map} +1 -1
  123. package/dist/public/assets/{_C52df4172.js → _VxrPs6zz2.js} +2 -2
  124. package/dist/public/assets/{_C52df4172.js.map → _VxrPs6zz2.js.map} +1 -1
  125. package/dist/public/assets/{_CPMq9qKD.js → _Yc_Nhrnz.js} +2 -2
  126. package/dist/public/assets/{_CPMq9qKD.js.map → _Yc_Nhrnz.js.map} +1 -1
  127. package/dist/public/assets/{_B5_K4U35.js → _ZXbjL1cp.js} +2 -2
  128. package/dist/public/assets/{_B5_K4U35.js.map → _ZXbjL1cp.js.map} +1 -1
  129. package/dist/public/assets/{_BGjK3KLI.js → _Zn6WQhNB.js} +2 -2
  130. package/dist/public/assets/{_BGjK3KLI.js.map → _Zn6WQhNB.js.map} +1 -1
  131. package/dist/public/assets/{_C0fM-yEd.js → _d0JPWXs6.js} +2 -2
  132. package/dist/public/assets/{_C0fM-yEd.js.map → _d0JPWXs6.js.map} +1 -1
  133. package/dist/public/assets/{_Czy_Rmr8.js → _kFADAUVE.js} +2 -2
  134. package/dist/public/assets/{_Czy_Rmr8.js.map → _kFADAUVE.js.map} +1 -1
  135. package/dist/public/assets/{_kf1lUDoU2.js → _lFKA9njW2.js} +2 -2
  136. package/dist/public/assets/{_kf1lUDoU2.js.map → _lFKA9njW2.js.map} +1 -1
  137. package/dist/public/assets/{_BCmJwCZ4.js → _ljVbYQrb.js} +2 -2
  138. package/dist/public/assets/{_BCmJwCZ4.js.map → _ljVbYQrb.js.map} +1 -1
  139. package/dist/public/assets/{_CIE37fGr.js → _lq6Mp4lM.js} +2 -2
  140. package/dist/public/assets/{_CIE37fGr.js.map → _lq6Mp4lM.js.map} +1 -1
  141. package/dist/public/assets/{_Cm0oW23H.js → _mIMmc1-4.js} +2 -2
  142. package/dist/public/assets/{_Cm0oW23H.js.map → _mIMmc1-4.js.map} +1 -1
  143. package/dist/public/assets/{_17ixsFjl2.js → _mwzbhNSE2.js} +2 -2
  144. package/dist/public/assets/{_17ixsFjl2.js.map → _mwzbhNSE2.js.map} +1 -1
  145. package/dist/public/assets/{_lENTdKos.js → _mz-it25q.js} +2 -2
  146. package/dist/public/assets/{_lENTdKos.js.map → _mz-it25q.js.map} +1 -1
  147. package/dist/public/assets/{_B7WJMKdz.js → _tEWpbjjW.js} +2 -2
  148. package/dist/public/assets/{_B7WJMKdz.js.map → _tEWpbjjW.js.map} +1 -1
  149. package/dist/public/assets/{_rDdCnzAf.js → _x39l6v9i.js} +2 -2
  150. package/dist/public/assets/{_rDdCnzAf.js.map → _x39l6v9i.js.map} +1 -1
  151. package/dist/public/assets/{index-BwlA-lgI.js → index-BnMs3duC.js} +63 -61
  152. package/dist/public/assets/index-BnMs3duC.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,16 @@ 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();
5587
+ let dismissedDrift = /* @__PURE__ */ new Set();
5593
5588
  const toggleRepo = _resume((worktree) => {
5594
5589
  const next = new Set(collapsedRepos);
5595
5590
  if (!next.delete(worktree)) next.add(worktree);
5596
5591
  collapsedRepos = next;
5597
- }, "d4", $scope0_id);
5592
+ }, "d3", $scope0_id);
5598
5593
  const nearBox = {
5599
5594
  io: null,
5600
5595
  cbs: null
@@ -5609,12 +5604,16 @@ var diff_view_default = _template("d", (input) => {
5609
5604
  }, { rootMargin: "600px 0px" });
5610
5605
  nearBox.cbs.set(el, cb);
5611
5606
  nearBox.io.observe(el);
5612
- }, "d5", $scope0_id);
5613
- const load = _resume(async (base = baseRef) => {
5607
+ }, "d4", $scope0_id);
5608
+ const load = _resume(async (dirty = uncommitted, refresh = false) => {
5614
5609
  loading = true;
5615
5610
  error = null;
5616
5611
  try {
5617
- const qs = base ? `?base=${encodeURIComponent(base)}` : "";
5612
+ const q = new URLSearchParams();
5613
+ if (dirty) q.set("uncommitted", "1");
5614
+ if (input.focusPr) q.set("pr", input.focusPr);
5615
+ if (refresh) q.set("refresh", "1");
5616
+ const qs = q.size ? `?${q}` : "";
5618
5617
  const [diffData, reviewData] = await Promise.all([api$3(`/api/workspaces/${input.workspaceId}/diff${qs}`), api$3(`/api/workspaces/${input.workspaceId}/review`)]);
5619
5618
  repos = diffData;
5620
5619
  comments = reviewData.comments || [];
@@ -5623,23 +5622,23 @@ var diff_view_default = _template("d", (input) => {
5623
5622
  error = String(err.message || err);
5624
5623
  }
5625
5624
  loading = false;
5626
- }, "d6", $scope0_id);
5625
+ }, "d5", $scope0_id);
5627
5626
  const loadReview = _resume(async () => {
5628
5627
  try {
5629
5628
  const reviewData = await api$3(`/api/workspaces/${input.workspaceId}/review`);
5630
5629
  comments = reviewData.comments || [];
5631
5630
  viewed = new Set(reviewData.viewed || []);
5632
5631
  } catch {}
5633
- }, "d7", $scope0_id);
5632
+ }, "d6", $scope0_id);
5634
5633
  const repoList = repos || [];
5635
5634
  const allFiles = repoList.flatMap((r) => r.files.map((f) => ({
5636
5635
  ...f,
5637
- repo: r.worktree,
5636
+ repo: r.id,
5638
5637
  key: `${r.worktree}/${f.path}`
5639
5638
  })));
5640
5639
  const fileMatch = makeFileFilter(fileFilter);
5641
5640
  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)]));
5641
+ const filesByRepo = new Map(repoList.map((r) => [r.id, shownFiles.filter((f) => f.repo === r.id)]));
5643
5642
  const commentsByFile = (() => {
5644
5643
  const map = /* @__PURE__ */ new Map();
5645
5644
  for (const c of comments) {
@@ -5648,12 +5647,16 @@ var diff_view_default = _template("d", (input) => {
5648
5647
  }
5649
5648
  return map;
5650
5649
  })();
5651
- const defaultBranches = [...new Set(repoList.map((r) => r.default_branch).filter(Boolean))];
5652
5650
  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);
5651
+ const localDiffers = _resume((repo) => !!repo.pr && !!repo.localHead && (repo.localHead !== repo.head || repo.branch !== repo.pr.headRefName), "d7");
5652
+ const driftRepos = repoList.filter((r) => r.githubDrift && !dismissedDrift.has(r.id));
5653
+ const dismissDrift = _resume(() => {
5654
+ dismissedDrift = /* @__PURE__ */ new Set([...dismissedDrift, ...driftRepos.map((r) => r.id)]);
5656
5655
  }, "d8", $scope0_id);
5656
+ const reload = _resume(() => {
5657
+ dismissedDrift = /* @__PURE__ */ new Set();
5658
+ return load(uncommitted, true);
5659
+ }, "d9", $scope0_id);
5657
5660
  const toggleViewed = _resume((key) => {
5658
5661
  const next = new Set(viewed);
5659
5662
  const isViewed = !next.has(key);
@@ -5671,7 +5674,7 @@ var diff_view_default = _template("d", (input) => {
5671
5674
  }).catch(() => {
5672
5675
  error = "Saving viewed state failed — it may reset on reload.";
5673
5676
  });
5674
- }, "d9", $scope0_id);
5677
+ }, "d10", $scope0_id);
5675
5678
  const saveComment = _resume(async (file, text) => {
5676
5679
  const range = draft;
5677
5680
  draft = null;
@@ -5693,11 +5696,11 @@ var diff_view_default = _template("d", (input) => {
5693
5696
  } catch (err) {
5694
5697
  error = String(err.message || err);
5695
5698
  }
5696
- }, "d10", $scope0_id);
5699
+ }, "d11", $scope0_id);
5697
5700
  const removeComment = _resume(async (id) => {
5698
5701
  comments = comments.filter((c) => c.id !== id);
5699
5702
  await fetch(`/api/review-comments/${id}`, { method: "DELETE" });
5700
- }, "d11", $scope0_id);
5703
+ }, "d12", $scope0_id);
5701
5704
  const clearComments = _resume(async () => {
5702
5705
  comments = [];
5703
5706
  draft = null;
@@ -5706,7 +5709,7 @@ var diff_view_default = _template("d", (input) => {
5706
5709
  headers: { "content-type": "application/json" },
5707
5710
  body: JSON.stringify({ action: "clear" })
5708
5711
  });
5709
- }, "d12", $scope0_id);
5712
+ }, "d13", $scope0_id);
5710
5713
  const copyMarkdown = _resume(async () => {
5711
5714
  if (!await copyText(commentMarkdown(comments, repos))) {
5712
5715
  error = "Copy failed — clipboard needs HTTPS on this browser.";
@@ -5717,7 +5720,7 @@ var diff_view_default = _template("d", (input) => {
5717
5720
  copied = false;
5718
5721
  }, 1500);
5719
5722
  await clearComments();
5720
- }, "d13", $scope0_id);
5723
+ }, "d14", $scope0_id);
5721
5724
  const sendTo = _resume(async (sessionId) => {
5722
5725
  if (!sessionId) return;
5723
5726
  sending = true;
@@ -5737,57 +5740,57 @@ var diff_view_default = _template("d", (input) => {
5737
5740
  error = String(err.message || err);
5738
5741
  sending = false;
5739
5742
  }
5740
- }, "d14", $scope0_id);
5743
+ }, "d15", $scope0_id);
5741
5744
  const jumpTo = _resume((key) => {
5742
5745
  const el = ((scrollEl) => scrollEl())(_el_read_error).querySelector(`details[data-key="${CSS.escape(key)}"]`);
5743
5746
  if (el) {
5744
5747
  el.open = true;
5745
5748
  el.scrollIntoView({ block: "start" });
5746
5749
  }
5747
- }, "d15", $scope0_id);
5750
+ }, "d16", $scope0_id);
5748
5751
  _html(`<div class=diff-view><aside class=${!treeOpen ? "\"tree closed\"" : "tree"}>`);
5749
- _for_of(repoList.filter((r) => filesByRepo.get(r.worktree)?.length), (repo) => {
5752
+ _for_of(repoList.filter((r) => filesByRepo.get(r.id)?.length), (repo) => {
5750
5753
  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")}`);
5754
+ _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
5755
  _if(() => {
5753
- if (!collapsedRepos.has(repo.worktree)) {
5756
+ if (!collapsedRepos.has(repo.id)) {
5754
5757
  const $scope6_id = _scope_id();
5755
5758
  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");
5759
+ const repoRows = treeRows(filesByRepo.get(repo.id) || []);
5760
+ const dirKey = _resume((path) => `${repo.id}/${path}`, "d17", $scope6_id);
5761
+ const rowDir = _resume((row) => row.kind === "dir" ? row.path : row.file.path.includes("/") ? row.file.path.slice(0, row.file.path.lastIndexOf("/")) : "", "d18");
5759
5762
  const hiddenByCollapse = _resume((row) => {
5760
5763
  const dir = rowDir(row);
5761
5764
  for (const collapsed of collapsedDirs) {
5762
5765
  const [wt, path] = [collapsed.slice(0, collapsed.indexOf("/")), collapsed.slice(collapsed.indexOf("/") + 1)];
5763
- if (wt !== repo.worktree) continue;
5766
+ if (wt !== repo.id) continue;
5764
5767
  if (row.kind === "dir" && row.path === path) continue;
5765
5768
  if (dir === path || dir.startsWith(`${path}/`)) return true;
5766
5769
  }
5767
5770
  return false;
5768
- }, "d18", $scope6_id);
5771
+ }, "d19", $scope6_id);
5769
5772
  _for_of(repoRows, (row) => {
5770
5773
  const $scope7_id = _scope_id();
5771
5774
  _if(() => {
5772
5775
  if (!hiddenByCollapse(row) && row.kind === "dir") {
5773
- const $scope22_id = _scope_id();
5774
- _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 })));
5776
+ const $scope23_id = _scope_id();
5777
+ _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($scope23_id, "b")}</span>${_escape(row.label)}${_el_resume($scope23_id, "c")}/</button>${_el_resume($scope23_id, "a")}`);
5778
+ _script($scope23_id, "d25");
5779
+ _subscribe($if_content4__dirKey__closures, _subscribe($collapsedDirs__closures, writeScope($scope23_id, { Ccg: 1 })));
5777
5780
  return 0;
5778
5781
  } else if (!hiddenByCollapse(row) && row.kind !== "dir") {
5779
5782
  const $scope8_id = _scope_id();
5780
5783
  _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
5784
  _if(() => {
5782
5785
  if (commentsByFile.has(row.file.key)) {
5783
- const $scope25_id = _scope_id();
5786
+ const $scope28_id = _scope_id();
5784
5787
  _html("<span class=tree-note>💬</span>");
5785
- writeScope($scope25_id, {});
5788
+ writeScope($scope28_id, {});
5786
5789
  return 0;
5787
5790
  }
5788
5791
  }, $scope8_id, "e", 1, 1, 1, 0, 1);
5789
5792
  _html(`</button>${_el_resume($scope8_id, "a")}`);
5790
- _script($scope8_id, "d27");
5793
+ _script($scope8_id, "d26");
5791
5794
  _subscribe($commentsByFile__closures, _subscribe($viewed__closures, writeScope($scope8_id, {})));
5792
5795
  return 1;
5793
5796
  }
@@ -5813,9 +5816,9 @@ var diff_view_default = _template("d", (input) => {
5813
5816
  }
5814
5817
  }, $scope5_id, "d");
5815
5818
  _html("</div>");
5816
- _script($scope5_id, "d28");
5817
- writeScope($scope5_id, { g: repo?.worktree });
5818
- }, "worktree", $scope0_id, "a", 1, 1, 1, "</aside>", 1);
5819
+ _script($scope5_id, "d27");
5820
+ writeScope($scope5_id, { g: repo?.id });
5821
+ }, "id", $scope0_id, "a", 1, 1, 1, "</aside>", 1);
5819
5822
  _html("<div class=diff-col><div class=diff-head>");
5820
5823
  _if(() => {
5821
5824
  if (input.onExit) {
@@ -5823,7 +5826,7 @@ var diff_view_default = _template("d", (input) => {
5823
5826
  _html("<button class=icon-btn title=\"Back to terminal\">");
5824
5827
  icon_back_default({ size: 14 });
5825
5828
  _html(`</button>${_el_resume($scope1_id, "a")}`);
5826
- _script($scope1_id, "d29");
5829
+ _script($scope1_id, "d28");
5827
5830
  writeScope($scope1_id, { _: _scope_with_id($scope0_id) });
5828
5831
  return 0;
5829
5832
  }
@@ -5837,7 +5840,7 @@ var diff_view_default = _template("d", (input) => {
5837
5840
  if ((input.sessions || []).length === 1) {
5838
5841
  const $scope3_id = _scope_id();
5839
5842
  _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");
5843
+ _script($scope3_id, "d29");
5841
5844
  _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
5845
  return 0;
5843
5846
  } else if ((input.sessions || []).length) {
@@ -5848,30 +5851,30 @@ var diff_view_default = _template("d", (input) => {
5848
5851
  }, "d0", $scope4_id), () => {
5849
5852
  _html(`<select aria-label="Send comments to session"${_attr("disabled", sending)}>`);
5850
5853
  _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, {});
5854
+ const $scope29_id = _scope_id();
5855
+ _html(`<option${_attr_option_value(s.id)}>${_escape(s.title || s.agent)}${_el_resume($scope29_id, "b", $sg__input_sessions)}</option>${_el_resume($scope29_id, "a", $sg__input_sessions)}`);
5856
+ $si__input_sessions && writeScope($scope29_id, {});
5854
5857
  }, "id", $scope4_id, "c", $sg__input_sessions, 1, $sg__input_sessions, "</select>", 1);
5855
5858
  });
5856
5859
  _html("</span>");
5857
- _script($scope4_id, "d31");
5860
+ _script($scope4_id, "d30");
5858
5861
  _subscribe($sendTarget__closures, _subscribe($sending__closures, _subscribe($si__input_sessions && $input_sessions__closures, writeScope($scope4_id, {
5859
5862
  _: _scope_with_id($scope2_id),
5860
- Cc6: 1
5863
+ Ccc: 1
5861
5864
  }))));
5862
5865
  return 1;
5863
5866
  }
5864
5867
  }, $scope2_id, "c", $sg__input_sessions, $sg__input_sessions, $sg__input_sessions, 0, 1);
5865
- _script($scope2_id, "d32");
5868
+ _script($scope2_id, "d31");
5866
5869
  writeScope($scope2_id, {});
5867
5870
  return 0;
5868
5871
  }
5869
5872
  }, $scope0_id, "g", 1, 1, 1, "</span>");
5870
5873
  _if(() => {
5871
5874
  if (loading) {
5872
- const $scope27_id = _scope_id();
5875
+ const $scope30_id = _scope_id();
5873
5876
  _html("<span class=\"refresh-chip dim\"><span class=spin></span>Refreshing…</span>");
5874
- writeScope($scope27_id, {});
5877
+ writeScope($scope30_id, {});
5875
5878
  return 0;
5876
5879
  }
5877
5880
  }, $scope0_id, "h", 1, 1, 1, 0, 1);
@@ -5881,85 +5884,169 @@ var diff_view_default = _template("d", (input) => {
5881
5884
  icon_refresh_default({ size: 14 });
5882
5885
  _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
5886
  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>`);
5887
+ }, "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
5888
  _if(() => {
5907
5889
  if (error) {
5908
5890
  const $scope17_id = _scope_id();
5909
5891
  _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");
5892
+ _script($scope17_id, "d32");
5911
5893
  writeScope($scope17_id, {});
5912
5894
  return 0;
5913
5895
  }
5914
- }, $scope0_id, "u", 1, 1, 1, 0, 1);
5896
+ }, $scope0_id, "r", 1, 1, 1, 0, 1);
5915
5897
  _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);
5898
+ const $scope20_id = _scope_id();
5899
+ _html(`<p class=toast-error>${_escape(repo.worktree)}${_el_resume($scope20_id, "a")}: base <!>${_escape(repo.base)}${_el_resume($scope20_id, "b")} not found<!>${uncommitted ? " — showing uncommitted changes only" : ""}${_el_resume($scope20_id, "c")}.</p>`);
5900
+ writeScope($scope20_id, {});
5901
+ }, "id", $scope0_id, "s", 1, 1, 1, 0, 1);
5902
+ _for_of(repoList.filter((r) => r.contained && r.files.length), (repo) => {
5903
+ const $scope21_id = _scope_id();
5904
+ _html(`<p class=dim>${_escape(repo.worktree)}${_el_resume($scope21_id, "a")}<!>${repo.pr ? ` #${_escape(`${repo.pr.number}`)}` : ""}${_el_resume($scope21_id, "b")}: already in <!>${_escape(repo.base)}${_el_resume($scope21_id, "c")}<!>${uncommitted ? " — showing uncommitted changes only" : ""}${_el_resume($scope21_id, "d")}.</p>`);
5905
+ writeScope($scope21_id, {});
5906
+ }, "id", $scope0_id, "t", 1, 1, 1, 0, 1);
5907
+ _if(() => {
5908
+ if (driftRepos.length) {
5909
+ const $scope18_id = _scope_id();
5910
+ _html("<p class=toast-error>");
5911
+ _for_of(driftRepos, (repo) => {
5912
+ const $scope31_id = _scope_id();
5913
+ _html(`${_escape(repo.worktree)}${_el_resume($scope31_id, "a")} #<!>${_escape(repo.pr.number)}${_el_resume($scope31_id, "b")}: origin/<!>${_escape(repo.pr.headRefName)}${_el_resume($scope31_id, "c")} is <!>${_escape(shortSha(repo.head))}${_el_resume($scope31_id, "d")}, GitHub last saw <!>${_escape(shortSha(repo.pr.headRefOid))}${_el_resume($scope31_id, "e")}.`);
5914
+ writeScope($scope31_id, {});
5915
+ }, "id", $scope18_id, "a");
5916
+ _html(`<button class=btn${_attr("disabled", loading)}>${loading ? "Reloading…" : "Reload"}${_el_resume($scope18_id, "c")}</button>${_el_resume($scope18_id, "b")}<button title=Dismiss>×</button>${_el_resume($scope18_id, "d")}</p>`);
5917
+ _script($scope18_id, "d33");
5918
+ writeScope($scope18_id, {});
5919
+ return 0;
5920
+ }
5921
+ }, $scope0_id, "u", 1, 1, 1, 0, 1);
5922
+ _for_of(repoList.filter(localDiffers), (repo) => {
5923
+ const $scope22_id = _scope_id();
5924
+ _html(`<p class=dim>${_escape(repo.worktree)}${_el_resume($scope22_id, "a")} #<!>${_escape(repo.pr.number)}${_el_resume($scope22_id, "b")}: this view is <!>${_escape(shortSha(repo.head))}${_el_resume($scope22_id, "c")} (<!>${_escape(repo.pr.headRefName)}${_el_resume($scope22_id, "d")}). Local is <!>${_escape(shortSha(repo.localHead))}${_el_resume($scope22_id, "e")} (<!>${_escape(repo.branch)}${_el_resume($scope22_id, "f")}<!>${uncommitted ? ", plus uncommitted" : ""}${_el_resume($scope22_id, "g")}).</p>`);
5925
+ writeScope($scope22_id, {});
5926
+ }, "id", $scope0_id, "v", 1, 1, 1, 0, 1);
5920
5927
  _if(() => {
5921
5928
  if (!repos && loading) {
5922
- const $scope30_id = _scope_id();
5929
+ const $scope32_id = _scope_id();
5923
5930
  _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, {});
5931
+ writeScope($scope32_id, {});
5925
5932
  return 0;
5926
5933
  }
5927
5934
  }, $scope0_id, "w", 1, 1, 1, 0, 1);
5928
5935
  _if(() => {
5929
5936
  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, {});
5937
+ const $scope26_id = _scope_id();
5938
+ _html(`<p class="dim center-pad">${repoList.some((r) => r.pr) ? "No file changes on these PRs." : "No changes vs base."}${_el_resume($scope26_id, "a")}</p>`);
5939
+ writeScope($scope26_id, {});
5933
5940
  return 0;
5934
5941
  } else if (repos && !shownFiles.length) {
5935
- const $scope18_id = _scope_id();
5936
- _html(`<p class="dim center-pad">No files match "<!>${_escape(fileFilter)}${_el_resume($scope18_id, "a")}".</p>`);
5937
- writeScope($scope18_id, {});
5942
+ const $scope19_id = _scope_id();
5943
+ _html(`<p class="dim center-pad">No files match "<!>${_escape(fileFilter)}${_el_resume($scope19_id, "a")}".</p>`);
5944
+ writeScope($scope19_id, {});
5938
5945
  return 1;
5939
5946
  }
5940
5947
  }, $scope0_id, "x", 1, 1, 1, 0, 1);
5941
5948
  _for_of(repoList, (repo) => {
5942
5949
  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();
5950
+ const $for_content3__repo_pr_number__closures = /* @__PURE__ */ new Set();
5951
+ const $for_content3__repo_pr_url__closures = /* @__PURE__ */ new Set();
5952
+ const $for_content3__repo_pr_title__closures = /* @__PURE__ */ new Set();
5953
+ const $for_content3__repo_pr_headRefName__closures = /* @__PURE__ */ new Set();
5954
+ const $for_content3__repo_head__closures = /* @__PURE__ */ new Set();
5945
5955
  const $for_content3__repo_base__closures = /* @__PURE__ */ new Set();
5956
+ const $for_content3__repo_baseSha__closures = /* @__PURE__ */ new Set();
5957
+ const $for_content3__repo_full_name__closures = /* @__PURE__ */ new Set();
5958
+ const $for_content3__repo_branch__closures = /* @__PURE__ */ new Set();
5959
+ const $for_content3__repo_localHead__closures = /* @__PURE__ */ new Set();
5960
+ const $for_content3__repo__closures = /* @__PURE__ */ new Set();
5961
+ const $for_content3__repo_id__closures = /* @__PURE__ */ new Set();
5962
+ const $for_content3__repo_pr__closures = /* @__PURE__ */ new Set();
5963
+ const $for_content3__repo_worktree__closures = /* @__PURE__ */ new Set();
5946
5964
  const $for_content3__repoFiles__closures = /* @__PURE__ */ new Set();
5947
- const repoFiles = filesByRepo.get(repo.worktree) || [];
5965
+ const repoFiles = filesByRepo.get(repo.id) || [];
5948
5966
  _if(() => {
5949
5967
  if (repoFiles.length) {
5950
5968
  const $scope10_id = _scope_id();
5951
- _html("<section class=repo-section>");
5969
+ _html(`<section class=repo-section${repo.pr ? _attr("data-pr", `${repo.worktree}/${repo.pr.number}`) : ""}>`);
5952
5970
  _if(() => {
5953
5971
  if (repoList.length > 1) {
5954
- 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 })))));
5972
+ const $scope24_id = _scope_id();
5973
+ _html(`<button class=${collapsedRepos.has(repo.id) ? "\"repo-sep closed\"" : "repo-sep"}><span class=tree-chev>${collapsedRepos.has(repo.id) ? "▸" : "▾"}${_el_resume($scope24_id, "b")}</span><span class=mono>${_escape(repo.worktree)}${_el_resume($scope24_id, "c")}</span>`);
5974
+ _if(() => {
5975
+ if (repo.pr) {
5976
+ const $scope33_id = _scope_id();
5977
+ _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($scope33_id, "b")}</a>${_el_resume($scope33_id, "a")}<span class=dim>${_escape(repo.pr.title)}${_el_resume($scope33_id, "c")}</span>`);
5978
+ _script($scope33_id, "d34");
5979
+ _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($scope33_id, {
5980
+ Ca1: 2,
5981
+ Ca2: 2,
5982
+ Ca3: 2
5983
+ }))))))));
5984
+ return 0;
5985
+ } else {
5986
+ const $scope34_id = _scope_id();
5987
+ _html(`<span class=dim>${_escape(repo.full_name)}${_el_resume($scope34_id, "a")}</span>`);
5988
+ _subscribe($for_content3__repo_full_name__closures, writeScope($scope34_id, {}));
5989
+ return 1;
5990
+ }
5991
+ }, $scope24_id, "d");
5992
+ _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($scope24_id, "f")}</span>${_el_resume($scope24_id, "e")}<span class=chip${_attr("title", `${repo.base} ${repo.baseSha}`)}>vs <!>${_escape(repo.base)}${_el_resume($scope24_id, "h")}</span>${_el_resume($scope24_id, "g")}`);
5993
+ _if(() => {
5994
+ if (localDiffers(repo)) {
5995
+ const $scope35_id = _scope_id();
5996
+ _html(`<span class=chip${_attr("title", `checked out ${repo.branch} ${repo.localHead}`)}>local <!>${_escape(repo.branch)}${_el_resume($scope35_id, "b")}</span>${_el_resume($scope35_id, "a")}`);
5997
+ _subscribe($for_content3__repo_localHead__closures, _subscribe($for_content3__repo_branch__closures, writeScope($scope35_id, { Ca5: 2 })));
5998
+ return 0;
5999
+ }
6000
+ }, $scope24_id, "i", 1, 1, 1, 0, 1);
6001
+ _html(`</button>${_el_resume($scope24_id, "a")}`);
6002
+ _script($scope24_id, "d35");
6003
+ _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($scope24_id, { Cch: 1 }))))))))));
5958
6004
  return 0;
6005
+ } else {
6006
+ const $scope27_id = _scope_id();
6007
+ _html("<p class=repo-meta>");
6008
+ _if(() => {
6009
+ if (repo.pr) {
6010
+ const $scope36_id = _scope_id();
6011
+ _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($scope36_id, "b")}</a>${_el_resume($scope36_id, "a")}<span class=dim>${_escape(repo.pr.title)}${_el_resume($scope36_id, "c")}</span>`);
6012
+ _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($scope36_id, {
6013
+ Cx: 1,
6014
+ Cy: 1,
6015
+ Cz: 1,
6016
+ Ca0: 1,
6017
+ Ca1: 3,
6018
+ Ca2: 3,
6019
+ Ca3: 3
6020
+ }))))))));
6021
+ return 0;
6022
+ }
6023
+ }, $scope27_id, "a");
6024
+ _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($scope27_id, "c")}</span>${_el_resume($scope27_id, "b")}<span class=chip${_attr("title", `${repo.base} ${repo.baseSha}`)}>vs <!>${_escape(repo.base)}${_el_resume($scope27_id, "e")}</span>${_el_resume($scope27_id, "d")}`);
6025
+ _if(() => {
6026
+ if (localDiffers(repo)) {
6027
+ const $scope37_id = _scope_id();
6028
+ _html(`<span class=chip${_attr("title", `checked out ${repo.branch} ${repo.localHead}`)}>local <!>${_escape(repo.branch)}${_el_resume($scope37_id, "b")}</span>${_el_resume($scope37_id, "a")}`);
6029
+ _subscribe($for_content3__repo_localHead__closures, _subscribe($for_content3__repo_branch__closures, writeScope($scope37_id, {
6030
+ Ca5: 3,
6031
+ Ca6: 1
6032
+ })));
6033
+ return 0;
6034
+ }
6035
+ }, $scope27_id, "f", 1, 1, 1, 0, 1);
6036
+ _html("</p>");
6037
+ _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($scope27_id, {
6038
+ Ct: 1,
6039
+ Cv: 1,
6040
+ Ca1: 1,
6041
+ Ca2: 1,
6042
+ Ca3: 1,
6043
+ Ca5: 1
6044
+ })))))));
6045
+ return 1;
5959
6046
  }
5960
- }, $scope10_id, "a", 1, 1, 1, 0, 1);
6047
+ }, $scope10_id, "b", 1, 1, 1, 0, 1);
5961
6048
  _if(() => {
5962
- if (!(repoList.length > 1 && collapsedRepos.has(repo.worktree))) {
6049
+ if (!(repoList.length > 1 && collapsedRepos.has(repo.id))) {
5963
6050
  const $scope11_id = _scope_id();
5964
6051
  _html("<div class=files>");
5965
6052
  _for_of(repoFiles, (file) => {
@@ -5972,39 +6059,39 @@ var diff_view_default = _template("d", (input) => {
5972
6059
  _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
6060
  _if(() => {
5974
6061
  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, {});
6062
+ const $scope38_id = _scope_id();
6063
+ _html(`<span class=chip>💬 <!>${_escape(fileComments.length)}${_el_resume($scope38_id, "a")}</span>`);
6064
+ writeScope($scope38_id, {});
5978
6065
  return 0;
5979
6066
  }
5980
6067
  }, $scope12_id, "f", 1, 1, 1, 0, 1);
5981
6068
  _html(`<label class=viewed><input${_attr_input_checked($scope12_id, "h", viewed.has(file.key), _resume(function() {
5982
6069
  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>`);
6070
+ }, "d2", $scope12_id))} type=checkbox>${_el_resume($scope12_id, "h")}<span class=dim>viewed</span></label>${_el_resume($scope12_id, "g")}</summary>`);
5984
6071
  _if(() => {
5985
6072
  if (file.binary) {
5986
- const $scope32_id = _scope_id();
6073
+ const $scope39_id = _scope_id();
5987
6074
  _html("<p class=\"dim diff-pad\">binary file</p>");
5988
- writeScope($scope32_id, {});
6075
+ writeScope($scope39_id, {});
5989
6076
  return 0;
5990
6077
  } else if (file.tooLarge) {
5991
- const $scope33_id = _scope_id();
6078
+ const $scope40_id = _scope_id();
5992
6079
  _html("<p class=\"dim diff-pad\">file too large to display</p>");
5993
- writeScope($scope33_id, {});
6080
+ writeScope($scope40_id, {});
5994
6081
  return 1;
5995
6082
  } else if (file.old == null && file.new == null) {
5996
- const $scope34_id = _scope_id();
6083
+ const $scope41_id = _scope_id();
5997
6084
  _html("<p class=\"dim diff-pad\">file could not be read</p>");
5998
- writeScope($scope34_id, {});
6085
+ writeScope($scope41_id, {});
5999
6086
  return 2;
6000
6087
  } else if (expanded) {
6001
6088
  const $scope15_id = _scope_id();
6002
6089
  _if(() => {
6003
6090
  {
6004
- const $scope24_id = _scope_id();
6005
- _html(`<div class=diff-wait></div>${_el_resume($scope24_id, "a")}`);
6006
- _script($scope24_id, "d35");
6007
- writeScope($scope24_id, {});
6091
+ const $scope25_id = _scope_id();
6092
+ _html(`<div class=diff-wait></div>${_el_resume($scope25_id, "a")}`);
6093
+ _script($scope25_id, "d36");
6094
+ writeScope($scope25_id, {});
6008
6095
  return 0;
6009
6096
  }
6010
6097
  }, $scope15_id, "a", 1, 1, 1, 0, 1);
@@ -6013,7 +6100,7 @@ var diff_view_default = _template("d", (input) => {
6013
6100
  }
6014
6101
  }, $scope12_id, "i");
6015
6102
  _html(`</details>${_el_resume($scope12_id, "a")}`);
6016
- _script($scope12_id, "d36");
6103
+ _script($scope12_id, "d37");
6017
6104
  _subscribe($toggleViewed2__closures, _subscribe($commentsByFile__closures, _subscribe($viewed__closures, writeScope($scope12_id, {
6018
6105
  k: file,
6019
6106
  l: file?.key,
@@ -6027,57 +6114,59 @@ var diff_view_default = _template("d", (input) => {
6027
6114
  z: $for_content4__file__closures,
6028
6115
  a0: $for_content4__file_key__closures,
6029
6116
  a1: $for_content4__fileComments__closures,
6030
- Cc1: 1,
6031
- Cch: 1
6117
+ Cc6: 1,
6118
+ Cco: 1
6032
6119
  }))));
6033
6120
  }, "key", $scope11_id, "a", 1, 1, 1, "</div>", 1);
6034
6121
  _subscribe($for_content3__repoFiles__closures, writeScope($scope11_id, {}));
6035
6122
  return 0;
6036
6123
  }
6037
- }, $scope10_id, "b", 1, 1, 1, 0, 1);
6038
- _html("</section>");
6124
+ }, $scope10_id, "c", 1, 1, 1, 0, 1);
6125
+ _html(`</section>${_el_resume($scope10_id, "a")}`);
6039
6126
  _subscribe($repoList_length__closures, _subscribe($collapsedRepos__closures, writeScope($scope10_id, {})));
6040
6127
  return 0;
6041
6128
  }
6042
6129
  }, $scope9_id, "a", 1, 1, 1, 0, 1);
6043
6130
  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
6131
+ c: repo,
6132
+ d: repo?.id,
6133
+ f: repo?.pr,
6134
+ g: repo?.worktree,
6135
+ h: repo?.pr?.number,
6136
+ i: repo?.pr?.url,
6137
+ j: repo?.pr?.title,
6138
+ k: repo?.pr?.headRefName,
6139
+ l: repo?.head,
6140
+ m: repo?.base,
6141
+ n: repo?.baseSha,
6142
+ o: repo?.full_name,
6143
+ p: repo?.branch,
6144
+ q: repo?.localHead,
6145
+ r: repoFiles,
6146
+ x: $for_content3__repo_pr_number__closures,
6147
+ y: $for_content3__repo_pr_url__closures,
6148
+ z: $for_content3__repo_pr_title__closures,
6149
+ a0: $for_content3__repo_pr_headRefName__closures,
6150
+ a1: $for_content3__repo_head__closures,
6151
+ a2: $for_content3__repo_base__closures,
6152
+ a3: $for_content3__repo_baseSha__closures,
6153
+ a4: $for_content3__repo_full_name__closures,
6154
+ a5: $for_content3__repo_branch__closures,
6155
+ a6: $for_content3__repo_localHead__closures,
6156
+ t: $for_content3__repo__closures,
6157
+ u: $for_content3__repo_id__closures,
6158
+ v: $for_content3__repo_pr__closures,
6159
+ w: $for_content3__repo_worktree__closures,
6160
+ a7: $for_content3__repoFiles__closures
6052
6161
  });
6053
- }, "worktree", $scope0_id, "y");
6054
- _html(`</div>${_el_resume($scope0_id, "t")}</div>`);
6162
+ }, "id", $scope0_id, "y");
6163
+ _html(`</div>${_el_resume($scope0_id, "q")}</div>`);
6055
6164
  _if(() => {}, $scope0_id, "z");
6056
6165
  _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
6166
  _script($scope0_id, "d40");
6167
+ _script($scope0_id, "d41");
6080
6168
  writeScope($scope0_id, {
6169
+ a2: input.focusPr,
6081
6170
  a3: input.workspaceId,
6082
6171
  a4: input.active,
6083
6172
  a5: input.sessions,
@@ -6096,49 +6185,52 @@ var diff_view_default = _template("d", (input) => {
6096
6185
  an: sending,
6097
6186
  ao: sendTarget,
6098
6187
  aq: fileFilter,
6099
- ar: baseRef,
6188
+ ar: uncommitted,
6100
6189
  at: treeOpen,
6101
- aw: collapsedDirs,
6102
- ax: collapsedRepos,
6190
+ av: collapsedDirs,
6191
+ aw: collapsedRepos,
6192
+ ax: dismissedDrift,
6103
6193
  ay: toggleRepo,
6104
6194
  b0: nearBox.cbs,
6105
6195
  b1: nearBox.io,
6106
6196
  b3: observeNear,
6107
6197
  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,
6198
+ b6: loadReview,
6199
+ b8: repoList,
6200
+ ba: repoList.length,
6201
+ bb: allFiles,
6202
+ bc: allFiles?.length,
6203
+ bd: fileMatch,
6204
+ bf: shownFiles,
6205
+ bj: filesByRepo,
6206
+ bl: commentsByFile,
6118
6207
  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
6208
+ bn: localDiffers,
6209
+ bp: driftRepos,
6210
+ bs: dismissDrift,
6211
+ bt: reload,
6212
+ bu: toggleViewed,
6213
+ bv: saveComment,
6214
+ bw: removeComment,
6215
+ bx: clearComments,
6216
+ c0: copyMarkdown,
6217
+ c1: sendTo,
6218
+ c2: jumpTo,
6219
+ cg: $collapsedDirs__closures,
6220
+ c6: $viewed__closures,
6221
+ co: $commentsByFile__closures,
6222
+ cn: $filesByRepo__closures,
6223
+ c4: $si__input_sessions_0_title && $input_sessions_0_title__closures,
6224
+ c5: $si__input_sessions_0_agent && $input_sessions_0_agent__closures,
6225
+ cc: $sending__closures,
6226
+ c3: $si__input_sessions && $input_sessions__closures,
6227
+ cd: $sendTarget__closures,
6228
+ ch: $collapsedRepos__closures,
6229
+ c8: $draft__closures,
6230
+ cs: $saveComment2__closures,
6231
+ ct: $removeComment2__closures,
6232
+ cr: $toggleViewed2__closures,
6233
+ ck: $repoList_length__closures
6142
6234
  });
6143
6235
  _resume_branch($scope0_id);
6144
6236
  });
@@ -6513,54 +6605,120 @@ var usage_stats_default = _template("n", (input) => {
6513
6605
  _resume_branch($scope0_id);
6514
6606
  });
6515
6607
  //#endregion
6516
- //#region src/tags/work-view.marko
6517
- var api$1;
6518
- var work_view_default = _template("o", (input) => {
6519
- const $scope0_reason = _scope_reason(), $sg__input_agents = _serialize_guard($scope0_reason, 8), $sg__input_workspace = _serialize_guard($scope0_reason, 2), $si__input_workspace = _serialize_if($scope0_reason, 2), $si__input_active = _serialize_if($scope0_reason, 4), $si__input_agents = _serialize_if($scope0_reason, 8), $si__input_preferred = _serialize_if($scope0_reason, 5);
6608
+ //#region src/tags/prompt-dialog.marko
6609
+ var prompt_dialog_default = _template("i", (input) => {
6610
+ 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);
6520
6611
  const $scope0_id = _scope_id();
6521
- const $input_active__closures = /* @__PURE__ */ new Set();
6522
- const $input_agents__closures = /* @__PURE__ */ new Set();
6523
- const $view__closures = /* @__PURE__ */ new Set();
6524
- const $ws_id__closures = /* @__PURE__ */ new Set();
6525
- const $ws_name__closures = /* @__PURE__ */ new Set();
6526
- const $shownSessions__closures = /* @__PURE__ */ new Set();
6527
- const $curTab__closures = /* @__PURE__ */ new Set();
6528
- const $closeSession2__closures = /* @__PURE__ */ new Set();
6529
- const $newSession2__closures = /* @__PURE__ */ new Set();
6530
- const $input_preferred__closures = /* @__PURE__ */ new Set();
6531
- const $spawning__closures = /* @__PURE__ */ new Set();
6532
- let sessions = null;
6533
- let tab = null;
6534
- let view = "term";
6535
- let drawerOpen = false;
6536
- let usageOpen = false;
6537
- let error = null;
6538
- let spawning = false;
6539
- let renaming = null;
6540
- const ws = input.workspace;
6541
- const shownSessions = sessions ?? input.allStatus?.[ws.id]?.sessions ?? null;
6542
- const curTab = (() => {
6543
- if (tab && shownSessions?.some((s) => s.id === tab)) return tab;
6544
- const saved = workspaceTab(ws.id);
6545
- if (saved && shownSessions?.some((s) => s.id === saved)) return saved;
6546
- return shownSessions?.[0]?.id ?? null;
6547
- })();
6548
- const createSession = _resume(async (agent, select) => {
6549
- spawning = true;
6550
- try {
6551
- const session = await api$1(`/api/workspaces/${ws.id}/sessions`, {
6552
- method: "POST",
6553
- headers: { "content-type": "application/json" },
6554
- body: JSON.stringify({ agent })
6555
- });
6556
- if (select) {
6557
- sessions = (sessions || []).concat(session);
6558
- tab = session.id;
6559
- }
6560
- return session;
6561
- } catch (err) {
6562
- error = String(err.message || err);
6563
- return null;
6612
+ const $modalview_content__subscribers = /* @__PURE__ */ new Set();
6613
+ const $input_submitLabel__closures = /* @__PURE__ */ new Set();
6614
+ const $value__closures = /* @__PURE__ */ new Set();
6615
+ const $input_label__closures = /* @__PURE__ */ new Set();
6616
+ const $input_placeholder__closures = /* @__PURE__ */ new Set();
6617
+ let value = input.value || "";
6618
+ const submit = _resume(() => {
6619
+ const v = value.trim();
6620
+ if (v) input.onSubmit(v);
6621
+ }, "i1", $scope0_id);
6622
+ _set_serialize_reason({
6623
+ 0: $sg__input_onCancel,
6624
+ 1: $sg__input_label,
6625
+ 2: $sg__input_onCancel
6626
+ });
6627
+ const $childScope = _peek_scope_id();
6628
+ modal_view_default({
6629
+ label: input.label,
6630
+ class: "prompt-dlg",
6631
+ onClose: _resume(function() {
6632
+ input.onCancel();
6633
+ }, "i2", $scope0_id),
6634
+ footer: attrTag({ content: _content_resume("i5", () => {
6635
+ _scope_reason();
6636
+ const $scope1_id = _scope_id();
6637
+ _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")}`);
6638
+ _script($scope1_id, "i3");
6639
+ _subscribe($value__closures, _subscribe($si__input_submitLabel && $input_submitLabel__closures, writeScope($scope1_id, { _: _scope_with_id($scope0_id) })));
6640
+ _resume_branch($scope1_id);
6641
+ }, $scope0_id) }),
6642
+ content: _content("i6", () => {
6643
+ _scope_reason();
6644
+ const $scope2_id = _scope_id();
6645
+ _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) => {
6646
+ value = _new_value;
6647
+ }, "i0", $scope2_id))}${_attr("placeholder", input.placeholder)}>${_el_resume($scope2_id, "b")}`);
6648
+ _script($scope2_id, "i4");
6649
+ _subscribe($value__closures, _subscribe($si__input_placeholder && $input_placeholder__closures, _subscribe($si__input_label && $input_label__closures, _subscribe($modalview_content__subscribers, writeScope($scope2_id, {
6650
+ _: _scope_with_id($scope0_id),
6651
+ Cp: 1
6652
+ })))));
6653
+ _resume_branch($scope2_id);
6654
+ })
6655
+ });
6656
+ _script($scope0_id, "i7");
6657
+ writeScope($scope0_id, {
6658
+ e: input.onSubmit,
6659
+ g: input.onCancel,
6660
+ h: input.submitLabel,
6661
+ j: value,
6662
+ l: submit,
6663
+ B2: $modalview_content__subscribers,
6664
+ n: $si__input_submitLabel && $input_submitLabel__closures,
6665
+ p: $value__closures,
6666
+ m: $si__input_label && $input_label__closures,
6667
+ o: $si__input_placeholder && $input_placeholder__closures,
6668
+ a: _serialize_if($scope0_reason, 0) && _existing_scope($childScope)
6669
+ });
6670
+ _resume_branch($scope0_id);
6671
+ });
6672
+ //#endregion
6673
+ //#region src/tags/work-view.marko
6674
+ var api$1;
6675
+ var work_view_default = _template("o", (input) => {
6676
+ const $scope0_reason = _scope_reason(), $sg__input_agents = _serialize_guard($scope0_reason, 8), $sg__input_workspace = _serialize_guard($scope0_reason, 2), $si__input_workspace = _serialize_if($scope0_reason, 2), $si__input_active = _serialize_if($scope0_reason, 4), $si__input_agents = _serialize_if($scope0_reason, 8), $si__input_preferred = _serialize_if($scope0_reason, 5);
6677
+ const $scope0_id = _scope_id();
6678
+ const $input_active__closures = /* @__PURE__ */ new Set();
6679
+ const $input_agents__closures = /* @__PURE__ */ new Set();
6680
+ const $view__closures = /* @__PURE__ */ new Set();
6681
+ const $ws_id__closures = /* @__PURE__ */ new Set();
6682
+ const $ws_name__closures = /* @__PURE__ */ new Set();
6683
+ const $shownSessions__closures = /* @__PURE__ */ new Set();
6684
+ const $curTab__closures = /* @__PURE__ */ new Set();
6685
+ const $closeSession2__closures = /* @__PURE__ */ new Set();
6686
+ const $newSession2__closures = /* @__PURE__ */ new Set();
6687
+ const $input_preferred__closures = /* @__PURE__ */ new Set();
6688
+ const $spawning__closures = /* @__PURE__ */ new Set();
6689
+ let sessions = null;
6690
+ let tab = null;
6691
+ let view = "term";
6692
+ let focusPr = "";
6693
+ let drawerOpen = false;
6694
+ let usageOpen = false;
6695
+ let error = null;
6696
+ let spawning = false;
6697
+ let renaming = null;
6698
+ const ws = input.workspace;
6699
+ const shownSessions = sessions ?? input.allStatus?.[ws.id]?.sessions ?? null;
6700
+ const curTab = (() => {
6701
+ if (tab && shownSessions?.some((s) => s.id === tab)) return tab;
6702
+ const saved = workspaceTab(ws.id);
6703
+ if (saved && shownSessions?.some((s) => s.id === saved)) return saved;
6704
+ return shownSessions?.[0]?.id ?? null;
6705
+ })();
6706
+ const createSession = _resume(async (agent, select) => {
6707
+ spawning = true;
6708
+ try {
6709
+ const session = await api$1(`/api/workspaces/${ws.id}/sessions`, {
6710
+ method: "POST",
6711
+ headers: { "content-type": "application/json" },
6712
+ body: JSON.stringify({ agent })
6713
+ });
6714
+ if (select) {
6715
+ sessions = (sessions || []).concat(session);
6716
+ tab = session.id;
6717
+ }
6718
+ return session;
6719
+ } catch (err) {
6720
+ error = String(err.message || err);
6721
+ return null;
6564
6722
  } finally {
6565
6723
  spawning = false;
6566
6724
  }
@@ -6648,7 +6806,7 @@ var work_view_default = _template("o", (input) => {
6648
6806
  13: _serialize_guard($scope0_reason, 6),
6649
6807
  14: $sg__input_agents,
6650
6808
  15: 1,
6651
- 16: 1
6809
+ 17: 1
6652
6810
  });
6653
6811
  const $childScope = _peek_scope_id();
6654
6812
  side_nav_default({
@@ -6688,14 +6846,17 @@ var work_view_default = _template("o", (input) => {
6688
6846
  }, "o13", $scope0_id),
6689
6847
  onRenameSession: _resume(function(s) {
6690
6848
  promptRename(s);
6691
- }, "o14", $scope0_id)
6849
+ }, "o14", $scope0_id),
6850
+ onReviewOpened: _resume(function(pr) {
6851
+ focusPr = pr || "";
6852
+ }, "o15", $scope0_id)
6692
6853
  });
6693
6854
  _html("<div class=main-col>");
6694
6855
  _if(() => {
6695
6856
  if (error) {
6696
6857
  const $scope5_id = _scope_id();
6697
6858
  _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");
6859
+ _script($scope5_id, "o29");
6699
6860
  writeScope($scope5_id, {});
6700
6861
  return 0;
6701
6862
  }
@@ -6708,13 +6869,14 @@ var work_view_default = _template("o", (input) => {
6708
6869
  workspaceId: ws.id,
6709
6870
  sessions: shownSessions || [],
6710
6871
  active: input.active && view === "diff",
6872
+ focusPr,
6711
6873
  onSent: _resume(function(sessionId) {
6712
6874
  tab = sessionId;
6713
6875
  view = "term";
6714
- }, "o15", $scope0_id),
6876
+ }, "o16", $scope0_id),
6715
6877
  onExit: _resume(function() {
6716
6878
  view = "term";
6717
- }, "o16", $scope0_id)
6879
+ }, "o17", $scope0_id)
6718
6880
  });
6719
6881
  _show_end($scope0_id, "f", $show);
6720
6882
  _html(`<div class=${view === "diff" ? "\"term-area as-bar\"" : "term-area"}>`);
@@ -6753,31 +6915,32 @@ var work_view_default = _template("o", (input) => {
6753
6915
  sessions: shownSessions,
6754
6916
  onNewSession: _resume(function(agent) {
6755
6917
  newSession(ws.id, agent);
6756
- }, "o17", $scope2_id),
6918
+ }, "o18", $scope2_id),
6757
6919
  onOpenTab: _resume(function(id) {
6758
6920
  tab = id;
6759
- }, "o18", $scope2_id),
6921
+ }, "o19", $scope2_id),
6760
6922
  onCloseSession: _resume(function(id) {
6761
6923
  closeSession(id);
6762
- }, "o19", $scope2_id),
6924
+ }, "o20", $scope2_id),
6763
6925
  onRenameSession: _resume(function(id) {
6764
6926
  const target = (shownSessions || []).find((x) => x.id === id);
6765
6927
  if (target) renaming = target;
6766
- }, "o20", $scope2_id),
6928
+ }, "o21", $scope2_id),
6767
6929
  active: input.active && view === "term" && s.id === curTab,
6768
6930
  mode: view,
6769
6931
  onToggleDiff: _resume(function() {
6932
+ focusPr = "";
6770
6933
  view = "diff";
6771
- }, "o21", $scope2_id),
6934
+ }, "o22", $scope2_id),
6772
6935
  onExitReview: _resume(function() {
6773
6936
  view = "term";
6774
- }, "o22", $scope2_id),
6937
+ }, "o23", $scope2_id),
6775
6938
  onOpenNav: _resume(function() {
6776
6939
  drawerOpen = true;
6777
- }, "o23", $scope2_id),
6940
+ }, "o24", $scope2_id),
6778
6941
  onManage: _resume(function() {
6779
6942
  input.onManage?.(ws.id);
6780
- }, "o24", $scope2_id)
6943
+ }, "o25", $scope2_id)
6781
6944
  });
6782
6945
  _show_end($scope2_id, "d", $show2);
6783
6946
  _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 +6952,7 @@ var work_view_default = _template("o", (input) => {
6789
6952
  if (!shownSessions.length) {
6790
6953
  const $scope3_id = _scope_id();
6791
6954
  _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");
6955
+ _script($scope3_id, "o30");
6793
6956
  _subscribe($spawning__closures, _subscribe($si__input_preferred && $input_preferred__closures, writeScope($scope3_id, {})));
6794
6957
  return 0;
6795
6958
  }
@@ -6809,7 +6972,7 @@ var work_view_default = _template("o", (input) => {
6809
6972
  const $scope4_id = _scope_id();
6810
6973
  usage_stats_default({ onClose: _resume(function() {
6811
6974
  usageOpen = false;
6812
- }, "o25", $scope4_id) });
6975
+ }, "o26", $scope4_id) });
6813
6976
  writeScope($scope4_id, {});
6814
6977
  return 0;
6815
6978
  }
@@ -6825,21 +6988,21 @@ var work_view_default = _template("o", (input) => {
6825
6988
  onSubmit: _resume(function(title) {
6826
6989
  renameSession(renaming.id, title);
6827
6990
  renaming = null;
6828
- }, "o26", $scope6_id),
6991
+ }, "o27", $scope6_id),
6829
6992
  onCancel: _resume(function() {
6830
6993
  renaming = null;
6831
- }, "o27", $scope6_id)
6994
+ }, "o28", $scope6_id)
6832
6995
  });
6833
6996
  writeScope($scope6_id, { a: _existing_scope($childScope4) });
6834
6997
  return 0;
6835
6998
  }
6836
6999
  }, $scope0_id, "i");
6837
7000
  _html("</div>");
6838
- _script($scope0_id, "o30");
6839
7001
  _script($scope0_id, "o31");
6840
7002
  _script($scope0_id, "o32");
6841
7003
  _script($scope0_id, "o33");
6842
7004
  _script($scope0_id, "o34");
7005
+ _script($scope0_id, "o35");
6843
7006
  writeScope($scope0_id, {
6844
7007
  m: input.allStatus,
6845
7008
  n: input.active,
@@ -6850,40 +7013,40 @@ var work_view_default = _template("o", (input) => {
6850
7013
  v: sessions,
6851
7014
  w: tab,
6852
7015
  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,
7016
+ a1: drawerOpen,
7017
+ a3: error,
7018
+ a4: spawning,
7019
+ a6: renaming?.title,
7020
+ a7: renaming?.agent,
7021
+ a8: renaming?.id,
7022
+ aa: ws?.id,
7023
+ af: ws?.status,
7024
+ ag: ws?.name,
7025
+ ah: shownSessions,
7026
+ aj: shownSessions?.length,
7027
+ ak: curTab,
7028
+ am: createSession,
7029
+ ap: loadMem.wanted,
7030
+ ar: loadMem.run,
7031
+ as: loadBody,
7032
+ au: loadSessions,
7033
+ aw: closeSession,
7034
+ ax: renameSession,
7035
+ ay: newSession,
7036
+ az: promptRename,
6874
7037
  a: _existing_scope($childScope),
6875
7038
  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
7039
+ b0: $si__input_active && $input_active__closures,
7040
+ b2: $si__input_agents && $input_agents__closures,
7041
+ b3: $view__closures,
7042
+ b9: $si__input_workspace && $ws_id__closures,
7043
+ ba: $si__input_workspace && $ws_name__closures,
7044
+ bb: $shownSessions__closures,
7045
+ bd: $curTab__closures,
7046
+ be: $closeSession2__closures,
7047
+ bg: $newSession2__closures,
7048
+ b1: $si__input_preferred && $input_preferred__closures,
7049
+ b5: $spawning__closures
6887
7050
  });
6888
7051
  _resume_branch($scope0_id);
6889
7052
  });
@@ -14022,6 +14185,12 @@ async function ensureBare(fullName) {
14022
14185
  async function bareDefaultBranch(bare) {
14023
14186
  return (await git(["symbolic-ref", "HEAD"], bare)).replace("refs/heads/", "");
14024
14187
  }
14188
+ /** `ref` is this workspace branch or a `-topic` lane of it. Another
14189
+ * workspace's branch can extend this prefix, so callers must also
14190
+ * subtract `otherWorkspaceBranches`. */
14191
+ function ownsBranch(ref, prefix) {
14192
+ return ref === prefix || ref.startsWith(`${prefix}-`);
14193
+ }
14025
14194
  /** Branches of every other workspace — slugs are user-chosen, so one
14026
14195
  * workspace's branch can be a strict prefix of another's; prefix matching
14027
14196
  * (cleanup, PR attribution) must exclude these. */
@@ -14032,13 +14201,11 @@ function otherWorkspaceBranches(id) {
14032
14201
  * minus any ref that actually belongs to another workspace whose branch
14033
14202
  * extends this one's (see otherWorkspaceBranches). */
14034
14203
  async function workspaceBranches(bare, branch, exclude) {
14035
- const out = await git([
14204
+ return (await git([
14036
14205
  "for-each-ref",
14037
14206
  "--format=%(refname:short)",
14038
14207
  "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)));
14208
+ ], bare).catch(() => "")).split("\n").filter((b) => ownsBranch(b, branch) && !exclude.some((x) => ownsBranch(b, x)));
14042
14209
  }
14043
14210
  /** Directory/branch-safe prefix for a workspace name. Decomposing first keeps
14044
14211
  * accented Latin readable (é → e); a name with nothing ASCII left (another
@@ -14067,16 +14234,33 @@ function writeAgentsMd(dir, branch, repos) {
14067
14234
  Workspace branch: **\`${branch}\`** — every repo here is checked out on it, and
14068
14235
  it is the required **prefix** for every other branch you create.
14069
14236
 
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.
14237
+ - **Branches:** stay on \`${branch}\` and keep pushing it — one open PR, more
14238
+ commits. After it lands, \`git fetch\` and reset \`${branch}\` to
14239
+ \`origin/<base>\` so the next change reuses this branch. Do not cut a
14240
+ \`${branch}-<topic>\` just to start the next piece of work.
14241
+ A topic lane is only for when \`${branch}\` must stay unmerged (a larger
14242
+ change) and you need a second PR on top of it. Then push every repo's
14243
+ workspace branch first, cut from the parent —
14244
+ \`git switch -c ${branch}-<topic> origin/${branch}\` — and keep the prefix.
14245
+ Only two levels: never \`${branch}-a-b\`. Tooling finds PRs by that prefix,
14246
+ so a branch named anything else is invisible: never invent one.
14074
14247
  - **Repos:**
14075
14248
  ${repos.map((r) => `- \`${r.worktree}/\` — ${r.full_name} (base: \`${r.base_branch}\`)`).join("\n")}
14249
+ - **No \`git stash\`:** every workspace using a repo shares one stash stack, so
14250
+ a pop can take another workspace's work. Commit as you go — uncommitted
14251
+ work isn't part of the review.
14076
14252
  - **Push:** \`git push -u origin HEAD\` in each repo, for review on GitHub.
14077
14253
  Never commit on a base branch.
14254
+ - **Rebase after merge:** when \`${branch}\` is still open and \`origin/<base>\`
14255
+ moves, rebase onto it — a squash-merge does not move the fork point, so the
14256
+ review keeps showing landed work until you do. On a topic lane, rebase onto
14257
+ \`${branch}\` when the parent moves; after the parent merges: \`git fetch
14258
+ --prune\`, rebase onto \`origin/<base>\`, and retarget (\`gh pr edit --base
14259
+ <base>\`).
14078
14260
  - **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.
14261
+ what changed and why. No verification logs or file-by-file detail. A topic
14262
+ lane's PR is \`gh pr create --base ${branch}\` — GitHub will otherwise target
14263
+ the repo default, and the local review will disagree with the files tab.
14080
14264
  `;
14081
14265
  fs.writeFileSync(path.join(dir, "AGENTS.md"), content);
14082
14266
  const claudeMd = path.join(dir, "CLAUDE.md");
@@ -15529,6 +15713,7 @@ function swr(ttlMs, load, seed) {
15529
15713
  }
15530
15714
  //#endregion
15531
15715
  //#region src/server/prs.ts
15716
+ var PR_JSON = "number,title,url,headRefName,baseRefName,headRefOid,isDraft,state,reviewDecision";
15532
15717
  var TTL_MS$3 = 12e4;
15533
15718
  /** PRs in every state for a repo, stale-while-revalidate cached. */
15534
15719
  var prs = swr(TTL_MS$3, fetchPrs);
@@ -15559,7 +15744,7 @@ var headPrs = swr(TTL_MS$3, async (key) => {
15559
15744
  "--limit",
15560
15745
  "20",
15561
15746
  "--json",
15562
- "number,title,url,headRefName,isDraft,state,reviewDecision"
15747
+ PR_JSON
15563
15748
  ]).catch(() => "[]");
15564
15749
  return JSON.parse(out);
15565
15750
  });
@@ -15582,7 +15767,7 @@ async function fetchPrs(fullName) {
15582
15767
  "--limit",
15583
15768
  "100",
15584
15769
  "--json",
15585
- "number,title,url,headRefName,isDraft,state,reviewDecision"
15770
+ PR_JSON
15586
15771
  ]).catch(() => "[]");
15587
15772
  return JSON.parse(out);
15588
15773
  }
@@ -15617,7 +15802,7 @@ async function refreshPrsBatch(fullNames, force = false) {
15617
15802
  "-f",
15618
15803
  `query=${"query {" + names.map((fullName, i) => {
15619
15804
  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 } } }`;
15805
+ 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
15806
  }).join(" ") + "}"}`
15622
15807
  ]).catch((err) => {
15623
15808
  if (!err.stdout) throw err;
@@ -15628,7 +15813,10 @@ async function refreshPrsBatch(fullNames, force = false) {
15628
15813
  names.forEach((fullName, i) => {
15629
15814
  const repo = data[`r${i}`];
15630
15815
  if (!repo) return;
15631
- prs.set(fullName, repo.pullRequests.nodes);
15816
+ prs.set(fullName, repo.pullRequests.nodes.map((n) => ({
15817
+ ...n,
15818
+ headRefOid: n.headRefOid || n.headRef?.oid
15819
+ })));
15632
15820
  });
15633
15821
  return true;
15634
15822
  } catch (err) {
@@ -15646,211 +15834,6 @@ function withoutDismissed(workspaceId, fullName, list) {
15646
15834
  return list.filter((pr) => pr.state === "OPEN" || !dismissed.has(pr.number));
15647
15835
  }
15648
15836
  //#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
15837
  //#region src/server/status.ts
15855
15838
  var TTL_MS$2 = 2e3;
15856
15839
  var snapshot = null;
@@ -15882,9 +15865,9 @@ function statusForAll() {
15882
15865
  }
15883
15866
  /** Push-based freshness: while any SSE client is connected, recompute the
15884
15867
  * (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. */
15868
+ * actually changed — ports appearing, PR/CI flips reach every client in
15869
+ * ~15s instead of waiting out their 60s poll, and idle systems push
15870
+ * nothing at all. */
15888
15871
  var WATCH_MS = 15e3;
15889
15872
  /** Right after a local push PR state flips within seconds — tick fast so
15890
15873
  * the badge lands while it still feels immediate. */
@@ -15938,6 +15921,8 @@ async function statusForAllFresh() {
15938
15921
  if (await refreshPrsBatch(activeRepoNames(), true).catch(() => false)) invalidateSnapshot();
15939
15922
  return statusForAll();
15940
15923
  }
15924
+ /** RSS here is the Node + Marko baseline (~80 MB empty, ~115 MB loaded),
15925
+ * not a leak and not worth chasing — young-gen flags do not move it. */
15941
15926
  async function computeStatus() {
15942
15927
  const workspaces = listWorkspaces().filter((ws) => ws.status === "active");
15943
15928
  const paths = new Map(workspaces.map((ws) => [ws.id, ws.path]));
@@ -15951,23 +15936,18 @@ async function computeStatus() {
15951
15936
  const statuses = await Promise.all(ws.repos.map((repo) => repoStatus(ws, repo, foreign)));
15952
15937
  return [ws.id, {
15953
15938
  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)
15939
+ repos: statuses,
15940
+ ...sessionCounts(ws.id, states)
15957
15941
  }];
15958
15942
  }));
15959
15943
  return Object.fromEntries(entries);
15960
15944
  }
15961
15945
  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)]);
15946
+ const repoPrs = await workspacePrs(repo.full_name, ws.branch, foreign).then((list) => list.filter((pr) => pr.state === "OPEN"));
15964
15947
  return {
15965
- status: {
15966
- full_name: repo.full_name,
15967
- worktree: repo.worktree,
15968
- prs: repoPrs
15969
- },
15970
- changed
15948
+ full_name: repo.full_name,
15949
+ worktree: repo.worktree,
15950
+ prs: repoPrs
15971
15951
  };
15972
15952
  }
15973
15953
  function sessionCounts(workspaceId, states) {
@@ -16568,8 +16548,359 @@ function post20(context) {
16568
16548
  return call(mware3, __mware22, context);
16569
16549
  }
16570
16550
  //#endregion
16551
+ //#region src/server/diff.ts
16552
+ var MAX_FILE_BYTES = 5e5;
16553
+ /** Diffs compare against remote-tracking refs, which only move when this
16554
+ * machine fetches or pushes — a PR merged on GitHub leaves them stale and
16555
+ * the diff shows already-merged work until the next fetch. Opening the
16556
+ * diff freshens the base refs itself (throttled per repo; each ref
16557
+ * fetched independently since one missing ref fails a combined fetch,
16558
+ * and capped so a slow remote can't hang the view — stale refs then
16559
+ * self-correct on the next open). */
16560
+ var FETCH_TTL_MS = 3e4;
16561
+ var FETCH_WAIT_MS = 8e3;
16562
+ var lastFetch = /* @__PURE__ */ new Map();
16563
+ async function freshenBaseRefs(dir, fullName, refs) {
16564
+ if (Date.now() - (lastFetch.get(fullName) ?? 0) < FETCH_TTL_MS) return;
16565
+ const fetches = [...new Set(refs)].map((ref) => git([
16566
+ "fetch",
16567
+ "--quiet",
16568
+ "--no-tags",
16569
+ "origin",
16570
+ ref
16571
+ ], dir).catch((err) => {
16572
+ if (missingRemoteRef(err)) return git([
16573
+ "update-ref",
16574
+ "-d",
16575
+ `refs/remotes/origin/${ref}`
16576
+ ], dir).catch(() => {});
16577
+ }));
16578
+ fetches.push(git([
16579
+ "remote",
16580
+ "prune",
16581
+ "origin"
16582
+ ], dir).catch(() => {}));
16583
+ let timer;
16584
+ await Promise.race([Promise.all(fetches), new Promise((resolve) => {
16585
+ timer = setTimeout(resolve, FETCH_WAIT_MS);
16586
+ })]);
16587
+ clearTimeout(timer);
16588
+ lastFetch.set(fullName, Date.now());
16589
+ }
16590
+ function missingRemoteRef(err) {
16591
+ const msg = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : String(err);
16592
+ return /couldn't find remote ref/.test(msg);
16593
+ }
16594
+ /** The ref the working tree is compared against by default, best first.
16595
+ * Workspace branch / detached / foreign → `origin/<base>`. Our `-topic`
16596
+ * lane → `origin/<ws-branch>` (only two levels; `<ws>-a-b` still parents
16597
+ * to the workspace branch). `foreign` is other workspaces' branches so a
16598
+ * longer prefix is not stolen. An explicit `baseRef` overrides all. */
16599
+ async function resolveBase(dir, wsBranch, baseBranch, headBranch, foreign, baseRef) {
16600
+ const ours = ownsBranch(headBranch, wsBranch) && !foreign.some((x) => ownsBranch(headBranch, x));
16601
+ const candidates = [];
16602
+ if (baseRef) candidates.push([`origin/${baseRef}`, baseRef], [baseRef, baseRef]);
16603
+ else if (headBranch === wsBranch || headBranch === "HEAD" || !ours) {
16604
+ candidates.push([`origin/${baseBranch}`, baseBranch]);
16605
+ candidates.push([`origin/${wsBranch}`, `origin/${wsBranch}`]);
16606
+ } else {
16607
+ candidates.push([`origin/${wsBranch}`, `origin/${wsBranch}`]);
16608
+ candidates.push([`origin/${headBranch}`, `origin/${headBranch}`]);
16609
+ candidates.push([`origin/${baseBranch}`, baseBranch]);
16610
+ }
16611
+ for (const [ref, label] of candidates) try {
16612
+ return {
16613
+ mergeBase: await git([
16614
+ "merge-base",
16615
+ ref,
16616
+ "HEAD"
16617
+ ], dir),
16618
+ ref,
16619
+ label
16620
+ };
16621
+ } catch {}
16622
+ return {
16623
+ mergeBase: "HEAD",
16624
+ ref: "HEAD",
16625
+ label: baseRef || baseBranch,
16626
+ missing: true
16627
+ };
16628
+ }
16629
+ /** True when merging `head` into `base` is a no-op (head's unique work
16630
+ * is already in `base` — the squash-merge case). A conflict is not
16631
+ * containment; the caller falls through to three-dot. */
16632
+ async function treeContainedIn(dir, base, head) {
16633
+ try {
16634
+ return (await git([
16635
+ "merge-tree",
16636
+ "--write-tree",
16637
+ base,
16638
+ head
16639
+ ], dir)).split("\n")[0] === await git(["rev-parse", `${base}^{tree}`], dir);
16640
+ } catch {
16641
+ return false;
16642
+ }
16643
+ }
16644
+ /** Open PRs as GitHub's files tab (remote head vs the PR's `--base`).
16645
+ * Repos with no open PR fall back to the local checkout vs the derived
16646
+ * base. `pr` (`worktree/number`) focuses one PR. `uncommitted` overlays
16647
+ * the working tree on the PR whose head is the current branch. */
16648
+ async function workspaceDiff(ws, opts = {}) {
16649
+ const { baseRef, uncommitted = false, pr: focus, refresh = false } = opts;
16650
+ if (refresh) for (const repo of ws.repos) lastFetch.delete(repo.full_name);
16651
+ const foreign = otherWorkspaceBranches(ws.id);
16652
+ return (await Promise.all(ws.repos.map(async (repo) => {
16653
+ const dir = path.join(ws.path, repo.worktree);
16654
+ const branch = await git([
16655
+ "rev-parse",
16656
+ "--abbrev-ref",
16657
+ "HEAD"
16658
+ ], dir).catch(() => "HEAD");
16659
+ const localHead = await git(["rev-parse", "HEAD"], dir).catch(() => "");
16660
+ const open = (await workspacePrs(repo.full_name, ws.branch, foreign, refresh)).filter((p) => p.state === "OPEN" && p.baseRefName && p.headRefName && (!focus || focus === `${repo.worktree}/${p.number}`));
16661
+ if (open.length) return Promise.all(open.map((p) => prDiff(dir, repo, p, {
16662
+ uncommitted: uncommitted && branch === p.headRefName,
16663
+ localHead,
16664
+ branch
16665
+ })));
16666
+ if (focus) return [];
16667
+ return [await localDiff(dir, ws, repo, branch, localHead, baseRef, uncommitted)];
16668
+ }))).flat();
16669
+ }
16670
+ async function prDiff(dir, repo, pr, ctx) {
16671
+ await freshenBaseRefs(dir, repo.full_name, [pr.baseRefName, pr.headRefName]);
16672
+ const headRef = `origin/${pr.headRefName}`;
16673
+ const baseRef = `origin/${pr.baseRefName}`;
16674
+ const head = await git(["rev-parse", headRef], dir).catch(() => "") || pr.headRefOid || "";
16675
+ const baseSha = await git(["rev-parse", baseRef], dir).catch(() => "");
16676
+ if (!head || !baseSha) return {
16677
+ id: `${repo.worktree}#${pr.number}`,
16678
+ worktree: repo.worktree,
16679
+ full_name: repo.full_name,
16680
+ base: pr.baseRefName,
16681
+ default_branch: repo.default_branch,
16682
+ branch: ctx.branch,
16683
+ head,
16684
+ baseSha,
16685
+ pr: prMeta(pr),
16686
+ localHead: ctx.localHead,
16687
+ baseMissing: true,
16688
+ files: []
16689
+ };
16690
+ const mergeBase = await git([
16691
+ "merge-base",
16692
+ baseRef,
16693
+ head
16694
+ ], dir).catch(() => "");
16695
+ const contained = await treeContainedIn(dir, baseRef, head);
16696
+ const files = await collectFiles(dir, contained ? head : mergeBase || baseRef, head, ctx.uncommitted);
16697
+ const githubDrift = !!pr.headRefOid && head !== pr.headRefOid;
16698
+ return {
16699
+ id: `${repo.worktree}#${pr.number}`,
16700
+ worktree: repo.worktree,
16701
+ full_name: repo.full_name,
16702
+ base: pr.baseRefName,
16703
+ default_branch: repo.default_branch,
16704
+ branch: ctx.branch,
16705
+ head,
16706
+ baseSha,
16707
+ pr: prMeta(pr),
16708
+ localHead: ctx.localHead,
16709
+ ...githubDrift && { githubDrift: true },
16710
+ ...contained && { contained: true },
16711
+ files
16712
+ };
16713
+ }
16714
+ async function localDiff(dir, ws, repo, branch, localHead, baseRef, uncommitted) {
16715
+ await freshenBaseRefs(dir, repo.full_name, [
16716
+ ws.branch,
16717
+ ...branch !== "HEAD" ? [branch] : [],
16718
+ ...baseRef ? [baseRef] : [repo.base_branch]
16719
+ ]);
16720
+ const { mergeBase, ref, label, missing } = await resolveBase(dir, ws.branch, repo.base_branch, branch, otherWorkspaceBranches(ws.id), baseRef);
16721
+ if (missing && !uncommitted) return {
16722
+ id: repo.worktree,
16723
+ worktree: repo.worktree,
16724
+ full_name: repo.full_name,
16725
+ base: label,
16726
+ default_branch: repo.default_branch,
16727
+ branch,
16728
+ head: localHead,
16729
+ baseSha: localHead,
16730
+ baseMissing: true,
16731
+ files: []
16732
+ };
16733
+ const contained = !missing && await treeContainedIn(dir, ref, "HEAD");
16734
+ const against = contained ? "HEAD" : mergeBase;
16735
+ const baseSha = missing ? localHead : await git(["rev-parse", ref], dir).catch(() => mergeBase);
16736
+ const files = await collectFiles(dir, against, "HEAD", uncommitted);
16737
+ return {
16738
+ id: repo.worktree,
16739
+ worktree: repo.worktree,
16740
+ full_name: repo.full_name,
16741
+ base: label,
16742
+ default_branch: repo.default_branch,
16743
+ branch,
16744
+ head: localHead,
16745
+ baseSha,
16746
+ ...missing && { baseMissing: true },
16747
+ ...contained && { contained: true },
16748
+ files
16749
+ };
16750
+ }
16751
+ function prMeta(pr) {
16752
+ return {
16753
+ number: pr.number,
16754
+ title: pr.title,
16755
+ url: pr.url,
16756
+ headRefName: pr.headRefName,
16757
+ baseRefName: pr.baseRefName,
16758
+ ...pr.headRefOid && { headRefOid: pr.headRefOid }
16759
+ };
16760
+ }
16761
+ async function collectFiles(dir, oldRev, newRev, uncommitted) {
16762
+ const diffArgs = uncommitted ? [
16763
+ "diff",
16764
+ "--name-status",
16765
+ "-z",
16766
+ "--no-renames",
16767
+ oldRev
16768
+ ] : [
16769
+ "diff",
16770
+ "--name-status",
16771
+ "-z",
16772
+ "--no-renames",
16773
+ oldRev,
16774
+ newRev
16775
+ ];
16776
+ const [nameStatus, untracked] = await Promise.all([git(diffArgs, dir).catch(() => ""), uncommitted ? git([
16777
+ "ls-files",
16778
+ "--others",
16779
+ "--exclude-standard",
16780
+ "-z"
16781
+ ], dir).catch(() => "") : Promise.resolve("")]);
16782
+ const entries = [];
16783
+ const parts = nameStatus.split("\0").filter(Boolean);
16784
+ for (let i = 0; i + 1 < parts.length; i += 2) entries.push({
16785
+ path: parts[i + 1],
16786
+ status: parts[i]
16787
+ });
16788
+ for (const file of untracked.split("\0").filter(Boolean)) entries.push({
16789
+ path: file,
16790
+ status: "A"
16791
+ });
16792
+ const olds = await batchContents(dir, oldRev, entries.filter((e) => e.status !== "A").map((e) => e.path));
16793
+ const news = uncommitted ? null : await batchContents(dir, newRev, entries.filter((e) => e.status !== "D").map((e) => e.path));
16794
+ 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))));
16795
+ files.sort((a, b) => a.path.localeCompare(b.path));
16796
+ return files;
16797
+ }
16798
+ /** Blob contents for many paths through a single `git cat-file --batch`
16799
+ * process (instead of one `git show` per file). Blobs over MAX_FILE_BYTES
16800
+ * are skipped and mapped to "tooLarge". */
16801
+ async function batchContents(dir, rev, paths) {
16802
+ const result = /* @__PURE__ */ new Map();
16803
+ if (!paths.length) return result;
16804
+ await new Promise((resolve) => {
16805
+ const proc = spawn("git", ["cat-file", "--batch"], { cwd: dir });
16806
+ let buf = Buffer.alloc(0);
16807
+ let index = 0;
16808
+ let need = -1;
16809
+ const parse = () => {
16810
+ for (;;) if (need >= 0) {
16811
+ if (buf.length < need + 1) return;
16812
+ const content = buf.subarray(0, need);
16813
+ buf = buf.subarray(need + 1);
16814
+ need = -1;
16815
+ const p = paths[index++];
16816
+ if (content.length > MAX_FILE_BYTES) result.set(p, "tooLarge");
16817
+ else result.set(p, content.toString("utf8"));
16818
+ } else {
16819
+ const nl = buf.indexOf(10);
16820
+ if (nl < 0) return;
16821
+ const header = buf.subarray(0, nl).toString();
16822
+ buf = buf.subarray(nl + 1);
16823
+ if (header.endsWith(" missing")) {
16824
+ index++;
16825
+ continue;
16826
+ }
16827
+ need = Number(header.split(" ")[2]) || 0;
16828
+ }
16829
+ };
16830
+ proc.stdout.on("data", (chunk) => {
16831
+ buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
16832
+ parse();
16833
+ });
16834
+ proc.on("close", () => resolve());
16835
+ proc.on("error", () => resolve());
16836
+ proc.stdin.on("error", () => {});
16837
+ proc.stdin.end(paths.map((p) => `${rev}:${p}`).join("\n") + "\n");
16838
+ });
16839
+ return result;
16840
+ }
16841
+ function fromBlobs(filePath, status, old, neu) {
16842
+ const result = {
16843
+ path: filePath,
16844
+ status,
16845
+ old: null,
16846
+ new: null
16847
+ };
16848
+ if (old === "tooLarge" || neu === "tooLarge") result.tooLarge = true;
16849
+ else {
16850
+ if (old !== void 0) result.old = old;
16851
+ if (neu !== void 0) result.new = neu;
16852
+ }
16853
+ if (result.old?.includes("\0") || result.new?.includes("\0")) {
16854
+ result.binary = true;
16855
+ result.old = null;
16856
+ result.new = null;
16857
+ }
16858
+ if (result.tooLarge) {
16859
+ result.old = null;
16860
+ result.new = null;
16861
+ }
16862
+ return result;
16863
+ }
16864
+ async function loadWorktreeFile(dir, filePath, status, old) {
16865
+ const result = {
16866
+ path: filePath,
16867
+ status,
16868
+ old: null,
16869
+ new: null
16870
+ };
16871
+ if (old === "tooLarge") result.tooLarge = true;
16872
+ else if (old !== void 0) result.old = old;
16873
+ if (status !== "D" && !result.tooLarge) try {
16874
+ const root = path.resolve(dir);
16875
+ const full = path.resolve(dir, filePath);
16876
+ if (full !== root && !full.startsWith(root + path.sep)) throw new Error(`path escapes worktree: ${filePath}`);
16877
+ if ((await fsp.stat(full)).size > MAX_FILE_BYTES) result.tooLarge = true;
16878
+ else result.new = await fsp.readFile(full, "utf8");
16879
+ } catch {}
16880
+ if (result.old?.includes("\0") || result.new?.includes("\0")) {
16881
+ result.binary = true;
16882
+ result.old = null;
16883
+ result.new = null;
16884
+ }
16885
+ if (result.tooLarge) {
16886
+ result.old = null;
16887
+ result.new = null;
16888
+ }
16889
+ return result;
16890
+ }
16891
+ //#endregion
16571
16892
  //#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");
16893
+ var getHandler$4 = normalizeHandler(Run.GET({ search: (value) => ({
16894
+ base: typeof value.base === "string" && value.base ? value.base : void 0,
16895
+ uncommitted: value.uncommitted === "1" || value.uncommitted === "true",
16896
+ pr: typeof value.pr === "string" && value.pr ? value.pr : void 0,
16897
+ refresh: "refresh" in value
16898
+ }) }, async (ctx) => Response.json(await workspaceDiff(ctx.data.workspace, {
16899
+ baseRef: ctx.search.base,
16900
+ uncommitted: ctx.search.uncommitted,
16901
+ pr: ctx.search.pr,
16902
+ refresh: ctx.search.refresh
16903
+ }))), "GET");
16573
16904
  var get21_options = mergeOptions(mware3, mware22, getHandler$4);
16574
16905
  var head21_options = mergeOptions(mware3, mware22);
16575
16906
  function get21(context) {
@@ -21166,4 +21497,4 @@ server.listen(port, () => {
21166
21497
  //#endregion
21167
21498
  export {};
21168
21499
 
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>"]}};
21500
+ ;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-BnMs3duC.js",6,"<script",0," async type=\"module\" crossorigin src=\"",1,"assets/index-BnMs3duC.js\"","></script>"]}};