aicodeman 1.33.2 → 1.33.3

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 (91) hide show
  1. package/dist/web/public/admin-ui.js.gz +0 -0
  2. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  3. package/dist/web/public/app.0879466b.js +43 -0
  4. package/dist/web/public/app.0879466b.js.br +0 -0
  5. package/dist/web/public/app.0879466b.js.gz +0 -0
  6. package/dist/web/public/approvals-ui.js.gz +0 -0
  7. package/dist/web/public/{constants.cc927eb8.js → constants.d95fade9.js} +19 -2
  8. package/dist/web/public/constants.d95fade9.js.br +0 -0
  9. package/dist/web/public/constants.d95fade9.js.gz +0 -0
  10. package/dist/web/public/cron-ui.js.gz +0 -0
  11. package/dist/web/public/entrance-animations.js.gz +0 -0
  12. package/dist/web/public/home-sessions.js.gz +0 -0
  13. package/dist/web/public/host-wake-ui.js.gz +0 -0
  14. package/dist/web/public/i18n.7f8be985.js +1 -0
  15. package/dist/web/public/i18n.7f8be985.js.br +0 -0
  16. package/dist/web/public/i18n.7f8be985.js.gz +0 -0
  17. package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
  18. package/dist/web/public/index.html +11 -8
  19. package/dist/web/public/index.html.br +0 -0
  20. package/dist/web/public/index.html.gz +0 -0
  21. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  22. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  23. package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
  24. package/dist/web/public/mobile-overview.js.gz +0 -0
  25. package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
  26. package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
  27. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  28. package/dist/web/public/{panels-ui.67646701.js → panels-ui.6a5898d9.js} +69 -67
  29. package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
  30. package/dist/web/public/panels-ui.6a5898d9.js.gz +0 -0
  31. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  32. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  33. package/dist/web/public/readmymind-ui.js.gz +0 -0
  34. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  35. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  36. package/dist/web/public/sanitize-html.e7147e76.js +1 -0
  37. package/dist/web/public/sanitize-html.e7147e76.js.br +0 -0
  38. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  39. package/dist/web/public/session-lineage.js.gz +0 -0
  40. package/dist/web/public/session-ui.7575a231.js.gz +0 -0
  41. package/dist/web/public/settings-ui.c68e7f59.js.gz +0 -0
  42. package/dist/web/public/{styles.3fe3312d.css → styles.f2b78b86.css} +1 -1
  43. package/dist/web/public/styles.f2b78b86.css.br +0 -0
  44. package/dist/web/public/styles.f2b78b86.css.gz +0 -0
  45. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  46. package/dist/web/public/sw.js +2 -2
  47. package/dist/web/public/sw.js.br +0 -0
  48. package/dist/web/public/sw.js.gz +0 -0
  49. package/dist/web/public/tab-rail-resize.dcdc5772.js.gz +0 -0
  50. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
  51. package/dist/web/public/terminal-split.js +192 -7
  52. package/dist/web/public/terminal-split.js.br +0 -0
  53. package/dist/web/public/terminal-split.js.gz +0 -0
  54. package/dist/web/public/terminal-ui.d5223c0e.js.gz +0 -0
  55. package/dist/web/public/ultracode-panel.js.gz +0 -0
  56. package/dist/web/public/ultracode-windows.js.gz +0 -0
  57. package/dist/web/public/upload.html.gz +0 -0
  58. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  59. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  60. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  61. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  62. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  63. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  64. package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
  65. package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
  66. package/dist/web/public/vendor/xterm.css.gz +0 -0
  67. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  68. package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
  69. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  70. package/dist/web/public/webview-tabs.js +15 -4
  71. package/dist/web/public/webview-tabs.js.br +0 -0
  72. package/dist/web/public/webview-tabs.js.gz +0 -0
  73. package/dist/web/routes/file-routes.d.ts.map +1 -1
  74. package/dist/web/routes/file-routes.js +2 -1
  75. package/dist/web/routes/file-routes.js.map +1 -1
  76. package/package.json +1 -1
  77. package/dist/web/public/app.91131d2c.js +0 -43
  78. package/dist/web/public/app.91131d2c.js.br +0 -0
  79. package/dist/web/public/app.91131d2c.js.gz +0 -0
  80. package/dist/web/public/constants.cc927eb8.js.br +0 -0
  81. package/dist/web/public/constants.cc927eb8.js.gz +0 -0
  82. package/dist/web/public/i18n.f094d01f.js +0 -1
  83. package/dist/web/public/i18n.f094d01f.js.br +0 -0
  84. package/dist/web/public/i18n.f094d01f.js.gz +0 -0
  85. package/dist/web/public/panels-ui.67646701.js.br +0 -0
  86. package/dist/web/public/panels-ui.67646701.js.gz +0 -0
  87. package/dist/web/public/sanitize-html.bc7078d6.js +0 -1
  88. package/dist/web/public/sanitize-html.bc7078d6.js.br +0 -0
  89. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  90. package/dist/web/public/styles.3fe3312d.css.br +0 -0
  91. package/dist/web/public/styles.3fe3312d.css.gz +0 -0
@@ -1,4 +1,4 @@
1
- "use strict";const AWAY_DIGEST_LAST_VIEWED_KEY="codeman-away-digest-last-viewed",FILE_BROWSER_SHOW_HIDDEN_KEY="codeman:fileBrowserShowHidden",TEXT_PREVIEW_MAX_BYTES=524288,TEXT_PREVIEW_MAX_LINES=500,AWAY_DIGEST_SECTIONS=[["needsAttention","Needs Attention"],["completed","Completed"],["stillRunning","Still Running"],["idle","Idle"],["informational","Informational"]];Object.assign(CodemanApp.prototype,{_addActivityEntry(e,t,s=50){const n=this.subagentActivity.get(e)||[];n.push(t),n.length>s&&n.shift(),this.subagentActivity.set(e,n)},_onTaskCreated(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskCompleted(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskFailed(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskUpdated(e){e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onMuxCreated(e){this.muxSessions.push(e),this.renderMuxSessions()},_onMuxKilled(e){this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e.sessionId),this.renderMuxSessions()},_onMuxDied(e){this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e.sessionId),this.renderMuxSessions(),this.showToast("Mux session died: "+this.getShortId(e.sessionId),"warning")},_onMuxStatsUpdated(e){this.muxSessions=e,document.getElementById("monitorPanel").classList.contains("open")&&this.renderMuxSessions()},_onRemoteSessionReconnected(e){const t=this.getShortId(e.sessionId);this.showToast(`Remote session ${t} reconnected`,"success"),this.activeSessionId===e.sessionId&&this._pollHostReachability?.(!0)},_onRemoteReconnectExhausted(e){const t=e.sessionId,s=this.getShortId(t);this.showToast(`Remote session ${s} dropped \u2014 auto-reconnect gave up`,"error",{duration:15e3,action:{label:"Reconnect",onClick:()=>{this.sessions&&this.sessions.has(t)?this.selectSession(t,{forceReload:!0}):this.showToast("Session no longer available","warning")}}})},_onBashToolStart(e){this.handleBashToolStart(e.sessionId,e.tool)},_onBashToolEnd(e){this.handleBashToolEnd(e.sessionId,e.tool)},_onBashToolsUpdate(e){this.handleBashToolsUpdate(e.sessionId,e.tools)},_onSubagentDiscovered(e){this.subagents.set(e.agentId,e),this.subagentActivity.set(e.agentId,[]),this.subagentToolResults.delete(e.agentId),this.subagentWindows.has(e.agentId)&&this.forceCloseSubagentWindow(e.agentId),this.renderSubagentPanel(),this.findParentSessionForSubagent(e.agentId),requestAnimationFrame(()=>{this.updateConnectionLines()});const t=this.subagentParentMap.get(e.agentId);this._notifySession(t||e.sessionId,"info","subagent-spawn","Subagent Spawned",e.description||"New background agent started")},_onSubagentUpdated(e){const t=this.subagents.get(e.agentId);t?(Object.assign(t,e),this.subagents.set(e.agentId,t)):this.subagents.set(e.agentId,e),this.renderSubagentPanel(),this.subagentWindows.has(e.agentId)&&(this.renderSubagentWindowContent(e.agentId),this.updateSubagentWindowHeader(e.agentId))},_onSubagentToolCall(e){this._addActivityEntry(e.agentId,{type:"tool",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.renderSubagentPanel(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentProgress(e){this._addActivityEntry(e.agentId,{type:"progress",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentMessage(e){this._addActivityEntry(e.agentId,{type:"message",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentToolResult(e){this.subagentToolResults.has(e.agentId)||this.subagentToolResults.set(e.agentId,new Map);const t=this.subagentToolResults.get(e.agentId);if(t.set(e.toolUseId,e),t.size>50){const s=t.keys().next().value;t.delete(s)}this._addActivityEntry(e.agentId,{type:"tool_result",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},async _onSubagentCompleted(e){const t=this.subagents.get(e.agentId);if(t&&(t.status="completed",this.subagents.set(e.agentId,t)),this.renderSubagentPanel(),this.updateSubagentWindows(),this.subagentWindows.has(e.agentId)){const n=this.subagentWindows.get(e.agentId);n&&!n.minimized&&(await this.closeSubagentWindow(e.agentId),this.saveSubagentWindowStates())}const s=this.subagentParentMap.get(e.agentId);this._notifySession(s||t?.sessionId||e.sessionId,"info","subagent-complete","Subagent Completed",t?.description||e.description||"Background agent finished"),setTimeout(()=>{this.subagents.get(e.agentId)?.status==="completed"&&(this.subagentActivity.delete(e.agentId),this.subagentToolResults.delete(e.agentId))},300*1e3),setTimeout(()=>{this.subagents.get(e.agentId)?.status==="completed"&&!this.subagentWindows.has(e.agentId)&&(this.subagents.delete(e.agentId),this.subagentParentMap.delete(e.agentId))},1800*1e3)},_onImageDetected(e){console.log("[Image Detected]",e),this.openImagePopup(e)},shouldOpenCommandPaletteFromShortcut(e){if(!e||!e.ctrlKey&&!e.metaKey&&!e.altKey)return!1;const s=typeof this.getShortcutRegistry=="function"&&typeof this.matchesShortcutEvent=="function"?this.getShortcutRegistry().find(a=>a.id==="command-palette"):null;if(s){if(s.disabled||!this.matchesShortcutEvent(e,s))return!1}else if((e.key||"").toLowerCase()!=="k"&&e.code!=="KeyK"||e.shiftKey)return!1;const n=e.target;if(!n)return!0;const i=(n.tagName||"").toUpperCase(),o=typeof n.className=="string"?n.className:"";return n.classList?.contains?.("xterm-helper-textarea")||o.includes("xterm-helper-textarea")?!0:!(i==="INPUT"||i==="TEXTAREA"||i==="SELECT"||n.isContentEditable||typeof n.closest=="function"&&n.closest('[contenteditable="true"]'))},openCommandPalette(){const e=document.getElementById("commandPaletteModal"),t=document.getElementById("commandPaletteSearch");!e||!t||(this.commandPaletteActiveIndex=0,t.value="",e.classList.add("active"),this._wireCommandPalette(),this.renderCommandPalette(),t.focus(),t.select?.())},closeCommandPalette(){const e=document.getElementById("commandPaletteModal");e&&e.classList.remove("active")},_wireCommandPalette(){if(this._commandPaletteWired)return;this._commandPaletteWired=!0;const e=document.getElementById("commandPaletteModal"),t=document.getElementById("commandPaletteSearch"),s=document.getElementById("commandPaletteList");t?.addEventListener("input",()=>{this.commandPaletteActiveIndex=0,this.renderCommandPalette()}),t?.addEventListener("keydown",async n=>{if(n.key==="ArrowDown"){n.preventDefault(),this.moveCommandPaletteSelection(1);return}if(n.key==="ArrowUp"){n.preventDefault(),this.moveCommandPaletteSelection(-1);return}if(n.key==="Enter"){n.preventDefault(),n.stopPropagation(),await this.activateCommandPaletteItem();return}n.key==="Escape"&&(n.preventDefault(),this.closeCommandPalette())}),e?.addEventListener("keydown",n=>{n.key==="Escape"&&(n.preventDefault(),this.closeCommandPalette())}),s?.addEventListener?.("click",n=>{const i=n.target?.closest?.("[data-command-index]");i&&(this.commandPaletteActiveIndex=Number(i.dataset.commandIndex)||0,this.activateCommandPaletteItem())})},buildCommandPaletteItems(e=""){const t=e.trim().toLowerCase(),s=[...Array.isArray(this.sessionOrder)?this.sessionOrder:[],...Array.from(this.sessions?.keys?.()||[]).filter(o=>!this.sessionOrder?.includes?.(o))],n=new Set,i=[];for(const o of s){if(n.has(o))continue;n.add(o);const r=this.sessions?.get?.(o);if(!r)continue;const a=this.getSessionName?.(r)||r.name||r.title||o.slice(0,8),l=[r.workingDir,r.mode,r.status].filter(Boolean),c=[a,r.workingDir,r.mode,r.status,o].filter(Boolean).join(" ").toLowerCase();t&&!c.includes(t)||i.push({id:`session:${o}`,type:"session",sessionId:o,title:a,subtitle:l.join(" \xB7 ")})}return i.push(this._buildCommandPaletteNewSessionItem(e)),i.push({id:"browse-sessions",type:"browse-sessions",title:"Browse all sessions\u2026",subtitle:"Open Session Manager"}),i},_buildCommandPaletteNewSessionItem(e=""){const t=this.runMode||this._runMode||"claude",s={claude:"Claude",opencode:"OpenCode",codex:"Codex",gemini:"Gemini",antigravity:"Antigravity",pi:"Pi",grok:"Grok",deepseek:"DeepSeek",omp:"OMP"},n=this._findCommandPaletteCaseMatch(e)||document.getElementById("quickStartCase")?.value||"testcase";return{id:"new-session",type:"new-session",caseName:n,title:"New session",subtitle:`Run ${s[t]||t} in ${n}`}},_findCommandPaletteCaseMatch(e=""){const t=e.trim().toLowerCase();if(!t||!Array.isArray(this.cases))return null;const s=o=>{const r=String(o?.name||"").trim();if(!r)return 0;const a=[r,o?.path,o?.casePath,o?.workingDir,o?.remote?.path,o?.remote?.hostId].filter(Boolean).join(" ").toLowerCase(),l=r.toLowerCase();return l===t?100:l.startsWith(t)?90:l.includes(t)?80:a.includes(t)?60:0};let n=null,i=0;for(const o of this.cases){const r=s(o);r>i&&(n=o,i=r)}return n?.name||null},renderCommandPalette(){const e=document.getElementById("commandPaletteSearch"),t=document.getElementById("commandPaletteList");if(!t)return;const s=e?.value||"",n=this.buildCommandPaletteItems(s);this.commandPaletteItems=n,this.commandPaletteActiveIndex=Math.max(0,Math.min(this.commandPaletteActiveIndex||0,n.length-1)),t.innerHTML=n.map((i,o)=>{const r=o===this.commandPaletteActiveIndex?" active":"",a=i.type==="new-session"?"+":i.type==="browse-sessions"?"\u2261":"\u203A",l=i.type==="browse-sessions"?" command-palette-item--browse":"";return`
1
+ "use strict";const AWAY_DIGEST_LAST_VIEWED_KEY="codeman-away-digest-last-viewed",FILE_BROWSER_SHOW_HIDDEN_KEY="codeman:fileBrowserShowHidden",TEXT_PREVIEW_MAX_BYTES=524288,TEXT_PREVIEW_MAX_LINES=500,MARKDOWN_PREVIEW_MAX_LINES=1e4,MARKDOWN_EXTS=new Set(["md","markdown"]),FILE_PREVIEW_PREF_KEYS={mdRendered:"codeman:filePreviewMdRendered",lineNumbers:"codeman:filePreviewLineNumbers",wrap:"codeman:filePreviewWrap"},FILE_PREVIEW_PREF_DEFAULTS={mdRendered:!0,lineNumbers:!1,wrap:!0},AWAY_DIGEST_SECTIONS=[["needsAttention","Needs Attention"],["completed","Completed"],["stillRunning","Still Running"],["idle","Idle"],["informational","Informational"]];Object.assign(CodemanApp.prototype,{_addActivityEntry(e,t,s=50){const n=this.subagentActivity.get(e)||[];n.push(t),n.length>s&&n.shift(),this.subagentActivity.set(e,n)},_onTaskCreated(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskCompleted(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskFailed(e){this.renderSessionTabs(),e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onTaskUpdated(e){e.sessionId===this.activeSessionId&&this.renderTaskPanel()},_onMuxCreated(e){this.muxSessions.push(e),this.renderMuxSessions()},_onMuxKilled(e){this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e.sessionId),this.renderMuxSessions()},_onMuxDied(e){this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e.sessionId),this.renderMuxSessions(),this.showToast("Mux session died: "+this.getShortId(e.sessionId),"warning")},_onMuxStatsUpdated(e){this.muxSessions=e,document.getElementById("monitorPanel").classList.contains("open")&&this.renderMuxSessions()},_onRemoteSessionReconnected(e){const t=this.getShortId(e.sessionId);this.showToast(`Remote session ${t} reconnected`,"success"),this.activeSessionId===e.sessionId&&this._pollHostReachability?.(!0)},_onRemoteReconnectExhausted(e){const t=e.sessionId,s=this.getShortId(t);this.showToast(`Remote session ${s} dropped \u2014 auto-reconnect gave up`,"error",{duration:15e3,action:{label:"Reconnect",onClick:()=>{this.sessions&&this.sessions.has(t)?this.selectSession(t,{forceReload:!0}):this.showToast("Session no longer available","warning")}}})},_onBashToolStart(e){this.handleBashToolStart(e.sessionId,e.tool)},_onBashToolEnd(e){this.handleBashToolEnd(e.sessionId,e.tool)},_onBashToolsUpdate(e){this.handleBashToolsUpdate(e.sessionId,e.tools)},_onSubagentDiscovered(e){this.subagents.set(e.agentId,e),this.subagentActivity.set(e.agentId,[]),this.subagentToolResults.delete(e.agentId),this.subagentWindows.has(e.agentId)&&this.forceCloseSubagentWindow(e.agentId),this.renderSubagentPanel(),this.findParentSessionForSubagent(e.agentId),requestAnimationFrame(()=>{this.updateConnectionLines()});const t=this.subagentParentMap.get(e.agentId);this._notifySession(t||e.sessionId,"info","subagent-spawn","Subagent Spawned",e.description||"New background agent started")},_onSubagentUpdated(e){const t=this.subagents.get(e.agentId);t?(Object.assign(t,e),this.subagents.set(e.agentId,t)):this.subagents.set(e.agentId,e),this.renderSubagentPanel(),this.subagentWindows.has(e.agentId)&&(this.renderSubagentWindowContent(e.agentId),this.updateSubagentWindowHeader(e.agentId))},_onSubagentToolCall(e){this._addActivityEntry(e.agentId,{type:"tool",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.renderSubagentPanel(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentProgress(e){this._addActivityEntry(e.agentId,{type:"progress",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentMessage(e){this._addActivityEntry(e.agentId,{type:"message",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},_onSubagentToolResult(e){this.subagentToolResults.has(e.agentId)||this.subagentToolResults.set(e.agentId,new Map);const t=this.subagentToolResults.get(e.agentId);if(t.set(e.toolUseId,e),t.size>50){const s=t.keys().next().value;t.delete(s)}this._addActivityEntry(e.agentId,{type:"tool_result",...e}),this.activeSubagentId===e.agentId&&this.renderSubagentDetail(),this.subagentWindows.has(e.agentId)&&this.scheduleSubagentWindowRender(e.agentId)},async _onSubagentCompleted(e){const t=this.subagents.get(e.agentId);if(t&&(t.status="completed",this.subagents.set(e.agentId,t)),this.renderSubagentPanel(),this.updateSubagentWindows(),this.subagentWindows.has(e.agentId)){const n=this.subagentWindows.get(e.agentId);n&&!n.minimized&&(await this.closeSubagentWindow(e.agentId),this.saveSubagentWindowStates())}const s=this.subagentParentMap.get(e.agentId);this._notifySession(s||t?.sessionId||e.sessionId,"info","subagent-complete","Subagent Completed",t?.description||e.description||"Background agent finished"),setTimeout(()=>{this.subagents.get(e.agentId)?.status==="completed"&&(this.subagentActivity.delete(e.agentId),this.subagentToolResults.delete(e.agentId))},300*1e3),setTimeout(()=>{this.subagents.get(e.agentId)?.status==="completed"&&!this.subagentWindows.has(e.agentId)&&(this.subagents.delete(e.agentId),this.subagentParentMap.delete(e.agentId))},1800*1e3)},_onImageDetected(e){console.log("[Image Detected]",e),this.openImagePopup(e)},shouldOpenCommandPaletteFromShortcut(e){if(!e||!e.ctrlKey&&!e.metaKey&&!e.altKey)return!1;const s=typeof this.getShortcutRegistry=="function"&&typeof this.matchesShortcutEvent=="function"?this.getShortcutRegistry().find(a=>a.id==="command-palette"):null;if(s){if(s.disabled||!this.matchesShortcutEvent(e,s))return!1}else if((e.key||"").toLowerCase()!=="k"&&e.code!=="KeyK"||e.shiftKey)return!1;const n=e.target;if(!n)return!0;const i=(n.tagName||"").toUpperCase(),o=typeof n.className=="string"?n.className:"";return n.classList?.contains?.("xterm-helper-textarea")||o.includes("xterm-helper-textarea")?!0:!(i==="INPUT"||i==="TEXTAREA"||i==="SELECT"||n.isContentEditable||typeof n.closest=="function"&&n.closest('[contenteditable="true"]'))},_rememberOverlayFocus(e){this[e]=typeof document<"u"&&document.activeElement||null},_restoreOverlayFocus(e,t){const s=this[e];this[e]=null;const n=typeof document<"u"?document.body:null,i=typeof document<"u"?document.activeElement:null;if(!(i&&i!==n&&t?.contains?.(i)===!1)){if(s&&s!==n&&s.isConnected!==!1&&typeof s.focus=="function"){s.focus();return}this.activeSessionId&&this._shouldFocusTerminalForTabSwitch?.()!==!1&&this.terminal?.focus?.()}},openCommandPalette(){const e=document.getElementById("commandPaletteModal"),t=document.getElementById("commandPaletteSearch");!e||!t||(this.commandPaletteActiveIndex=0,t.value="",e.classList.add("active"),this._wireCommandPalette(),this.renderCommandPalette(),this._rememberOverlayFocus("_commandPalettePrevFocus"),t.focus(),t.select?.())},closeCommandPalette(){const e=document.getElementById("commandPaletteModal");e?.classList?.contains("active")&&(e.classList.remove("active"),this._restoreOverlayFocus("_commandPalettePrevFocus",e))},_wireCommandPalette(){if(this._commandPaletteWired)return;this._commandPaletteWired=!0;const e=document.getElementById("commandPaletteModal"),t=document.getElementById("commandPaletteSearch"),s=document.getElementById("commandPaletteList");t?.addEventListener("input",()=>{this.commandPaletteActiveIndex=0,this.renderCommandPalette()}),t?.addEventListener("keydown",async n=>{if(n.key==="ArrowDown"){n.preventDefault(),this.moveCommandPaletteSelection(1);return}if(n.key==="ArrowUp"){n.preventDefault(),this.moveCommandPaletteSelection(-1);return}if(n.key==="Enter"){n.preventDefault(),n.stopPropagation(),await this.activateCommandPaletteItem();return}n.key==="Escape"&&(n.preventDefault(),this.closeCommandPalette())}),e?.addEventListener("keydown",n=>{n.key==="Escape"&&(n.preventDefault(),this.closeCommandPalette())}),s?.addEventListener?.("click",n=>{const i=n.target?.closest?.("[data-command-index]");i&&(this.commandPaletteActiveIndex=Number(i.dataset.commandIndex)||0,this.activateCommandPaletteItem())})},buildCommandPaletteItems(e=""){const t=e.trim().toLowerCase(),s=[...Array.isArray(this.sessionOrder)?this.sessionOrder:[],...Array.from(this.sessions?.keys?.()||[]).filter(o=>!this.sessionOrder?.includes?.(o))],n=new Set,i=[];for(const o of s){if(n.has(o))continue;n.add(o);const r=this.sessions?.get?.(o);if(!r)continue;const a=this.getSessionName?.(r)||r.name||r.title||o.slice(0,8),l=[r.workingDir,r.mode,r.status].filter(Boolean),c=[a,r.workingDir,r.mode,r.status,o].filter(Boolean).join(" ").toLowerCase();t&&!c.includes(t)||i.push({id:`session:${o}`,type:"session",sessionId:o,title:a,subtitle:l.join(" \xB7 ")})}return i.push(this._buildCommandPaletteNewSessionItem(e)),i.push({id:"browse-sessions",type:"browse-sessions",title:"Browse all sessions\u2026",subtitle:"Open Session Manager"}),i},_buildCommandPaletteNewSessionItem(e=""){const t=this.runMode||this._runMode||"claude",s={claude:"Claude",opencode:"OpenCode",codex:"Codex",gemini:"Gemini",antigravity:"Antigravity",pi:"Pi",grok:"Grok",deepseek:"DeepSeek",omp:"OMP"},n=this._findCommandPaletteCaseMatch(e)||document.getElementById("quickStartCase")?.value||"testcase";return{id:"new-session",type:"new-session",caseName:n,title:"New session",subtitle:`Run ${s[t]||t} in ${n}`}},_findCommandPaletteCaseMatch(e=""){const t=e.trim().toLowerCase();if(!t||!Array.isArray(this.cases))return null;const s=o=>{const r=String(o?.name||"").trim();if(!r)return 0;const a=[r,o?.path,o?.casePath,o?.workingDir,o?.remote?.path,o?.remote?.hostId].filter(Boolean).join(" ").toLowerCase(),l=r.toLowerCase();return l===t?100:l.startsWith(t)?90:l.includes(t)?80:a.includes(t)?60:0};let n=null,i=0;for(const o of this.cases){const r=s(o);r>i&&(n=o,i=r)}return n?.name||null},renderCommandPalette(){const e=document.getElementById("commandPaletteSearch"),t=document.getElementById("commandPaletteList");if(!t)return;const s=e?.value||"",n=this.buildCommandPaletteItems(s);this.commandPaletteItems=n,this.commandPaletteActiveIndex=Math.max(0,Math.min(this.commandPaletteActiveIndex||0,n.length-1)),t.innerHTML=n.map((i,o)=>{const r=o===this.commandPaletteActiveIndex?" active":"",a=i.type==="new-session"?"+":i.type==="browse-sessions"?"\u2261":"\u203A",l=i.type==="browse-sessions"?" command-palette-item--browse":"";return`
2
2
  <button class="command-palette-item${r}${l}" type="button" data-command-index="${o}">
3
3
  <span class="command-palette-icon" aria-hidden="true">${a}</span>
4
4
  <span class="command-palette-text">
@@ -6,7 +6,7 @@
6
6
  <span class="command-palette-subtitle">${escapeHtml(i.subtitle||"")}</span>
7
7
  </span>
8
8
  </button>
9
- `}).join("")},moveCommandPaletteSelection(e){const t=this.commandPaletteItems||this.buildCommandPaletteItems(document.getElementById("commandPaletteSearch")?.value||"");t.length&&(this.commandPaletteActiveIndex=(this.commandPaletteActiveIndex+e+t.length)%t.length,this.renderCommandPalette())},async activateCommandPaletteItem(e=this.commandPaletteActiveIndex||0){const t=(this.commandPaletteItems||[])[e];if(t){if(this.closeCommandPalette(),t.type==="session"&&t.sessionId){await this.selectSession(t.sessionId);return}if(t.type==="browse-sessions"){this.openSessionManager();return}if(t.type==="new-session"){const s=document.getElementById("quickStartCase");if(s&&t.caseName){if(s.tagName==="SELECT"&&typeof s.appendChild=="function"&&!Array.from(s.options||[]).some(n=>n.value===t.caseName)){const n=document.createElement("option");n.value=t.caseName,n.textContent=t.caseName,s.appendChild(n)}typeof this.selectQuickStartCase=="function"?this.selectQuickStartCase(t.caseName):s.value=t.caseName}await this.run()}}},async openSessionManager(){const e=document.getElementById("sessionManagerModal");if(e&&(e.classList.add("active"),this._sessionManagerEscWired||(this._sessionManagerEscWired=!0,e.addEventListener("keydown",s=>{s.key==="Escape"&&(s.preventDefault(),s.stopPropagation(),this.closeSessionManager())}))),!Array.isArray(this.cases)||this.cases.length===0)try{const s=await fetch("/api/cases"),n=s.ok?await s.json():null;this.cases=n?.data||[]}catch{this.cases=this.cases||[]}const t=document.getElementById("sessionManagerSearch");t&&(this._sessionManagerSearchWired||(this._sessionManagerSearchWired=!0,t.addEventListener("input",()=>{const s=t.value.trim();this._debouncedCall("sessionManagerSearch",()=>this._loadSessionManagerList(s),200)})),t.value="",t.focus()),await this._loadSessionManagerList("")},closeSessionManager(){const e=document.getElementById("sessionManagerModal");e&&e.classList.remove("active")},_setSessionManagerMessage(e,t){e.replaceChildren();const s=document.createElement("p");s.className="empty-message",s.textContent=t,e.appendChild(s)},async _loadSessionManagerList(e=""){this._sessionManagerQuery=e;const t=document.getElementById("sessionManagerList");if(t)try{const s="/api/sessions/unified?limit=200"+(e?"&q="+encodeURIComponent(e):""),n=await fetch(s),i=await n.json().catch(()=>null);if(!n.ok||!i||i.success===!1||!i.data){this._setSessionManagerMessage(t,i?.error||`Failed to load sessions (HTTP ${n.status})`);return}const o=i.data.sessions||[];if(t.replaceChildren(),o.length===0){this._setSessionManagerMessage(t,e?"No sessions match your search":"No sessions found");return}for(const r of o){const a={sessionId:r.sessionId,workingDir:r.workingDir||"",sizeBytes:r.sizeBytes??0,lastModified:new Date(r.lastActivityAt??r.createdAt??Date.now()).toISOString(),firstPrompt:r.firstPrompt||r.name||"",gitBranch:r.gitBranch,worktreeName:r.worktreeName,worktreeRepo:r.worktreeRepo,mode:r.mode,claudeSessionId:r.claudeSessionId,resumeId:r.resumeId},l=!!this.sessions?.has?.(r.sessionId),c=this._buildHistoryItem(a,this.cases,{showViewAll:!1,onActivate:()=>{this.closeSessionManager(),l?this.selectSession(r.sessionId):a.workingDir&&this.resumeHistorySession(r.claudeSessionId||r.sessionId,a.workingDir,void 0,r.mode,r.resumeId)}});t.appendChild(c)}}catch(s){console.error("[_loadSessionManagerList]",s),this._setSessionManagerMessage(t,"Failed to load sessions")}},async openAwayDigest(e="since-last-visit"){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this._awayDigestSinceLastVisitGeneratedAt=void 0;const t=document.getElementById("awayDigestModal");t&&t.classList.add("active"),this.updateAwayDigestRangeControls(),await this.loadAwayDigest()},closeAwayDigest(){const e=document.getElementById("awayDigestModal"),t=this._awayDigestSinceLastVisitGeneratedAt;if(Number.isFinite(t))try{localStorage.setItem(AWAY_DIGEST_LAST_VIEWED_KEY,String(t))}catch(s){console.warn("Failed to save away digest last-viewed marker:",s)}e&&e.classList.remove("active")},_onSessionListMaybeChanged(){const e=document.getElementById("sessionManagerModal");e&&e.classList.contains("active")&&this._debouncedCall("sessionManagerRefresh",()=>this._loadSessionManagerList(this._sessionManagerQuery||""),400);const t=document.getElementById("welcomeOverlay");t&&t.classList.contains("visible")&&this._debouncedCall("welcomeHistoryRefresh",()=>this.loadHistorySessions(),600)},setAwayDigestRange(e){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this.updateAwayDigestRangeControls(),this.loadAwayDigest()},updateAwayDigestRangeControls(){const e=this.awayDigestRange||"since-last-visit";document.querySelectorAll("[data-away-range]").forEach(s=>{s.classList.toggle("active",s.dataset.awayRange===e)});const t=document.getElementById("awayDigestCustomRange");t&&t.classList.toggle("active",e==="custom"),e==="custom"&&this.ensureAwayDigestCustomDefaults()},ensureAwayDigestCustomDefaults(){const e=document.getElementById("awayDigestCustomSince"),t=document.getElementById("awayDigestCustomUntil");if(!e||!t)return;const s=new Date;if(t.value||(t.value=this.formatAwayDigestDateTimeLocal(s)),!e.value){const n=new Date(s.getTime()-36e5);e.value=this.formatAwayDigestDateTimeLocal(n)}},formatAwayDigestDateTimeLocal(e){const t=s=>String(s).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`},async loadAwayDigest(){const e=document.getElementById("awayDigestSummary"),t=document.getElementById("awayDigestFreshness"),s=document.getElementById("awayDigestSections");e&&(e.innerHTML='<div class="away-digest-loading">Loading digest...</div>'),t&&(t.textContent=""),s&&(s.innerHTML="");try{const n=this.awayDigestRange||"since-last-visit",i=new URLSearchParams({range:n});if(n==="since-last-visit"){const a=this.readAwayDigestLastViewed();Number.isFinite(a)&&i.set("lastViewed",String(a))}if(n==="custom"){this.ensureAwayDigestCustomDefaults();const a=this.readAwayDigestDateTimeInput("awayDigestCustomSince"),l=this.readAwayDigestDateTimeInput("awayDigestCustomUntil");if(!Number.isFinite(a))throw new Error("Choose a custom start time");i.set("since",String(a)),Number.isFinite(l)&&i.set("until",String(l))}const o=await fetch(`/api/away-digest?${i.toString()}`),r=await o.json();if(!o.ok||!r.success)throw new Error(r.error||"Failed to load away digest");this._awayDigestLoadedSuccessfully=!0,this._awayDigestGeneratedAt=r.digest.generatedAt,n==="since-last-visit"&&(this._awayDigestSinceLastVisitGeneratedAt=r.digest.generatedAt),this.renderAwayDigest(r.digest)}catch(n){const i=n instanceof Error?n.message:"Failed to load away digest";console.error("Failed to fetch away digest:",n),e&&(e.innerHTML='<div class="away-digest-load-error">Failed to load away digest</div>'),s&&(s.innerHTML=`<div class="empty-message">${escapeHtml(i)}</div>`),this.showToast(i,"error")}},readAwayDigestLastViewed(){try{const e=localStorage.getItem(AWAY_DIGEST_LAST_VIEWED_KEY),t=Number(e);return Number.isFinite(t)?t:void 0}catch{return}},readAwayDigestDateTimeInput(e){const t=document.getElementById(e);if(!t||!t.value)return;const s=Date.parse(t.value);return Number.isFinite(s)?s:void 0},renderAwayDigest(e){const t=document.getElementById("awayDigestSummary"),s=document.getElementById("awayDigestFreshness"),n=document.getElementById("awayDigestSections");if(!t||!s||!n)return;const i=e.totals.inputTokens||0,o=e.totals.outputTokens||0,r=e.totals.estimatedCost||0;t.innerHTML=`
9
+ `}).join("")},moveCommandPaletteSelection(e){const t=this.commandPaletteItems||this.buildCommandPaletteItems(document.getElementById("commandPaletteSearch")?.value||"");t.length&&(this.commandPaletteActiveIndex=(this.commandPaletteActiveIndex+e+t.length)%t.length,this.renderCommandPalette())},async activateCommandPaletteItem(e=this.commandPaletteActiveIndex||0){const t=(this.commandPaletteItems||[])[e];if(t){if(this.closeCommandPalette(),t.type==="session"&&t.sessionId){await this.selectSession(t.sessionId);return}if(t.type==="browse-sessions"){this.openSessionManager();return}if(t.type==="new-session"){const s=document.getElementById("quickStartCase");if(s&&t.caseName){if(s.tagName==="SELECT"&&typeof s.appendChild=="function"&&!Array.from(s.options||[]).some(n=>n.value===t.caseName)){const n=document.createElement("option");n.value=t.caseName,n.textContent=t.caseName,s.appendChild(n)}typeof this.selectQuickStartCase=="function"?this.selectQuickStartCase(t.caseName):s.value=t.caseName}await this.run()}}},async openSessionManager(){const e=document.getElementById("sessionManagerModal");if(e&&(e.classList.add("active"),this._sessionManagerEscWired||(this._sessionManagerEscWired=!0,e.addEventListener("keydown",s=>{s.key==="Escape"&&(s.preventDefault(),s.stopPropagation(),this.closeSessionManager())}))),!Array.isArray(this.cases)||this.cases.length===0)try{const s=await fetch("/api/cases"),n=s.ok?await s.json():null;this.cases=n?.data||[]}catch{this.cases=this.cases||[]}const t=document.getElementById("sessionManagerSearch");t&&(this._sessionManagerSearchWired||(this._sessionManagerSearchWired=!0,t.addEventListener("input",()=>{const s=t.value.trim();this._debouncedCall("sessionManagerSearch",()=>this._loadSessionManagerList(s),200)})),t.value="",this._rememberOverlayFocus("_sessionManagerPrevFocus"),t.focus()),await this._loadSessionManagerList("")},closeSessionManager(){const e=document.getElementById("sessionManagerModal");e?.classList?.contains("active")&&(e.classList.remove("active"),this._restoreOverlayFocus("_sessionManagerPrevFocus",e))},_setSessionManagerMessage(e,t){e.replaceChildren();const s=document.createElement("p");s.className="empty-message",s.textContent=t,e.appendChild(s)},async _loadSessionManagerList(e=""){this._sessionManagerQuery=e;const t=document.getElementById("sessionManagerList");if(t)try{const s="/api/sessions/unified?limit=200"+(e?"&q="+encodeURIComponent(e):""),n=await fetch(s),i=await n.json().catch(()=>null);if(!n.ok||!i||i.success===!1||!i.data){this._setSessionManagerMessage(t,i?.error||`Failed to load sessions (HTTP ${n.status})`);return}const o=i.data.sessions||[];if(t.replaceChildren(),o.length===0){this._setSessionManagerMessage(t,e?"No sessions match your search":"No sessions found");return}for(const r of o){const a={sessionId:r.sessionId,workingDir:r.workingDir||"",sizeBytes:r.sizeBytes??0,lastModified:new Date(r.lastActivityAt??r.createdAt??Date.now()).toISOString(),firstPrompt:r.firstPrompt||r.name||"",gitBranch:r.gitBranch,worktreeName:r.worktreeName,worktreeRepo:r.worktreeRepo,mode:r.mode,claudeSessionId:r.claudeSessionId,resumeId:r.resumeId},l=!!this.sessions?.has?.(r.sessionId),c=this._buildHistoryItem(a,this.cases,{showViewAll:!1,onActivate:()=>{this.closeSessionManager(),l?this.selectSession(r.sessionId):a.workingDir&&this.resumeHistorySession(r.claudeSessionId||r.sessionId,a.workingDir,void 0,r.mode,r.resumeId)}});t.appendChild(c)}}catch(s){console.error("[_loadSessionManagerList]",s),this._setSessionManagerMessage(t,"Failed to load sessions")}},async openAwayDigest(e="since-last-visit"){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this._awayDigestSinceLastVisitGeneratedAt=void 0;const t=document.getElementById("awayDigestModal");t&&t.classList.add("active"),this.updateAwayDigestRangeControls(),await this.loadAwayDigest()},closeAwayDigest(){const e=document.getElementById("awayDigestModal"),t=this._awayDigestSinceLastVisitGeneratedAt;if(Number.isFinite(t))try{localStorage.setItem(AWAY_DIGEST_LAST_VIEWED_KEY,String(t))}catch(s){console.warn("Failed to save away digest last-viewed marker:",s)}e&&e.classList.remove("active")},_onSessionListMaybeChanged(){const e=document.getElementById("sessionManagerModal");e&&e.classList.contains("active")&&this._debouncedCall("sessionManagerRefresh",()=>this._loadSessionManagerList(this._sessionManagerQuery||""),400);const t=document.getElementById("welcomeOverlay");t&&t.classList.contains("visible")&&this._debouncedCall("welcomeHistoryRefresh",()=>this.loadHistorySessions(),600)},setAwayDigestRange(e){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this.updateAwayDigestRangeControls(),this.loadAwayDigest()},updateAwayDigestRangeControls(){const e=this.awayDigestRange||"since-last-visit";document.querySelectorAll("[data-away-range]").forEach(s=>{s.classList.toggle("active",s.dataset.awayRange===e)});const t=document.getElementById("awayDigestCustomRange");t&&t.classList.toggle("active",e==="custom"),e==="custom"&&this.ensureAwayDigestCustomDefaults()},ensureAwayDigestCustomDefaults(){const e=document.getElementById("awayDigestCustomSince"),t=document.getElementById("awayDigestCustomUntil");if(!e||!t)return;const s=new Date;if(t.value||(t.value=this.formatAwayDigestDateTimeLocal(s)),!e.value){const n=new Date(s.getTime()-36e5);e.value=this.formatAwayDigestDateTimeLocal(n)}},formatAwayDigestDateTimeLocal(e){const t=s=>String(s).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`},async loadAwayDigest(){const e=document.getElementById("awayDigestSummary"),t=document.getElementById("awayDigestFreshness"),s=document.getElementById("awayDigestSections");e&&(e.innerHTML='<div class="away-digest-loading">Loading digest...</div>'),t&&(t.textContent=""),s&&(s.innerHTML="");try{const n=this.awayDigestRange||"since-last-visit",i=new URLSearchParams({range:n});if(n==="since-last-visit"){const a=this.readAwayDigestLastViewed();Number.isFinite(a)&&i.set("lastViewed",String(a))}if(n==="custom"){this.ensureAwayDigestCustomDefaults();const a=this.readAwayDigestDateTimeInput("awayDigestCustomSince"),l=this.readAwayDigestDateTimeInput("awayDigestCustomUntil");if(!Number.isFinite(a))throw new Error("Choose a custom start time");i.set("since",String(a)),Number.isFinite(l)&&i.set("until",String(l))}const o=await fetch(`/api/away-digest?${i.toString()}`),r=await o.json();if(!o.ok||!r.success)throw new Error(r.error||"Failed to load away digest");this._awayDigestLoadedSuccessfully=!0,this._awayDigestGeneratedAt=r.digest.generatedAt,n==="since-last-visit"&&(this._awayDigestSinceLastVisitGeneratedAt=r.digest.generatedAt),this.renderAwayDigest(r.digest)}catch(n){const i=n instanceof Error?n.message:"Failed to load away digest";console.error("Failed to fetch away digest:",n),e&&(e.innerHTML='<div class="away-digest-load-error">Failed to load away digest</div>'),s&&(s.innerHTML=`<div class="empty-message">${escapeHtml(i)}</div>`),this.showToast(i,"error")}},readAwayDigestLastViewed(){try{const e=localStorage.getItem(AWAY_DIGEST_LAST_VIEWED_KEY),t=Number(e);return Number.isFinite(t)?t:void 0}catch{return}},readAwayDigestDateTimeInput(e){const t=document.getElementById(e);if(!t||!t.value)return;const s=Date.parse(t.value);return Number.isFinite(s)?s:void 0},renderAwayDigest(e){const t=document.getElementById("awayDigestSummary"),s=document.getElementById("awayDigestFreshness"),n=document.getElementById("awayDigestSections");if(!t||!s||!n)return;const i=e.totals.inputTokens||0,o=e.totals.outputTokens||0,r=e.totals.estimatedCost||0;t.innerHTML=`
10
10
  <div class="away-digest-card">
11
11
  <span class="away-digest-card-label">Needs Attention</span>
12
12
  <span class="away-digest-card-value">${e.totals.needsAttention}</span>
@@ -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},o=new Date;o.setDate(o.getDate()-7);const r=t.filter(f=>new Date(f.date)>=o),a=r.reduce((f,S)=>f+S.inputTokens,0),l=r.reduce((f,S)=>f+S.outputTokens,0),c=this.estimateCost(a,l),u=s.totalInputTokens,d=s.totalOutputTokens,h=this.estimateCost(u,d),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(w=>w.date===n)||{inputTokens:0,outputTokens:0,estimatedCost:0},o=new Date;o.setDate(o.getDate()-7);const r=t.filter(w=>new Date(w.date)>=o),a=r.reduce((w,S)=>w+S.inputTokens,0),l=r.reduce((w,S)=>w+S.outputTokens,0),c=this.estimateCost(a,l),h=s.totalInputTokens,d=s.totalOutputTokens,m=this.estimateCost(h,d),u=document.getElementById("statsSummary");u.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,27 +64,27 @@
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(u+d)}</span>
68
- <span class="stat-card-cost">~$${h.toFixed(2)}</span>
67
+ <span class="stat-card-value">${this.formatTokens(h+d)}</span>
68
+ <span class="stat-card-cost">~$${m.toFixed(2)}</span>
69
69
  </div>
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(C=>C.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=`
70
+ `;const f=document.getElementById("statsChart"),p=document.getElementById("statsChartDays"),v=[];for(let w=6;w>=0;w--){const S=new Date;S.setDate(S.getDate()-w);const T=S.toISOString().split("T")[0],y=t.find(C=>C.date===T);v.push({date:T,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:y?y.inputTokens+y.outputTokens:0,cost:y?y.estimatedCost:0})}const g=Math.max(...v.map(w=>w.tokens),1);f.innerHTML=v.map(w=>{const S=Math.max(w.tokens/g*100,3),T=`${w.dayName}: ${this.formatTokens(w.tokens)} (~$${w.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${T}"></div>`}).join(""),p.innerHTML=v.map(w=>`<span>${w.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>
74
74
  <span>Output</span>
75
75
  <span>Cost</span>
76
76
  </div>
77
- ${$.map(f=>`
77
+ ${$.map(w=>`
78
78
  <div class="stats-table-row">
79
- <span class="cell cell-date">${new Date(f.date+"T00:00:00").toLocaleDateString("en-US",{month:"short",day:"numeric"})}</span>
80
- <span class="cell">${this.formatTokens(f.inputTokens)}</span>
81
- <span class="cell">${this.formatTokens(f.outputTokens)}</span>
82
- <span class="cell cell-cost">$${f.estimatedCost.toFixed(2)}</span>
79
+ <span class="cell cell-date">${new Date(w.date+"T00:00:00").toLocaleDateString("en-US",{month:"short",day:"numeric"})}</span>
80
+ <span class="cell">${this.formatTokens(w.inputTokens)}</span>
81
+ <span class="cell">${this.formatTokens(w.outputTokens)}</span>
82
+ <span class="cell cell-cost">$${w.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,o,r;const a=u=>{if(u.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const d=getEventCoords(u);n=d.clientX,i=d.clientY;const h=e.getBoundingClientRect();o=h.left,r=h.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),u.preventDefault()},l=u=>{if(!s)return;const d=getEventCoords(u),h=d.clientX-n,p=d.clientY-i;let m=o+h,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=a,t._touchDragHandler=a,t.addEventListener("mousedown",a),t.addEventListener("touchstart",a,{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,o,r;const a=u=>{if(u.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const d=getEventCoords(u);n=d.clientX,i=d.clientY;const h=e.getBoundingClientRect();o=h.left,r=h.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),u.preventDefault()},l=u=>{if(!s)return;const d=getEventCoords(u),h=d.clientX-n,p=d.clientY-i;let m=o+h,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=a,t._touchDragHandler=a,t.addEventListener("mousedown",a),t.addEventListener("touchstart",a,{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 o=(l,c)=>{const u=l.status==="running"?"":l.status==="completed"?"&#x2713;":"&#x2717;",d=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let h="";if(l.children&&l.children.length>0){h='<div class="task-children">';for(const p of l.children){const m=c.find(g=>g.id===p);m&&(h+=`<div class="task-node">${o(m,c)}</div>`)}h+="</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,o,r;const a=h=>{if(h.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const d=getEventCoords(h);n=d.clientX,i=d.clientY;const m=e.getBoundingClientRect();o=m.left,r=m.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 d=getEventCoords(h),m=d.clientX-n,u=d.clientY-i;let f=o+m,p=r+u;const v=e.getBoundingClientRect();f=Math.max(0,Math.min(window.innerWidth-v.width,f)),p=Math.max(0,Math.min(window.innerHeight-v.height,p)),e.style.left=f+"px",e.style.top=p+"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=a,t._touchDragHandler=a,t.addEventListener("mousedown",a),t.addEventListener("touchstart",a,{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,o,r;const a=h=>{if(h.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const d=getEventCoords(h);n=d.clientX,i=d.clientY;const m=e.getBoundingClientRect();o=m.left,r=m.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 d=getEventCoords(h),m=d.clientX-n,u=d.clientY-i;let f=o+m,p=r+u;const v=e.getBoundingClientRect();f=Math.max(0,Math.min(window.innerWidth-v.width,f)),p=Math.max(0,Math.min(window.innerHeight-v.height,p)),e.style.left=f+"px",e.style.top=p+"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=a,t._touchDragHandler=a,t.addEventListener("mousedown",a),t.addEventListener("touchstart",a,{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 o=(l,c)=>{const h=l.status==="running"?"":l.status==="completed"?"&#x2713;":"&#x2717;",d=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let m="";if(l.children&&l.children.length>0){m='<div class="task-children">';for(const u of l.children){const f=c.find(p=>p.id===u);f&&(m+=`<div class="task-node">${o(f,c)}</div>`)}m+="</div>"}return`
86
86
  <div class="task-item">
87
- <span class="task-status-icon ${l.status}">${u}</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">
@@ -93,19 +93,19 @@
93
93
  </div>
94
94
  </div>
95
95
  </div>
96
- ${h}
97
- `},r=this.flattenTaskTree(e.taskTree);let a='<div class="task-tree">';for(const l of e.taskTree)a+=`<div class="task-node">${o(l,r)}</div>`;a+="</div>",t.innerHTML=a},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,o=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],a=r[r.length-1],l=a?.type==="tool"?a.tool:null,c=this.subagentWindows.has(n.agentId),u=n.status==="active"||n.status==="idle",d=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",h=this.getTeammateInfo(n),p=h?h.name:n.description||n.agentId.substring(0,7),m=this.getTeammateBadgeHtml(n),g=h?`<span class="subagent-icon teammate-dot teammate-color-${h.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
98
- <div class="subagent-item ${o} ${i?"selected":""}${h?" is-teammate":""}"
96
+ ${m}
97
+ `},r=this.flattenTaskTree(e.taskTree);let a='<div class="task-tree">';for(const l of e.taskTree)a+=`<div class="task-node">${o(l,r)}</div>`;a+="</div>",t.innerHTML=a},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,o=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],a=r[r.length-1],l=a?.type==="tool"?a.tool:null,c=this.subagentWindows.has(n.agentId),h=n.status==="active"||n.status==="idle",d=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",m=this.getTeammateInfo(n),u=m?m.name:n.description||n.agentId.substring(0,7),f=this.getTeammateBadgeHtml(n),p=m?`<span class="subagent-icon teammate-dot teammate-color-${m.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
98
+ <div class="subagent-item ${o} ${i?"selected":""}${m?" is-teammate":""}"
99
99
  onclick="app.selectSubagent(${escapeHtml(JSON.stringify(n.agentId))})"
100
100
  ondblclick="app.openSubagentWindow(${escapeHtml(JSON.stringify(n.agentId))})"
101
101
  title="Double-click to open tracking window">
102
102
  <div class="subagent-header">
103
- ${g}
104
- <span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(p.length>40?p.substring(0,40)+"...":p)}</span>
105
- ${m}
103
+ ${p}
104
+ <span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(u.length>40?u.substring(0,40)+"...":u)}</span>
105
+ ${f}
106
106
  ${d}
107
107
  <span class="subagent-status ${o}">${n.status}</span>
108
- ${u?`<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,15 +122,15 @@
122
122
  <span class="detail">${escapeHtml(c.primary)}</span>
123
123
  ${c.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(a.toolUseId))})">\u25B6</button>`:""}
124
124
  ${c.hasMore?`<div class="tool-params-expanded" id="tool-params-${escapeHtml(a.toolUseId)}" style="display:none;"><pre>${escapeHtml(JSON.stringify(a.fullInput||a.input,null,2))}</pre></div>`:""}
125
- </div>`}else if(a.type==="tool_result"){const c=a.isError?"\u274C":"\u{1F4C4}",u=a.isError?"error":"",d=a.contentLength>500?` (${this.formatBytes(a.contentLength)})`:"",h=a.preview.length>80?a.preview.substring(0,80)+"...":a.preview;return`<div class="subagent-activity tool-result ${u}">
125
+ </div>`}else if(a.type==="tool_result"){const c=a.isError?"\u274C":"\u{1F4C4}",h=a.isError?"error":"",d=a.contentLength>500?` (${this.formatBytes(a.contentLength)})`:"",m=a.preview.length>80?a.preview.substring(0,80)+"...":a.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(a.tool||"result")}</span>
129
- <span class="detail">${escapeHtml(h)}${d}</span>
130
- </div>`}else if(a.type==="progress"){const c=a.hookEvent||a.hookName,u=c?"\u{1FA9D}":a.progressType==="query_update"?"\u27F3":"\u2713",d=c?" hook":"",h=c?a.hookName||a.hookEvent:a.query||a.progressType;return`<div class="subagent-activity progress${d}">
129
+ <span class="detail">${escapeHtml(m)}${d}</span>
130
+ </div>`}else if(a.type==="progress"){const c=a.hookEvent||a.hookName,h=c?"\u{1FA9D}":a.progressType==="query_update"?"\u27F3":"\u2713",d=c?" hook":"",m=c?a.hookName||a.hookEvent:a.query||a.progressType;return`<div class="subagent-activity progress${d}">
131
131
  <span class="time">${l}</span>
132
- <span class="icon">${u}</span>
133
- <span class="detail">${escapeHtml(h)}</span>
132
+ <span class="icon">${h}</span>
133
+ <span class="detail">${escapeHtml(m)}</span>
134
134
  </div>`}else if(a.type==="message"){const c=a.text.length>100?a.text.substring(0,100)+"...":a.text;return`<div class="subagent-activity message">
135
135
  <span class="time">${l}</span>
136
136
  <span class="icon">\u{1F4AC}</span>
@@ -187,10 +187,10 @@
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),u=c?c.name:t.description||e.substring(0,7),d=u.length>50?u.substring(0,50)+"...":u;n.textContent=d}let i=s.querySelector(".teammate-badge");const o=this.getTeammateInfo(t);if(o&&!i){const c=s.querySelector(".subagent-window-title");if(c){const u=document.createElement("span");u.className=`teammate-badge teammate-color-${o.color}`,u.title=`Team: ${o.teamName}`,u.textContent=`@${o.name}`;const d=c.querySelector(".status");d&&d.insertAdjacentElement("beforebegin",u)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let a=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!a){a=document.createElement("span"),a.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",a)}a.className=`subagent-model-badge ${t.modelShort}`,a.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,o=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:o}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=this.loadAppSettingsFromStorage?.()||{},a=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(r.terminalFontFamily),...window.CodemanTerminalFont.resolveWeights(r),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),l=new FitAddon.FitAddon;if(a.loadAddon(l),typeof Unicode11Addon<"u")try{const d=new Unicode11Addon.Unicode11Addon;a.loadAddon(d),a.unicode.activeVersion="11"}catch{}try{a.open(n)}catch(d){console.warn("[TeammateTerminal] Failed to open terminal:",d),this.teammateTerminals.delete(e);return}a.attachCustomKeyEventHandler(d=>d.type==="keydown"&&d.key.toLowerCase()==="z"&&d.ctrlKey&&!d.altKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),!1):!0),setTimeout(()=>{try{l.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(d=>d.json()).then(d=>{if(d.success&&d.data?.buffer)try{a.write(d.data.buffer)}catch{}}).catch(d=>console.error("[TeammateTerminal] Failed to fetch buffer:",d));for(const d of o)try{a.write(d)}catch{}o.length=0},100),a.onData(d=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:d})}).catch(h=>console.error("[TeammateTerminal] Failed to send input:",h))});const c=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{l.fit()}catch{}})});c.observe(n);const u=this.teammateTerminals.get(e);u&&(u.terminal=a,u.fitAddon=l,u.resizeObserver=c)})},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,o=20,r=window.innerWidth,a=window.innerHeight,l=50,c=120,u=Math.floor((r-l-50)/(n+o))||1,d=Math.floor((a-c-50)/(i+o))||1,h=s%u,p=Math.floor(s/u)%d;let m=l+h*(n+o),g=c+p*(i+o);m=Math.max(10,Math.min(m,r-n-10)),g=Math.max(10,Math.min(g,a-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=`
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),d=h.length>50?h.substring(0,50)+"...":h;n.textContent=d}let i=s.querySelector(".teammate-badge");const o=this.getTeammateInfo(t);if(o&&!i){const c=s.querySelector(".subagent-window-title");if(c){const h=document.createElement("span");h.className=`teammate-badge teammate-color-${o.color}`,h.title=`Team: ${o.teamName}`,h.textContent=`@${o.name}`;const d=c.querySelector(".status");d&&d.insertAdjacentElement("beforebegin",h)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let a=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!a){a=document.createElement("span"),a.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",a)}a.className=`subagent-model-badge ${t.modelShort}`,a.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,o=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:o}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=this.loadAppSettingsFromStorage?.()||{},a=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(r.terminalFontFamily),...window.CodemanTerminalFont.resolveWeights(r),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),l=new FitAddon.FitAddon;if(a.loadAddon(l),typeof Unicode11Addon<"u")try{const d=new Unicode11Addon.Unicode11Addon;a.loadAddon(d),a.unicode.activeVersion="11"}catch{}try{a.open(n)}catch(d){console.warn("[TeammateTerminal] Failed to open terminal:",d),this.teammateTerminals.delete(e);return}a.attachCustomKeyEventHandler(d=>d.type==="keydown"&&d.key.toLowerCase()==="z"&&d.ctrlKey&&!d.altKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),!1):!0),setTimeout(()=>{try{l.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(d=>d.json()).then(d=>{if(d.success&&d.data?.buffer)try{a.write(d.data.buffer)}catch{}}).catch(d=>console.error("[TeammateTerminal] Failed to fetch buffer:",d));for(const d of o)try{a.write(d)}catch{}o.length=0},100),a.onData(d=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:d})}).catch(m=>console.error("[TeammateTerminal] Failed to send input:",m))});const c=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{l.fit()}catch{}})});c.observe(n);const h=this.teammateTerminals.get(e);h&&(h.terminal=a,h.fitAddon=l,h.resizeObserver=c)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const y=this.subagentWindows.get(t);y.hidden&&(y.element.style.display="flex",y.hidden=!1),y.element.style.zIndex=++this.subagentWindowZIndex,y.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,o=20,r=window.innerWidth,a=window.innerHeight,l=50,c=120,h=Math.floor((r-l-50)/(n+o))||1,d=Math.floor((a-c-50)/(i+o))||1,m=s%h,u=Math.floor(s/h)%d;let f=l+m*(n+o),p=c+u*(i+o);f=Math.max(10,Math.min(f,r-n-10)),p=Math.max(10,Math.min(p,a-i-10));const v=e.color||"blue",g=document.createElement("div");g.className="subagent-window has-terminal",g.id=`subagent-window-${t}`,g.style.zIndex=++this.subagentWindowZIndex,g.style.left=`${f}px`,g.style.top=`${p}px`,g.style.width=`${n}px`,g.style.height=`${i}px`,g.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
- <span class="icon" style="color: var(--team-color-${y}, #339af0)">\u2B24</span>
193
+ <span class="icon" style="color: var(--team-color-${v}, #339af0)">\u2B24</span>
194
194
  <span class="id">${escapeHtml(e.teammateName)}</span>
195
195
  <span class="status running">terminal</span>
196
196
  </div>
@@ -200,54 +200,56 @@
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,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)||[],o=i.filter(p=>p.status==="completed").length,r=i.length,a=r>0?Math.round(o/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=`${a}%`);const u=e.querySelector(".team-tasks-progress-text");u&&(u.textContent=`${o}/${r}`);const d=e.querySelector(".team-tasks-list");if(!d)return;if(i.length===0){d.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const h=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
- <span class="team-task-subject">${escapeHtml(p.subject)}</span>
206
- ${y}
207
- </div>`}).join("");d.innerHTML=h},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("/"),o=[];for(const r of i)r===""||r==="."||(r===".."?o.length>1&&o.pop():o.push(r));return"/"+o.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 o=this.getFilename(n),r=this.getFilename(i);if(o!==r)return!1;const a=this.isShallowRootPath(e),l=this.isShallowRootPath(t),c=this.isPathInWorkingDir(n,s),u=this.isPathInWorkingDir(i,s);return!!(a&&u||l&&c)},pickBetterPath(e,t,s){if(s){const o=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(o&&!r)return e;if(r&&!o)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 o of e)for(const r of o.filePaths)s.push({rawPath:r,toolId:o.id});if(s.length<=1){const o=new Map;for(const r of s)o.set(this.normalizeFilePath(r.rawPath,t),r);return o}s.sort((o,r)=>{const a=this.isPathInWorkingDir(o.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return a&&!l?-1:l&&!a?1:r.rawPath.length-o.rawPath.length});const n=new Map,i=new Set;for(const{rawPath:o,toolId:r}of s){const a=this.normalizeFilePath(o,t);let l=!1;for(const[,c]of n)if(this.pathsAreEquivalent(o,c.rawPath,t)){l=!0;break}!l&&!i.has(a)&&(n.set(a,{rawPath:o,toolId:r}),i.add(a))}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(o=>o.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 o=(this.projectInsights.get(this.activeSessionId)||[]).filter(d=>d.status==="running");if(o.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const a=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(o,a),c=new Set(Array.from(l.values()).map(d=>d.rawPath)),u=[];for(const d of o){const h=d.filePaths.filter(m=>c.has(m));if(h.length===0)continue;const p=d.command.length>50?d.command.substring(0,50)+"...":d.command;u.push(`
203
+ `,document.body.appendChild(g);const b=this.makeWindowDraggable(g,g.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(g);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:g,minimized:!1,hidden:S,dragListeners:b,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(g.style.display="none"),g.addEventListener("mousedown",()=>{g.style.zIndex=++this.subagentWindowZIndex});const T=new ResizeObserver(()=>{this.updateConnectionLines()});if(T.observe(g),this.subagentWindows.get(t).resizeObserver=T,S){const y=this.subagentWindows.get(t);y&&(y._lazyTerminal=!0,y._lazyPaneTarget=e.paneTarget,y._lazySessionId=e.sessionId)}else this.initTeammateTerminal(t,e,g);requestAnimationFrame(()=>{g.style.transition="transform 0.3s ease, opacity 0.3s ease",g.style.transform="scale(1)",g.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[u,f]of this.teams)if(f.leadSessionId===this.activeSessionId){t=f,s=u;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 p=e.querySelector(".team-tasks-header");p&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,p))}const i=this.teamTasks.get(s)||[],o=i.filter(u=>u.status==="completed").length,r=i.length,a=r>0?Math.round(o/r*100):0,l=e.querySelector(".team-tasks-header-text");if(l){const u=t.members.filter(f=>f.agentType!=="team-lead").length;l.textContent=`Team Tasks (${u} teammates)`}const c=e.querySelector(".team-tasks-progress-fill");c&&(c.style.width=`${a}%`);const h=e.querySelector(".team-tasks-progress-text");h&&(h.textContent=`${o}/${r}`);const d=e.querySelector(".team-tasks-list");if(!d)return;if(i.length===0){d.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const m=i.map(u=>{const f=u.status==="completed"?"\u2713":u.status==="in_progress"?"\u25C9":"\u25CB",p=u.status.replace("_","-"),v=u.owner?`<span class="team-task-owner teammate-color-${this.getTeammateColor(u.owner)}">${escapeHtml(u.owner)}</span>`:"";return`<div class="team-task-item ${p}">
204
+ <span class="team-task-status">${f}</span>
205
+ <span class="team-task-subject">${escapeHtml(u.subject)}</span>
206
+ ${v}
207
+ </div>`}).join("");d.innerHTML=m},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("/"),o=[];for(const r of i)r===""||r==="."||(r===".."?o.length>1&&o.pop():o.push(r));return"/"+o.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 o=this.getFilename(n),r=this.getFilename(i);if(o!==r)return!1;const a=this.isShallowRootPath(e),l=this.isShallowRootPath(t),c=this.isPathInWorkingDir(n,s),h=this.isPathInWorkingDir(i,s);return!!(a&&h||l&&c)},pickBetterPath(e,t,s){if(s){const o=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(o&&!r)return e;if(r&&!o)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 o of e)for(const r of o.filePaths)s.push({rawPath:r,toolId:o.id});if(s.length<=1){const o=new Map;for(const r of s)o.set(this.normalizeFilePath(r.rawPath,t),r);return o}s.sort((o,r)=>{const a=this.isPathInWorkingDir(o.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return a&&!l?-1:l&&!a?1:r.rawPath.length-o.rawPath.length});const n=new Map,i=new Set;for(const{rawPath:o,toolId:r}of s){const a=this.normalizeFilePath(o,t);let l=!1;for(const[,c]of n)if(this.pathsAreEquivalent(o,c.rawPath,t)){l=!0;break}!l&&!i.has(a)&&(n.set(a,{rawPath:o,toolId:r}),i.add(a))}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(o=>o.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 o=(this.projectInsights.get(this.activeSessionId)||[]).filter(d=>d.status==="running");if(o.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const a=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(o,a),c=new Set(Array.from(l.values()).map(d=>d.rawPath)),h=[];for(const d of o){const m=d.filePaths.filter(f=>c.has(f));if(m.length===0)continue;const u=d.command.length>50?d.command.substring(0,50)+"...":d.command;h.push(`
208
208
  <div class="project-insight-item" data-tool-id="${d.id}">
209
209
  <div class="project-insight-command">
210
210
  <span class="icon">\u{1F4BB}</span>
211
- <span class="cmd" title="${escapeHtml(d.command)}">${escapeHtml(p)}</span>
211
+ <span class="cmd" title="${escapeHtml(d.command)}">${escapeHtml(u)}</span>
212
212
  <span class="project-insight-status ${d.status}">${d.status}</span>
213
213
  ${d.timeout?`<span class="project-insight-timeout">${escapeHtml(d.timeout)}</span>`:""}
214
214
  </div>
215
215
  <div class="project-insight-paths">
216
- `);for(const m of h){const g=m.split("/").pop();u.push(`
216
+ `);for(const f of m){const p=f.split("/").pop();h.push(`
217
217
  <span class="project-insight-filepath"
218
- onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(m))}, ${escapeHtml(JSON.stringify(d.sessionId))})"
219
- title="${escapeHtml(m)}">${escapeHtml(g)}</span>
220
- `)}u.push(`
218
+ onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(f))}, ${escapeHtml(JSON.stringify(d.sessionId))})"
219
+ title="${escapeHtml(f)}">${escapeHtml(p)}</span>
220
+ `)}h.push(`
221
221
  </div>
222
222
  </div>
223
- `)}t.innerHTML=u.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"),o=this.$?.("fileBrowserTree"),r=this.$?.("fileBrowserStatus"),a=i?.classList.contains("visible")===!0;o&&(o.innerHTML=a?`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`:""),r&&(r.textContent=a?"Loading files...":""),a&&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:o}=e;s.textContent=`${n} files, ${i} dirs${o?" (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,o=this.fileBrowserShowHidden===!0,r=this.loadFileBrowser(n,{force:!0}),a=e.treeEpoch;if(!r?.then||(await r,!s||s.length>256))return;const l={ownerSessionId:n,showHidden:o,treeEpoch:a,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,o=s.treeEpoch,r=s.treeInFlight;if(!t&&this._isFileBrowserNormalCompatible(r,e,i,o))return r.promise;const a=s.normalState;if(!t&&this._isFileBrowserNormalCompatible(a,e,i,o)&&(a.phase==="ready"||a.phase==="error"))return a.phase==="ready"&&(this.fileBrowserData=a.data),this._renderFileBrowserNormalState(a),Promise.resolve(a);const l={sessionId:e,showHidden:i,treeEpoch:o,phase:"loading"};s.normalState=l,this.fileBrowserData=null,this._renderFileBrowserNormalState(l);const c={sessionId:e,showHidden:i,treeEpoch:o,promise:null},u=(async()=>{try{const d=await fetch(`/api/sessions/${encodeURIComponent(e)}/files?depth=5&showHidden=${i}`);if(!d.ok)throw new Error("Failed to load files");const h=await d.json(),p=this._validateFileBrowserTreeEnvelope(h);if(!p){const y=h&&typeof h=="object"&&typeof h.error=="string"?h.error:"Failed to load files";throw new Error(y)}if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const m={sessionId:e,showHidden:i,treeEpoch:o,phase:"ready",data:p};s.normalState=m,this.fileBrowserData=p,this._completeDeferredFileBrowserDirectory?.(m)===!0||this._renderFileBrowserNormalState(m)}catch(d){if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const h={sessionId:e,showHidden:i,treeEpoch:o,phase:"error",error:this._normalizeFileBrowserTreeError(d)};s.normalState=h,this.fileBrowserData=null,this._completeDeferredFileBrowserDirectory?.(h),console.error("Failed to load file browser:",d),this._renderFileBrowserNormalState(h)}})();return c.promise=u.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 o=[],r=(a,l)=>{const c=a.type==="directory",u=this.fileBrowserExpandedDirs.has(a.path),d=c?u?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(a.extension),h=c?`<span class="file-tree-expand${u?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',p=!c&&a.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(a.size)}</span>`:"",m=c?"file-tree-name directory":"file-tree-name",g=c?"":`<a class="file-tree-download" href="${escapeHtml(CodemanBase.url(`/api/sessions/${encodeURIComponent(n)}/file-raw?path=${encodeURIComponent(a.path)}&download=true`))}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;if(o.push(`
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"),o=this.$?.("fileBrowserTree"),r=this.$?.("fileBrowserStatus"),a=i?.classList.contains("visible")===!0;o&&(o.innerHTML=a?`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`:""),r&&(r.textContent=a?"Loading files...":""),a&&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:o}=e;s.textContent=`${n} files, ${i} dirs${o?" (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,o=this.fileBrowserShowHidden===!0,r=this.loadFileBrowser(n,{force:!0}),a=e.treeEpoch;if(!r?.then||(await r,!s||s.length>256))return;const l={ownerSessionId:n,showHidden:o,treeEpoch:a,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,o=s.treeEpoch,r=s.treeInFlight;if(!t&&this._isFileBrowserNormalCompatible(r,e,i,o))return r.promise;const a=s.normalState;if(!t&&this._isFileBrowserNormalCompatible(a,e,i,o)&&(a.phase==="ready"||a.phase==="error"))return a.phase==="ready"&&(this.fileBrowserData=a.data),this._renderFileBrowserNormalState(a),Promise.resolve(a);const l={sessionId:e,showHidden:i,treeEpoch:o,phase:"loading"};s.normalState=l,this.fileBrowserData=null,this._renderFileBrowserNormalState(l);const c={sessionId:e,showHidden:i,treeEpoch:o,promise:null},h=(async()=>{try{const d=await fetch(`/api/sessions/${encodeURIComponent(e)}/files?depth=5&showHidden=${i}`);if(!d.ok)throw new Error("Failed to load files");const m=await d.json(),u=this._validateFileBrowserTreeEnvelope(m);if(!u){const v=m&&typeof m=="object"&&typeof m.error=="string"?m.error:"Failed to load files";throw new Error(v)}if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const f={sessionId:e,showHidden:i,treeEpoch:o,phase:"ready",data:u};s.normalState=f,this.fileBrowserData=u,this._completeDeferredFileBrowserDirectory?.(f)===!0||this._renderFileBrowserNormalState(f)}catch(d){if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const m={sessionId:e,showHidden:i,treeEpoch:o,phase:"error",error:this._normalizeFileBrowserTreeError(d)};s.normalState=m,this.fileBrowserData=null,this._completeDeferredFileBrowserDirectory?.(m),console.error("Failed to load file browser:",d),this._renderFileBrowserNormalState(m)}})();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 o=[],r=(a,l)=>{const c=a.type==="directory",h=this.fileBrowserExpandedDirs.has(a.path),d=c?h?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(a.extension),m=c?`<span class="file-tree-expand${h?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',u=!c&&a.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(a.size)}</span>`:"",f=c?"file-tree-name directory":"file-tree-name",p=c?"":`<a class="file-tree-download" href="${escapeHtml(CodemanBase.url(`/api/sessions/${encodeURIComponent(n)}/file-raw?path=${encodeURIComponent(a.path)}&download=true`))}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;if(o.push(`
224
224
  <div class="file-tree-item" data-path="${escapeHtml(a.path)}" data-type="${escapeHtml(a.type)}" data-depth="${l}">
225
- ${h}
225
+ ${m}
226
226
  <span class="file-tree-icon">${d}</span>
227
- <span class="${m}">${escapeHtml(a.name)}</span>
227
+ <span class="${f}">${escapeHtml(a.name)}</span>
228
+ ${u}
228
229
  ${p}
229
- ${g}
230
230
  </div>
231
- `),c&&u&&a.children)for(const y of a.children)r(y,l+1)};for(const a of i)r(a,0);t.innerHTML=o.join(""),t.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const l=a.dataset.path;a.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 u=this.$("fileBrowserTree"),d=this.$("fileBrowserStatus");u&&(u.innerHTML=`<div class="file-browser-empty">${escapeHtml(c)}</div>`),d&&(d.textContent=c);return}const o=this.$("fileBrowserPanel"),r=this.$("fileBrowserTree");if(!i||!o||!r)return;const a={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=a,this._syncFileBrowserExpandBtn(),r.innerHTML=`<div class="file-browser-loading">${escapeHtml("Searching...")}</div>`;const l=this.$("fileBrowserStatus");l&&(l.textContent="Searching..."),a.timer=setTimeout(async()=>{a.timer=null;try{const c=await fetch(`/api/sessions/${encodeURIComponent(i)}/files?depth=5&showHidden=${a.showHidden}&q=${encodeURIComponent(n)}`);if(!c.ok)throw new Error("Search failed");const u=await c.json(),d=this._validateFileBrowserSearchEnvelope(u);if(!d)throw new Error("Search failed");const h=this._canRenderFileBrowserSearch(a);if(t.inFlight===a&&(t.inFlight=null),!h)return;t.view="search-results",t.matches=d.matches,this._renderFileBrowserSearchResults(d.matches,i,d)}catch(c){const u=this._canRenderFileBrowserSearch(a);if(t.inFlight===a&&(t.inFlight=null),!u)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(),o={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 a=encodeURIComponent(t);n.innerHTML=e.map(l=>{const c=l.type==="directory",u=c?"\u{1F4C1}":this.getFileIcon(l.extension||""),d=!c&&l.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(l.size)}</span>`:"",h=c?"file-tree-name directory":"file-tree-name",p=c?"":`<a class="file-tree-download" href="${escapeHtml(CodemanBase.url(`/api/sessions/${a}/file-raw?path=${encodeURIComponent(l.path)}&download=true`))}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;return`
231
+ `),c&&h&&a.children)for(const v of a.children)r(v,l+1)};for(const a of i)r(a,0);t.innerHTML=o.join(""),t.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const l=a.dataset.path;a.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"),d=this.$("fileBrowserStatus");h&&(h.innerHTML=`<div class="file-browser-empty">${escapeHtml(c)}</div>`),d&&(d.textContent=c);return}const o=this.$("fileBrowserPanel"),r=this.$("fileBrowserTree");if(!i||!o||!r)return;const a={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=a,this._syncFileBrowserExpandBtn(),r.innerHTML=`<div class="file-browser-loading">${escapeHtml("Searching...")}</div>`;const l=this.$("fileBrowserStatus");l&&(l.textContent="Searching..."),a.timer=setTimeout(async()=>{a.timer=null;try{const c=await fetch(`/api/sessions/${encodeURIComponent(i)}/files?depth=5&showHidden=${a.showHidden}&q=${encodeURIComponent(n)}`);if(!c.ok)throw new Error("Search failed");const h=await c.json(),d=this._validateFileBrowserSearchEnvelope(h);if(!d)throw new Error("Search failed");const m=this._canRenderFileBrowserSearch(a);if(t.inFlight===a&&(t.inFlight=null),!m)return;t.view="search-results",t.matches=d.matches,this._renderFileBrowserSearchResults(d.matches,i,d)}catch(c){const h=this._canRenderFileBrowserSearch(a);if(t.inFlight===a&&(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(),o={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 a=encodeURIComponent(t);n.innerHTML=e.map(l=>{const c=l.type==="directory",h=c?"\u{1F4C1}":this.getFileIcon(l.extension||""),d=!c&&l.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(l.size)}</span>`:"",m=c?"file-tree-name directory":"file-tree-name",u=c?"":`<a class="file-tree-download" href="${escapeHtml(CodemanBase.url(`/api/sessions/${a}/file-raw?path=${encodeURIComponent(l.path)}&download=true`))}" title="Download" onclick="event.stopPropagation()">&#x2B07;</a>`;return`
232
232
  <div class="file-tree-item" data-path="${escapeHtml(l.path)}" data-type="${escapeHtml(l.type)}" data-owner="${escapeHtml(t)}">
233
233
  <span class="file-tree-expand"></span>
234
- <span class="file-tree-icon">${u}</span>
235
- <span class="${h}">${escapeHtml(l.name)}</span>
234
+ <span class="file-tree-icon">${h}</span>
235
+ <span class="${m}">${escapeHtml(l.name)}</span>
236
236
  ${d}
237
- ${p}
237
+ ${u}
238
238
  </div>
239
- `}).join("")}n.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const l=a.dataset.path;a.dataset.type==="directory"?this._openFileBrowserSearchDirectory({...o,path:l}):this.openFilePreview(l,t)})});const r=this.$("fileBrowserStatus");if(r){const a=s.matchCount===void 0?e.length:s.matchCount;r.textContent=`${a} ${a===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"),o=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!o)return;this._resetFilePreviewEdit(),this._stopFilePreviewMedia(),this.filePreviewDetachUrl="";const a=this.$("filePreviewDetachBtn");a&&(a.hidden=!0),n.classList.add("visible"),i.textContent=e,o.innerHTML='<div class="binary-message">Loading...</div>',r.textContent="";const l=(e.split(".").pop()||"").toLowerCase();let c="",u=0;if(!s&&this._isExternalPreviewPath(e,t)){const h=await this._registerExternalPreview(e,t);s=h.attachmentId||null,c=h.error||"",u=h.size||0}if(!s&&c){r.textContent="",o.innerHTML=`<div class="binary-message">${escapeHtml(c)}</div>`;return}const d=l==="docx"||l==="pptx";if(this.filePreviewDetachUrl=CodemanBase.url(s?`/api/sessions/${t}/attachments/${encodeURIComponent(s)}/${d?"preview":"raw"}`:d?`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`),a&&(a.hidden=!1),s){const h=CodemanBase.url(`/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=u?`${this.formatFileSize(u)} \u2022 ${l}`:l.toUpperCase(),p.has(l))o.innerHTML=`<img src="${escapeHtml(`${h}/raw`)}" alt="${escapeHtml(e)}">`;else if(m.has(l))o.innerHTML=`<video src="${escapeHtml(`${h}/raw`)}" controls autoplay playsinline preload="metadata"></video>`;else if(g.has(l))o.innerHTML=`<audio src="${escapeHtml(`${h}/raw`)}" controls autoplay preload="metadata"></audio>`;else if(l==="pdf")o.innerHTML=`<iframe src="${escapeHtml(`${h}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(l==="docx"||l==="pptx")o.innerHTML=`<iframe src="${escapeHtml(`${h}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const y=await fetch(`${h}/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(`
240
- `),f=$.length>500,S=f?$.slice(0,500).join(`
241
- `):w;if(o.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){o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(y.message)}</div>`}return}if(l==="docx"||l==="pptx"){r.textContent=l.toUpperCase();const h=CodemanBase.url(`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`);o.innerHTML=`<iframe src="${escapeHtml(h)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="pdf"){r.textContent="PDF";const h=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);o.innerHTML=`<iframe src="${escapeHtml(h)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="svg"){r.textContent="SVG";try{const h=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!h.ok)throw new Error("Failed to load image");const p=URL.createObjectURL(new Blob([await h.text()],{type:"image/svg+xml"}));o.innerHTML=`<img src="${p}" alt="${escapeHtml(e)}">`;const m=o.querySelector("img");m&&(m.onload=()=>URL.revokeObjectURL(p))}catch(h){o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(h.message)}</div>`}return}try{const h=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!h.ok)throw new Error("Failed to load file");const p=await h.json();if(!p.success)throw new Error(p.error||"Failed to load file");const m=p.data;if(m.type==="image")o.innerHTML=`<img src="${escapeHtml(CodemanBase.url(m.url))}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="video")o.innerHTML=`<video src="${escapeHtml(CodemanBase.url(m.url))}" controls autoplay playsinline preload="metadata"></video>`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="audio")o.innerHTML=`<audio src="${escapeHtml(CodemanBase.url(m.url))}" controls autoplay preload="metadata"></audio>`,r.textContent=`${this.formatFileSize(m.size)} \u2022 ${m.extension}`;else if(m.type==="binary"){const g=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`);o.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,o.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(h){console.error("Failed to preview file:",h),o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(h.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 a=await fetch(`/api/sessions/${e.sessionId}/file-content?path=${encodeURIComponent(e.filePath)}&edit=1`),l=await a.json().catch(()=>null);if(!l||l.success!==!0)throw new Error(l?.error||`Failed to load file for editing (HTTP ${a.status})`);n=l.data}catch(a){this.showToast(a.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 o=this.$("filePreviewEditBtn");o&&(o.hidden=!0);const r=this.$("filePreviewEditBar");if(r&&(r.hidden=!1),s){const a=n.eol==="crlf"?" \u2022 CRLF":"";s.textContent=`Editing \u2022 ${n.totalLines} lines \u2022 ${this.formatFileSize(n.size)}${a}`}},_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 o=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,o=await c.json().catch(()=>null)}catch(c){i(),this.showToast(`Save failed: ${c.message}`,"error");return}if(r===409||o?.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(!o||o.success!==!0){i(),this.showToast(`Save failed: ${o?.error||`HTTP ${r}`}`,"error");return}const{sessionId:a,filePath:l}=t;this._resetFilePreviewEdit(),this.showToast("Saved","success"),this.openFilePreview(l,a)},_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=CodemanBase.url(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:o,attachmentType:r,extension:a,attachmentId:l,rawUrl:c,previewUrl:u,thumbnailUrl:d}=e,h=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 I=this.attachmentCards.keys().next().value;I&&this.closeAttachmentCard(I)}const g=this.ensureAttachmentCardStack(),w=this.sessions.get(t)?.name||t.substring(0,8),b=CodemanBase.url(c||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/raw`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(h)}`)),$=CodemanBase.url(u||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/preview`:null)),f=CodemanBase.url(d||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/thumbnail`:`/api/sessions/${t}/file-thumbnail?path=${encodeURIComponent(h)}`)),S=l?`${b}?download=true`:`${b}&download=true`,T=(a||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=`
239
+ `}).join("")}n.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const l=a.dataset.path;a.dataset.type==="directory"?this._openFileBrowserSearchDirectory({...o,path:l}):this.openFilePreview(l,t)})});const r=this.$("fileBrowserStatus");if(r){const a=s.matchCount===void 0?e.length:s.matchCount;r.textContent=`${a} ${a===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"),o=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!o)return;this._resetFilePreviewEdit(),this._stopFilePreviewMedia(),this.filePreviewDetachUrl="";const a=this.$("filePreviewDetachBtn");a&&(a.hidden=!0),this.filePreviewText=null,this._updateFilePreviewToolbar("none"),n.classList.add("visible"),i.textContent=e,o.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 u=await this._registerExternalPreview(e,t);s=u.attachmentId||null,c=u.error||"",h=u.size||0}if(!s&&c){r.textContent="",o.innerHTML=`<div class="binary-message">${escapeHtml(c)}</div>`;return}const d=l==="docx"||l==="pptx";if(this.filePreviewDetachUrl=CodemanBase.url(s?`/api/sessions/${t}/attachments/${encodeURIComponent(s)}/${d?"preview":"raw"}`:d?`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`),a&&(a.hidden=!1),s){const u=CodemanBase.url(`/api/sessions/${t}/attachments/${encodeURIComponent(s)}`),f=new Set(["png","jpg","jpeg","gif","webp","bmp","svg"]),p=new Set(["mp4","webm","mov","m4v","ogv"]),v=new Set(["mp3","wav","ogg","oga","m4a","aac","flac","opus"]);if(r.textContent=h?`${this.formatFileSize(h)} \u2022 ${l}`:l.toUpperCase(),f.has(l))o.innerHTML=`<img src="${escapeHtml(`${u}/raw`)}" alt="${escapeHtml(e)}">`;else if(p.has(l))o.innerHTML=`<video src="${escapeHtml(`${u}/raw`)}" controls autoplay playsinline preload="metadata"></video>`;else if(v.has(l))o.innerHTML=`<audio src="${escapeHtml(`${u}/raw`)}" controls autoplay preload="metadata"></audio>`;else if(l==="pdf")o.innerHTML=`<iframe src="${escapeHtml(`${u}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(l==="docx"||l==="pptx")o.innerHTML=`<iframe src="${escapeHtml(`${u}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const g=await fetch(`${u}/raw`,{headers:{Range:"bytes=0-524287"}});if(!g.ok)throw new Error("Failed to load attachment");const b=await g.text(),$=g.status===206&&b.length>=524288,w=b.split(`
240
+ `),S=MARKDOWN_EXTS.has(l)?1/0:500,T=w.length>S,y=T?w.slice(0,S).join(`
241
+ `):b;if(this.filePreviewContent=y,this.filePreviewText={ext:l,sessionId:t,filePath:e,attachmentId:s},this._renderFilePreviewText(),T||$){const C=T?`showing first ${S} lines`:"showing the start of the file";r.textContent=`${r.textContent} (${C})`}}catch(g){o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(g.message)}</div>`}return}if(l==="docx"||l==="pptx"){r.textContent=l.toUpperCase();const u=CodemanBase.url(`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`);o.innerHTML=`<iframe src="${escapeHtml(u)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="pdf"){r.textContent="PDF";const u=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);o.innerHTML=`<iframe src="${escapeHtml(u)}" title="${escapeHtml(e)}"></iframe>`;return}if(l==="svg"){r.textContent="SVG";try{const u=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!u.ok)throw new Error("Failed to load image");const f=URL.createObjectURL(new Blob([await u.text()],{type:"image/svg+xml"}));o.innerHTML=`<img src="${f}" alt="${escapeHtml(e)}">`;const p=o.querySelector("img");p&&(p.onload=()=>URL.revokeObjectURL(f))}catch(u){o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(u.message)}</div>`}return}const m=MARKDOWN_EXTS.has(l)?1e4:500;try{const u=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=${m}`);if(!u.ok)throw new Error("Failed to load file");const f=await u.json();if(!f.success)throw new Error(f.error||"Failed to load file");const p=f.data;if(p.type==="image")o.innerHTML=`<img src="${escapeHtml(CodemanBase.url(p.url))}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(p.size)} \u2022 ${p.extension}`;else if(p.type==="video")o.innerHTML=`<video src="${escapeHtml(CodemanBase.url(p.url))}" controls autoplay playsinline preload="metadata"></video>`,r.textContent=`${this.formatFileSize(p.size)} \u2022 ${p.extension}`;else if(p.type==="audio")o.innerHTML=`<audio src="${escapeHtml(CodemanBase.url(p.url))}" controls autoplay preload="metadata"></audio>`,r.textContent=`${this.formatFileSize(p.size)} \u2022 ${p.extension}`;else if(p.type==="binary"){const v=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`);o.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(p.size)})<br>Cannot preview<br><a href="${escapeHtml(v)}" download>Download</a></div>`,r.textContent=p.extension||"binary"}else{this.filePreviewContent=p.content,this.filePreviewText={ext:l,sessionId:t,filePath:e},this._renderFilePreviewText();const v=p.truncated?` (showing ${m}/${p.totalLines} lines)`:"";if(r.textContent=`${p.totalLines} lines \u2022 ${this.formatFileSize(p.size)}${v}`,p.editable){this.filePreviewEditTarget={sessionId:t,filePath:e};const g=this.$("filePreviewEditBtn");g&&(g.hidden=!1)}}}catch(u){console.error("Failed to preview file:",u),o.innerHTML=`<div class="binary-message">Error: ${escapeHtml(u.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.filePreviewText=null,this._updateFilePreviewToolbar("none"),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=""}},_filePreviewPref(e){try{const t=localStorage.getItem(FILE_PREVIEW_PREF_KEYS[e]);if(t==="1")return!0;if(t==="0")return!1}catch{}return FILE_PREVIEW_PREF_DEFAULTS[e]},_setFilePreviewPref(e,t){try{localStorage.setItem(FILE_PREVIEW_PREF_KEYS[e],t?"1":"0")}catch{}},_renderFilePreviewText(){const e=this.filePreviewText,t=this.$("filePreviewBody");if(!e||!t)return;const n=MARKDOWN_EXTS.has(e.ext)&&this._filePreviewPref("mdRendered");if(n){const i=document.createElement("template");i.innerHTML=`<div class="rv-text file-preview-md" data-i18n-skip>${this._renderMarkdown(this.filePreviewContent,{breaks:!1})}</div>`;const o=i.content.firstElementChild;this._rebaseFilePreviewMarkdownRefs(o,e),this._linkifyFilePaths(o);for(const r of o.querySelectorAll("a.rv-path:not([data-session-id])"))r.dataset.sessionId=e.sessionId;t.replaceChildren(i.content),this._bindResponseViewerInteractions(t)}else{const i=document.createElement("pre");i.className="file-preview-text",i.classList.toggle("wrap",this._filePreviewPref("wrap")),i.classList.toggle("show-lines",this._filePreviewPref("lineNumbers"));const o=document.createElement("code");o.innerHTML=this.filePreviewContent.split(`
242
+ `).map(r=>`<span class="fp-line">${escapeHtml(r)}</span>`).join(`
243
+ `),i.appendChild(o),t.replaceChildren(i)}this._updateFilePreviewToolbar(n?"markdown":"text")},_rebaseFilePreviewMarkdownRefs(e,{sessionId:t,filePath:s,attachmentId:n}){const i=s.includes("/")?s.slice(0,s.lastIndexOf("/")+1):"",o=!!n&&!s.startsWith("/"),r=l=>!!l&&!/^[a-z][a-z0-9+.-]*:/i.test(l)&&!l.startsWith("//")&&!l.startsWith("#"),a=l=>{let c=l.split("#")[0].split("?")[0];try{c=decodeURIComponent(c)}catch{}const h=[];for(const d of(c.startsWith("/")?c.slice(1):i+c).split("/"))d==="."||d===""&&h.length||(d===".."&&h.length&&h[h.length-1]!==".."&&h[h.length-1]!==""?h.pop():h.push(d));return h.join("/")};for(const l of e.querySelectorAll("img[src]")){const c=l.getAttribute("src")||"";if(o&&r(c)){l.replaceWith(l.getAttribute("alt")||c);continue}if(r(c)){const h=a(c);l.setAttribute("src",CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(h)}`))}l.addEventListener("error",()=>l.replaceWith(l.getAttribute("alt")||c),{once:!0})}for(const l of e.querySelectorAll("a[href]")){const c=l.getAttribute("href")||"";if(r(c)){if(o){l.replaceWith(...l.childNodes);continue}l.className="rv-path",l.dataset.path=a(c),l.dataset.sessionId=t,l.setAttribute("href","#"),l.removeAttribute("target"),l.removeAttribute("rel")}}},_updateFilePreviewToolbar(e){const t=(n,i,o)=>{const r=this.$(n);r&&(r.hidden=!i,i&&r.setAttribute("aria-pressed",String(o)))},s=e!=="none"&&MARKDOWN_EXTS.has(this.filePreviewText?.ext||"");t("filePreviewMdBtn",s,e==="markdown"),t("filePreviewLinesBtn",e==="text",this._filePreviewPref("lineNumbers")),t("filePreviewWrapBtn",e==="text",this._filePreviewPref("wrap"))},toggleFilePreviewMd(){this._setFilePreviewPref("mdRendered",!this._filePreviewPref("mdRendered")),this._renderFilePreviewText()},toggleFilePreviewLines(){this._toggleFilePreviewTextClass("lineNumbers","show-lines")},toggleFilePreviewWrap(){this._toggleFilePreviewTextClass("wrap","wrap")},_toggleFilePreviewTextClass(e,t){const s=!this._filePreviewPref(e);this._setFilePreviewPref(e,s);const n=this.$("filePreviewBody")?.querySelector(":scope > pre.file-preview-text");n&&n.classList.toggle(t,s),this._updateFilePreviewToolbar("text")},_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 a=await fetch(`/api/sessions/${e.sessionId}/file-content?path=${encodeURIComponent(e.filePath)}&edit=1`),l=await a.json().catch(()=>null);if(!l||l.success!==!0)throw new Error(l?.error||`Failed to load file for editing (HTTP ${a.status})`);n=l.data}catch(a){this.showToast(a.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),this._updateFilePreviewToolbar("none");const o=this.$("filePreviewEditBtn");o&&(o.hidden=!0);const r=this.$("filePreviewEditBar");if(r&&(r.hidden=!1),s){const a=n.eol==="crlf"?" \u2022 CRLF":"";s.textContent=`Editing \u2022 ${n.totalLines} lines \u2022 ${this.formatFileSize(n.size)}${a}`}},_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 o=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,o=await c.json().catch(()=>null)}catch(c){i(),this.showToast(`Save failed: ${c.message}`,"error");return}if(r===409||o?.errorCode==="CONFLICT"){i(),confirm(`File changed on disk since you loaded it.
244
+ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFilePreviewEdit(!0);return}if(!o||o.success!==!0){i(),this.showToast(`Save failed: ${o?.error||`HTTP ${r}`}`,"error");return}const{sessionId:a,filePath:l}=t;this._resetFilePreviewEdit(),this.showToast("Saved","success"),this.openFilePreview(l,a)},_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=CodemanBase.url(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:o,attachmentType:r,extension:a,attachmentId:l,rawUrl:c,previewUrl:h,thumbnailUrl:d}=e,m=s||n,u=l||`${t}-${i}-${n}`;if(this.attachmentCards.has(u)){this.attachmentCards.get(u).element.focus?.();return}if(this.attachmentCards.size>=10){const I=this.attachmentCards.keys().next().value;I&&this.closeAttachmentCard(I)}const p=this.ensureAttachmentCardStack(),g=this.sessions.get(t)?.name||t.substring(0,8),b=CodemanBase.url(c||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/raw`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(m)}`)),$=CodemanBase.url(h||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/preview`:null)),w=CodemanBase.url(d||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/thumbnail`:`/api/sessions/${t}/file-thumbnail?path=${encodeURIComponent(m)}`)),S=l?`${b}?download=true`:`${b}&download=true`,T=(a||r||"file").toUpperCase(),y=document.createElement("article");y.className=`attachment-card attachment-${escapeHtml(r||"file")}`,y.tabIndex=0,y.dataset.attachmentId=u,y.dataset.previewUrl=$||"",y.innerHTML=`
243
245
  <div class="attachment-thumbnail">
244
- ${f?`<img class="attachment-thumbnail-img" src="${escapeHtml(f)}" alt="">`:""}
245
- <div class="attachment-thumbnail-fallback ${f?"":"visible"}">${escapeHtml(T)}</div>
246
+ ${w?`<img class="attachment-thumbnail-img" src="${escapeHtml(w)}" alt="">`:""}
247
+ <div class="attachment-thumbnail-fallback ${w?"":"visible"}">${escapeHtml(T)}</div>
246
248
  </div>
247
249
  <div class="attachment-card-main">
248
- <div class="attachment-file-name" title="${escapeHtml(h)}">${escapeHtml(n)}</div>
250
+ <div class="attachment-file-name" title="${escapeHtml(m)}">${escapeHtml(n)}</div>
249
251
  <div class="attachment-file-meta">
250
- <span>${escapeHtml(w)}</span>
252
+ <span>${escapeHtml(g)}</span>
251
253
  <span>${this.formatFileSize(o||0)}</span>
252
254
  </div>
253
255
  <div class="attachment-actions">
@@ -257,7 +259,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
257
259
  </div>
258
260
  </div>
259
261
  <button type="button" class="attachment-close-btn" title="Dismiss">&times;</button>
260
- `;const C=v.querySelector(".attachment-thumbnail-img");C&&(C.onerror=()=>{C.remove(),v.querySelector(".attachment-thumbnail-fallback")?.classList.add("visible")}),v.querySelector(".attachment-preview-btn")?.addEventListener("click",()=>{this.openFilePreview(h,t,l||null)}),v.querySelector(".attachment-open-btn")?.addEventListener("click",()=>{this.openAttachmentInNewTab(t,h,l||null)}),v.querySelector(".attachment-close-btn")?.addEventListener("click",()=>{this.closeAttachmentCard(p)}),g.prepend(v),this.attachmentCards.set(p,{element:v,sessionId:t,filePath:h}),this._refreshAttachmentClearAll()},_refreshAttachmentClearAll(){const e=this.attachmentCardStack;if(!e)return;let t=e.querySelector(".attachment-clear-all");if(this.attachmentCards.size<2){t&&(t.hidden=!0);return}t||(t=document.createElement("button"),t.type="button",t.className="attachment-clear-all",t.textContent="Clear all",t.title="Dismiss all attachment cards",t.addEventListener("click",()=>this.closeAllAttachmentCards()),e.prepend(t)),t.hidden=!1},closeAttachmentCard(e){const t=this.attachmentCards.get(e);t&&(t.element.remove(),this.attachmentCards.delete(e),this.attachmentCardStack&&this.attachmentCards.size===0?(this.attachmentCardStack.remove(),this.attachmentCardStack=null):this._refreshAttachmentClearAll())},closeAllAttachmentCards(){for(const e of[...this.attachmentCards.keys()])this.closeAttachmentCard(e)},closeSessionAttachmentCards(e){const t=[];for(const[s,n]of this.attachmentCards)n.sessionId===e&&t.push(s);for(const s of t)this.closeAttachmentCard(s)},updateAttachmentHistoryBadge(e=null){const t=document.getElementById("attachmentHistoryBadge"),s=document.getElementById("attachmentsHistoryBtn"),n=this.activeSessionId,i=e??(n&&this.attachmentHistoryCounts.get(n)||0);t&&(t.textContent=i>99?"99+":String(i),t.style.display=i>0?"":"none"),s&&(s.classList.toggle("active",this.attachmentHistoryDrawerOpen),s.setAttribute("aria-expanded",this.attachmentHistoryDrawerOpen?"true":"false"))},ensureAttachmentHistoryDrawer(){let e=document.getElementById("attachmentHistoryDrawer");return e||(e=document.createElement("aside"),e.id="attachmentHistoryDrawer",e.className="attachment-history-drawer",e.setAttribute("aria-label","Attachment history"),e.innerHTML=`
262
+ `;const C=y.querySelector(".attachment-thumbnail-img");C&&(C.onerror=()=>{C.remove(),y.querySelector(".attachment-thumbnail-fallback")?.classList.add("visible")}),y.querySelector(".attachment-preview-btn")?.addEventListener("click",()=>{this.openFilePreview(m,t,l||null)}),y.querySelector(".attachment-open-btn")?.addEventListener("click",()=>{this.openAttachmentInNewTab(t,m,l||null)}),y.querySelector(".attachment-close-btn")?.addEventListener("click",()=>{this.closeAttachmentCard(u)}),p.prepend(y),this.attachmentCards.set(u,{element:y,sessionId:t,filePath:m}),this._refreshAttachmentClearAll()},_refreshAttachmentClearAll(){const e=this.attachmentCardStack;if(!e)return;let t=e.querySelector(".attachment-clear-all");if(this.attachmentCards.size<2){t&&(t.hidden=!0);return}t||(t=document.createElement("button"),t.type="button",t.className="attachment-clear-all",t.textContent="Clear all",t.title="Dismiss all attachment cards",t.addEventListener("click",()=>this.closeAllAttachmentCards()),e.prepend(t)),t.hidden=!1},closeAttachmentCard(e){const t=this.attachmentCards.get(e);t&&(t.element.remove(),this.attachmentCards.delete(e),this.attachmentCardStack&&this.attachmentCards.size===0?(this.attachmentCardStack.remove(),this.attachmentCardStack=null):this._refreshAttachmentClearAll())},closeAllAttachmentCards(){for(const e of[...this.attachmentCards.keys()])this.closeAttachmentCard(e)},closeSessionAttachmentCards(e){const t=[];for(const[s,n]of this.attachmentCards)n.sessionId===e&&t.push(s);for(const s of t)this.closeAttachmentCard(s)},updateAttachmentHistoryBadge(e=null){const t=document.getElementById("attachmentHistoryBadge"),s=document.getElementById("attachmentsHistoryBtn"),n=this.activeSessionId,i=e??(n&&this.attachmentHistoryCounts.get(n)||0);t&&(t.textContent=i>99?"99+":String(i),t.style.display=i>0?"":"none"),s&&(s.classList.toggle("active",this.attachmentHistoryDrawerOpen),s.setAttribute("aria-expanded",this.attachmentHistoryDrawerOpen?"true":"false"))},ensureAttachmentHistoryDrawer(){let e=document.getElementById("attachmentHistoryDrawer");return e||(e=document.createElement("aside"),e.id="attachmentHistoryDrawer",e.className="attachment-history-drawer",e.setAttribute("aria-label","Attachment history"),e.innerHTML=`
261
263
  <div class="attachment-history-header">
262
264
  <div>
263
265
  <div class="attachment-history-title">Attachments</div>
@@ -293,7 +295,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
293
295
  </div>
294
296
  </div>
295
297
  </div>
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(CodemanBase.url(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(CodemanBase.url(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 u=this.logViewerWindows.get(s);u.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,i=100+n%5*30,o=100+n%5*30,r=e.split("/").pop(),a=document.createElement("div");a.className="log-viewer-window",a.id=`log-viewer-window-${s}`,a.style.left=`${i}px`,a.style.top=`${o}px`,a.style.zIndex=++this.logViewerWindowZIndex,a.innerHTML=`
298
+ `},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(CodemanBase.url(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(CodemanBase.url(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,o=100+n%5*30,r=e.split("/").pop(),a=document.createElement("div");a.className="log-viewer-window",a.id=`log-viewer-window-${s}`,a.style.left=`${i}px`,a.style.top=`${o}px`,a.style.zIndex=++this.logViewerWindowZIndex,a.innerHTML=`
297
299
  <div class="log-viewer-window-header">
298
300
  <div class="log-viewer-window-title" title="${escapeHtml(e)}">
299
301
  <span class="icon">\u{1F4C4}</span>
@@ -307,33 +309,33 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
307
309
  <div class="log-viewer-window-body" id="log-viewer-body-${s}">
308
310
  <div class="log-info">Connecting to ${escapeHtml(e)}...</div>
309
311
  </div>
310
- `,document.body.appendChild(a);const l=this.makeWindowDraggable(a,a.querySelector(".log-viewer-window-header")),c=new EventSource(CodemanBase.url(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`));c.onmessage=u=>{const d=JSON.parse(u.data),h=document.getElementById(`log-viewer-body-${s}`);if(h)switch(d.type){case"connected":h.innerHTML="";break;case"data":const p=h.scrollTop+h.clientHeight>=h.scrollHeight-10,m=escapeHtml(d.content);h.innerHTML+=m,p&&(h.scrollTop=h.scrollHeight),h.innerHTML.length>5e5&&(h.innerHTML=h.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":h.innerHTML+=`<div class="log-error">${escapeHtml(d.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:a,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:o,size:r}=e,a=`${t}-${o}`;if(this.imagePopups.has(a)){const f=this.imagePopups.get(a);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,u=(window.innerWidth-600)/2,d=(window.innerHeight-500)/2,h=u+c%5*30,p=d+c%5*30,g=this.sessions.get(t)?.name||t.substring(0,8),y=(r/1024).toFixed(1),w=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||i)}`),b=document.createElement("div");b.className="image-popup-window",b.id=`image-popup-${a}`,b.style.left=`${h}px`,b.style.top=`${p}px`,b.style.zIndex=++this.imagePopupZIndex,b.innerHTML=`
312
+ `,document.body.appendChild(a);const l=this.makeWindowDraggable(a,a.querySelector(".log-viewer-window-header")),c=new EventSource(CodemanBase.url(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`));c.onmessage=h=>{const d=JSON.parse(h.data),m=document.getElementById(`log-viewer-body-${s}`);if(m)switch(d.type){case"connected":m.innerHTML="";break;case"data":const u=m.scrollTop+m.clientHeight>=m.scrollHeight-10,f=escapeHtml(d.content);m.innerHTML+=f,u&&(m.scrollTop=m.scrollHeight),m.innerHTML.length>5e5&&(m.innerHTML=m.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":m.innerHTML+=`<div class="log-error">${escapeHtml(d.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:a,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:o,size:r}=e,a=`${t}-${o}`;if(this.imagePopups.has(a)){const w=this.imagePopups.get(a);w.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const w=this.imagePopups.keys().next().value;w&&this.closeImagePopup(w)}const c=this.imagePopups.size,h=(window.innerWidth-600)/2,d=(window.innerHeight-500)/2,m=h+c%5*30,u=d+c%5*30,p=this.sessions.get(t)?.name||t.substring(0,8),v=(r/1024).toFixed(1),g=CodemanBase.url(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||i)}`),b=document.createElement("div");b.className="image-popup-window",b.id=`image-popup-${a}`,b.style.left=`${m}px`,b.style.top=`${u}px`,b.style.zIndex=++this.imagePopupZIndex,b.innerHTML=`
311
313
  <div class="image-popup-header">
312
314
  <div class="image-popup-title" title="${escapeHtml(s)}">
313
315
  <span class="icon">\u{1F5BC}\uFE0F</span>
314
316
  <span class="filename">${escapeHtml(i)}</span>
315
- <span class="session-badge">${escapeHtml(g)}</span>
316
- <span class="size-badge">${y} KB</span>
317
+ <span class="session-badge">${escapeHtml(p)}</span>
318
+ <span class="size-badge">${v} KB</span>
317
319
  </div>
318
320
  <div class="image-popup-actions">
319
- <button onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(w))})" title="Open in new tab">\u2197</button>
321
+ <button onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(g))})" title="Open in new tab">\u2197</button>
320
322
  <button onclick="app.closeImagePopup(${escapeHtml(JSON.stringify(a))})" title="Close">\xD7</button>
321
323
  </div>
322
324
  </div>
323
325
  <div class="image-popup-body">
324
- <img src="${w}" alt="${escapeHtml(i)}"
326
+ <img src="${g}" alt="${escapeHtml(i)}"
325
327
  onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
326
- onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(w))})" />
328
+ onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(g))})" />
327
329
  </div>
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(a,{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._resetTerminalForReplay(),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._resetTerminalForReplay(),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),o=i?i.status:"unknown",r=i?i.isWorking:!1;let a,l;o==="idle"&&!r?(a="IDLE",l="status-idle"):o==="busy"||r?(a="WORKING",l="status-working"):o==="stopped"?(a="STOPPED",l="status-stopped"):(a=o.toUpperCase(),l="");const c=i&&i.tokens?i.tokens:null,u=i?i.totalCost:0,d=i&&i.cliModel||"",h=d.includes("opus")?"opus":d.includes("sonnet")?"sonnet":d.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="";u>0&&(y=`<span class="process-stat cost">$${u.toFixed(2)}</span>`);let w="";h&&(w=`<span class="monitor-model-badge ${h}">${h}</span>`);const b=escapeHtml(JSON.stringify(s.sessionId));t+=`
330
+ `,document.body.appendChild(b);const $=this.makeWindowDraggable(b,b.querySelector(".image-popup-header"));b.addEventListener("mousedown",()=>{b.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(a,{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._resetTerminalForReplay(),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._resetTerminalForReplay(),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),o=i?i.status:"unknown",r=i?i.isWorking:!1;let a,l;o==="idle"&&!r?(a="IDLE",l="status-idle"):o==="busy"||r?(a="WORKING",l="status-working"):o==="stopped"?(a="STOPPED",l="status-stopped"):(a=o.toUpperCase(),l="");const c=i&&i.tokens?i.tokens:null,h=i?i.totalCost:0,d=i&&i.cliModel||"",m=d.includes("opus")?"opus":d.includes("sonnet")?"sonnet":d.includes("haiku")?"haiku":"",u=i?i.ralphTodoStats:null;let f="";if(u&&u.total>0){const $=Math.round(u.completed/u.total*100);f=`<span class="process-stat todo-progress">${u.completed}/${u.total} (${$}%)</span>`}let p="";c&&c.total>0&&(p=`<span class="process-stat tokens">${(c.total/1e3).toFixed(1)}k tok</span>`);let v="";h>0&&(v=`<span class="process-stat cost">$${h.toFixed(2)}</span>`);let g="";m&&(g=`<span class="monitor-model-badge ${m}">${m}</span>`);const b=escapeHtml(JSON.stringify(s.sessionId));t+=`
329
331
  <div class="process-item process-item-clickable" onclick="app.selectSession(${b})" title="Switch to session">
330
332
  <span class="monitor-status-badge ${l}">${a}</span>
331
333
  <div class="process-info">
332
- <div class="process-name">${w} ${escapeHtml(s.name||s.muxName)}</div>
334
+ <div class="process-name">${g} ${escapeHtml(s.name||s.muxName)}</div>
333
335
  <div class="process-meta">
334
- ${g}
335
- ${y}
336
- ${m}
336
+ ${p}
337
+ ${v}
338
+ ${f}
337
339
  <span class="process-stat memory">${n.memoryMB}MB</span>
338
340
  <span class="process-stat cpu">${n.cpuPercent}%</span>
339
341
  </div>
@@ -356,4 +358,4 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
356
358
  ${o.status!=="completed"?`<button class="btn-toolbar btn-sm btn-danger" onclick="app.killSubagent(${escapeHtml(JSON.stringify(o.agentId))})" title="Kill agent">Kill</button>`:""}
357
359
  </div>
358
360
  </div>
359
- `}e.innerHTML=i},async killMuxSession(e){if(confirm("Kill this mux session?")){try{await this.closeSession(e,!0)}catch{try{await fetch(`/api/mux-sessions/${e}`,{method:"DELETE"})}catch{}this.showToast("Tmux session killed","success")}this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e),this.renderMuxSessions()}},async reconcileMuxSessions(){try{const t=await(await fetch("/api/mux-sessions/reconcile",{method:"POST"})).json();t.data?.dead&&t.data.dead.length>0?(this.showToast(`Found ${t.data.dead.length} dead mux session(s)`,"warning"),await this.loadMuxSessions()):this.showToast("All mux sessions are alive","success")}catch{this.showToast("Failed to reconcile mux sessions","error")}},toggleNotifications(){this.notificationManager?.toggleDrawer()},async launchMultiMonitor(){try{const e=await fetch("/api/system/span-displays",{method:"POST"}),t=await e.json().catch(()=>({}));e.ok&&t.success?this.showToast("Opening Codeman across all displays\u2026","success"):this.showToast(t.error||"Could not open spanning window","error")}catch(e){this.showToast("Could not open spanning window: "+(e?.message||e),"error")}},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=3e3,action:i}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.className="toast-message",r.textContent=e,o.appendChild(r),i){const u=document.createElement("button");u.textContent=i.label,u.style.cssText="margin-left:12px;padding:2px 10px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:3px;color:inherit;cursor:pointer;font-size:12px",u.onclick=d=>{d.stopPropagation(),i.onClick(),o.remove()},o.appendChild(u)}let a=null;const l=()=>{a&&clearTimeout(a),o.classList.remove("show"),setTimeout(()=>o.remove(),200)},c=document.createElement("button");return c.className="toast-close",c.textContent="\xD7",c.setAttribute("aria-label","Dismiss"),c.onclick=u=>{u.stopPropagation(),l()},o.appendChild(c),this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),n>0&&(a=setTimeout(l,n)),{dismiss:l,setMessage:u=>{r.textContent=u}}},_showCenterStatus(e,t={}){const{type:s="info",onCancel:n}=t;let i=document.getElementById("customModelCenterStatus");i||(i=document.createElement("div"),i.id="customModelCenterStatus",document.body.appendChild(i)),i._hideTimer&&(clearTimeout(i._hideTimer),i._hideTimer=null),i.className=`center-status-banner center-status-${s}`,i.innerHTML="";const o=()=>{i.classList.remove("show"),i._hideTimer=setTimeout(()=>{i.hidden=!0,i._hideTimer=null},200)};if(s!=="error"){const a=document.createElement("span");a.className="center-status-spinner",a.setAttribute("aria-hidden","true"),i.appendChild(a)}const r=document.createElement("span");if(r.className="center-status-text",r.textContent=e,i.appendChild(r),s==="error"){const a=document.createElement("button");a.className="center-status-close",a.textContent="\xD7",a.setAttribute("aria-label","Dismiss"),a.onclick=l=>{l.stopPropagation(),o()},i.appendChild(a)}else if(n){const a=document.createElement("button");a.className="center-status-cancel",a.textContent="Cancel",a.onclick=l=>{l.stopPropagation(),n()},i.appendChild(a)}return i.hidden=!1,requestAnimationFrame(()=>i.classList.add("show")),{dismiss:o,setMessage:a=>{const l=i.querySelector(".center-status-text");l&&(l.textContent=a)}}},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s.data)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),i=this.$("statMemBar");if(t&&s&&(t.textContent=`${e.cpu}%`,s.style.width=`${Math.min(100,e.cpu)}%`,s.classList.remove("medium","high"),t.classList.remove("high"),e.cpu>80?(s.classList.add("high"),t.classList.add("high")):e.cpu>50&&s.classList.add("medium")),n&&i){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,i.style.width=`${Math.min(100,e.memory.percent)}%`,i.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(i.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&i.classList.add("medium")}},async _onClipboardWrite(e){const t=e?.text;if(typeof t=="string")try{await navigator.clipboard.writeText(t),this.showToast(`Copied to clipboard (${t.length} chars)`,"success")}catch{this._showClipboardFallback(t)}},_showClipboardFallback(e){const t=document.createElement("div");t.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;display:flex;align-items:center;justify-content:center";const s=document.createElement("div");s.style.cssText="background:#1e1e2e;border:1px solid #444;border-radius:8px;padding:16px;max-width:600px;width:90%;max-height:60vh;display:flex;flex-direction:column;gap:12px";const n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center";const i=document.createElement("span");i.style.cssText="color:#cdd6f4;font-weight:600",i.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(i),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const a=document.createElement("button");a.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",a.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(a),t.appendChild(s),document.body.appendChild(t),a.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});
361
+ `}e.innerHTML=i},async killMuxSession(e){if(confirm("Kill this mux session?")){try{await this.closeSession(e,!0)}catch{try{await fetch(`/api/mux-sessions/${e}`,{method:"DELETE"})}catch{}this.showToast("Tmux session killed","success")}this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e),this.renderMuxSessions()}},async reconcileMuxSessions(){try{const t=await(await fetch("/api/mux-sessions/reconcile",{method:"POST"})).json();t.data?.dead&&t.data.dead.length>0?(this.showToast(`Found ${t.data.dead.length} dead mux session(s)`,"warning"),await this.loadMuxSessions()):this.showToast("All mux sessions are alive","success")}catch{this.showToast("Failed to reconcile mux sessions","error")}},toggleNotifications(){this.notificationManager?.toggleDrawer()},async launchMultiMonitor(){try{const e=await fetch("/api/system/span-displays",{method:"POST"}),t=await e.json().catch(()=>({}));e.ok&&t.success?this.showToast("Opening Codeman across all displays\u2026","success"):this.showToast(t.error||"Could not open spanning window","error")}catch(e){this.showToast("Could not open spanning window: "+(e?.message||e),"error")}},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=3e3,action:i}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.className="toast-message",r.textContent=e,o.appendChild(r),i){const h=document.createElement("button");h.textContent=i.label,h.style.cssText="margin-left:12px;padding:2px 10px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:3px;color:inherit;cursor:pointer;font-size:12px",h.onclick=d=>{d.stopPropagation(),i.onClick(),o.remove()},o.appendChild(h)}let a=null;const l=()=>{a&&clearTimeout(a),o.classList.remove("show"),setTimeout(()=>o.remove(),200)},c=document.createElement("button");return c.className="toast-close",c.textContent="\xD7",c.setAttribute("aria-label","Dismiss"),c.onclick=h=>{h.stopPropagation(),l()},o.appendChild(c),this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),n>0&&(a=setTimeout(l,n)),{dismiss:l,setMessage:h=>{r.textContent=h}}},_showCenterStatus(e,t={}){const{type:s="info",onCancel:n}=t;let i=document.getElementById("customModelCenterStatus");i||(i=document.createElement("div"),i.id="customModelCenterStatus",document.body.appendChild(i)),i._hideTimer&&(clearTimeout(i._hideTimer),i._hideTimer=null),i.className=`center-status-banner center-status-${s}`,i.innerHTML="";const o=()=>{i.classList.remove("show"),i._hideTimer=setTimeout(()=>{i.hidden=!0,i._hideTimer=null},200)};if(s!=="error"){const a=document.createElement("span");a.className="center-status-spinner",a.setAttribute("aria-hidden","true"),i.appendChild(a)}const r=document.createElement("span");if(r.className="center-status-text",r.textContent=e,i.appendChild(r),s==="error"){const a=document.createElement("button");a.className="center-status-close",a.textContent="\xD7",a.setAttribute("aria-label","Dismiss"),a.onclick=l=>{l.stopPropagation(),o()},i.appendChild(a)}else if(n){const a=document.createElement("button");a.className="center-status-cancel",a.textContent="Cancel",a.onclick=l=>{l.stopPropagation(),n()},i.appendChild(a)}return i.hidden=!1,requestAnimationFrame(()=>i.classList.add("show")),{dismiss:o,setMessage:a=>{const l=i.querySelector(".center-status-text");l&&(l.textContent=a)}}},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s.data)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),i=this.$("statMemBar");if(t&&s&&(t.textContent=`${e.cpu}%`,s.style.width=`${Math.min(100,e.cpu)}%`,s.classList.remove("medium","high"),t.classList.remove("high"),e.cpu>80?(s.classList.add("high"),t.classList.add("high")):e.cpu>50&&s.classList.add("medium")),n&&i){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,i.style.width=`${Math.min(100,e.memory.percent)}%`,i.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(i.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&i.classList.add("medium")}},async _onClipboardWrite(e){const t=e?.text;if(typeof t=="string")try{await navigator.clipboard.writeText(t),this.showToast(`Copied to clipboard (${t.length} chars)`,"success")}catch{this._showClipboardFallback(t)}},_showClipboardFallback(e){const t=document.createElement("div");t.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;display:flex;align-items:center;justify-content:center";const s=document.createElement("div");s.style.cssText="background:#1e1e2e;border:1px solid #444;border-radius:8px;padding:16px;max-width:600px;width:90%;max-height:60vh;display:flex;flex-direction:column;gap:12px";const n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center";const i=document.createElement("span");i.style.cssText="color:#cdd6f4;font-weight:600",i.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(i),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const a=document.createElement("button");a.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",a.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(a),t.appendChild(s),document.body.appendChild(t),a.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});