aicodeman 1.23.0 → 1.23.2

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 (102) hide show
  1. package/README.md +2 -2
  2. package/dist/usage-telemetry.d.ts +7 -2
  3. package/dist/usage-telemetry.d.ts.map +1 -1
  4. package/dist/usage-telemetry.js +30 -2
  5. package/dist/usage-telemetry.js.map +1 -1
  6. package/dist/utils/codex-cli-resolver.d.ts +7 -0
  7. package/dist/utils/codex-cli-resolver.d.ts.map +1 -1
  8. package/dist/utils/codex-cli-resolver.js +81 -1
  9. package/dist/utils/codex-cli-resolver.js.map +1 -1
  10. package/dist/utils/index.d.ts +1 -1
  11. package/dist/utils/index.d.ts.map +1 -1
  12. package/dist/utils/index.js +1 -1
  13. package/dist/utils/index.js.map +1 -1
  14. package/dist/web/plan-usage-latest.d.ts +7 -5
  15. package/dist/web/plan-usage-latest.d.ts.map +1 -1
  16. package/dist/web/plan-usage-latest.js +12 -5
  17. package/dist/web/plan-usage-latest.js.map +1 -1
  18. package/dist/web/public/admin-ui.js.gz +0 -0
  19. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  20. package/dist/web/public/app.3734fd6f.js +38 -0
  21. package/dist/web/public/app.3734fd6f.js.br +0 -0
  22. package/dist/web/public/app.3734fd6f.js.gz +0 -0
  23. package/dist/web/public/approvals-ui.js.gz +0 -0
  24. package/dist/web/public/constants.f4ea0a19.js.gz +0 -0
  25. package/dist/web/public/cron-ui.js.gz +0 -0
  26. package/dist/web/public/entrance-animations.js.gz +0 -0
  27. package/dist/web/public/home-sessions.js.gz +0 -0
  28. package/dist/web/public/i18n.9ae15b51.js.gz +0 -0
  29. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  30. package/dist/web/public/index.html +7 -7
  31. package/dist/web/public/index.html.br +0 -0
  32. package/dist/web/public/index.html.gz +0 -0
  33. package/dist/web/public/input-cjk.87f14251.js.gz +0 -0
  34. package/dist/web/public/keyboard-accessory.0454a3e9.js.gz +0 -0
  35. package/dist/web/public/mobile-handlers.0f83a608.js.gz +0 -0
  36. package/dist/web/public/mobile-overview.js.gz +0 -0
  37. package/dist/web/public/mobile.cab92e3f.css.gz +0 -0
  38. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  39. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  40. package/dist/web/public/{panels-ui.50200a8d.js → panels-ui.344de87d.js} +40 -32
  41. package/dist/web/public/panels-ui.344de87d.js.br +0 -0
  42. package/dist/web/public/panels-ui.344de87d.js.gz +0 -0
  43. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  44. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  45. package/dist/web/public/readmymind-ui.js.gz +0 -0
  46. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  47. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  48. package/dist/web/public/session-lineage.js.gz +0 -0
  49. package/dist/web/public/session-ui.103656d3.js.gz +0 -0
  50. package/dist/web/public/{settings-ui.45f311a7.js → settings-ui.3261b896.js} +1 -1
  51. package/dist/web/public/settings-ui.3261b896.js.br +0 -0
  52. package/dist/web/public/{settings-ui.45f311a7.js.gz → settings-ui.3261b896.js.gz} +0 -0
  53. package/dist/web/public/styles.0d71ea9a.css +1 -0
  54. package/dist/web/public/styles.0d71ea9a.css.br +0 -0
  55. package/dist/web/public/styles.0d71ea9a.css.gz +0 -0
  56. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  57. package/dist/web/public/sw.js.gz +0 -0
  58. package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
  59. package/dist/web/public/terminal-ui.5e0c230c.js +2 -0
  60. package/dist/web/public/terminal-ui.5e0c230c.js.br +0 -0
  61. package/dist/web/public/terminal-ui.5e0c230c.js.gz +0 -0
  62. package/dist/web/public/ultracode-panel.js.gz +0 -0
  63. package/dist/web/public/ultracode-windows.js.gz +0 -0
  64. package/dist/web/public/upload.html.gz +0 -0
  65. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  66. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  67. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  68. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  69. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  70. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  71. package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
  72. package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
  73. package/dist/web/public/vendor/xterm.css.gz +0 -0
  74. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  75. package/dist/web/public/voice-input.4ec670b0.js.gz +0 -0
  76. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  77. package/dist/web/public/webview-tabs.js.gz +0 -0
  78. package/dist/web/routes/status-telemetry-routes.js +3 -3
  79. package/dist/web/routes/status-telemetry-routes.js.map +1 -1
  80. package/dist/web/schemas.js +1 -1
  81. package/dist/web/schemas.js.map +1 -1
  82. package/dist/web/server.d.ts +3 -0
  83. package/dist/web/server.d.ts.map +1 -1
  84. package/dist/web/server.js +33 -1
  85. package/dist/web/server.js.map +1 -1
  86. package/dist/web/sse-events.d.ts +1 -1
  87. package/dist/web/sse-events.d.ts.map +1 -1
  88. package/dist/web/sse-events.js +1 -1
  89. package/dist/web/sse-events.js.map +1 -1
  90. package/package.json +1 -1
  91. package/dist/web/public/app.360ece48.js +0 -36
  92. package/dist/web/public/app.360ece48.js.br +0 -0
  93. package/dist/web/public/app.360ece48.js.gz +0 -0
  94. package/dist/web/public/panels-ui.50200a8d.js.br +0 -0
  95. package/dist/web/public/panels-ui.50200a8d.js.gz +0 -0
  96. package/dist/web/public/settings-ui.45f311a7.js.br +0 -0
  97. package/dist/web/public/styles.1938fd02.css +0 -1
  98. package/dist/web/public/styles.1938fd02.css.br +0 -0
  99. package/dist/web/public/styles.1938fd02.css.gz +0 -0
  100. package/dist/web/public/terminal-ui.ef68a666.js +0 -2
  101. package/dist/web/public/terminal-ui.ef68a666.js.br +0 -0
  102. package/dist/web/public/terminal-ui.ef68a666.js.gz +0 -0
@@ -51,7 +51,7 @@
51
51
  </div>
52
52
  ${i}
53
53
  </article>
54
- `},attachAwayDigestActions(){const e=document.getElementById("awayDigestSections");e&&e.querySelectorAll("[data-away-link-type]").forEach(t=>{t.addEventListener("click",()=>{this.openAwayDigestItem(t.dataset.awayLinkType,t.dataset.awaySessionId||void 0)})})},async openAwayDigestItem(e,t){if(e==="session"&&t){await this.selectSession(t),this.closeAwayDigest();return}if(e==="run_summary"&&t){await this.openRunSummary(t),this.closeAwayDigest();return}e==="lifecycle"&&(this.openLifecycleLog(),this.closeAwayDigest())},formatAwayDigestTimestamp(e){return Number.isFinite(e)?new Date(e).toLocaleString([],{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}):""},formatAwayDigestSource(e){return{lifecycle:"Lifecycle",run_summary:"Run Summary",status:"Status",token_stats:"Token Stats",subagent:"Subagent"}[e]||e},async openTokenStats(){try{const t=await(await fetch("/api/token-stats")).json();t.success?(this.renderTokenStats(t.data),document.getElementById("tokenStatsModal").classList.add("active")):this.showToast("Failed to load token stats","error")}catch(e){console.error("Failed to fetch token stats:",e),this.showToast("Failed to load token stats","error")}},renderTokenStats(e){const{daily:t,totals:s}=e,n=new Date().toISOString().split("T")[0],i=t.find(f=>f.date===n)||{inputTokens:0,outputTokens:0,estimatedCost:0},a=new Date;a.setDate(a.getDate()-7);const r=t.filter(f=>new Date(f.date)>=a),o=r.reduce((f,S)=>f+S.inputTokens,0),l=r.reduce((f,S)=>f+S.outputTokens,0),c=this.estimateCost(o,l),m=s.totalInputTokens,u=s.totalOutputTokens,d=this.estimateCost(m,u),p=document.getElementById("statsSummary");p.innerHTML=`
54
+ `},attachAwayDigestActions(){const e=document.getElementById("awayDigestSections");e&&e.querySelectorAll("[data-away-link-type]").forEach(t=>{t.addEventListener("click",()=>{this.openAwayDigestItem(t.dataset.awayLinkType,t.dataset.awaySessionId||void 0)})})},async openAwayDigestItem(e,t){if(e==="session"&&t){await this.selectSession(t),this.closeAwayDigest();return}if(e==="run_summary"&&t){await this.openRunSummary(t),this.closeAwayDigest();return}e==="lifecycle"&&(this.openLifecycleLog(),this.closeAwayDigest())},formatAwayDigestTimestamp(e){return Number.isFinite(e)?new Date(e).toLocaleString([],{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}):""},formatAwayDigestSource(e){return{lifecycle:"Lifecycle",run_summary:"Run Summary",status:"Status",token_stats:"Token Stats",subagent:"Subagent"}[e]||e},async openTokenStats(){try{const t=await(await fetch("/api/token-stats")).json();t.success?(this.renderTokenStats(t.data),document.getElementById("tokenStatsModal").classList.add("active")):this.showToast("Failed to load token stats","error")}catch(e){console.error("Failed to fetch token stats:",e),this.showToast("Failed to load token stats","error")}},renderTokenStats(e){const{daily:t,totals:s}=e,n=new Date().toISOString().split("T")[0],i=t.find(f=>f.date===n)||{inputTokens:0,outputTokens:0,estimatedCost:0},a=new Date;a.setDate(a.getDate()-7);const r=t.filter(f=>new Date(f.date)>=a),o=r.reduce((f,S)=>f+S.inputTokens,0),l=r.reduce((f,S)=>f+S.outputTokens,0),c=this.estimateCost(o,l),h=s.totalInputTokens,u=s.totalOutputTokens,d=this.estimateCost(h,u),p=document.getElementById("statsSummary");p.innerHTML=`
55
55
  <div class="stat-card">
56
56
  <span class="stat-card-label">Today</span>
57
57
  <span class="stat-card-value">${this.formatTokens(i.inputTokens+i.outputTokens)}</span>
@@ -64,10 +64,10 @@
64
64
  </div>
65
65
  <div class="stat-card">
66
66
  <span class="stat-card-label">Lifetime</span>
67
- <span class="stat-card-value">${this.formatTokens(m+u)}</span>
67
+ <span class="stat-card-value">${this.formatTokens(h+u)}</span>
68
68
  <span class="stat-card-cost">~$${d.toFixed(2)}</span>
69
69
  </div>
70
- `;const h=document.getElementById("statsChart"),g=document.getElementById("statsChartDays"),y=[];for(let f=6;f>=0;f--){const S=new Date;S.setDate(S.getDate()-f);const T=S.toISOString().split("T")[0],v=t.find(I=>I.date===T);y.push({date:T,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:v?v.inputTokens+v.outputTokens:0,cost:v?v.estimatedCost:0})}const w=Math.max(...y.map(f=>f.tokens),1);h.innerHTML=y.map(f=>{const S=Math.max(f.tokens/w*100,3),T=`${f.dayName}: ${this.formatTokens(f.tokens)} (~$${f.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${T}"></div>`}).join(""),g.innerHTML=y.map(f=>`<span>${f.dayName}</span>`).join("");const b=document.getElementById("statsTable"),$=t.slice(0,14);$.length===0?b.innerHTML='<div class="stats-no-data">No usage data recorded yet</div>':b.innerHTML=`
70
+ `;const m=document.getElementById("statsChart"),g=document.getElementById("statsChartDays"),y=[];for(let f=6;f>=0;f--){const S=new Date;S.setDate(S.getDate()-f);const T=S.toISOString().split("T")[0],v=t.find(I=>I.date===T);y.push({date:T,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:v?v.inputTokens+v.outputTokens:0,cost:v?v.estimatedCost:0})}const w=Math.max(...y.map(f=>f.tokens),1);m.innerHTML=y.map(f=>{const S=Math.max(f.tokens/w*100,3),T=`${f.dayName}: ${this.formatTokens(f.tokens)} (~$${f.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${T}"></div>`}).join(""),g.innerHTML=y.map(f=>`<span>${f.dayName}</span>`).join("");const b=document.getElementById("statsTable"),$=t.slice(0,14);$.length===0?b.innerHTML='<div class="stats-no-data">No usage data recorded yet</div>':b.innerHTML=`
71
71
  <div class="stats-table-header">
72
72
  <span>Date</span>
73
73
  <span>Input</span>
@@ -82,9 +82,9 @@
82
82
  <span class="cell cell-cost">$${f.estimatedCost.toFixed(2)}</span>
83
83
  </div>
84
84
  `).join("")}
85
- `},closeTokenStats(){const e=document.getElementById("tokenStatsModal");e&&e.classList.remove("active")},async toggleMonitorPanel(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorToggleBtn");e.classList.toggle("open"),e.classList.contains("open")?(e.style.display="",await this.loadMuxSessions(),await fetch("/api/mux-sessions/stats/start",{method:"POST"}),this.renderTaskPanel(),t&&(t.innerHTML="&#x25BC;")):(await fetch("/api/mux-sessions/stats/stop",{method:"POST"}),t&&(t.innerHTML="&#x25B2;"))},toggleTaskPanel(){this.toggleMonitorPanel()},toggleMonitorDetach(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorDetachBtn");e.classList.contains("detached")?(e.classList.remove("detached"),e.style.top="",e.style.left="",e.style.width="",e.style.height="",t&&(t.innerHTML="&#x29C9;",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="&#x229E;",t.title="Attach panel"),this.setupMonitorDrag())},setupMonitorDrag(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorPanelHeader");if(!e||!t)return;let s=!1,n,i,a,r;const o=m=>{if(m.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(m);n=u.clientX,i=u.clientY;const d=e.getBoundingClientRect();a=d.left,r=d.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),m.preventDefault()},l=m=>{if(!s)return;const u=getEventCoords(m),d=u.clientX-n,p=u.clientY-i;let h=a+d,g=r+p;const y=e.getBoundingClientRect();h=Math.max(0,Math.min(window.innerWidth-y.width,h)),g=Math.max(0,Math.min(window.innerHeight-y.height,g)),e.style.left=h+"px",e.style.top=g+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},toggleSubagentsDetach(){const e=document.getElementById("subagentsPanel"),t=document.getElementById("subagentsDetachBtn");e.classList.contains("detached")?(e.classList.remove("detached"),e.style.top="",e.style.left="",e.style.width="",e.style.height="",t&&(t.innerHTML="&#x29C9;",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="&#x229E;",t.title="Attach panel"),this.setupSubagentsDrag())},setupSubagentsDrag(){const e=document.getElementById("subagentsPanel"),t=document.getElementById("subagentsPanelHeader");if(!e||!t)return;let s=!1,n,i,a,r;const o=m=>{if(m.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(m);n=u.clientX,i=u.clientY;const d=e.getBoundingClientRect();a=d.left,r=d.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),m.preventDefault()},l=m=>{if(!s)return;const u=getEventCoords(m),d=u.clientX-n,p=u.clientY-i;let h=a+d,g=r+p;const y=e.getBoundingClientRect();h=Math.max(0,Math.min(window.innerWidth-y.width,h)),g=Math.max(0,Math.min(window.innerHeight-y.height,g)),e.style.left=h+"px",e.style.top=g+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},renderTaskPanel(){this._debouncedCall("taskPanel",this._renderTaskPanelImmediate)},_renderTaskPanelImmediate(){const e=this.sessions.get(this.activeSessionId),t=document.getElementById("backgroundTasksBody"),s=document.getElementById("taskPanelStats"),n=document.getElementById("backgroundTasksSection");if(!e||!e.taskTree||e.taskTree.length===0){n&&(n.style.display="none"),t.innerHTML="",s.textContent="0 tasks";return}n&&(n.style.display="");const i=e.taskStats||{running:0,completed:0,failed:0,total:0};s.textContent=`${i.running} running, ${i.completed} done`;const a=(l,c)=>{const m=l.status==="running"?"":l.status==="completed"?"&#x2713;":"&#x2717;",u=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let d="";if(l.children&&l.children.length>0){d='<div class="task-children">';for(const p of l.children){const h=c.find(g=>g.id===p);h&&(d+=`<div class="task-node">${a(h,c)}</div>`)}d+="</div>"}return`
85
+ `},closeTokenStats(){const e=document.getElementById("tokenStatsModal");e&&e.classList.remove("active")},async toggleMonitorPanel(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorToggleBtn");e.classList.toggle("open"),e.classList.contains("open")?(e.style.display="",await this.loadMuxSessions(),await fetch("/api/mux-sessions/stats/start",{method:"POST"}),this.renderTaskPanel(),t&&(t.innerHTML="&#x25BC;")):(await fetch("/api/mux-sessions/stats/stop",{method:"POST"}),t&&(t.innerHTML="&#x25B2;"))},toggleTaskPanel(){this.toggleMonitorPanel()},toggleMonitorDetach(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorDetachBtn");e.classList.contains("detached")?(e.classList.remove("detached"),e.style.top="",e.style.left="",e.style.width="",e.style.height="",t&&(t.innerHTML="&#x29C9;",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="&#x229E;",t.title="Attach panel"),this.setupMonitorDrag())},setupMonitorDrag(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorPanelHeader");if(!e||!t)return;let s=!1,n,i,a,r;const o=h=>{if(h.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(h);n=u.clientX,i=u.clientY;const d=e.getBoundingClientRect();a=d.left,r=d.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),h.preventDefault()},l=h=>{if(!s)return;const u=getEventCoords(h),d=u.clientX-n,p=u.clientY-i;let m=a+d,g=r+p;const y=e.getBoundingClientRect();m=Math.max(0,Math.min(window.innerWidth-y.width,m)),g=Math.max(0,Math.min(window.innerHeight-y.height,g)),e.style.left=m+"px",e.style.top=g+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},toggleSubagentsDetach(){const e=document.getElementById("subagentsPanel"),t=document.getElementById("subagentsDetachBtn");e.classList.contains("detached")?(e.classList.remove("detached"),e.style.top="",e.style.left="",e.style.width="",e.style.height="",t&&(t.innerHTML="&#x29C9;",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="&#x229E;",t.title="Attach panel"),this.setupSubagentsDrag())},setupSubagentsDrag(){const e=document.getElementById("subagentsPanel"),t=document.getElementById("subagentsPanelHeader");if(!e||!t)return;let s=!1,n,i,a,r;const o=h=>{if(h.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(h);n=u.clientX,i=u.clientY;const d=e.getBoundingClientRect();a=d.left,r=d.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),h.preventDefault()},l=h=>{if(!s)return;const u=getEventCoords(h),d=u.clientX-n,p=u.clientY-i;let m=a+d,g=r+p;const y=e.getBoundingClientRect();m=Math.max(0,Math.min(window.innerWidth-y.width,m)),g=Math.max(0,Math.min(window.innerHeight-y.height,g)),e.style.left=m+"px",e.style.top=g+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},renderTaskPanel(){this._debouncedCall("taskPanel",this._renderTaskPanelImmediate)},_renderTaskPanelImmediate(){const e=this.sessions.get(this.activeSessionId),t=document.getElementById("backgroundTasksBody"),s=document.getElementById("taskPanelStats"),n=document.getElementById("backgroundTasksSection");if(!e||!e.taskTree||e.taskTree.length===0){n&&(n.style.display="none"),t.innerHTML="",s.textContent="0 tasks";return}n&&(n.style.display="");const i=e.taskStats||{running:0,completed:0,failed:0,total:0};s.textContent=`${i.running} running, ${i.completed} done`;const a=(l,c)=>{const h=l.status==="running"?"":l.status==="completed"?"&#x2713;":"&#x2717;",u=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let d="";if(l.children&&l.children.length>0){d='<div class="task-children">';for(const p of l.children){const m=c.find(g=>g.id===p);m&&(d+=`<div class="task-node">${a(m,c)}</div>`)}d+="</div>"}return`
86
86
  <div class="task-item">
87
- <span class="task-status-icon ${l.status}">${m}</span>
87
+ <span class="task-status-icon ${l.status}">${h}</span>
88
88
  <div class="task-info">
89
89
  <div class="task-description">${escapeHtml(l.description)}</div>
90
90
  <div class="task-meta">
@@ -94,7 +94,7 @@
94
94
  </div>
95
95
  </div>
96
96
  ${d}
97
- `},r=this.flattenTaskTree(e.taskTree);let o='<div class="task-tree">';for(const l of e.taskTree)o+=`<div class="task-node">${a(l,r)}</div>`;o+="</div>",t.innerHTML=o},flattenTaskTree(e,t=[]){for(const s of e)t.push(s);return t},toggleSubagentPanel(){this.toggleSubagentsPanel()},updateSubagentBadge(){const e=this.$("subagentCountBadge"),t=Array.from(this.subagents.values()).filter(s=>s.status==="active"||s.status==="idle").length;e&&(e.textContent=t>0?t:"")},renderSubagentPanel(){this._subagentPanelRenderTimeout&&clearTimeout(this._subagentPanelRenderTimeout),this._subagentPanelRenderTimeout=setTimeout(()=>{scheduleBackground(()=>this._renderSubagentPanelImmediate())},150)},_renderSubagentPanelImmediate(){const e=this.$("subagentList");if(!e||(this.updateSubagentBadge(),this.renderMonitorSubagents(),!this.subagentPanelVisible))return;if(this.subagents.size===0){e.innerHTML='<div class="subagent-empty">No background agents detected</div>';return}const t=[],s=Array.from(this.subagents.values()).sort((n,i)=>n.status==="active"&&i.status!=="active"?-1:i.status==="active"&&n.status!=="active"?1:(i.lastActivityAt||0)-(n.lastActivityAt||0));for(const n of s){const i=this.activeSubagentId===n.agentId,a=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],o=r[r.length-1],l=o?.type==="tool"?o.tool:null,c=this.subagentWindows.has(n.agentId),m=n.status==="active"||n.status==="idle",u=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",d=this.getTeammateInfo(n),p=d?d.name:n.description||n.agentId.substring(0,7),h=this.getTeammateBadgeHtml(n),g=d?`<span class="subagent-icon teammate-dot teammate-color-${d.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
97
+ `},r=this.flattenTaskTree(e.taskTree);let o='<div class="task-tree">';for(const l of e.taskTree)o+=`<div class="task-node">${a(l,r)}</div>`;o+="</div>",t.innerHTML=o},flattenTaskTree(e,t=[]){for(const s of e)t.push(s);return t},toggleSubagentPanel(){this.toggleSubagentsPanel()},updateSubagentBadge(){const e=this.$("subagentCountBadge"),t=Array.from(this.subagents.values()).filter(s=>s.status==="active"||s.status==="idle").length;e&&(e.textContent=t>0?t:"")},renderSubagentPanel(){this._subagentPanelRenderTimeout&&clearTimeout(this._subagentPanelRenderTimeout),this._subagentPanelRenderTimeout=setTimeout(()=>{scheduleBackground(()=>this._renderSubagentPanelImmediate())},150)},_renderSubagentPanelImmediate(){const e=this.$("subagentList");if(!e||(this.updateSubagentBadge(),this.renderMonitorSubagents(),!this.subagentPanelVisible))return;if(this.subagents.size===0){e.innerHTML='<div class="subagent-empty">No background agents detected</div>';return}const t=[],s=Array.from(this.subagents.values()).sort((n,i)=>n.status==="active"&&i.status!=="active"?-1:i.status==="active"&&n.status!=="active"?1:(i.lastActivityAt||0)-(n.lastActivityAt||0));for(const n of s){const i=this.activeSubagentId===n.agentId,a=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],o=r[r.length-1],l=o?.type==="tool"?o.tool:null,c=this.subagentWindows.has(n.agentId),h=n.status==="active"||n.status==="idle",u=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",d=this.getTeammateInfo(n),p=d?d.name:n.description||n.agentId.substring(0,7),m=this.getTeammateBadgeHtml(n),g=d?`<span class="subagent-icon teammate-dot teammate-color-${d.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
98
98
  <div class="subagent-item ${a} ${i?"selected":""}${d?" is-teammate":""}"
99
99
  onclick="app.selectSubagent(${escapeHtml(JSON.stringify(n.agentId))})"
100
100
  ondblclick="app.openSubagentWindow(${escapeHtml(JSON.stringify(n.agentId))})"
@@ -102,10 +102,10 @@
102
102
  <div class="subagent-header">
103
103
  ${g}
104
104
  <span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(p.length>40?p.substring(0,40)+"...":p)}</span>
105
- ${h}
105
+ ${m}
106
106
  ${u}
107
107
  <span class="subagent-status ${a}">${n.status}</span>
108
- ${m?`<button class="subagent-kill-btn" onclick="event.stopPropagation(); app.killSubagent(${escapeHtml(JSON.stringify(n.agentId))})" title="Kill agent">&#x2715;</button>`:""}
108
+ ${h?`<button class="subagent-kill-btn" onclick="event.stopPropagation(); app.killSubagent(${escapeHtml(JSON.stringify(n.agentId))})" title="Kill agent">&#x2715;</button>`:""}
109
109
  <button class="subagent-window-btn" onclick="event.stopPropagation(); app.${c?"closeSubagentWindow":"openSubagentWindow"}(${escapeHtml(JSON.stringify(n.agentId))})" title="${c?"Close window":"Open in window"}">
110
110
  ${c?"\u2715":"\u29C9"}
111
111
  </button>
@@ -122,14 +122,14 @@
122
122
  <span class="detail">${escapeHtml(c.primary)}</span>
123
123
  ${c.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(o.toolUseId))})">\u25B6</button>`:""}
124
124
  ${c.hasMore?`<div class="tool-params-expanded" id="tool-params-${escapeHtml(o.toolUseId)}" style="display:none;"><pre>${escapeHtml(JSON.stringify(o.fullInput||o.input,null,2))}</pre></div>`:""}
125
- </div>`}else if(o.type==="tool_result"){const c=o.isError?"\u274C":"\u{1F4C4}",m=o.isError?"error":"",u=o.contentLength>500?` (${this.formatBytes(o.contentLength)})`:"",d=o.preview.length>80?o.preview.substring(0,80)+"...":o.preview;return`<div class="subagent-activity tool-result ${m}">
125
+ </div>`}else if(o.type==="tool_result"){const c=o.isError?"\u274C":"\u{1F4C4}",h=o.isError?"error":"",u=o.contentLength>500?` (${this.formatBytes(o.contentLength)})`:"",d=o.preview.length>80?o.preview.substring(0,80)+"...":o.preview;return`<div class="subagent-activity tool-result ${h}">
126
126
  <span class="time">${l}</span>
127
127
  <span class="icon">${c}</span>
128
128
  <span class="name">${escapeHtml(o.tool||"result")}</span>
129
129
  <span class="detail">${escapeHtml(d)}${u}</span>
130
- </div>`}else if(o.type==="progress"){const c=o.hookEvent||o.hookName,m=c?"\u{1FA9D}":o.progressType==="query_update"?"\u27F3":"\u2713",u=c?" hook":"",d=c?o.hookName||o.hookEvent:o.query||o.progressType;return`<div class="subagent-activity progress${u}">
130
+ </div>`}else if(o.type==="progress"){const c=o.hookEvent||o.hookName,h=c?"\u{1FA9D}":o.progressType==="query_update"?"\u27F3":"\u2713",u=c?" hook":"",d=c?o.hookName||o.hookEvent:o.query||o.progressType;return`<div class="subagent-activity progress${u}">
131
131
  <span class="time">${l}</span>
132
- <span class="icon">${m}</span>
132
+ <span class="icon">${h}</span>
133
133
  <span class="detail">${escapeHtml(d)}</span>
134
134
  </div>`}else if(o.type==="message"){const c=o.text.length>100?o.text.substring(0,100)+"...":o.text;return`<div class="subagent-activity message">
135
135
  <span class="time">${l}</span>
@@ -187,7 +187,7 @@
187
187
  <span class="tool-detail">${escapeHtml(i)}</span>
188
188
  </div>`}else if(e.type==="message"){const s=e.text.length>150?e.text.substring(0,150)+"...":e.text;return`<div class="message-line">
189
189
  <span class="time">${t}</span> \u{1F4AC} ${escapeHtml(s)}
190
- </div>`}return""},updateSubagentWindows(){for(const e of this.subagentWindows.keys())this.renderSubagentWindowContent(e),this.updateSubagentWindowHeader(e)},updateSubagentWindowHeader(e){const t=this.subagents.get(e);if(!t)return;const s=document.getElementById(`subagent-window-${e}`);if(!s)return;const n=s.querySelector(".subagent-window-title .id");if(n){const c=this.getTeammateInfo(t),m=c?c.name:t.description||e.substring(0,7),u=m.length>50?m.substring(0,50)+"...":m;n.textContent=u}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const c=s.querySelector(".subagent-window-title");if(c){const m=document.createElement("span");m.className=`teammate-badge teammate-color-${a.color}`,m.title=`Team: ${a.teamName}`,m.textContent=`@${a.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",m)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(this.loadAppSettingsFromStorage?.().terminalFontFamily),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const m=new Unicode11Addon.Unicode11Addon;r.loadAddon(m),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(m){console.warn("[TeammateTerminal] Failed to open terminal:",m),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(m=>m.json()).then(m=>{if(m.success&&m.data?.buffer)try{r.write(m.data.buffer)}catch{}}).catch(m=>console.error("[TeammateTerminal] Failed to fetch buffer:",m));for(const m of a)try{r.write(m)}catch{}a.length=0},100),r.onData(m=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:m})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=o,c.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const v=this.subagentWindows.get(t);v.hidden&&(v.element.style.display="flex",v.hidden=!1),v.element.style.zIndex=++this.subagentWindowZIndex,v.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,a=20,r=window.innerWidth,o=window.innerHeight,l=50,c=120,m=Math.floor((r-l-50)/(n+a))||1,u=Math.floor((o-c-50)/(i+a))||1,d=s%m,p=Math.floor(s/m)%u;let h=l+d*(n+a),g=c+p*(i+a);h=Math.max(10,Math.min(h,r-n-10)),g=Math.max(10,Math.min(g,o-i-10));const y=e.color||"blue",w=document.createElement("div");w.className="subagent-window has-terminal",w.id=`subagent-window-${t}`,w.style.zIndex=++this.subagentWindowZIndex,w.style.left=`${h}px`,w.style.top=`${g}px`,w.style.width=`${n}px`,w.style.height=`${i}px`,w.innerHTML=`
190
+ </div>`}return""},updateSubagentWindows(){for(const e of this.subagentWindows.keys())this.renderSubagentWindowContent(e),this.updateSubagentWindowHeader(e)},updateSubagentWindowHeader(e){const t=this.subagents.get(e);if(!t)return;const s=document.getElementById(`subagent-window-${e}`);if(!s)return;const n=s.querySelector(".subagent-window-title .id");if(n){const c=this.getTeammateInfo(t),h=c?c.name:t.description||e.substring(0,7),u=h.length>50?h.substring(0,50)+"...":h;n.textContent=u}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const c=s.querySelector(".subagent-window-title");if(c){const h=document.createElement("span");h.className=`teammate-badge teammate-color-${a.color}`,h.title=`Team: ${a.teamName}`,h.textContent=`@${a.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",h)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(this.loadAppSettingsFromStorage?.().terminalFontFamily),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const h=new Unicode11Addon.Unicode11Addon;r.loadAddon(h),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(h){console.warn("[TeammateTerminal] Failed to open terminal:",h),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(h=>h.json()).then(h=>{if(h.success&&h.data?.buffer)try{r.write(h.data.buffer)}catch{}}).catch(h=>console.error("[TeammateTerminal] Failed to fetch buffer:",h));for(const h of a)try{r.write(h)}catch{}a.length=0},100),r.onData(h=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:h})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=o,c.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const v=this.subagentWindows.get(t);v.hidden&&(v.element.style.display="flex",v.hidden=!1),v.element.style.zIndex=++this.subagentWindowZIndex,v.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,a=20,r=window.innerWidth,o=window.innerHeight,l=50,c=120,h=Math.floor((r-l-50)/(n+a))||1,u=Math.floor((o-c-50)/(i+a))||1,d=s%h,p=Math.floor(s/h)%u;let m=l+d*(n+a),g=c+p*(i+a);m=Math.max(10,Math.min(m,r-n-10)),g=Math.max(10,Math.min(g,o-i-10));const y=e.color||"blue",w=document.createElement("div");w.className="subagent-window has-terminal",w.id=`subagent-window-${t}`,w.style.zIndex=++this.subagentWindowZIndex,w.style.left=`${m}px`,w.style.top=`${g}px`,w.style.width=`${n}px`,w.style.height=`${i}px`,w.innerHTML=`
191
191
  <div class="subagent-window-header">
192
192
  <div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
193
193
  <span class="icon" style="color: var(--team-color-${y}, #339af0)">\u2B24</span>
@@ -200,11 +200,11 @@
200
200
  </div>
201
201
  <div class="subagent-window-body teammate-terminal-body" id="subagent-window-body-${t}">
202
202
  </div>
203
- `,document.body.appendChild(w);const b=this.makeWindowDraggable(w,w.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(w);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:w,minimized:!1,hidden:S,dragListeners:b,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(w.style.display="none"),w.addEventListener("mousedown",()=>{w.style.zIndex=++this.subagentWindowZIndex});const T=new ResizeObserver(()=>{this.updateConnectionLines()});if(T.observe(w),this.subagentWindows.get(t).resizeObserver=T,S){const v=this.subagentWindows.get(t);v&&(v._lazyTerminal=!0,v._lazyPaneTarget=e.paneTarget,v._lazySessionId=e.sessionId)}else this.initTeammateTerminal(t,e,w);requestAnimationFrame(()=>{w.style.transition="transform 0.3s ease, opacity 0.3s ease",w.style.transform="scale(1)",w.style.opacity="1"})},rebuildTeammateMap(){this.teammateMap.clear();for(const[e,t]of this.teams)for(const s of t.members)s.agentType!=="team-lead"&&this.teammateMap.set(s.name,{name:s.name,color:s.color||"blue",teamName:e,agentId:s.agentId})},getTeammateInfo(e){if(!e?.description)return null;const t=e.description.match(/<teammate-message\s+teammate_id="?([^">\s]+)/);if(!t)return null;const n=t[1].split("@")[0];return this.teammateMap.get(n)||{name:n,color:"blue",teamName:"unknown"}},getTeammateBadgeHtml(e){const t=this.getTeammateInfo(e);return t?`<span class="teammate-badge teammate-color-${t.color}" title="Team: ${escapeHtml(t.teamName)}">@${escapeHtml(t.name)}</span>`:""},renderTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");if(!e)return;let t=null,s=null;if(this.activeSessionId){for(const[p,h]of this.teams)if(h.leadSessionId===this.activeSessionId){t=h,s=p;break}}if(!t){e.style.display="none";return}const n=e.style.display==="none";if(e.style.display="flex",n&&!this.teamTasksDragListeners){e.style.left=`${Math.max(10,window.innerWidth-360-20)}px`,e.style.top=`${Math.max(10,window.innerHeight-300-70)}px`;const g=e.querySelector(".team-tasks-header");g&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,g))}const i=this.teamTasks.get(s)||[],a=i.filter(p=>p.status==="completed").length,r=i.length,o=r>0?Math.round(a/r*100):0,l=e.querySelector(".team-tasks-header-text");if(l){const p=t.members.filter(h=>h.agentType!=="team-lead").length;l.textContent=`Team Tasks (${p} teammates)`}const c=e.querySelector(".team-tasks-progress-fill");c&&(c.style.width=`${o}%`);const m=e.querySelector(".team-tasks-progress-text");m&&(m.textContent=`${a}/${r}`);const u=e.querySelector(".team-tasks-list");if(!u)return;if(i.length===0){u.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const d=i.map(p=>{const h=p.status==="completed"?"\u2713":p.status==="in_progress"?"\u25C9":"\u25CB",g=p.status.replace("_","-"),y=p.owner?`<span class="team-task-owner teammate-color-${this.getTeammateColor(p.owner)}">${escapeHtml(p.owner)}</span>`:"";return`<div class="team-task-item ${g}">
204
- <span class="team-task-status">${h}</span>
203
+ `,document.body.appendChild(w);const b=this.makeWindowDraggable(w,w.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(w);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:w,minimized:!1,hidden:S,dragListeners:b,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(w.style.display="none"),w.addEventListener("mousedown",()=>{w.style.zIndex=++this.subagentWindowZIndex});const T=new ResizeObserver(()=>{this.updateConnectionLines()});if(T.observe(w),this.subagentWindows.get(t).resizeObserver=T,S){const v=this.subagentWindows.get(t);v&&(v._lazyTerminal=!0,v._lazyPaneTarget=e.paneTarget,v._lazySessionId=e.sessionId)}else this.initTeammateTerminal(t,e,w);requestAnimationFrame(()=>{w.style.transition="transform 0.3s ease, opacity 0.3s ease",w.style.transform="scale(1)",w.style.opacity="1"})},rebuildTeammateMap(){this.teammateMap.clear();for(const[e,t]of this.teams)for(const s of t.members)s.agentType!=="team-lead"&&this.teammateMap.set(s.name,{name:s.name,color:s.color||"blue",teamName:e,agentId:s.agentId})},getTeammateInfo(e){if(!e?.description)return null;const t=e.description.match(/<teammate-message\s+teammate_id="?([^">\s]+)/);if(!t)return null;const n=t[1].split("@")[0];return this.teammateMap.get(n)||{name:n,color:"blue",teamName:"unknown"}},getTeammateBadgeHtml(e){const t=this.getTeammateInfo(e);return t?`<span class="teammate-badge teammate-color-${t.color}" title="Team: ${escapeHtml(t.teamName)}">@${escapeHtml(t.name)}</span>`:""},renderTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");if(!e)return;let t=null,s=null;if(this.activeSessionId){for(const[p,m]of this.teams)if(m.leadSessionId===this.activeSessionId){t=m,s=p;break}}if(!t){e.style.display="none";return}const n=e.style.display==="none";if(e.style.display="flex",n&&!this.teamTasksDragListeners){e.style.left=`${Math.max(10,window.innerWidth-360-20)}px`,e.style.top=`${Math.max(10,window.innerHeight-300-70)}px`;const g=e.querySelector(".team-tasks-header");g&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,g))}const i=this.teamTasks.get(s)||[],a=i.filter(p=>p.status==="completed").length,r=i.length,o=r>0?Math.round(a/r*100):0,l=e.querySelector(".team-tasks-header-text");if(l){const p=t.members.filter(m=>m.agentType!=="team-lead").length;l.textContent=`Team Tasks (${p} teammates)`}const c=e.querySelector(".team-tasks-progress-fill");c&&(c.style.width=`${o}%`);const h=e.querySelector(".team-tasks-progress-text");h&&(h.textContent=`${a}/${r}`);const u=e.querySelector(".team-tasks-list");if(!u)return;if(i.length===0){u.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const d=i.map(p=>{const m=p.status==="completed"?"\u2713":p.status==="in_progress"?"\u25C9":"\u25CB",g=p.status.replace("_","-"),y=p.owner?`<span class="team-task-owner teammate-color-${this.getTeammateColor(p.owner)}">${escapeHtml(p.owner)}</span>`:"";return`<div class="team-task-item ${g}">
204
+ <span class="team-task-status">${m}</span>
205
205
  <span class="team-task-subject">${escapeHtml(p.subject)}</span>
206
206
  ${y}
207
- </div>`}).join("");u.innerHTML=d},hideTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");e&&(e.style.display="none"),this.teamTasksDragListeners&&(document.removeEventListener("mousemove",this.teamTasksDragListeners.move),document.removeEventListener("mouseup",this.teamTasksDragListeners.up),this.teamTasksDragListeners.touchMove&&(document.removeEventListener("touchmove",this.teamTasksDragListeners.touchMove),document.removeEventListener("touchend",this.teamTasksDragListeners.up),document.removeEventListener("touchcancel",this.teamTasksDragListeners.up)),this.teamTasksDragListeners.handle&&(this.teamTasksDragListeners.handle.removeEventListener("mousedown",this.teamTasksDragListeners.handleMouseDown),this.teamTasksDragListeners.handle.removeEventListener("touchstart",this.teamTasksDragListeners.handleTouchStart)),this.teamTasksDragListeners=null)},getTeammateColor(e){return this.teammateMap.get(e)?.color||"blue"},normalizeFilePath(e,t){if(!e)return"";let s=e.trim();const n="/home/"+(window.USER||"user");s.startsWith("~/")?s=n+s.slice(1):s==="~"&&(s=n),!s.startsWith("/")&&t&&(s=t+"/"+s);const i=s.split("/"),a=[];for(const r of i)r===""||r==="."||(r===".."?a.length>1&&a.pop():a.push(r));return"/"+a.join("/")},getFilename(e){const t=e.split("/");return t[t.length-1]||""},isShallowRootPath(e){return e.startsWith("/")?e.split("/").filter(s=>s!=="").length===1:!1},isPathInWorkingDir(e,t){if(!t)return!1;const s=this.normalizeFilePath(e,t);return s.startsWith(t+"/")||s===t},pathsAreEquivalent(e,t,s){const n=this.normalizeFilePath(e,s),i=this.normalizeFilePath(t,s);if(n===i)return!0;const a=this.getFilename(n),r=this.getFilename(i);if(a!==r)return!1;const o=this.isShallowRootPath(e),l=this.isShallowRootPath(t),c=this.isPathInWorkingDir(n,s),m=this.isPathInWorkingDir(i,s);return!!(o&&m||l&&c)},pickBetterPath(e,t,s){if(s){const a=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(a&&!r)return e;if(r&&!a)return t}const n=e.startsWith("/"),i=t.startsWith("/");return n&&!i?e:i&&!n?t:e.length!==t.length?e.length>t.length?e:t:!e.includes("~")&&t.includes("~")?e:!t.includes("~")&&e.includes("~")?t:e},deduplicateProjectInsightPaths(e,t){const s=[];for(const a of e)for(const r of a.filePaths)s.push({rawPath:r,toolId:a.id});if(s.length<=1){const a=new Map;for(const r of s)a.set(this.normalizeFilePath(r.rawPath,t),r);return a}s.sort((a,r)=>{const o=this.isPathInWorkingDir(a.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return o&&!l?-1:l&&!o?1:r.rawPath.length-a.rawPath.length});const n=new Map,i=new Set;for(const{rawPath:a,toolId:r}of s){const o=this.normalizeFilePath(a,t);let l=!1;for(const[,c]of n)if(this.pathsAreEquivalent(a,c.rawPath,t)){l=!0;break}!l&&!i.has(o)&&(n.set(o,{rawPath:a,toolId:r}),i.add(o))}return n},handleBashToolStart(e,t){let s=this.projectInsights.get(e)||[];s=s.filter(n=>n.id!==t.id),s.push(t),this.projectInsights.set(e,s),this.renderProjectInsightsPanel()},handleBashToolEnd(e,t){const n=(this.projectInsights.get(e)||[]).find(i=>i.id===t.id);n&&(n.status="completed"),this.renderProjectInsightsPanel(),setTimeout(()=>{const i=this.projectInsights.get(e)||[];this.projectInsights.set(e,i.filter(a=>a.id!==t.id)),this.renderProjectInsightsPanel()},2e3)},handleBashToolsUpdate(e,t){this.projectInsights.set(e,t),this.renderProjectInsightsPanel()},renderProjectInsightsPanel(){const e=this.$("projectInsightsPanel"),t=this.$("projectInsightsList");if(!e||!t)return;if(!(this.loadAppSettingsFromStorage().showProjectInsights??!1)){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}const a=(this.projectInsights.get(this.activeSessionId)||[]).filter(u=>u.status==="running");if(a.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const o=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(a,o),c=new Set(Array.from(l.values()).map(u=>u.rawPath)),m=[];for(const u of a){const d=u.filePaths.filter(h=>c.has(h));if(d.length===0)continue;const p=u.command.length>50?u.command.substring(0,50)+"...":u.command;m.push(`
207
+ </div>`}).join("");u.innerHTML=d},hideTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");e&&(e.style.display="none"),this.teamTasksDragListeners&&(document.removeEventListener("mousemove",this.teamTasksDragListeners.move),document.removeEventListener("mouseup",this.teamTasksDragListeners.up),this.teamTasksDragListeners.touchMove&&(document.removeEventListener("touchmove",this.teamTasksDragListeners.touchMove),document.removeEventListener("touchend",this.teamTasksDragListeners.up),document.removeEventListener("touchcancel",this.teamTasksDragListeners.up)),this.teamTasksDragListeners.handle&&(this.teamTasksDragListeners.handle.removeEventListener("mousedown",this.teamTasksDragListeners.handleMouseDown),this.teamTasksDragListeners.handle.removeEventListener("touchstart",this.teamTasksDragListeners.handleTouchStart)),this.teamTasksDragListeners=null)},getTeammateColor(e){return this.teammateMap.get(e)?.color||"blue"},normalizeFilePath(e,t){if(!e)return"";let s=e.trim();const n="/home/"+(window.USER||"user");s.startsWith("~/")?s=n+s.slice(1):s==="~"&&(s=n),!s.startsWith("/")&&t&&(s=t+"/"+s);const i=s.split("/"),a=[];for(const r of i)r===""||r==="."||(r===".."?a.length>1&&a.pop():a.push(r));return"/"+a.join("/")},getFilename(e){const t=e.split("/");return t[t.length-1]||""},isShallowRootPath(e){return e.startsWith("/")?e.split("/").filter(s=>s!=="").length===1:!1},isPathInWorkingDir(e,t){if(!t)return!1;const s=this.normalizeFilePath(e,t);return s.startsWith(t+"/")||s===t},pathsAreEquivalent(e,t,s){const n=this.normalizeFilePath(e,s),i=this.normalizeFilePath(t,s);if(n===i)return!0;const a=this.getFilename(n),r=this.getFilename(i);if(a!==r)return!1;const o=this.isShallowRootPath(e),l=this.isShallowRootPath(t),c=this.isPathInWorkingDir(n,s),h=this.isPathInWorkingDir(i,s);return!!(o&&h||l&&c)},pickBetterPath(e,t,s){if(s){const a=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(a&&!r)return e;if(r&&!a)return t}const n=e.startsWith("/"),i=t.startsWith("/");return n&&!i?e:i&&!n?t:e.length!==t.length?e.length>t.length?e:t:!e.includes("~")&&t.includes("~")?e:!t.includes("~")&&e.includes("~")?t:e},deduplicateProjectInsightPaths(e,t){const s=[];for(const a of e)for(const r of a.filePaths)s.push({rawPath:r,toolId:a.id});if(s.length<=1){const a=new Map;for(const r of s)a.set(this.normalizeFilePath(r.rawPath,t),r);return a}s.sort((a,r)=>{const o=this.isPathInWorkingDir(a.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return o&&!l?-1:l&&!o?1:r.rawPath.length-a.rawPath.length});const n=new Map,i=new Set;for(const{rawPath:a,toolId:r}of s){const o=this.normalizeFilePath(a,t);let l=!1;for(const[,c]of n)if(this.pathsAreEquivalent(a,c.rawPath,t)){l=!0;break}!l&&!i.has(o)&&(n.set(o,{rawPath:a,toolId:r}),i.add(o))}return n},handleBashToolStart(e,t){let s=this.projectInsights.get(e)||[];s=s.filter(n=>n.id!==t.id),s.push(t),this.projectInsights.set(e,s),this.renderProjectInsightsPanel()},handleBashToolEnd(e,t){const n=(this.projectInsights.get(e)||[]).find(i=>i.id===t.id);n&&(n.status="completed"),this.renderProjectInsightsPanel(),setTimeout(()=>{const i=this.projectInsights.get(e)||[];this.projectInsights.set(e,i.filter(a=>a.id!==t.id)),this.renderProjectInsightsPanel()},2e3)},handleBashToolsUpdate(e,t){this.projectInsights.set(e,t),this.renderProjectInsightsPanel()},renderProjectInsightsPanel(){const e=this.$("projectInsightsPanel"),t=this.$("projectInsightsList");if(!e||!t)return;if(!(this.loadAppSettingsFromStorage().showProjectInsights??!1)){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}const a=(this.projectInsights.get(this.activeSessionId)||[]).filter(u=>u.status==="running");if(a.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const o=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(a,o),c=new Set(Array.from(l.values()).map(u=>u.rawPath)),h=[];for(const u of a){const d=u.filePaths.filter(m=>c.has(m));if(d.length===0)continue;const p=u.command.length>50?u.command.substring(0,50)+"...":u.command;h.push(`
208
208
  <div class="project-insight-item" data-tool-id="${u.id}">
209
209
  <div class="project-insight-command">
210
210
  <span class="icon">\u{1F4BB}</span>
@@ -213,25 +213,33 @@
213
213
  ${u.timeout?`<span class="project-insight-timeout">${escapeHtml(u.timeout)}</span>`:""}
214
214
  </div>
215
215
  <div class="project-insight-paths">
216
- `);for(const h of d){const g=h.split("/").pop();m.push(`
216
+ `);for(const m of d){const g=m.split("/").pop();h.push(`
217
217
  <span class="project-insight-filepath"
218
- onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(h))}, ${escapeHtml(JSON.stringify(u.sessionId))})"
219
- title="${escapeHtml(h)}">${escapeHtml(g)}</span>
220
- `)}m.push(`
218
+ onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(m))}, ${escapeHtml(JSON.stringify(u.sessionId))})"
219
+ title="${escapeHtml(m)}">${escapeHtml(g)}</span>
220
+ `)}h.push(`
221
221
  </div>
222
222
  </div>
223
- `)}t.innerHTML=m.join("")},closeProjectInsightsPanel(){const e=this.$("projectInsightsPanel");e&&(e.classList.remove("visible"),this.projectInsightsPanelVisible=!1)},_loadFileBrowserShowHidden(){try{return localStorage.getItem(FILE_BROWSER_SHOW_HIDDEN_KEY)==="1"}catch{return!1}},_syncFileBrowserHiddenBtn(){const e=this.$("fileBrowserHiddenBtn");if(!e)return;const t=this.fileBrowserShowHidden===!0;e.classList.toggle("active",t),e.setAttribute("aria-pressed",String(t));const s=t?"Hide hidden files and folders":"Show hidden files and folders";e.setAttribute("title",s),e.setAttribute("aria-label",s)},async toggleFileBrowserHidden(){this.fileBrowserShowHidden=!this.fileBrowserShowHidden;try{localStorage.setItem(FILE_BROWSER_SHOW_HIDDEN_KEY,this.fileBrowserShowHidden?"1":"0")}catch{}this._syncFileBrowserHiddenBtn(),this.activeSessionId&&await this.loadFileBrowser(this.activeSessionId)},async loadFileBrowser(e){if(!e)return;const t=this.$("fileBrowserTree"),s=this.$("fileBrowserStatus");if(this._syncFileBrowserHiddenBtn(),!!t){t.innerHTML='<div class="file-browser-loading">Loading files...</div>';try{const n=this.fileBrowserShowHidden===!0,i=await fetch(`/api/sessions/${e}/files?depth=5&showHidden=${n}`);if(!i.ok)throw new Error("Failed to load files");const a=await i.json();if(!a.success)throw new Error(a.error||"Failed to load files");if(this.fileBrowserData=a.data,this.renderFileBrowserTree(),s){const{totalFiles:r,totalDirectories:o,truncated:l}=a.data;s.textContent=`${r} files, ${o} dirs${l?" (truncated)":""}${n?" \xB7 hidden shown":""}`}}catch(n){console.error("Failed to load file browser:",n),t.innerHTML=`<div class="file-browser-empty">Failed to load files: ${escapeHtml(n.message)}</div>`}}},renderFileBrowserTree(){const e=this.$("fileBrowserTree");if(!e||!this.fileBrowserData)return;const{tree:t}=this.fileBrowserData;if(!t||t.length===0){e.innerHTML='<div class="file-browser-empty">No files found</div>';return}const s=[],n=this.fileBrowserFilter.toLowerCase(),i=(a,r)=>{const o=a.type==="directory",l=this.fileBrowserExpandedDirs.has(a.path),c=!n||a.name.toLowerCase().includes(n);let m=!1;o&&n&&a.children&&(m=this.hasMatchingChild(a,n));const d=!(c||m)&&n?" hidden-by-filter":"",p=o?l?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(a.extension),h=o?`<span class="file-tree-expand${l?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',g=!o&&a.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(a.size)}</span>`:"",y=o?"file-tree-name directory":"file-tree-name",w=o?"":`<a class="file-tree-download" href="/api/sessions/${this.activeSessionId}/file-raw?path=${encodeURIComponent(a.path)}&download=true" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;if(s.push(`
224
- <div class="file-tree-item${d}" data-path="${escapeHtml(a.path)}" data-type="${a.type}" data-depth="${r}">
225
- ${h}
226
- <span class="file-tree-icon">${p}</span>
227
- <span class="${y}">${escapeHtml(a.name)}</span>
223
+ `)}t.innerHTML=h.join("")},closeProjectInsightsPanel(){const e=this.$("projectInsightsPanel");e&&(e.classList.remove("visible"),this.projectInsightsPanelVisible=!1)},_loadFileBrowserShowHidden(){try{return localStorage.getItem(FILE_BROWSER_SHOW_HIDDEN_KEY)==="1"}catch{return!1}},_syncFileBrowserHiddenBtn(){const e=this.$("fileBrowserHiddenBtn");if(!e)return;const t=this.fileBrowserShowHidden===!0;e.classList.toggle("active",t),e.setAttribute("aria-pressed",String(t));const s=t?"Hide hidden files and folders":"Show hidden files and folders";e.setAttribute("title",s),e.setAttribute("aria-label",s)},_ensureFileBrowserState(){if(!this._fileBrowserState){const e=this.activeSessionId||null,t=this.fileBrowserShowHidden===!0;this._fileBrowserState={treeEpoch:0,searchEpoch:0,ownerSessionId:e,view:"normal",normalState:this.fileBrowserData?{sessionId:e,showHidden:t,treeEpoch:0,phase:"ready",data:this.fileBrowserData}:null,treeInFlight:null,inFlight:null,matches:[],deferredDirectoryTarget:null,filter:typeof this.fileBrowserFilter=="string"?this.fileBrowserFilter:""}}return this._fileBrowserState},_activateFileBrowserSession(e){if(!e)return;const t=this._ensureFileBrowserState();t.inFlight?.timer!==void 0&&t.inFlight?.timer!==null&&clearTimeout(t.inFlight.timer),t.searchEpoch++,t.treeEpoch++,t.ownerSessionId=e,t.treeInFlight=null,t.inFlight=null,t.normalState=null,t.matches=[],t.deferredDirectoryTarget=null,t.filter="",t.view="normal",this.fileBrowserData=null,this.fileBrowserFilter="",this.fileBrowserExpandedDirs?.clear?.(),this.fileBrowserAllExpanded=!1;const s=this.$?.("fileBrowserSearch");s&&(s.value=""),this._syncFileBrowserExpandBtn();const n=this.$?.("fileBrowserExpandBtn");n&&(n.innerHTML="\u229E");const i=this.$?.("fileBrowserPanel"),a=this.$?.("fileBrowserTree"),r=this.$?.("fileBrowserStatus"),o=i?.classList.contains("visible")===!0;a&&(a.innerHTML=o?`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`:""),r&&(r.textContent=o?"Loading files...":""),o&&this.loadFileBrowser?.(e)?.catch?.(()=>{})},_resetFileBrowserForHide(){const e=this._ensureFileBrowserState();e.inFlight?.timer!==void 0&&e.inFlight?.timer!==null&&clearTimeout(e.inFlight.timer),e.searchEpoch++,e.treeEpoch++,e.treeInFlight=null,e.inFlight=null,e.normalState=null,e.matches=[],e.deferredDirectoryTarget=null,e.filter="",e.view="normal",this.fileBrowserData=null,this.fileBrowserFilter="",this.fileBrowserExpandedDirs?.clear?.(),this.fileBrowserAllExpanded=!1;const t=this.$?.("fileBrowserSearch");t&&(t.value=""),this._syncFileBrowserExpandBtn();const s=this.$?.("fileBrowserExpandBtn");s&&(s.innerHTML="\u229E");const n=this.$?.("fileBrowserTree");n&&(n.innerHTML="");const i=this.$?.("fileBrowserStatus");i&&(i.textContent="")},_setFileBrowserExpandDisabled(e){const t=this.$("fileBrowserExpandBtn");t&&(t.disabled=e)},_hasFileBrowserQuery(){const e=this._ensureFileBrowserState(),t=this.$?.("fileBrowserSearch"),s=typeof t?.value=="string"?t.value:"",n=typeof e.filter=="string"?e.filter:"";return s.trim()!==""||n.trim()!==""},_syncFileBrowserExpandBtn(){this._setFileBrowserExpandDisabled(this._hasFileBrowserQuery())},_renderFileBrowserNormalStatus(e,t){const s=this.$("fileBrowserStatus");if(!s||!e)return;const{totalFiles:n,totalDirectories:i,truncated:a}=e;s.textContent=`${n} files, ${i} dirs${a?" (truncated)":""}${t?" \xB7 hidden shown":""}`},_isFileBrowserNormalCompatible(e,t,s,n){return e?.sessionId===t&&e.showHidden===s&&e.treeEpoch===n},_isFileBrowserTreeContextCurrent(e,t=!1){const s=this._ensureFileBrowserState();return(!t||s.treeInFlight===e)&&s.ownerSessionId===e.sessionId&&s.treeEpoch===e.treeEpoch&&this.fileBrowserShowHidden===!0===e.showHidden},_canRenderFileBrowserNormal(e){const t=this._ensureFileBrowserState();return t.view==="normal"&&this.activeSessionId===e?.sessionId&&this._isFileBrowserNormalCompatible(e,t.ownerSessionId,this.fileBrowserShowHidden===!0,t.treeEpoch)&&this.$("fileBrowserPanel")?.classList.contains("visible")===!0},_renderFileBrowserNormalState(e){if(!e||!this._canRenderFileBrowserNormal(e))return;const t=this.$("fileBrowserTree"),s=this.$("fileBrowserStatus");if(t){if(e.phase==="loading"){this.fileBrowserData=null,t.innerHTML=`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`,s&&(s.textContent="Loading files...");return}if(e.phase==="error"){this.fileBrowserData=null;const i=`Failed to load files${e.error&&e.error!=="Failed to load files"?`: ${e.error}`:""}`;t.innerHTML=`<div class="file-browser-empty">${escapeHtml(i)}</div>`,s&&(s.textContent=i);return}e.phase==="ready"&&(this.fileBrowserData=e.data,this._syncFileBrowserExpandBtn(),this.renderFileBrowserTree(e.sessionId),this._renderFileBrowserNormalStatus(e.data,e.showHidden))}},_validateFileBrowserTreeEnvelope(e){if(!e||typeof e!="object"||e.success!==!0)return null;const t=e.data;if(!t||typeof t!="object"||!Array.isArray(t.tree)||t.mode==="search"||typeof t.totalFiles!="number"||!Number.isFinite(t.totalFiles)||t.totalFiles<0||typeof t.totalDirectories!="number"||!Number.isFinite(t.totalDirectories)||t.totalDirectories<0||typeof t.truncated!="boolean")return null;const s=n=>n.every(i=>!(!i||typeof i!="object"||typeof i.name!="string"||typeof i.path!="string"||i.type!=="file"&&i.type!=="directory"||i.size!==void 0&&(typeof i.size!="number"||!Number.isFinite(i.size))||i.extension!==void 0&&typeof i.extension!="string"||i.children!==void 0&&(!Array.isArray(i.children)||!s(i.children))));return s(t.tree)?t:null},_normalizeFileBrowserTreeError(e){return typeof e?.message=="string"&&e.message?e.message:"Failed to load files"},_validateFileBrowserSearchEnvelope(e){if(!e||typeof e!="object"||e.success!==!0)return null;const t=e.data;if(!t||typeof t!="object"||t.mode!=="search"||!Array.isArray(t.matches)||typeof t.truncated!="boolean"||t.matchCount!==void 0&&(typeof t.matchCount!="number"||!Number.isFinite(t.matchCount)||t.matchCount<0))return null;for(const s of t.matches)if(!s||typeof s!="object"||typeof s.name!="string"||typeof s.path!="string"||s.type!=="file"&&s.type!=="directory"||s.size!==void 0&&(typeof s.size!="number"||!Number.isFinite(s.size))||s.extension!==void 0&&typeof s.extension!="string")return null;return t},_canRenderFileBrowserSearch(e){const t=this._ensureFileBrowserState(),s=this.$("fileBrowserPanel");return t.searchEpoch===e.epoch&&t.ownerSessionId===e.ownerSessionId&&this.activeSessionId===e.ownerSessionId&&this.fileBrowserShowHidden===!0===e.showHidden&&t.filter===e.rawInput&&s?.classList.contains("visible")===!0},_renderFileBrowserSearchError(){const e=this.$("fileBrowserTree"),t=this.$("fileBrowserStatus"),s="Search failed";e&&(e.innerHTML=`<div class="file-browser-empty">${escapeHtml(s)}</div>`),t&&(t.textContent=s)},_canContinueFileBrowserHiddenReload(e,t){const s=this._ensureFileBrowserState(),n=this.$?.("fileBrowserSearch"),i=typeof n?.value=="string"?n.value:s.filter;return s.view==="normal"&&s.searchEpoch===e.searchEpoch&&s.treeEpoch===e.treeEpoch&&s.ownerSessionId===e.ownerSessionId&&this.activeSessionId===e.ownerSessionId&&this.fileBrowserShowHidden===!0===e.showHidden&&s.filter===e.rawInput&&i===e.rawInput&&i.trim()===e.query&&this.$?.("fileBrowserPanel")?.classList.contains("visible")===!0&&t?.phase==="ready"&&this._isFileBrowserNormalCompatible(t,e.ownerSessionId,e.showHidden,e.treeEpoch)},async toggleFileBrowserHidden(){const e=this._ensureFileBrowserState(),t=typeof e.filter=="string"?e.filter:"",s=t.trim();this.fileBrowserShowHidden=!this.fileBrowserShowHidden;try{localStorage.setItem(FILE_BROWSER_SHOW_HIDDEN_KEY,this.fileBrowserShowHidden?"1":"0")}catch{}this._syncFileBrowserHiddenBtn(),e.inFlight?.timer!==void 0&&e.inFlight?.timer!==null&&clearTimeout(e.inFlight.timer),e.searchEpoch++,e.inFlight=null,e.matches=[],e.deferredDirectoryTarget=null,e.normalState=null,this.fileBrowserData=null,s.length<=256&&(e.view="normal"),this._syncFileBrowserExpandBtn();const n=e.ownerSessionId||this.activeSessionId;if(!n||this.activeSessionId!==n)return;const i=e.searchEpoch,a=this.fileBrowserShowHidden===!0,r=this.loadFileBrowser(n,{force:!0}),o=e.treeEpoch;if(!r?.then||(await r,!s||s.length>256))return;const l={ownerSessionId:n,showHidden:a,treeEpoch:o,searchEpoch:i,rawInput:t,query:s};this._canContinueFileBrowserHiddenReload(l,e.normalState)&&this.filterFileBrowser(t)},loadFileBrowser(e,{force:t=!1}={}){if(!e)return;const s=this._ensureFileBrowserState(),n=this.$("fileBrowserTree");if(this._syncFileBrowserHiddenBtn(),!n||(s.ownerSessionId||(s.ownerSessionId=e),s.ownerSessionId!==e))return;t&&s.treeEpoch++;const i=this.fileBrowserShowHidden===!0,a=s.treeEpoch,r=s.treeInFlight;if(!t&&this._isFileBrowserNormalCompatible(r,e,i,a))return r.promise;const o=s.normalState;if(!t&&this._isFileBrowserNormalCompatible(o,e,i,a)&&(o.phase==="ready"||o.phase==="error"))return o.phase==="ready"&&(this.fileBrowserData=o.data),this._renderFileBrowserNormalState(o),Promise.resolve(o);const l={sessionId:e,showHidden:i,treeEpoch:a,phase:"loading"};s.normalState=l,this.fileBrowserData=null,this._renderFileBrowserNormalState(l);const c={sessionId:e,showHidden:i,treeEpoch:a,promise:null},h=(async()=>{try{const u=await fetch(`/api/sessions/${encodeURIComponent(e)}/files?depth=5&showHidden=${i}`);if(!u.ok)throw new Error("Failed to load files");const d=await u.json(),p=this._validateFileBrowserTreeEnvelope(d);if(!p){const y=d&&typeof d=="object"&&typeof d.error=="string"?d.error:"Failed to load files";throw new Error(y)}if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const m={sessionId:e,showHidden:i,treeEpoch:a,phase:"ready",data:p};s.normalState=m,this.fileBrowserData=p,this._completeDeferredFileBrowserDirectory?.(m)===!0||this._renderFileBrowserNormalState(m)}catch(u){if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const d={sessionId:e,showHidden:i,treeEpoch:a,phase:"error",error:this._normalizeFileBrowserTreeError(u)};s.normalState=d,this.fileBrowserData=null,this._completeDeferredFileBrowserDirectory?.(d),console.error("Failed to load file browser:",u),this._renderFileBrowserNormalState(d)}})();return c.promise=h.finally(()=>{s.treeInFlight===c&&(s.treeInFlight=null)}),s.treeInFlight=c,c.promise},renderFileBrowserTree(e){const t=this.$("fileBrowserTree");if(!t||!this.fileBrowserData)return;const s=this._ensureFileBrowserState(),n=e||s.normalState?.sessionId||s.ownerSessionId||this.activeSessionId;if(!n)return;const{tree:i}=this.fileBrowserData;if(!i||i.length===0){t.innerHTML='<div class="file-browser-empty">No files found</div>';return}const a=[],r=(o,l)=>{const c=o.type==="directory",h=this.fileBrowserExpandedDirs.has(o.path),u=c?h?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(o.extension),d=c?`<span class="file-tree-expand${h?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',p=!c&&o.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(o.size)}</span>`:"",m=c?"file-tree-name directory":"file-tree-name",g=c?"":`<a class="file-tree-download" href="${escapeHtml(`/api/sessions/${encodeURIComponent(n)}/file-raw?path=${encodeURIComponent(o.path)}&download=true`)}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;if(a.push(`
224
+ <div class="file-tree-item" data-path="${escapeHtml(o.path)}" data-type="${escapeHtml(o.type)}" data-depth="${l}">
225
+ ${d}
226
+ <span class="file-tree-icon">${u}</span>
227
+ <span class="${m}">${escapeHtml(o.name)}</span>
228
+ ${p}
228
229
  ${g}
229
- ${w}
230
230
  </div>
231
- `),o&&l&&a.children)for(const b of a.children)i(b,r+1)};for(const a of t)i(a,0);e.innerHTML=s.join(""),e.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const r=a.dataset.path;a.dataset.type==="directory"?this.toggleFileBrowserFolder(r):this.openFilePreview(r)})})},hasMatchingChild(e,t){if(!e.children)return!1;for(const s of e.children)if(s.name.toLowerCase().includes(t)||s.type==="directory"&&this.hasMatchingChild(s,t))return!0;return!1},toggleFileBrowserFolder(e){this.fileBrowserExpandedDirs.has(e)?this.fileBrowserExpandedDirs.delete(e):this.fileBrowserExpandedDirs.add(e),this.renderFileBrowserTree()},filterFileBrowser(e){this.fileBrowserFilter=e,e&&this.expandAllDirectories(this.fileBrowserData?.tree||[]),this.renderFileBrowserTree()},expandAllDirectories(e){for(const t of e)t.type==="directory"&&(this.fileBrowserExpandedDirs.add(t.path),t.children&&this.expandAllDirectories(t.children))},collapseAllDirectories(){this.fileBrowserExpandedDirs.clear()},toggleFileBrowserExpand(){this.fileBrowserAllExpanded=!this.fileBrowserAllExpanded;const e=this.$("fileBrowserExpandBtn");this.fileBrowserAllExpanded?(this.expandAllDirectories(this.fileBrowserData?.tree||[]),e&&(e.innerHTML="\u229F")):(this.collapseAllDirectories(),e&&(e.innerHTML="\u229E")),this.renderFileBrowserTree()},refreshFileBrowser(){if(this.activeSessionId){this.fileBrowserExpandedDirs.clear(),this.fileBrowserFilter="",this.fileBrowserAllExpanded=!1;const e=this.$("fileBrowserSearch");e&&(e.value=""),this.loadFileBrowser(this.activeSessionId)}},toggleFileBrowserButton(){const t=this.$("fileBrowserPanel")?.classList.contains("visible"),s=document.querySelector(".btn-file-viewer");if(t){this.closeFileBrowserPanel(),s&&s.setAttribute("aria-expanded","false");return}if(!this.activeSessionId){this.showToast("Open a session to browse its files","info");return}const n=this.loadAppSettingsFromStorage();n.showFileBrowser=!0,this.saveAppSettingsToStorage(n);const i=document.getElementById("appSettingsShowFileBrowser");i&&(i.checked=!0),this.applyMonitorVisibility(),s&&s.setAttribute("aria-expanded","true")},closeFileBrowserPanel(){const e=this.$("fileBrowserPanel");if(e&&(e.classList.remove("visible"),e.style.left="",e.style.top="",e.style.bottom="",e.style.right=""),this.fileBrowserDragListeners){const i=this.fileBrowserDragListeners;document.removeEventListener("mousemove",i.move),document.removeEventListener("mouseup",i.up),document.removeEventListener("touchmove",i.touchMove),document.removeEventListener("touchend",i.up),document.removeEventListener("touchcancel",i.up),i.handle&&(i.handle.removeEventListener("mousedown",i.handleMouseDown),i.handle.removeEventListener("touchstart",i.handleTouchStart),i._onFirstDrag&&(i.handle.removeEventListener("mousedown",i._onFirstDrag),i.handle.removeEventListener("touchstart",i._onFirstDrag))),this.fileBrowserDragListeners=null}const t=this.loadAppSettingsFromStorage();t.showFileBrowser=!1,this.saveAppSettingsToStorage(t);const s=document.getElementById("appSettingsShowFileBrowser");s&&(s.checked=!1);const n=document.querySelector(".btn-file-viewer");n&&n.setAttribute("aria-expanded","false")},_isExternalPreviewPath(e,t){if(typeof e!="string"||!e.startsWith("/"))return!1;const s=this.sessions.get(t)?.workingDir;if(!s)return!1;const n=s.endsWith("/")?s:`${s}/`;return e!==s&&!e.startsWith(n)},async _registerExternalPreview(e,t){try{const s=await fetch(`/api/sessions/${t}/attachments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e,notify:!1})}),n=await s.json().catch(()=>null);if(s.ok&&n?.success&&n.data?.attachmentId)return{attachmentId:n.data.attachmentId,size:n.data.size||0};const i=n?.error||`Cannot open this file (HTTP ${s.status})`;return/unsupported/i.test(i)?{error:`Cannot preview .${(e.split(".").pop()||"").toLowerCase()} from outside the session workspace (images, video, audio, PDF, Office documents and text files only).`}:{error:i}}catch(s){return{error:s.message||"Cannot open this file"}}},async openFilePreview(e,t=this.activeSessionId,s=null){if(!t||!e)return;const n=this.$("filePreviewOverlay"),i=this.$("filePreviewTitle"),a=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!a)return;this._resetFilePreviewEdit(),this._stopFilePreviewMedia(),this.filePreviewDetachUrl="";const o=this.$("filePreviewDetachBtn");o&&(o.hidden=!0),n.classList.add("visible"),i.textContent=e,a.innerHTML='<div class="binary-message">Loading...</div>',r.textContent="";const l=(e.split(".").pop()||"").toLowerCase();let c="",m=0;if(!s&&this._isExternalPreviewPath(e,t)){const d=await this._registerExternalPreview(e,t);s=d.attachmentId||null,c=d.error||"",m=d.size||0}if(!s&&c){r.textContent="",a.innerHTML=`<div class="binary-message">${escapeHtml(c)}</div>`;return}const u=l==="docx"||l==="pptx";if(this.filePreviewDetachUrl=s?`/api/sessions/${t}/attachments/${encodeURIComponent(s)}/${u?"preview":"raw"}`:u?`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`,o&&(o.hidden=!1),s){const d=`/api/sessions/${t}/attachments/${encodeURIComponent(s)}`,p=new Set(["png","jpg","jpeg","gif","webp","bmp","svg"]),h=new Set(["mp4","webm","mov","m4v","ogv"]),g=new Set(["mp3","wav","ogg","oga","m4a","aac","flac","opus"]);if(r.textContent=m?`${this.formatFileSize(m)} \u2022 ${l}`:l.toUpperCase(),p.has(l))a.innerHTML=`<img src="${escapeHtml(`${d}/raw`)}" alt="${escapeHtml(e)}">`;else if(h.has(l))a.innerHTML=`<video src="${escapeHtml(`${d}/raw`)}" controls autoplay playsinline preload="metadata"></video>`;else if(g.has(l))a.innerHTML=`<audio src="${escapeHtml(`${d}/raw`)}" controls autoplay preload="metadata"></audio>`;else if(l==="pdf")a.innerHTML=`<iframe src="${escapeHtml(`${d}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(l==="docx"||l==="pptx")a.innerHTML=`<iframe src="${escapeHtml(`${d}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const y=await fetch(`${d}/raw`,{headers:{Range:"bytes=0-524287"}});if(!y.ok)throw new Error("Failed to load attachment");const w=await y.text(),b=y.status===206&&w.length>=524288,$=w.split(`
231
+ `),c&&h&&o.children)for(const y of o.children)r(y,l+1)};for(const o of i)r(o,0);t.innerHTML=a.join(""),t.querySelectorAll(".file-tree-item").forEach(o=>{o.addEventListener("click",()=>{const l=o.dataset.path;o.dataset.type==="directory"?this.toggleFileBrowserFolder(l):this.openFilePreview(l,n)})})},toggleFileBrowserFolder(e){this.fileBrowserExpandedDirs.has(e)?this.fileBrowserExpandedDirs.delete(e):this.fileBrowserExpandedDirs.add(e),this.renderFileBrowserTree()},filterFileBrowser(e){const t=this._ensureFileBrowserState(),s=String(e??""),n=s.trim();t.searchEpoch++,t.filter=s,t.deferredDirectoryTarget=null,this.fileBrowserFilter=s,this._syncFileBrowserExpandBtn(),t.inFlight?.timer!==void 0&&t.inFlight?.timer!==null&&clearTimeout(t.inFlight.timer),t.inFlight=null,!t.ownerSessionId&&this.activeSessionId&&(t.ownerSessionId=this.activeSessionId);const i=t.ownerSessionId||null;if(!this.activeSessionId||!i||this.activeSessionId!==i)return;if(!n){t.view="normal",t.matches=[],this._syncFileBrowserExpandBtn();const c=t.normalState;i&&this._isFileBrowserNormalCompatible(c,i,this.fileBrowserShowHidden===!0,t.treeEpoch)&&this._renderFileBrowserNormalState(c);return}if(n.length>256){const c="Search queries are limited to 256 characters";t.view="query-error",t.matches=[],this._syncFileBrowserExpandBtn();const h=this.$("fileBrowserTree"),u=this.$("fileBrowserStatus");h&&(h.innerHTML=`<div class="file-browser-empty">${escapeHtml(c)}</div>`),u&&(u.textContent=c);return}const a=this.$("fileBrowserPanel"),r=this.$("fileBrowserTree");if(!i||!a||!r)return;const o={epoch:t.searchEpoch,treeEpoch:t.treeEpoch,ownerSessionId:i,showHidden:this.fileBrowserShowHidden===!0,rawInput:s,query:n,timer:null};t.view="search-pending",t.matches=[],t.inFlight=o,this._syncFileBrowserExpandBtn(),r.innerHTML=`<div class="file-browser-loading">${escapeHtml("Searching...")}</div>`;const l=this.$("fileBrowserStatus");l&&(l.textContent="Searching..."),o.timer=setTimeout(async()=>{o.timer=null;try{const c=await fetch(`/api/sessions/${encodeURIComponent(i)}/files?depth=5&showHidden=${o.showHidden}&q=${encodeURIComponent(n)}`);if(!c.ok)throw new Error("Search failed");const h=await c.json(),u=this._validateFileBrowserSearchEnvelope(h);if(!u)throw new Error("Search failed");const d=this._canRenderFileBrowserSearch(o);if(t.inFlight===o&&(t.inFlight=null),!d)return;t.view="search-results",t.matches=u.matches,this._renderFileBrowserSearchResults(u.matches,i,u)}catch(c){const h=this._canRenderFileBrowserSearch(o);if(t.inFlight===o&&(t.inFlight=null),!h)return;console.error("Failed to search file browser:",c),t.view="search-error",t.matches=[],this._renderFileBrowserSearchError()}},250)},_renderFileBrowserSearchResults(e,t,s){const n=this.$("fileBrowserTree");if(!n||!t)return;const i=this._ensureFileBrowserState(),a={ownerSessionId:t,showHidden:this.fileBrowserShowHidden===!0,treeEpoch:i.treeEpoch,searchEpoch:i.searchEpoch,rawInput:i.filter,query:i.filter.trim(),view:i.view};if(e.length===0)n.innerHTML=`<div class="file-browser-empty">${escapeHtml("No matches")}</div>`;else{const o=encodeURIComponent(t);n.innerHTML=e.map(l=>{const c=l.type==="directory",h=c?"\u{1F4C1}":this.getFileIcon(l.extension||""),u=!c&&l.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(l.size)}</span>`:"",d=c?"file-tree-name directory":"file-tree-name",p=c?"":`<a class="file-tree-download" href="${escapeHtml(`/api/sessions/${o}/file-raw?path=${encodeURIComponent(l.path)}&download=true`)}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;return`
232
+ <div class="file-tree-item" data-path="${escapeHtml(l.path)}" data-type="${escapeHtml(l.type)}" data-owner="${escapeHtml(t)}">
233
+ <span class="file-tree-expand"></span>
234
+ <span class="file-tree-icon">${h}</span>
235
+ <span class="${d}">${escapeHtml(l.name)}</span>
236
+ ${u}
237
+ ${p}
238
+ </div>
239
+ `}).join("")}n.querySelectorAll(".file-tree-item").forEach(o=>{o.addEventListener("click",()=>{const l=o.dataset.path;o.dataset.type==="directory"?this._openFileBrowserSearchDirectory({...a,path:l}):this.openFilePreview(l,t)})});const r=this.$("fileBrowserStatus");if(r){const o=s.matchCount===void 0?e.length:s.matchCount;r.textContent=`${o} ${o===1?"match":"matches"}${s.truncated?" (truncated)":""}`}},_findFileBrowserDirectory(e,t,s=[]){if(!Array.isArray(e))return null;for(const n of e){if(!n||typeof n!="object")continue;if(n.type==="directory"&&n.path===t)return{target:n,ancestors:[...s]};if(n.type!=="directory"||!Array.isArray(n.children))continue;const i=this._findFileBrowserDirectory(n.children,t,[...s,n.path]);if(i)return i}return null},_isFileBrowserDirectoryContextCurrent(e){const t=this._ensureFileBrowserState(),s=this.$?.("fileBrowserSearch"),n=typeof s?.value=="string"?s.value:t.filter;return e&&t.ownerSessionId===e.ownerSessionId&&this.activeSessionId===e.ownerSessionId&&t.treeEpoch===e.treeEpoch&&t.searchEpoch===e.searchEpoch&&this.fileBrowserShowHidden===!0===e.showHidden&&t.filter===e.rawInput&&n===e.rawInput&&n.trim()===e.query&&t.view===e.view&&this.$?.("fileBrowserPanel")?.classList.contains("visible")===!0},_promptFileBrowserDirectoryReload(){this.showToast?.("Reload files before opening this folder","info")},_openFileBrowserSearchDirectory(e){if(!this._isFileBrowserDirectoryContextCurrent(e))return;const t=this._ensureFileBrowserState(),s=t.normalState;if(!this._isFileBrowserNormalCompatible(s,e.ownerSessionId,e.showHidden,e.treeEpoch)){t.deferredDirectoryTarget=null,this._promptFileBrowserDirectoryReload();return}if(s.phase==="loading"){t.deferredDirectoryTarget={...e};return}if(t.deferredDirectoryTarget=null,s.phase!=="ready"){this._promptFileBrowserDirectoryReload();return}const n=this._findFileBrowserDirectory(s.data?.tree,e.path);if(!n){this._promptFileBrowserDirectoryReload();return}this._leaveFileBrowserSearchForDirectory([...n.ancestors,n.target.path],s)},_completeDeferredFileBrowserDirectory(e){const t=this._ensureFileBrowserState(),s=t.deferredDirectoryTarget;if(!s)return!1;if(!this._isFileBrowserDirectoryContextCurrent(s))return t.deferredDirectoryTarget===s&&(t.deferredDirectoryTarget=null),!1;if(!this._isFileBrowserNormalCompatible(e,s.ownerSessionId,s.showHidden,s.treeEpoch)||e.phase!=="ready"&&e.phase!=="error")return!1;if(t.deferredDirectoryTarget=null,e.phase==="error")return this._promptFileBrowserDirectoryReload(),!1;const n=this._findFileBrowserDirectory(e.data?.tree,s.path);return n?(this._leaveFileBrowserSearchForDirectory([...n.ancestors,n.target.path],e),!0):(this._promptFileBrowserDirectoryReload(),!1)},_leaveFileBrowserSearchForDirectory(e,t){const s=this._ensureFileBrowserState();s.inFlight?.timer!==void 0&&s.inFlight?.timer!==null&&clearTimeout(s.inFlight.timer),s.searchEpoch++,s.inFlight=null,s.filter="",s.matches=[],s.deferredDirectoryTarget=null,s.view="normal",this.fileBrowserFilter="";const n=this.$?.("fileBrowserSearch");n&&(n.value=""),this._syncFileBrowserExpandBtn();for(const i of e)typeof i=="string"&&this.fileBrowserExpandedDirs?.add?.(i);this.fileBrowserData=t.data,this._renderFileBrowserNormalState(t)},expandAllDirectories(e){for(const t of e)t.type==="directory"&&(this.fileBrowserExpandedDirs.add(t.path),t.children&&this.expandAllDirectories(t.children))},collapseAllDirectories(){this.fileBrowserExpandedDirs.clear()},toggleFileBrowserExpand(){if(this._hasFileBrowserQuery()){this._syncFileBrowserExpandBtn();return}this.fileBrowserAllExpanded=!this.fileBrowserAllExpanded;const e=this.$("fileBrowserExpandBtn");this.fileBrowserAllExpanded?(this.expandAllDirectories(this.fileBrowserData?.tree||[]),e&&(e.innerHTML="\u229F")):(this.collapseAllDirectories(),e&&(e.innerHTML="\u229E")),this.renderFileBrowserTree()},refreshFileBrowser(){const e=this._ensureFileBrowserState();e.inFlight?.timer!==void 0&&e.inFlight?.timer!==null&&clearTimeout(e.inFlight.timer),e.inFlight=null,e.searchEpoch++,e.filter="",e.matches=[],e.deferredDirectoryTarget=null,e.view="normal",this.fileBrowserFilter="",this.fileBrowserExpandedDirs.clear(),this.fileBrowserAllExpanded=!1;const t=this.$("fileBrowserExpandBtn");t&&(t.innerHTML="\u229E");const s=this.$("fileBrowserSearch");s&&(s.value=""),this._syncFileBrowserExpandBtn();const n=e.ownerSessionId||this.activeSessionId;if(!(!n||this.activeSessionId!==n))return this.loadFileBrowser(n,{force:!0})},toggleFileBrowserButton(){const t=this.$("fileBrowserPanel")?.classList.contains("visible"),s=document.querySelector(".btn-file-viewer");if(t){this.closeFileBrowserPanel(),s&&s.setAttribute("aria-expanded","false");return}if(!this.activeSessionId){this.showToast("Open a session to browse its files","info");return}const n=this.loadAppSettingsFromStorage();n.showFileBrowser=!0,this.saveAppSettingsToStorage(n);const i=document.getElementById("appSettingsShowFileBrowser");i&&(i.checked=!0),this.applyMonitorVisibility(),s&&s.setAttribute("aria-expanded","true")},closeFileBrowserPanel(){const e=this.$("fileBrowserPanel");if(this._resetFileBrowserForHide(),e&&(e.classList.remove("visible"),e.style.left="",e.style.top="",e.style.bottom="",e.style.right=""),this.fileBrowserDragListeners){const i=this.fileBrowserDragListeners;document.removeEventListener("mousemove",i.move),document.removeEventListener("mouseup",i.up),document.removeEventListener("touchmove",i.touchMove),document.removeEventListener("touchend",i.up),document.removeEventListener("touchcancel",i.up),i.handle&&(i.handle.removeEventListener("mousedown",i.handleMouseDown),i.handle.removeEventListener("touchstart",i.handleTouchStart),i._onFirstDrag&&(i.handle.removeEventListener("mousedown",i._onFirstDrag),i.handle.removeEventListener("touchstart",i._onFirstDrag))),this.fileBrowserDragListeners=null}const t=this.loadAppSettingsFromStorage();t.showFileBrowser=!1,this.saveAppSettingsToStorage(t);const s=document.getElementById("appSettingsShowFileBrowser");s&&(s.checked=!1);const n=document.querySelector(".btn-file-viewer");n&&n.setAttribute("aria-expanded","false")},_isExternalPreviewPath(e,t){if(typeof e!="string"||!e.startsWith("/"))return!1;const s=this.sessions.get(t)?.workingDir;if(!s)return!1;const n=s.endsWith("/")?s:`${s}/`;return e!==s&&!e.startsWith(n)},async _registerExternalPreview(e,t){try{const s=await fetch(`/api/sessions/${t}/attachments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e,notify:!1})}),n=await s.json().catch(()=>null);if(s.ok&&n?.success&&n.data?.attachmentId)return{attachmentId:n.data.attachmentId,size:n.data.size||0};const i=n?.error||`Cannot open this file (HTTP ${s.status})`;return/unsupported/i.test(i)?{error:`Cannot preview .${(e.split(".").pop()||"").toLowerCase()} from outside the session workspace (images, video, audio, PDF, Office documents and text files only).`}:{error:i}}catch(s){return{error:s.message||"Cannot open this file"}}},async openFilePreview(e,t=this.activeSessionId,s=null){if(!t||!e)return;const n=this.$("filePreviewOverlay"),i=this.$("filePreviewTitle"),a=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!a)return;this._resetFilePreviewEdit(),this._stopFilePreviewMedia(),this.filePreviewDetachUrl="";const o=this.$("filePreviewDetachBtn");o&&(o.hidden=!0),n.classList.add("visible"),i.textContent=e,a.innerHTML='<div class="binary-message">Loading...</div>',r.textContent="";const l=(e.split(".").pop()||"").toLowerCase();let c="",h=0;if(!s&&this._isExternalPreviewPath(e,t)){const d=await this._registerExternalPreview(e,t);s=d.attachmentId||null,c=d.error||"",h=d.size||0}if(!s&&c){r.textContent="",a.innerHTML=`<div class="binary-message">${escapeHtml(c)}</div>`;return}const u=l==="docx"||l==="pptx";if(this.filePreviewDetachUrl=s?`/api/sessions/${t}/attachments/${encodeURIComponent(s)}/${u?"preview":"raw"}`:u?`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`,o&&(o.hidden=!1),s){const d=`/api/sessions/${t}/attachments/${encodeURIComponent(s)}`,p=new Set(["png","jpg","jpeg","gif","webp","bmp","svg"]),m=new Set(["mp4","webm","mov","m4v","ogv"]),g=new Set(["mp3","wav","ogg","oga","m4a","aac","flac","opus"]);if(r.textContent=h?`${this.formatFileSize(h)} \u2022 ${l}`:l.toUpperCase(),p.has(l))a.innerHTML=`<img src="${escapeHtml(`${d}/raw`)}" alt="${escapeHtml(e)}">`;else if(m.has(l))a.innerHTML=`<video src="${escapeHtml(`${d}/raw`)}" controls autoplay playsinline preload="metadata"></video>`;else if(g.has(l))a.innerHTML=`<audio src="${escapeHtml(`${d}/raw`)}" controls autoplay preload="metadata"></audio>`;else if(l==="pdf")a.innerHTML=`<iframe src="${escapeHtml(`${d}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(l==="docx"||l==="pptx")a.innerHTML=`<iframe src="${escapeHtml(`${d}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const y=await fetch(`${d}/raw`,{headers:{Range:"bytes=0-524287"}});if(!y.ok)throw new Error("Failed to load attachment");const w=await y.text(),b=y.status===206&&w.length>=524288,$=w.split(`
232
240
  `),f=$.length>500,S=f?$.slice(0,500).join(`
233
- `):w;if(a.innerHTML=`<pre><code>${escapeHtml(S)}</code></pre>`,this.filePreviewContent=S,f||b){const T=f?"showing first 500 lines":"showing the start of the file";r.textContent=`${r.textContent} (${T})`}}catch(y){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(y.message)}</div>`}return}if(l==="docx"||l==="pptx"){r.textContent=l.toUpperCase();const d=`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(d)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="pdf"){r.textContent="PDF";const d=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(d)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="svg"){r.textContent="SVG";try{const d=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!d.ok)throw new Error("Failed to load image");const p=URL.createObjectURL(new Blob([await d.text()],{type:"image/svg+xml"}));a.innerHTML=`<img src="${p}" alt="${escapeHtml(e)}">`;const h=a.querySelector("img");h&&(h.onload=()=>URL.revokeObjectURL(p))}catch(d){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(d.message)}</div>`}return}try{const d=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!d.ok)throw new Error("Failed to load file");const p=await d.json();if(!p.success)throw new Error(p.error||"Failed to load file");const h=p.data;if(h.type==="image")a.innerHTML=`<img src="${h.url}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(h.size)} \u2022 ${h.extension}`;else if(h.type==="video")a.innerHTML=`<video src="${escapeHtml(h.url)}" controls autoplay playsinline preload="metadata"></video>`,r.textContent=`${this.formatFileSize(h.size)} \u2022 ${h.extension}`;else if(h.type==="audio")a.innerHTML=`<audio src="${escapeHtml(h.url)}" controls autoplay preload="metadata"></audio>`,r.textContent=`${this.formatFileSize(h.size)} \u2022 ${h.extension}`;else if(h.type==="binary"){const g=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`;a.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(h.size)})<br>Cannot preview<br><a href="${escapeHtml(g)}" download>Download</a></div>`,r.textContent=h.extension||"binary"}else{this.filePreviewContent=h.content,a.innerHTML=`<pre><code>${escapeHtml(h.content)}</code></pre>`;const g=h.truncated?` (showing 500/${h.totalLines} lines)`:"";if(r.textContent=`${h.totalLines} lines \u2022 ${this.formatFileSize(h.size)}${g}`,h.editable){this.filePreviewEditTarget={sessionId:t,filePath:e};const y=this.$("filePreviewEditBtn");y&&(y.hidden=!1)}}}catch(d){console.error("Failed to preview file:",d),a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(d.message)}</div>`}},closeFilePreview(){if(this.filePreviewEdit?.dirty&&!confirm("Discard unsaved changes?"))return;this._resetFilePreviewEdit();const e=this.$("filePreviewOverlay");e&&e.classList.remove("visible"),this._stopFilePreviewMedia(),this.filePreviewContent="",this.filePreviewDetachUrl="";const t=this.$("filePreviewDetachBtn");t&&(t.hidden=!0)},detachFilePreview(){if(!this.filePreviewDetachUrl)return;const e=window.open(this.filePreviewDetachUrl,"_blank");if(!e){this.showToast("Pop-up blocked: allow pop-ups for this site to detach previews","error");return}e.opener=null,this.closeFilePreview()},_stopFilePreviewMedia(){const e=this.$("filePreviewBody");if(e){for(const t of e.querySelectorAll("video, audio"))try{t.pause(),t.removeAttribute("src"),t.load()}catch(s){console.warn("Failed to stop preview media:",s)}e.innerHTML=""}},_resetFilePreviewEdit(){this.filePreviewEdit=null,this.filePreviewEditTarget=null;const e=this.$("filePreviewEditBtn");e&&(e.hidden=!0);const t=this.$("filePreviewEditBar");t&&(t.hidden=!0);const s=this.$("filePreviewDirty");s&&(s.hidden=!0);const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!0,n.textContent="Save")},async enterFilePreviewEdit(){const e=this.filePreviewEditTarget;if(!e||this.filePreviewEdit)return;const t=this.$("filePreviewBody"),s=this.$("filePreviewFooter");if(!t)return;let n;try{const o=await fetch(`/api/sessions/${e.sessionId}/file-content?path=${encodeURIComponent(e.filePath)}&edit=1`),l=await o.json().catch(()=>null);if(!l||l.success!==!0)throw new Error(l?.error||`Failed to load file for editing (HTTP ${o.status})`);n=l.data}catch(o){this.showToast(o.message,"error");return}this.filePreviewEdit={sessionId:e.sessionId,filePath:e.filePath,baseHash:n.hash,eol:n.eol,original:n.content,dirty:!1,saving:!1};const i=document.createElement("textarea");i.className="file-preview-editor",i.spellcheck=!1,i.setAttribute("autocapitalize","off"),i.setAttribute("autocorrect","off"),i.setAttribute("autocomplete","off"),i.wrap="off",i.value=n.content,i.addEventListener("input",()=>this._onFilePreviewEditInput()),t.innerHTML="",t.appendChild(i);const a=this.$("filePreviewEditBtn");a&&(a.hidden=!0);const r=this.$("filePreviewEditBar");if(r&&(r.hidden=!1),s){const o=n.eol==="crlf"?" \u2022 CRLF":"";s.textContent=`Editing \u2022 ${n.totalLines} lines \u2022 ${this.formatFileSize(n.size)}${o}`}},_onFilePreviewEditInput(){const e=this.filePreviewEdit;if(!e)return;const t=this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor");if(!t)return;e.dirty=t.value!==e.original;const s=this.$("filePreviewDirty");s&&(s.hidden=!e.dirty);const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!e.dirty||e.saving)},cancelFilePreviewEdit(){const e=this.filePreviewEdit;if(!e||e.dirty&&!confirm("Discard unsaved changes?"))return;const{sessionId:t,filePath:s}=e;this._resetFilePreviewEdit(),this.openFilePreview(s,t)},async saveFilePreviewEdit(e=!1){const t=this.filePreviewEdit;if(!t||t.saving)return;const s=this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor");if(!s)return;t.saving=!0;const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!0,n.textContent="Saving\u2026");const i=()=>{t.saving=!1,n&&(n.textContent="Save"),this._onFilePreviewEditInput()};let a=null,r=0;try{const c=await fetch(`/api/sessions/${t.sessionId}/file-content`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t.filePath,content:s.value,baseHash:t.baseHash,eol:t.eol??void 0,force:e||void 0})});r=c.status,a=await c.json().catch(()=>null)}catch(c){i(),this.showToast(`Save failed: ${c.message}`,"error");return}if(r===409||a?.errorCode==="CONFLICT"){i(),confirm(`File changed on disk since you loaded it.
234
- OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFilePreviewEdit(!0);return}if(!a||a.success!==!0){i(),this.showToast(`Save failed: ${a?.error||`HTTP ${r}`}`,"error");return}const{sessionId:o,filePath:l}=t;this._resetFilePreviewEdit(),this.showToast("Saved","success"),this.openFilePreview(l,o)},_onAttachmentDetected(e){if(console.log("[Attachment Detected]",e),this.addAttachmentCard(e),e.sessionId){const t=this.attachmentHistoryCounts.get(e.sessionId)??this.sessions.get(e.sessionId)?.attachmentHistory?.length??0;this.attachmentHistoryCounts.set(e.sessionId,Math.min(t+1,100)),e.sessionId===this.activeSessionId&&(this.updateAttachmentHistoryBadge(),this.attachmentHistoryDrawerOpen&&this._debouncedCall("attachmentHistoryRefresh",()=>{this.attachmentHistoryDrawerOpen&&this.activeSessionId===e.sessionId&&this.loadAttachmentHistory(e.sessionId)},250))}},ensureAttachmentCardStack(){let e=this.attachmentCardStack||document.getElementById("attachmentCardStack");return e||(e=document.createElement("div"),e.id="attachmentCardStack",e.className="attachment-card-stack",document.body.appendChild(e)),this.attachmentCardStack=e,e},openAttachmentInNewTab(e,t,s=null){const n=s?`/api/sessions/${e}/attachments/${encodeURIComponent(s)}/raw`:`/api/sessions/${e}/file-raw?path=${encodeURIComponent(t)}`;window.open(n,"_blank")},addAttachmentCard(e){const{sessionId:t,relativePath:s,fileName:n,timestamp:i,size:a,attachmentType:r,extension:o,attachmentId:l,rawUrl:c,previewUrl:m,thumbnailUrl:u}=e,d=s||n,p=l||`${t}-${i}-${n}`;if(this.attachmentCards.has(p)){this.attachmentCards.get(p).element.focus?.();return}if(this.attachmentCards.size>=10){const D=this.attachmentCards.keys().next().value;D&&this.closeAttachmentCard(D)}const g=this.ensureAttachmentCardStack(),w=this.sessions.get(t)?.name||t.substring(0,8),b=c||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/raw`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(d)}`),$=m||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/preview`:null),f=u||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/thumbnail`:`/api/sessions/${t}/file-thumbnail?path=${encodeURIComponent(d)}`),S=l?`${b}?download=true`:`${b}&download=true`,T=(o||r||"file").toUpperCase(),v=document.createElement("article");v.className=`attachment-card attachment-${escapeHtml(r||"file")}`,v.tabIndex=0,v.dataset.attachmentId=p,v.dataset.previewUrl=$||"",v.innerHTML=`
241
+ `):w;if(a.innerHTML=`<pre><code>${escapeHtml(S)}</code></pre>`,this.filePreviewContent=S,f||b){const T=f?"showing first 500 lines":"showing the start of the file";r.textContent=`${r.textContent} (${T})`}}catch(y){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(y.message)}</div>`}return}if(l==="docx"||l==="pptx"){r.textContent=l.toUpperCase();const d=`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(d)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="pdf"){r.textContent="PDF";const d=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(d)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="svg"){r.textContent="SVG";try{const d=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!d.ok)throw new Error("Failed to load image");const p=URL.createObjectURL(new Blob([await d.text()],{type:"image/svg+xml"}));a.innerHTML=`<img src="${p}" alt="${escapeHtml(e)}">`;const m=a.querySelector("img");m&&(m.onload=()=>URL.revokeObjectURL(p))}catch(d){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(d.message)}</div>`}return}try{const d=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!d.ok)throw new Error("Failed to load file");const p=await d.json();if(!p.success)throw new Error(p.error||"Failed to load file");const m=p.data;if(m.type==="image")a.innerHTML=`<img src="${m.url}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="video")a.innerHTML=`<video src="${escapeHtml(m.url)}" controls autoplay playsinline preload="metadata"></video>`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="audio")a.innerHTML=`<audio src="${escapeHtml(m.url)}" controls autoplay preload="metadata"></audio>`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="binary"){const g=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`;a.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(m.size)})<br>Cannot preview<br><a href="${escapeHtml(g)}" download>Download</a></div>`,r.textContent=m.extension||"binary"}else{this.filePreviewContent=m.content,a.innerHTML=`<pre><code>${escapeHtml(m.content)}</code></pre>`;const g=m.truncated?` (showing 500/${m.totalLines} lines)`:"";if(r.textContent=`${m.totalLines} lines \u2022 ${this.formatFileSize(m.size)}${g}`,m.editable){this.filePreviewEditTarget={sessionId:t,filePath:e};const y=this.$("filePreviewEditBtn");y&&(y.hidden=!1)}}}catch(d){console.error("Failed to preview file:",d),a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(d.message)}</div>`}},closeFilePreview(){if(this.filePreviewEdit?.dirty&&!confirm("Discard unsaved changes?"))return;this._resetFilePreviewEdit();const e=this.$("filePreviewOverlay");e&&e.classList.remove("visible"),this._stopFilePreviewMedia(),this.filePreviewContent="",this.filePreviewDetachUrl="";const t=this.$("filePreviewDetachBtn");t&&(t.hidden=!0)},detachFilePreview(){if(!this.filePreviewDetachUrl)return;const e=window.open(this.filePreviewDetachUrl,"_blank");if(!e){this.showToast("Pop-up blocked: allow pop-ups for this site to detach previews","error");return}e.opener=null,this.closeFilePreview()},_stopFilePreviewMedia(){const e=this.$("filePreviewBody");if(e){for(const t of e.querySelectorAll("video, audio"))try{t.pause(),t.removeAttribute("src"),t.load()}catch(s){console.warn("Failed to stop preview media:",s)}e.innerHTML=""}},_resetFilePreviewEdit(){this.filePreviewEdit=null,this.filePreviewEditTarget=null;const e=this.$("filePreviewEditBtn");e&&(e.hidden=!0);const t=this.$("filePreviewEditBar");t&&(t.hidden=!0);const s=this.$("filePreviewDirty");s&&(s.hidden=!0);const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!0,n.textContent="Save")},async enterFilePreviewEdit(){const e=this.filePreviewEditTarget;if(!e||this.filePreviewEdit)return;const t=this.$("filePreviewBody"),s=this.$("filePreviewFooter");if(!t)return;let n;try{const o=await fetch(`/api/sessions/${e.sessionId}/file-content?path=${encodeURIComponent(e.filePath)}&edit=1`),l=await o.json().catch(()=>null);if(!l||l.success!==!0)throw new Error(l?.error||`Failed to load file for editing (HTTP ${o.status})`);n=l.data}catch(o){this.showToast(o.message,"error");return}this.filePreviewEdit={sessionId:e.sessionId,filePath:e.filePath,baseHash:n.hash,eol:n.eol,original:n.content,dirty:!1,saving:!1};const i=document.createElement("textarea");i.className="file-preview-editor",i.spellcheck=!1,i.setAttribute("autocapitalize","off"),i.setAttribute("autocorrect","off"),i.setAttribute("autocomplete","off"),i.wrap="off",i.value=n.content,i.addEventListener("input",()=>this._onFilePreviewEditInput()),t.innerHTML="",t.appendChild(i);const a=this.$("filePreviewEditBtn");a&&(a.hidden=!0);const r=this.$("filePreviewEditBar");if(r&&(r.hidden=!1),s){const o=n.eol==="crlf"?" \u2022 CRLF":"";s.textContent=`Editing \u2022 ${n.totalLines} lines \u2022 ${this.formatFileSize(n.size)}${o}`}},_onFilePreviewEditInput(){const e=this.filePreviewEdit;if(!e)return;const t=this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor");if(!t)return;e.dirty=t.value!==e.original;const s=this.$("filePreviewDirty");s&&(s.hidden=!e.dirty);const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!e.dirty||e.saving)},cancelFilePreviewEdit(){const e=this.filePreviewEdit;if(!e||e.dirty&&!confirm("Discard unsaved changes?"))return;const{sessionId:t,filePath:s}=e;this._resetFilePreviewEdit(),this.openFilePreview(s,t)},async saveFilePreviewEdit(e=!1){const t=this.filePreviewEdit;if(!t||t.saving)return;const s=this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor");if(!s)return;t.saving=!0;const n=this.$("filePreviewSaveBtn");n&&(n.disabled=!0,n.textContent="Saving\u2026");const i=()=>{t.saving=!1,n&&(n.textContent="Save"),this._onFilePreviewEditInput()};let a=null,r=0;try{const c=await fetch(`/api/sessions/${t.sessionId}/file-content`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t.filePath,content:s.value,baseHash:t.baseHash,eol:t.eol??void 0,force:e||void 0})});r=c.status,a=await c.json().catch(()=>null)}catch(c){i(),this.showToast(`Save failed: ${c.message}`,"error");return}if(r===409||a?.errorCode==="CONFLICT"){i(),confirm(`File changed on disk since you loaded it.
242
+ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFilePreviewEdit(!0);return}if(!a||a.success!==!0){i(),this.showToast(`Save failed: ${a?.error||`HTTP ${r}`}`,"error");return}const{sessionId:o,filePath:l}=t;this._resetFilePreviewEdit(),this.showToast("Saved","success"),this.openFilePreview(l,o)},_onAttachmentDetected(e){if(console.log("[Attachment Detected]",e),this.addAttachmentCard(e),e.sessionId){const t=this.attachmentHistoryCounts.get(e.sessionId)??this.sessions.get(e.sessionId)?.attachmentHistory?.length??0;this.attachmentHistoryCounts.set(e.sessionId,Math.min(t+1,100)),e.sessionId===this.activeSessionId&&(this.updateAttachmentHistoryBadge(),this.attachmentHistoryDrawerOpen&&this._debouncedCall("attachmentHistoryRefresh",()=>{this.attachmentHistoryDrawerOpen&&this.activeSessionId===e.sessionId&&this.loadAttachmentHistory(e.sessionId)},250))}},ensureAttachmentCardStack(){let e=this.attachmentCardStack||document.getElementById("attachmentCardStack");return e||(e=document.createElement("div"),e.id="attachmentCardStack",e.className="attachment-card-stack",document.body.appendChild(e)),this.attachmentCardStack=e,e},openAttachmentInNewTab(e,t,s=null){const n=s?`/api/sessions/${e}/attachments/${encodeURIComponent(s)}/raw`:`/api/sessions/${e}/file-raw?path=${encodeURIComponent(t)}`;window.open(n,"_blank")},addAttachmentCard(e){const{sessionId:t,relativePath:s,fileName:n,timestamp:i,size:a,attachmentType:r,extension:o,attachmentId:l,rawUrl:c,previewUrl:h,thumbnailUrl:u}=e,d=s||n,p=l||`${t}-${i}-${n}`;if(this.attachmentCards.has(p)){this.attachmentCards.get(p).element.focus?.();return}if(this.attachmentCards.size>=10){const D=this.attachmentCards.keys().next().value;D&&this.closeAttachmentCard(D)}const g=this.ensureAttachmentCardStack(),w=this.sessions.get(t)?.name||t.substring(0,8),b=c||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/raw`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(d)}`),$=h||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/preview`:null),f=u||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/thumbnail`:`/api/sessions/${t}/file-thumbnail?path=${encodeURIComponent(d)}`),S=l?`${b}?download=true`:`${b}&download=true`,T=(o||r||"file").toUpperCase(),v=document.createElement("article");v.className=`attachment-card attachment-${escapeHtml(r||"file")}`,v.tabIndex=0,v.dataset.attachmentId=p,v.dataset.previewUrl=$||"",v.innerHTML=`
235
243
  <div class="attachment-thumbnail">
236
244
  ${f?`<img class="attachment-thumbnail-img" src="${escapeHtml(f)}" alt="">`:""}
237
245
  <div class="attachment-thumbnail-fallback ${f?"":"visible"}">${escapeHtml(T)}</div>
@@ -285,7 +293,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
285
293
  </div>
286
294
  </div>
287
295
  </div>
288
- `},getAttachmentHistoryItem(e){return(this.attachmentHistoryItems||[]).find(t=>t.id===e)||null},previewAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!t||t.missing)return;const s=t.relativePath||t.fileName;this.openFilePreview(s,t.sessionId,t.attachmentId||null),this.closeAttachmentHistory()},openAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!(!t||t.missing)){if(t.rawUrl||t.url){window.open(t.rawUrl||t.url,"_blank");return}this.openAttachmentInNewTab(t.sessionId,t.relativePath||t.fileName,t.attachmentId||null)}},downloadAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||!t.downloadUrl||window.open(t.downloadUrl,"_blank")},reshowAttachmentCard(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||this.addAttachmentCard({sessionId:t.sessionId,relativePath:t.relativePath,fileName:t.fileName,timestamp:t.timestamp??Date.now(),size:t.size,attachmentType:t.attachmentType,extension:t.extension,attachmentId:t.attachmentId,rawUrl:t.rawUrl,previewUrl:t.previewUrl,thumbnailUrl:t.thumbnailUrl})},copyFilePreviewContent(){const e=this.filePreviewEdit?this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor"):null,t=e?e.value:this.filePreviewContent;t?navigator.clipboard.writeText(t).then(()=>{this.showToast("Copied to clipboard","success")}).catch(()=>{this.showToast("Failed to copy","error")}):this.showToast("Nothing to copy in this preview","info")},getFileIcon(e){return e&&{ts:"\u{1F4D8}",tsx:"\u{1F4D8}",js:"\u{1F4D2}",jsx:"\u{1F4D2}",mjs:"\u{1F4D2}",cjs:"\u{1F4D2}",py:"\u{1F40D}",pyx:"\u{1F40D}",pyw:"\u{1F40D}",rs:"\u{1F980}",go:"\u{1F439}",c:"\u2699\uFE0F",cpp:"\u2699\uFE0F",h:"\u2699\uFE0F",hpp:"\u2699\uFE0F",html:"\u{1F310}",htm:"\u{1F310}",css:"\u{1F3A8}",scss:"\u{1F3A8}",sass:"\u{1F3A8}",less:"\u{1F3A8}",json:"\u{1F4CB}",yaml:"\u{1F4CB}",yml:"\u{1F4CB}",xml:"\u{1F4CB}",toml:"\u{1F4CB}",csv:"\u{1F4CB}",md:"\u{1F4DD}",markdown:"\u{1F4DD}",txt:"\u{1F4DD}",rst:"\u{1F4DD}",png:"\u{1F5BC}\uFE0F",jpg:"\u{1F5BC}\uFE0F",jpeg:"\u{1F5BC}\uFE0F",gif:"\u{1F5BC}\uFE0F",svg:"\u{1F5BC}\uFE0F",webp:"\u{1F5BC}\uFE0F",ico:"\u{1F5BC}\uFE0F",bmp:"\u{1F5BC}\uFE0F",mp4:"\u{1F3AC}",webm:"\u{1F3AC}",mov:"\u{1F3AC}",mp3:"\u{1F3B5}",wav:"\u{1F3B5}",ogg:"\u{1F3B5}",sh:"\u{1F4BB}",bash:"\u{1F4BB}",zsh:"\u{1F4BB}",env:"\u{1F510}",gitignore:"\u{1F6AB}",dockerfile:"\u{1F433}",lock:"\u{1F512}"}[e.toLowerCase()]||"\u{1F4C4}"},formatFileSize(e){return e==null?"":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(1)} GB`},openLogViewerWindow(e,t){if(t=t||this.activeSessionId,!t)return;const s=`${t}-${e.replace(/[^a-zA-Z0-9]/g,"_")}`;if(this.logViewerWindows.has(s)){const m=this.logViewerWindows.get(s);m.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,i=100+n%5*30,a=100+n%5*30,r=e.split("/").pop(),o=document.createElement("div");o.className="log-viewer-window",o.id=`log-viewer-window-${s}`,o.style.left=`${i}px`,o.style.top=`${a}px`,o.style.zIndex=++this.logViewerWindowZIndex,o.innerHTML=`
296
+ `},getAttachmentHistoryItem(e){return(this.attachmentHistoryItems||[]).find(t=>t.id===e)||null},previewAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!t||t.missing)return;const s=t.relativePath||t.fileName;this.openFilePreview(s,t.sessionId,t.attachmentId||null),this.closeAttachmentHistory()},openAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!(!t||t.missing)){if(t.rawUrl||t.url){window.open(t.rawUrl||t.url,"_blank");return}this.openAttachmentInNewTab(t.sessionId,t.relativePath||t.fileName,t.attachmentId||null)}},downloadAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||!t.downloadUrl||window.open(t.downloadUrl,"_blank")},reshowAttachmentCard(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||this.addAttachmentCard({sessionId:t.sessionId,relativePath:t.relativePath,fileName:t.fileName,timestamp:t.timestamp??Date.now(),size:t.size,attachmentType:t.attachmentType,extension:t.extension,attachmentId:t.attachmentId,rawUrl:t.rawUrl,previewUrl:t.previewUrl,thumbnailUrl:t.thumbnailUrl})},copyFilePreviewContent(){const e=this.filePreviewEdit?this.$("filePreviewBody")?.querySelector("textarea.file-preview-editor"):null,t=e?e.value:this.filePreviewContent;t?navigator.clipboard.writeText(t).then(()=>{this.showToast("Copied to clipboard","success")}).catch(()=>{this.showToast("Failed to copy","error")}):this.showToast("Nothing to copy in this preview","info")},getFileIcon(e){return e&&{ts:"\u{1F4D8}",tsx:"\u{1F4D8}",js:"\u{1F4D2}",jsx:"\u{1F4D2}",mjs:"\u{1F4D2}",cjs:"\u{1F4D2}",py:"\u{1F40D}",pyx:"\u{1F40D}",pyw:"\u{1F40D}",rs:"\u{1F980}",go:"\u{1F439}",c:"\u2699\uFE0F",cpp:"\u2699\uFE0F",h:"\u2699\uFE0F",hpp:"\u2699\uFE0F",html:"\u{1F310}",htm:"\u{1F310}",css:"\u{1F3A8}",scss:"\u{1F3A8}",sass:"\u{1F3A8}",less:"\u{1F3A8}",json:"\u{1F4CB}",yaml:"\u{1F4CB}",yml:"\u{1F4CB}",xml:"\u{1F4CB}",toml:"\u{1F4CB}",csv:"\u{1F4CB}",md:"\u{1F4DD}",markdown:"\u{1F4DD}",txt:"\u{1F4DD}",rst:"\u{1F4DD}",png:"\u{1F5BC}\uFE0F",jpg:"\u{1F5BC}\uFE0F",jpeg:"\u{1F5BC}\uFE0F",gif:"\u{1F5BC}\uFE0F",svg:"\u{1F5BC}\uFE0F",webp:"\u{1F5BC}\uFE0F",ico:"\u{1F5BC}\uFE0F",bmp:"\u{1F5BC}\uFE0F",mp4:"\u{1F3AC}",webm:"\u{1F3AC}",mov:"\u{1F3AC}",mp3:"\u{1F3B5}",wav:"\u{1F3B5}",ogg:"\u{1F3B5}",sh:"\u{1F4BB}",bash:"\u{1F4BB}",zsh:"\u{1F4BB}",env:"\u{1F510}",gitignore:"\u{1F6AB}",dockerfile:"\u{1F433}",lock:"\u{1F512}"}[e.toLowerCase()]||"\u{1F4C4}"},formatFileSize(e){return e==null?"":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(1)} GB`},openLogViewerWindow(e,t){if(t=t||this.activeSessionId,!t)return;const s=`${t}-${e.replace(/[^a-zA-Z0-9]/g,"_")}`;if(this.logViewerWindows.has(s)){const h=this.logViewerWindows.get(s);h.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,i=100+n%5*30,a=100+n%5*30,r=e.split("/").pop(),o=document.createElement("div");o.className="log-viewer-window",o.id=`log-viewer-window-${s}`,o.style.left=`${i}px`,o.style.top=`${a}px`,o.style.zIndex=++this.logViewerWindowZIndex,o.innerHTML=`
289
297
  <div class="log-viewer-window-header">
290
298
  <div class="log-viewer-window-title" title="${escapeHtml(e)}">
291
299
  <span class="icon">\u{1F4C4}</span>
@@ -299,7 +307,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
299
307
  <div class="log-viewer-window-body" id="log-viewer-body-${s}">
300
308
  <div class="log-info">Connecting to ${escapeHtml(e)}...</div>
301
309
  </div>
302
- `,document.body.appendChild(o);const l=this.makeWindowDraggable(o,o.querySelector(".log-viewer-window-header")),c=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);c.onmessage=m=>{const u=JSON.parse(m.data),d=document.getElementById(`log-viewer-body-${s}`);if(d)switch(u.type){case"connected":d.innerHTML="";break;case"data":const p=d.scrollTop+d.clientHeight>=d.scrollHeight-10,h=escapeHtml(u.content);d.innerHTML+=h,p&&(d.scrollTop=d.scrollHeight),d.innerHTML.length>5e5&&(d.innerHTML=d.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":d.innerHTML+=`<div class="log-error">${escapeHtml(u.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:o,eventSource:c,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:i,timestamp:a,size:r}=e,o=`${t}-${a}`;if(this.imagePopups.has(o)){const f=this.imagePopups.get(o);f.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const f=this.imagePopups.keys().next().value;f&&this.closeImagePopup(f)}const c=this.imagePopups.size,m=(window.innerWidth-600)/2,u=(window.innerHeight-500)/2,d=m+c%5*30,p=u+c%5*30,g=this.sessions.get(t)?.name||t.substring(0,8),y=(r/1024).toFixed(1),w=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||i)}`,b=document.createElement("div");b.className="image-popup-window",b.id=`image-popup-${o}`,b.style.left=`${d}px`,b.style.top=`${p}px`,b.style.zIndex=++this.imagePopupZIndex,b.innerHTML=`
310
+ `,document.body.appendChild(o);const l=this.makeWindowDraggable(o,o.querySelector(".log-viewer-window-header")),c=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);c.onmessage=h=>{const u=JSON.parse(h.data),d=document.getElementById(`log-viewer-body-${s}`);if(d)switch(u.type){case"connected":d.innerHTML="";break;case"data":const p=d.scrollTop+d.clientHeight>=d.scrollHeight-10,m=escapeHtml(u.content);d.innerHTML+=m,p&&(d.scrollTop=d.scrollHeight),d.innerHTML.length>5e5&&(d.innerHTML=d.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":d.innerHTML+=`<div class="log-error">${escapeHtml(u.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:o,eventSource:c,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:i,timestamp:a,size:r}=e,o=`${t}-${a}`;if(this.imagePopups.has(o)){const f=this.imagePopups.get(o);f.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const f=this.imagePopups.keys().next().value;f&&this.closeImagePopup(f)}const c=this.imagePopups.size,h=(window.innerWidth-600)/2,u=(window.innerHeight-500)/2,d=h+c%5*30,p=u+c%5*30,g=this.sessions.get(t)?.name||t.substring(0,8),y=(r/1024).toFixed(1),w=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||i)}`,b=document.createElement("div");b.className="image-popup-window",b.id=`image-popup-${o}`,b.style.left=`${d}px`,b.style.top=`${p}px`,b.style.zIndex=++this.imagePopupZIndex,b.innerHTML=`
303
311
  <div class="image-popup-header">
304
312
  <div class="image-popup-title" title="${escapeHtml(s)}">
305
313
  <span class="icon">\u{1F5BC}\uFE0F</span>
@@ -317,7 +325,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
317
325
  onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
318
326
  onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(w))})" />
319
327
  </div>
320
- `,document.body.appendChild(b);const $=this.makeWindowDraggable(b,b.querySelector(".image-popup-header"));b.addEventListener("mousedown",()=>{b.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(o,{element:b,sessionId:t,filePath:s,dragListeners:$})},closeImagePopup(e){const t=this.imagePopups.get(e);t&&(t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.touchMove&&(document.removeEventListener("touchmove",t.dragListeners.touchMove),document.removeEventListener("touchend",t.dragListeners.up),document.removeEventListener("touchcancel",t.dragListeners.up)),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.imagePopups.delete(e))},openImageInNewTab(e){window.open(e,"_blank")},closeSessionImagePopups(e){const t=[];for(const[s,n]of this.imagePopups)n.sessionId===e&&t.push(s);for(const s of t)this.closeImagePopup(s)},async loadMuxSessions(){try{const t=await(await fetch("/api/mux-sessions")).json();this.muxSessions=t.data?.sessions||[],this.renderMuxSessions()}catch(e){console.error("Failed to load mux sessions:",e)}},killAllMuxSessions(){const e=this.muxSessions?.length||0;if(e===0){alert("No sessions to kill");return}document.getElementById("killAllCount").textContent=e;const t=document.getElementById("killAllModal");t.classList.add("active"),this.activeFocusTrap=new FocusTrap(t),this.activeFocusTrap.activate()},closeKillAllModal(){document.getElementById("killAllModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},async confirmKillAll(e){this.closeKillAllModal();try{if(e){if((await(await fetch("/api/sessions",{method:"DELETE"})).json()).success){this.sessions.clear(),this.muxSessions=[],this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.renderMuxSessions(),this.terminal.clear(),this.terminal.reset(),this.toast("All sessions and tmux killed","success")}}else{this.sessions.clear(),this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.terminal.clear(),this.terminal.reset(),this.toast("All tabs removed, tmux still running","info")}}catch(t){console.error("Failed to kill sessions:",t),this.toast("Failed to kill sessions: "+t.message,"error")}},renderMuxSessions(){this._debouncedCall("muxSessions",this._renderMuxSessionsImmediate)},_renderMuxSessionsImmediate(){const e=document.getElementById("muxSessionsBody");if(!this.muxSessions||this.muxSessions.length===0){e.innerHTML='<div class="monitor-empty">No mux sessions</div>';return}let t="";for(const s of this.muxSessions){const n=s.stats||{memoryMB:0,cpuPercent:0,childCount:0},i=this.sessions.get(s.sessionId),a=i?i.status:"unknown",r=i?i.isWorking:!1;let o,l;a==="idle"&&!r?(o="IDLE",l="status-idle"):a==="busy"||r?(o="WORKING",l="status-working"):a==="stopped"?(o="STOPPED",l="status-stopped"):(o=a.toUpperCase(),l="");const c=i&&i.tokens?i.tokens:null,m=i?i.totalCost:0,u=i&&i.cliModel||"",d=u.includes("opus")?"opus":u.includes("sonnet")?"sonnet":u.includes("haiku")?"haiku":"",p=i?i.ralphTodoStats:null;let h="";if(p&&p.total>0){const $=Math.round(p.completed/p.total*100);h=`<span class="process-stat todo-progress">${p.completed}/${p.total} (${$}%)</span>`}let g="";c&&c.total>0&&(g=`<span class="process-stat tokens">${(c.total/1e3).toFixed(1)}k tok</span>`);let y="";m>0&&(y=`<span class="process-stat cost">$${m.toFixed(2)}</span>`);let w="";d&&(w=`<span class="monitor-model-badge ${d}">${d}</span>`);const b=escapeHtml(JSON.stringify(s.sessionId));t+=`
328
+ `,document.body.appendChild(b);const $=this.makeWindowDraggable(b,b.querySelector(".image-popup-header"));b.addEventListener("mousedown",()=>{b.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(o,{element:b,sessionId:t,filePath:s,dragListeners:$})},closeImagePopup(e){const t=this.imagePopups.get(e);t&&(t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.touchMove&&(document.removeEventListener("touchmove",t.dragListeners.touchMove),document.removeEventListener("touchend",t.dragListeners.up),document.removeEventListener("touchcancel",t.dragListeners.up)),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.imagePopups.delete(e))},openImageInNewTab(e){window.open(e,"_blank")},closeSessionImagePopups(e){const t=[];for(const[s,n]of this.imagePopups)n.sessionId===e&&t.push(s);for(const s of t)this.closeImagePopup(s)},async loadMuxSessions(){try{const t=await(await fetch("/api/mux-sessions")).json();this.muxSessions=t.data?.sessions||[],this.renderMuxSessions()}catch(e){console.error("Failed to load mux sessions:",e)}},killAllMuxSessions(){const e=this.muxSessions?.length||0;if(e===0){alert("No sessions to kill");return}document.getElementById("killAllCount").textContent=e;const t=document.getElementById("killAllModal");t.classList.add("active"),this.activeFocusTrap=new FocusTrap(t),this.activeFocusTrap.activate()},closeKillAllModal(){document.getElementById("killAllModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},async confirmKillAll(e){this.closeKillAllModal();try{if(e){if((await(await fetch("/api/sessions",{method:"DELETE"})).json()).success){this.sessions.clear(),this.muxSessions=[],this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.renderMuxSessions(),this.terminal.clear(),this.terminal.reset(),this.toast("All sessions and tmux killed","success")}}else{this.sessions.clear(),this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.terminal.clear(),this.terminal.reset(),this.toast("All tabs removed, tmux still running","info")}}catch(t){console.error("Failed to kill sessions:",t),this.toast("Failed to kill sessions: "+t.message,"error")}},renderMuxSessions(){this._debouncedCall("muxSessions",this._renderMuxSessionsImmediate)},_renderMuxSessionsImmediate(){const e=document.getElementById("muxSessionsBody");if(!this.muxSessions||this.muxSessions.length===0){e.innerHTML='<div class="monitor-empty">No mux sessions</div>';return}let t="";for(const s of this.muxSessions){const n=s.stats||{memoryMB:0,cpuPercent:0,childCount:0},i=this.sessions.get(s.sessionId),a=i?i.status:"unknown",r=i?i.isWorking:!1;let o,l;a==="idle"&&!r?(o="IDLE",l="status-idle"):a==="busy"||r?(o="WORKING",l="status-working"):a==="stopped"?(o="STOPPED",l="status-stopped"):(o=a.toUpperCase(),l="");const c=i&&i.tokens?i.tokens:null,h=i?i.totalCost:0,u=i&&i.cliModel||"",d=u.includes("opus")?"opus":u.includes("sonnet")?"sonnet":u.includes("haiku")?"haiku":"",p=i?i.ralphTodoStats:null;let m="";if(p&&p.total>0){const $=Math.round(p.completed/p.total*100);m=`<span class="process-stat todo-progress">${p.completed}/${p.total} (${$}%)</span>`}let g="";c&&c.total>0&&(g=`<span class="process-stat tokens">${(c.total/1e3).toFixed(1)}k tok</span>`);let y="";h>0&&(y=`<span class="process-stat cost">$${h.toFixed(2)}</span>`);let w="";d&&(w=`<span class="monitor-model-badge ${d}">${d}</span>`);const b=escapeHtml(JSON.stringify(s.sessionId));t+=`
321
329
  <div class="process-item process-item-clickable" onclick="app.selectSession(${b})" title="Switch to session">
322
330
  <span class="monitor-status-badge ${l}">${o}</span>
323
331
  <div class="process-info">
@@ -325,7 +333,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
325
333
  <div class="process-meta">
326
334
  ${g}
327
335
  ${y}
328
- ${h}
336
+ ${m}
329
337
  <span class="process-stat memory">${n.memoryMB}MB</span>
330
338
  <span class="process-stat cpu">${n.cpuPercent}%</span>
331
339
  </div>
Binary file