aicodeman 1.28.0 → 1.28.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/config/cli-registry/schema.d.ts +4 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +7 -0
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +8 -0
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +19 -0
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/docker-hosts.d.ts +22 -1
- package/dist/docker-hosts.d.ts.map +1 -1
- package/dist/docker-hosts.js +57 -3
- package/dist/docker-hosts.js.map +1 -1
- package/dist/hooks-config.d.ts +93 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +211 -6
- package/dist/hooks-config.js.map +1 -1
- package/dist/session-cli-registry-bridge.d.ts +8 -0
- package/dist/session-cli-registry-bridge.d.ts.map +1 -1
- package/dist/session-cli-registry-bridge.js +14 -3
- package/dist/session-cli-registry-bridge.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +6 -1
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +21 -0
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +53 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/usage-telemetry.d.ts +4 -1
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +6 -3
- package/dist/usage-telemetry.js.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.78bf0bf8.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.9cd8ed69.js → constants.258b140f.js} +50 -0
- package/dist/web/public/constants.258b140f.js.br +0 -0
- package/dist/web/public/constants.258b140f.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/i18n.561ca09c.js +1 -0
- package/dist/web/public/i18n.561ca09c.js.br +0 -0
- package/dist/web/public/i18n.561ca09c.js.gz +0 -0
- package/dist/web/public/image-input.cd4b97c4.js.gz +0 -0
- package/dist/web/public/index.html +48 -12
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.87f14251.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.1d0dda9b.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.2e9c214a.js → mobile-handlers.6f354a87.js} +61 -13
- package/dist/web/public/mobile-handlers.6f354a87.js.br +0 -0
- package/dist/web/public/mobile-handlers.6f354a87.js.gz +0 -0
- package/dist/web/public/mobile-overview.js +2 -2
- package/dist/web/public/mobile-overview.js.br +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/{mobile.65176832.css → mobile.e9d0b53e.css} +1 -1
- package/dist/web/public/mobile.e9d0b53e.css.br +0 -0
- package/dist/web/public/mobile.e9d0b53e.css.gz +0 -0
- package/dist/web/public/notification-manager.36ea4624.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.4e9ea307.js → panels-ui.5b07ad14.js} +77 -77
- package/dist/web/public/panels-ui.5b07ad14.js.br +0 -0
- package/dist/web/public/panels-ui.5b07ad14.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.5d0a7b15.js +77 -0
- package/dist/web/public/session-ui.5d0a7b15.js.br +0 -0
- package/dist/web/public/session-ui.5d0a7b15.js.gz +0 -0
- package/dist/web/public/settings-ui.e0c7f6b4.js +67 -0
- package/dist/web/public/settings-ui.e0c7f6b4.js.br +0 -0
- package/dist/web/public/settings-ui.e0c7f6b4.js.gz +0 -0
- package/dist/web/public/styles.5d36726d.css +1 -0
- package/dist/web/public/styles.5d36726d.css.br +0 -0
- package/dist/web/public/styles.5d36726d.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.6ea8fe37.js.gz +0 -0
- package/dist/web/public/terminal-ui.0cbe8637.js +2 -0
- package/dist/web/public/terminal-ui.0cbe8637.js.br +0 -0
- package/dist/web/public/terminal-ui.0cbe8637.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +14 -22
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts +3 -2
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +6 -4
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +32 -22
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +0 -2
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +6 -8
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +4 -1
- package/dist/web/server.js.map +1 -1
- package/package.json +10 -9
- package/dist/web/public/constants.9cd8ed69.js.br +0 -0
- package/dist/web/public/constants.9cd8ed69.js.gz +0 -0
- package/dist/web/public/i18n.9db8c4e0.js +0 -1
- package/dist/web/public/i18n.9db8c4e0.js.br +0 -0
- package/dist/web/public/i18n.9db8c4e0.js.gz +0 -0
- package/dist/web/public/mobile-handlers.2e9c214a.js.br +0 -0
- package/dist/web/public/mobile-handlers.2e9c214a.js.gz +0 -0
- package/dist/web/public/mobile.65176832.css.br +0 -0
- package/dist/web/public/mobile.65176832.css.gz +0 -0
- package/dist/web/public/panels-ui.4e9ea307.js.br +0 -0
- package/dist/web/public/panels-ui.4e9ea307.js.gz +0 -0
- package/dist/web/public/session-ui.cd222710.js +0 -77
- package/dist/web/public/session-ui.cd222710.js.br +0 -0
- package/dist/web/public/session-ui.cd222710.js.gz +0 -0
- package/dist/web/public/settings-ui.c175db9a.js +0 -67
- package/dist/web/public/settings-ui.c175db9a.js.br +0 -0
- package/dist/web/public/settings-ui.c175db9a.js.gz +0 -0
- package/dist/web/public/styles.0d41361c.css +0 -1
- package/dist/web/public/styles.0d41361c.css.br +0 -0
- package/dist/web/public/styles.0d41361c.css.gz +0 -0
- package/dist/web/public/terminal-ui.89e4eb1a.js +0 -2
- package/dist/web/public/terminal-ui.89e4eb1a.js.br +0 -0
- package/dist/web/public/terminal-ui.89e4eb1a.js.gz +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
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")},_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(
|
|
2
|
-
<button class="command-palette-item${r}${l}" type="button" data-command-index="${
|
|
3
|
-
<span class="command-palette-icon" aria-hidden="true">${
|
|
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")},_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`
|
|
2
|
+
<button class="command-palette-item${r}${l}" type="button" data-command-index="${o}">
|
|
3
|
+
<span class="command-palette-icon" aria-hidden="true">${a}</span>
|
|
4
4
|
<span class="command-palette-text">
|
|
5
5
|
<span class="command-palette-title">${escapeHtml(i.title)}</span>
|
|
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
|
|
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},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>
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
<div class="away-digest-card">
|
|
23
23
|
<span class="away-digest-card-label">Tokens</span>
|
|
24
|
-
<span class="away-digest-card-value">${this.formatTokens(i+
|
|
24
|
+
<span class="away-digest-card-value">${this.formatTokens(i+o)}</span>
|
|
25
25
|
<span class="away-digest-card-cost">~$${r.toFixed(2)}</span>
|
|
26
26
|
</div>
|
|
27
|
-
`;const
|
|
27
|
+
`;const a=[];(e.dataFreshness.runSummariesLiveOnly||e.dataFreshness.subagentsLiveOnly)&&a.push("Run summaries and subagent completions use recent live state; lifecycle and token stats are persisted."),e.totals.tokenWindowPrecision==="day"&&a.push("Token totals are aggregated at day precision."),s.textContent=a.join(" "),n.innerHTML=AWAY_DIGEST_SECTIONS.map(([l,c])=>this.renderAwayDigestSection(c,e.sections[l]||[])).join(""),this.attachAwayDigestActions()},renderAwayDigestSection(e,t){const s=t.length,n=s?t.map(i=>this.renderAwayDigestItem(i)).join(""):'<div class="away-digest-empty">No items</div>';return`
|
|
28
28
|
<section class="away-digest-section">
|
|
29
29
|
<div class="away-digest-section-title">
|
|
30
30
|
<h4>${escapeHtml(e)}</h4>
|
|
@@ -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},
|
|
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=`
|
|
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>
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
</div>
|
|
60
60
|
<div class="stat-card">
|
|
61
61
|
<span class="stat-card-label">7 Days</span>
|
|
62
|
-
<span class="stat-card-value">${this.formatTokens(
|
|
62
|
+
<span class="stat-card-value">${this.formatTokens(a+l)}</span>
|
|
63
63
|
<span class="stat-card-cost">~$${c.toFixed(2)}</span>
|
|
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(
|
|
68
|
-
<span class="stat-card-cost">~$${
|
|
67
|
+
<span class="stat-card-value">${this.formatTokens(u+d)}</span>
|
|
68
|
+
<span class="stat-card-cost">~$${h.toFixed(2)}</span>
|
|
69
69
|
</div>
|
|
70
70
|
`;const m=document.getElementById("statsChart"),g=document.getElementById("statsChartDays"),y=[];for(let f=6;f>=0;f--){const S=new Date;S.setDate(S.getDate()-f);const T=S.toISOString().split("T")[0],v=t.find(I=>I.date===T);y.push({date:T,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:v?v.inputTokens+v.outputTokens:0,cost:v?v.estimatedCost:0})}const w=Math.max(...y.map(f=>f.tokens),1);m.innerHTML=y.map(f=>{const S=Math.max(f.tokens/w*100,3),T=`${f.dayName}: ${this.formatTokens(f.tokens)} (~$${f.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${T}"></div>`}).join(""),g.innerHTML=y.map(f=>`<span>${f.dayName}</span>`).join("");const b=document.getElementById("statsTable"),$=t.slice(0,14);$.length===0?b.innerHTML='<div class="stats-no-data">No usage data recorded yet</div>':b.innerHTML=`
|
|
71
71
|
<div class="stats-table-header">
|
|
@@ -82,20 +82,20 @@
|
|
|
82
82
|
<span class="cell cell-cost">$${f.estimatedCost.toFixed(2)}</span>
|
|
83
83
|
</div>
|
|
84
84
|
`).join("")}
|
|
85
|
-
`},closeTokenStats(){const e=document.getElementById("tokenStatsModal");e&&e.classList.remove("active")},async toggleMonitorPanel(){const e=document.getElementById("monitorPanel"),t=document.getElementById("monitorToggleBtn");e.classList.toggle("open"),e.classList.contains("open")?(e.style.display="",await this.loadMuxSessions(),await fetch("/api/mux-sessions/stats/start",{method:"POST"}),this.renderTaskPanel(),t&&(t.innerHTML="▼")):(await fetch("/api/mux-sessions/stats/stop",{method:"POST"}),t&&(t.innerHTML="▲"))},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="⧉",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="⊞",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,
|
|
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="▼")):(await fetch("/api/mux-sessions/stats/stop",{method:"POST"}),t&&(t.innerHTML="▲"))},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="⧉",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="⊞",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="⧉",t.title="Detach panel")):(e.classList.add("detached"),e.classList.add("open"),t&&(t.innerHTML="⊞",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"?"✓":"✗",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`
|
|
86
86
|
<div class="task-item">
|
|
87
|
-
<span class="task-status-icon ${l.status}">${
|
|
87
|
+
<span class="task-status-icon ${l.status}">${u}</span>
|
|
88
88
|
<div class="task-info">
|
|
89
89
|
<div class="task-description">${escapeHtml(l.description)}</div>
|
|
90
90
|
<div class="task-meta">
|
|
91
91
|
<span class="task-type">${l.subagentType}</span>
|
|
92
|
-
<span>${
|
|
92
|
+
<span>${d}</span>
|
|
93
93
|
</div>
|
|
94
94
|
</div>
|
|
95
95
|
</div>
|
|
96
|
-
${
|
|
97
|
-
`},r=this.flattenTaskTree(e.taskTree);let
|
|
98
|
-
<div class="subagent-item ${
|
|
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":""}"
|
|
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">
|
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
${g}
|
|
104
104
|
<span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(p.length>40?p.substring(0,40)+"...":p)}</span>
|
|
105
105
|
${m}
|
|
106
|
-
${
|
|
107
|
-
<span class="subagent-status ${
|
|
108
|
-
${
|
|
106
|
+
${d}
|
|
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">✕</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>
|
|
@@ -115,30 +115,30 @@
|
|
|
115
115
|
${l?`<span class="subagent-last-tool">${this.getToolIcon(l)} ${l}</span>`:""}
|
|
116
116
|
</div>
|
|
117
117
|
</div>
|
|
118
|
-
`)}e.innerHTML=t.join("")},selectSubagent(e){this.activeSubagentId=e,this.renderSubagentPanel(),this.renderSubagentDetail()},renderSubagentDetail(){const e=this.$("subagentDetail");if(!e)return;if(!this.activeSubagentId){e.innerHTML='<div class="subagent-empty">Select an agent to view details</div>';return}const t=this.subagents.get(this.activeSubagentId),s=this.subagentActivity.get(this.activeSubagentId)||[];if(!t){e.innerHTML='<div class="subagent-empty">Agent not found</div>';return}const n=s.slice(-30).map(
|
|
118
|
+
`)}e.innerHTML=t.join("")},selectSubagent(e){this.activeSubagentId=e,this.renderSubagentPanel(),this.renderSubagentDetail()},renderSubagentDetail(){const e=this.$("subagentDetail");if(!e)return;if(!this.activeSubagentId){e.innerHTML='<div class="subagent-empty">Select an agent to view details</div>';return}const t=this.subagents.get(this.activeSubagentId),s=this.subagentActivity.get(this.activeSubagentId)||[];if(!t){e.innerHTML='<div class="subagent-empty">Agent not found</div>';return}const n=s.slice(-30).map(a=>{const l=new Date(a.timestamp).toLocaleTimeString("en-US",{hour12:!1});if(a.type==="tool"){const c=this.getToolDetailExpanded(a.tool,a.input,a.fullInput,a.toolUseId);return`<div class="subagent-activity tool" data-tool-use-id="${escapeHtml(a.toolUseId||"")}">
|
|
119
119
|
<span class="time">${l}</span>
|
|
120
|
-
<span class="icon">${this.getToolIcon(
|
|
121
|
-
<span class="name">${escapeHtml(
|
|
120
|
+
<span class="icon">${this.getToolIcon(a.tool)}</span>
|
|
121
|
+
<span class="name">${escapeHtml(a.tool)}</span>
|
|
122
122
|
<span class="detail">${escapeHtml(c.primary)}</span>
|
|
123
|
-
${c.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(
|
|
124
|
-
${c.hasMore?`<div class="tool-params-expanded" id="tool-params-${escapeHtml(
|
|
125
|
-
</div>`}else if(
|
|
123
|
+
${c.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(a.toolUseId))})">\u25B6</button>`:""}
|
|
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}">
|
|
126
126
|
<span class="time">${l}</span>
|
|
127
127
|
<span class="icon">${c}</span>
|
|
128
|
-
<span class="name">${escapeHtml(
|
|
129
|
-
<span class="detail">${escapeHtml(
|
|
130
|
-
</div>`}else if(
|
|
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}">
|
|
131
131
|
<span class="time">${l}</span>
|
|
132
|
-
<span class="icon">${
|
|
133
|
-
<span class="detail">${escapeHtml(
|
|
134
|
-
</div>`}else if(
|
|
132
|
+
<span class="icon">${u}</span>
|
|
133
|
+
<span class="detail">${escapeHtml(h)}</span>
|
|
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>
|
|
137
137
|
<span class="detail">${escapeHtml(c)}</span>
|
|
138
|
-
</div>`}return""}).join(""),i=t.description||`Agent ${t.agentId}`,
|
|
138
|
+
</div>`}return""}).join(""),i=t.description||`Agent ${t.agentId}`,o=t.modelShort?`<span class="subagent-model-badge ${escapeHtml(t.modelShort)}">${escapeHtml(t.modelShort)}</span>`:"",r=t.totalInputTokens||t.totalOutputTokens?`<span>Tokens: ${this.formatTokenCount(t.totalInputTokens||0)}\u2193 ${this.formatTokenCount(t.totalOutputTokens||0)}\u2191</span>`:"";e.innerHTML=`
|
|
139
139
|
<div class="subagent-detail-header">
|
|
140
140
|
<span class="subagent-id" title="${escapeHtml(t.description||t.agentId)}">${escapeHtml(i.length>60?i.substring(0,60)+"...":i)}</span>
|
|
141
|
-
${
|
|
141
|
+
${o}
|
|
142
142
|
<span class="subagent-status ${t.status}">${t.status}</span>
|
|
143
143
|
<button class="subagent-transcript-btn" onclick="app.viewSubagentTranscript(${escapeHtml(JSON.stringify(t.agentId))})">
|
|
144
144
|
View Full Transcript
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
<div class="subagent-activity-log">
|
|
154
154
|
${n||'<div class="subagent-empty">No activity yet</div>'}
|
|
155
155
|
</div>
|
|
156
|
-
`},toggleToolParams(e){const t=document.getElementById(`tool-params-${e}`);if(!t)return;const s=t.previousElementSibling;t.style.display==="none"?(t.style.display="block",s&&(s.textContent="\u25BC")):(t.style.display="none",s&&(s.textContent="\u25B6"))},formatTokenCount(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"k":e.toString()},formatBytes(e){return e>=1024*1024?(e/(1024*1024)).toFixed(1)+"MB":e>=1024?(e/1024).toFixed(1)+"KB":e+"B"},getToolIcon(e){return{WebSearch:"\u{1F50D}",WebFetch:"\u{1F310}",Read:"\u{1F4D6}",Write:"\u{1F4DD}",Edit:"\u270F\uFE0F",Bash:"\u{1F4BB}",Glob:"\u{1F4C1}",Grep:"\u{1F50E}",Task:"\u{1F916}"}[e]||"\u{1F527}"},getToolDetail(e,t){if(!t)return"";if(e==="WebSearch"&&t.query)return`"${t.query}"`;if(e==="WebFetch"&&t.url)return t.url;if(e==="Read"&&t.file_path||(e==="Write"||e==="Edit")&&t.file_path)return t.file_path;if(e==="Bash"&&t.command){const s=t.command;return s.length>40?s.substring(0,40)+"...":s}return e==="Glob"&&t.pattern||e==="Grep"&&t.pattern?t.pattern:""},getToolDetailExpanded(e,t,s,n){const i=this.getToolDetail(e,t),
|
|
156
|
+
`},toggleToolParams(e){const t=document.getElementById(`tool-params-${e}`);if(!t)return;const s=t.previousElementSibling;t.style.display==="none"?(t.style.display="block",s&&(s.textContent="\u25BC")):(t.style.display="none",s&&(s.textContent="\u25B6"))},formatTokenCount(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"k":e.toString()},formatBytes(e){return e>=1024*1024?(e/(1024*1024)).toFixed(1)+"MB":e>=1024?(e/1024).toFixed(1)+"KB":e+"B"},getToolIcon(e){return{WebSearch:"\u{1F50D}",WebFetch:"\u{1F310}",Read:"\u{1F4D6}",Write:"\u{1F4DD}",Edit:"\u270F\uFE0F",Bash:"\u{1F4BB}",Glob:"\u{1F4C1}",Grep:"\u{1F50E}",Task:"\u{1F916}"}[e]||"\u{1F527}"},getToolDetail(e,t){if(!t)return"";if(e==="WebSearch"&&t.query)return`"${t.query}"`;if(e==="WebFetch"&&t.url)return t.url;if(e==="Read"&&t.file_path||(e==="Write"||e==="Edit")&&t.file_path)return t.file_path;if(e==="Bash"&&t.command){const s=t.command;return s.length>40?s.substring(0,40)+"...":s}return e==="Glob"&&t.pattern||e==="Grep"&&t.pattern?t.pattern:""},getToolDetailExpanded(e,t,s,n){const i=this.getToolDetail(e,t),o=["query","url","file_path","command","pattern"],l=Object.keys(s||t||{}).filter(c=>!o.includes(c)).length>0||s&&JSON.stringify(s).length>100;return{primary:i,hasMore:l,fullInput:s||t}},async killSubagent(e){try{const s=await(await this._apiDelete(`/api/subagents/${e}`))?.json();if(s?.success){const n=this.subagents.get(e);n&&(n.status="completed",this.subagents.set(e,n)),this.renderSubagentPanel(),this.renderSubagentDetail(),this.updateSubagentWindows(),this.showToast(`Subagent ${e.substring(0,7)} killed`,"success")}else this.showToast(s.error||"Failed to kill subagent","error")}catch(t){console.error("Failed to kill subagent:",t),this.showToast("Failed to kill subagent: "+t.message,"error")}},async viewSubagentTranscript(e){try{const s=await(await fetch(`/api/subagents/${e}/transcript?format=formatted`)).json();if(!s.success){alert("Failed to load transcript");return}const n=s.data.formatted.join(`
|
|
157
157
|
`);window.open("","_blank","width=800,height=600").document.write(`
|
|
158
158
|
<html>
|
|
159
159
|
<head>
|
|
@@ -168,26 +168,26 @@
|
|
|
168
168
|
<pre>${escapeHtml(n)}</pre>
|
|
169
169
|
</body>
|
|
170
170
|
</html>
|
|
171
|
-
`)}catch(t){console.error("Failed to load transcript:",t),alert("Failed to load transcript: "+t.message)}},findParentSessionForSubagent(e){if(this.subagentParentMap.has(e)){const s=this.subagentParentMap.get(e);if(this.sessions.has(s)){const n=this.subagents.get(e);if(n&&!n.parentSessionId){n.parentSessionId=s;const i=this.sessions.get(s);i&&(n.parentSessionName=this.getSessionName(i)),this.subagents.set(e,n),this.updateSubagentWindowParent(e)}return}this.subagentParentMap.delete(e)}const t=this.subagents.get(e);if(t){if(t.sessionId){for(const[s,n]of this.sessions)if(n.claudeSessionId===t.sessionId){this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}}if(this.activeSessionId&&this.sessions.has(this.activeSessionId)){this.setAgentParentSessionId(e,this.activeSessionId),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}if(this.sessions.size>0){const s=this.sessions.keys().next().value;this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines()}}},recheckOrphanSubagents(){let e=!1;for(const[t,s]of this.subagents)if(!this.subagentParentMap.has(t))this.findParentSessionForSubagent(t),this.subagentParentMap.has(t)&&(e=!0);else if(s.sessionId){const n=this.subagentParentMap.get(t),i=this.sessions.get(n);if(i&&i.claudeSessionId!==s.sessionId){for(const[
|
|
171
|
+
`)}catch(t){console.error("Failed to load transcript:",t),alert("Failed to load transcript: "+t.message)}},findParentSessionForSubagent(e){if(this.subagentParentMap.has(e)){const s=this.subagentParentMap.get(e);if(this.sessions.has(s)){const n=this.subagents.get(e);if(n&&!n.parentSessionId){n.parentSessionId=s;const i=this.sessions.get(s);i&&(n.parentSessionName=this.getSessionName(i)),this.subagents.set(e,n),this.updateSubagentWindowParent(e)}return}this.subagentParentMap.delete(e)}const t=this.subagents.get(e);if(t){if(t.sessionId){for(const[s,n]of this.sessions)if(n.claudeSessionId===t.sessionId){this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}}if(this.activeSessionId&&this.sessions.has(this.activeSessionId)){this.setAgentParentSessionId(e,this.activeSessionId),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}if(this.sessions.size>0){const s=this.sessions.keys().next().value;this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines()}}},recheckOrphanSubagents(){let e=!1;for(const[t,s]of this.subagents)if(!this.subagentParentMap.has(t))this.findParentSessionForSubagent(t),this.subagentParentMap.has(t)&&(e=!0);else if(s.sessionId){const n=this.subagentParentMap.get(t),i=this.sessions.get(n);if(i&&i.claudeSessionId!==s.sessionId){for(const[o,r]of this.sessions)if(r.claudeSessionId===s.sessionId){this.subagentParentMap.set(t,o),s.parentSessionId=o,s.parentSessionName=this.getSessionName(r),this.subagents.set(t,s),this.updateSubagentWindowParent(t),e=!0;break}}}e&&(this.saveSubagentParentMap(),this.updateConnectionLines())},updateSubagentParentNames(e){const t=this.sessions.get(e);if(!t)return;const s=this.getSessionName(t);if(this._parentNameCache?.get(e)!==s){this._parentNameCache||(this._parentNameCache=new Map),this._parentNameCache.set(e,s);for(const[i,o]of this.subagentParentMap)if(o===e){const r=this.subagents.get(i);if(r){r.parentSessionName=s,this.subagents.set(i,r);const a=this.subagentWindows.get(i);if(a){const l=a.element.querySelector(".subagent-window-parent .parent-name");l&&(l.textContent=s)}}}}},updateSubagentWindowParent(e){const t=this.subagentWindows.get(e);if(!t)return;const s=this.subagentParentMap.get(e);if(!s)return;const n=this.sessions.get(s),i=n?this.getSessionName(n):"Unknown",o=t.element,r=o.querySelector(".subagent-window-parent");if(r){r.dataset.parentSession=s;const l=r.querySelector(".parent-name");l&&(l.textContent=i,l.onclick=()=>this.selectSession(s));return}const a=o.querySelector(".subagent-window-header");if(a){const l=document.createElement("div");l.className="subagent-window-parent",l.dataset.parentSession=s,l.innerHTML=`
|
|
172
172
|
<span class="parent-label">from</span>
|
|
173
173
|
<span class="parent-name" onclick="app.selectSession(${escapeHtml(JSON.stringify(s))})">${escapeHtml(i)}</span>
|
|
174
|
-
`,
|
|
174
|
+
`,a.insertAdjacentElement("afterend",l)}},updateSubagentWindowVisibility(){const t=this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0;for(const[s,n]of this.subagentWindows){const i=this.subagentParentMap.get(s),o=this.subagents.get(s),r=i||o?.parentSessionId;let a;t?a=!!!r||r===this.activeSessionId:a=!0,a?(n.minimized||(n.element.style.display="flex",n._lazyTerminal&&this._restoreTeammateTerminalFromLazy(s)),n.hidden=!1):(this._disposeTeammateTerminalForMinimize(s),n.element.style.display="none",n.hidden=!0)}this.updateConnectionLines(),this.relayoutMobileSubagentWindows()},closeSessionSubagentWindows(e,t=!1){const s=[];for(const[n,i]of this.subagentWindows){const o=this.subagents.get(n),r=this.subagentParentMap.get(n);(o?.parentSessionId===e||r===e)&&s.push(n)}for(const n of s)this.forceCloseSubagentWindow(n),t&&(this.subagents.delete(n),this.subagentActivity.delete(n),this.subagentToolResults.delete(n),this.subagentParentMap.delete(n));this.minimizedSubagents.delete(e),this.renderSessionTabs()},forceCloseSubagentWindow(e){const t=this.subagentWindows.get(e);t&&(t.resizeObserver&&t.resizeObserver.disconnect(),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.subagentWindows.delete(e));const s=this.teammateTerminals.get(e);if(s){if(s.resizeObserver&&s.resizeObserver.disconnect(),s.terminal)try{s.terminal.dispose()}catch{}this.teammateTerminals.delete(e)}},minimizeSubagentWindow(e){const t=this.subagentWindows.get(e);t&&(this._disposeTeammateTerminalForMinimize(e),t.element.style.display="none",t.minimized=!0,this.updateConnectionLines())},scheduleSubagentWindowRender(e){this.subagentWindows.get(e)?.minimized||(this._subagentWindowRenderTimeouts||(this._subagentWindowRenderTimeouts=new Map),this._subagentWindowRenderTimeouts.has(e)&&clearTimeout(this._subagentWindowRenderTimeouts.get(e)),this._subagentWindowRenderTimeouts.set(e,setTimeout(()=>{this._subagentWindowRenderTimeouts.delete(e),scheduleBackground(()=>this.renderSubagentWindowContent(e))},100)))},renderSubagentWindowContent(e){if(this.teammateTerminals.has(e)||this.subagentWindows.get(e)?._lazyTerminal)return;const s=document.getElementById(`subagent-window-body-${e}`);if(!s)return;const n=this.subagentActivity.get(e)||[];if(n.length===0){s.innerHTML='<div class="subagent-empty">No activity yet</div>';return}const i=s.dataset.renderedCount?parseInt(s.dataset.renderedCount,10):0,o=100,r=n.slice(-o);if(i===0||i>r.length||s.children.length===0||s.children.length===1&&s.querySelector(".subagent-empty")){const a=r.map(l=>this._renderActivityItem(l)).join("");s.innerHTML=a,s.dataset.renderedCount=String(r.length)}else{const a=r.slice(i);if(a.length>0){const l=a.map(c=>this._renderActivityItem(c)).join("");for(s.insertAdjacentHTML("beforeend",l),s.dataset.renderedCount=String(r.length);s.children.length>o;)s.removeChild(s.firstChild)}}s.scrollTop=s.scrollHeight},_renderActivityItem(e){const t=new Date(e.timestamp).toLocaleTimeString("en-US",{hour12:!1});if(e.type==="tool")return`<div class="activity-line">
|
|
175
175
|
<span class="time">${t}</span>
|
|
176
176
|
<span class="tool-icon">${this.getToolIcon(e.tool)}</span>
|
|
177
177
|
<span class="tool-name">${escapeHtml(e.tool)}</span>
|
|
178
178
|
<span class="tool-detail">${escapeHtml(this.getToolDetail(e.tool,e.input))}</span>
|
|
179
|
-
</div>`;if(e.type==="tool_result"){const s=e.isError?"\u274C":"\u{1F4C4}",n=e.isError?" error":"",i=e.contentLength>500?` (${this.formatBytes(e.contentLength)})`:"",
|
|
179
|
+
</div>`;if(e.type==="tool_result"){const s=e.isError?"\u274C":"\u{1F4C4}",n=e.isError?" error":"",i=e.contentLength>500?` (${this.formatBytes(e.contentLength)})`:"",o=e.preview.length>60?e.preview.substring(0,60)+"...":e.preview;return`<div class="activity-line result-line${n}">
|
|
180
180
|
<span class="time">${t}</span>
|
|
181
181
|
<span class="tool-icon">${s}</span>
|
|
182
182
|
<span class="tool-name">${escapeHtml(e.tool||"\u2192")}</span>
|
|
183
|
-
<span class="tool-detail">${escapeHtml(
|
|
183
|
+
<span class="tool-detail">${escapeHtml(o)}${i}</span>
|
|
184
184
|
</div>`}else if(e.type==="progress"){const s=e.hookEvent||e.hookName,n=s?"\u{1FA9D}":e.progressType==="query_update"?"\u27F3":"\u2713",i=s?e.hookName||e.hookEvent:e.query||e.progressType;return`<div class="activity-line progress-line${s?" hook-line":""}">
|
|
185
185
|
<span class="time">${t}</span>
|
|
186
186
|
<span class="tool-icon">${n}</span>
|
|
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),
|
|
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=`
|
|
191
191
|
<div class="subagent-window-header">
|
|
192
192
|
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
|
|
193
193
|
<span class="icon" style="color: var(--team-color-${y}, #339af0)">\u2B24</span>
|
|
@@ -200,55 +200,55 @@
|
|
|
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)||[],
|
|
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
204
|
<span class="team-task-status">${m}</span>
|
|
205
205
|
<span class="team-task-subject">${escapeHtml(p.subject)}</span>
|
|
206
206
|
${y}
|
|
207
|
-
</div>`}).join("");
|
|
208
|
-
<div class="project-insight-item" data-tool-id="${
|
|
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(`
|
|
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(
|
|
212
|
-
<span class="project-insight-status ${
|
|
213
|
-
${
|
|
211
|
+
<span class="cmd" title="${escapeHtml(d.command)}">${escapeHtml(p)}</span>
|
|
212
|
+
<span class="project-insight-status ${d.status}">${d.status}</span>
|
|
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
|
|
216
|
+
`);for(const m of h){const g=m.split("/").pop();u.push(`
|
|
217
217
|
<span class="project-insight-filepath"
|
|
218
|
-
onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(m))}, ${escapeHtml(JSON.stringify(
|
|
218
|
+
onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(m))}, ${escapeHtml(JSON.stringify(d.sessionId))})"
|
|
219
219
|
title="${escapeHtml(m)}">${escapeHtml(g)}</span>
|
|
220
|
-
`)}
|
|
220
|
+
`)}u.push(`
|
|
221
221
|
</div>
|
|
222
222
|
</div>
|
|
223
|
-
`)}t.innerHTML=h.join("")},closeProjectInsightsPanel(){const e=this.$("projectInsightsPanel");e&&(e.classList.remove("visible"),this.projectInsightsPanelVisible=!1)},_loadFileBrowserShowHidden(){try{return localStorage.getItem(FILE_BROWSER_SHOW_HIDDEN_KEY)==="1"}catch{return!1}},_syncFileBrowserHiddenBtn(){const e=this.$("fileBrowserHiddenBtn");if(!e)return;const t=this.fileBrowserShowHidden===!0;e.classList.toggle("active",t),e.setAttribute("aria-pressed",String(t));const s=t?"Hide hidden files and folders":"Show hidden files and folders";e.setAttribute("title",s),e.setAttribute("aria-label",s)},_ensureFileBrowserState(){if(!this._fileBrowserState){const e=this.activeSessionId||null,t=this.fileBrowserShowHidden===!0;this._fileBrowserState={treeEpoch:0,searchEpoch:0,ownerSessionId:e,view:"normal",normalState:this.fileBrowserData?{sessionId:e,showHidden:t,treeEpoch:0,phase:"ready",data:this.fileBrowserData}:null,treeInFlight:null,inFlight:null,matches:[],deferredDirectoryTarget:null,filter:typeof this.fileBrowserFilter=="string"?this.fileBrowserFilter:""}}return this._fileBrowserState},_activateFileBrowserSession(e){if(!e)return;const t=this._ensureFileBrowserState();t.inFlight?.timer!==void 0&&t.inFlight?.timer!==null&&clearTimeout(t.inFlight.timer),t.searchEpoch++,t.treeEpoch++,t.ownerSessionId=e,t.treeInFlight=null,t.inFlight=null,t.normalState=null,t.matches=[],t.deferredDirectoryTarget=null,t.filter="",t.view="normal",this.fileBrowserData=null,this.fileBrowserFilter="",this.fileBrowserExpandedDirs?.clear?.(),this.fileBrowserAllExpanded=!1;const s=this.$?.("fileBrowserSearch");s&&(s.value=""),this._syncFileBrowserExpandBtn();const n=this.$?.("fileBrowserExpandBtn");n&&(n.innerHTML="\u229E");const i=this.$?.("fileBrowserPanel"),a=this.$?.("fileBrowserTree"),r=this.$?.("fileBrowserStatus"),o=i?.classList.contains("visible")===!0;a&&(a.innerHTML=o?`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`:""),r&&(r.textContent=o?"Loading files...":""),o&&this.loadFileBrowser?.(e)?.catch?.(()=>{})},_resetFileBrowserForHide(){const e=this._ensureFileBrowserState();e.inFlight?.timer!==void 0&&e.inFlight?.timer!==null&&clearTimeout(e.inFlight.timer),e.searchEpoch++,e.treeEpoch++,e.treeInFlight=null,e.inFlight=null,e.normalState=null,e.matches=[],e.deferredDirectoryTarget=null,e.filter="",e.view="normal",this.fileBrowserData=null,this.fileBrowserFilter="",this.fileBrowserExpandedDirs?.clear?.(),this.fileBrowserAllExpanded=!1;const t=this.$?.("fileBrowserSearch");t&&(t.value=""),this._syncFileBrowserExpandBtn();const s=this.$?.("fileBrowserExpandBtn");s&&(s.innerHTML="\u229E");const n=this.$?.("fileBrowserTree");n&&(n.innerHTML="");const i=this.$?.("fileBrowserStatus");i&&(i.textContent="")},_setFileBrowserExpandDisabled(e){const t=this.$("fileBrowserExpandBtn");t&&(t.disabled=e)},_hasFileBrowserQuery(){const e=this._ensureFileBrowserState(),t=this.$?.("fileBrowserSearch"),s=typeof t?.value=="string"?t.value:"",n=typeof e.filter=="string"?e.filter:"";return s.trim()!==""||n.trim()!==""},_syncFileBrowserExpandBtn(){this._setFileBrowserExpandDisabled(this._hasFileBrowserQuery())},_renderFileBrowserNormalStatus(e,t){const s=this.$("fileBrowserStatus");if(!s||!e)return;const{totalFiles:n,totalDirectories:i,truncated:a}=e;s.textContent=`${n} files, ${i} dirs${a?" (truncated)":""}${t?" \xB7 hidden shown":""}`},_isFileBrowserNormalCompatible(e,t,s,n){return e?.sessionId===t&&e.showHidden===s&&e.treeEpoch===n},_isFileBrowserTreeContextCurrent(e,t=!1){const s=this._ensureFileBrowserState();return(!t||s.treeInFlight===e)&&s.ownerSessionId===e.sessionId&&s.treeEpoch===e.treeEpoch&&this.fileBrowserShowHidden===!0===e.showHidden},_canRenderFileBrowserNormal(e){const t=this._ensureFileBrowserState();return t.view==="normal"&&this.activeSessionId===e?.sessionId&&this._isFileBrowserNormalCompatible(e,t.ownerSessionId,this.fileBrowserShowHidden===!0,t.treeEpoch)&&this.$("fileBrowserPanel")?.classList.contains("visible")===!0},_renderFileBrowserNormalState(e){if(!e||!this._canRenderFileBrowserNormal(e))return;const t=this.$("fileBrowserTree"),s=this.$("fileBrowserStatus");if(t){if(e.phase==="loading"){this.fileBrowserData=null,t.innerHTML=`<div class="file-browser-loading">${escapeHtml("Loading files...")}</div>`,s&&(s.textContent="Loading files...");return}if(e.phase==="error"){this.fileBrowserData=null;const i=`Failed to load files${e.error&&e.error!=="Failed to load files"?`: ${e.error}`:""}`;t.innerHTML=`<div class="file-browser-empty">${escapeHtml(i)}</div>`,s&&(s.textContent=i);return}e.phase==="ready"&&(this.fileBrowserData=e.data,this._syncFileBrowserExpandBtn(),this.renderFileBrowserTree(e.sessionId),this._renderFileBrowserNormalStatus(e.data,e.showHidden))}},_validateFileBrowserTreeEnvelope(e){if(!e||typeof e!="object"||e.success!==!0)return null;const t=e.data;if(!t||typeof t!="object"||!Array.isArray(t.tree)||t.mode==="search"||typeof t.totalFiles!="number"||!Number.isFinite(t.totalFiles)||t.totalFiles<0||typeof t.totalDirectories!="number"||!Number.isFinite(t.totalDirectories)||t.totalDirectories<0||typeof t.truncated!="boolean")return null;const s=n=>n.every(i=>!(!i||typeof i!="object"||typeof i.name!="string"||typeof i.path!="string"||i.type!=="file"&&i.type!=="directory"||i.size!==void 0&&(typeof i.size!="number"||!Number.isFinite(i.size))||i.extension!==void 0&&typeof i.extension!="string"||i.children!==void 0&&(!Array.isArray(i.children)||!s(i.children))));return s(t.tree)?t:null},_normalizeFileBrowserTreeError(e){return typeof e?.message=="string"&&e.message?e.message:"Failed to load files"},_validateFileBrowserSearchEnvelope(e){if(!e||typeof e!="object"||e.success!==!0)return null;const t=e.data;if(!t||typeof t!="object"||t.mode!=="search"||!Array.isArray(t.matches)||typeof t.truncated!="boolean"||t.matchCount!==void 0&&(typeof t.matchCount!="number"||!Number.isFinite(t.matchCount)||t.matchCount<0))return null;for(const s of t.matches)if(!s||typeof s!="object"||typeof s.name!="string"||typeof s.path!="string"||s.type!=="file"&&s.type!=="directory"||s.size!==void 0&&(typeof s.size!="number"||!Number.isFinite(s.size))||s.extension!==void 0&&typeof s.extension!="string")return null;return t},_canRenderFileBrowserSearch(e){const t=this._ensureFileBrowserState(),s=this.$("fileBrowserPanel");return t.searchEpoch===e.epoch&&t.ownerSessionId===e.ownerSessionId&&this.activeSessionId===e.ownerSessionId&&this.fileBrowserShowHidden===!0===e.showHidden&&t.filter===e.rawInput&&s?.classList.contains("visible")===!0},_renderFileBrowserSearchError(){const e=this.$("fileBrowserTree"),t=this.$("fileBrowserStatus"),s="Search failed";e&&(e.innerHTML=`<div class="file-browser-empty">${escapeHtml(s)}</div>`),t&&(t.textContent=s)},_canContinueFileBrowserHiddenReload(e,t){const s=this._ensureFileBrowserState(),n=this.$?.("fileBrowserSearch"),i=typeof n?.value=="string"?n.value:s.filter;return s.view==="normal"&&s.searchEpoch===e.searchEpoch&&s.treeEpoch===e.treeEpoch&&s.ownerSessionId===e.ownerSessionId&&this.activeSessionId===e.ownerSessionId&&this.fileBrowserShowHidden===!0===e.showHidden&&s.filter===e.rawInput&&i===e.rawInput&&i.trim()===e.query&&this.$?.("fileBrowserPanel")?.classList.contains("visible")===!0&&t?.phase==="ready"&&this._isFileBrowserNormalCompatible(t,e.ownerSessionId,e.showHidden,e.treeEpoch)},async toggleFileBrowserHidden(){const e=this._ensureFileBrowserState(),t=typeof e.filter=="string"?e.filter:"",s=t.trim();this.fileBrowserShowHidden=!this.fileBrowserShowHidden;try{localStorage.setItem(FILE_BROWSER_SHOW_HIDDEN_KEY,this.fileBrowserShowHidden?"1":"0")}catch{}this._syncFileBrowserHiddenBtn(),e.inFlight?.timer!==void 0&&e.inFlight?.timer!==null&&clearTimeout(e.inFlight.timer),e.searchEpoch++,e.inFlight=null,e.matches=[],e.deferredDirectoryTarget=null,e.normalState=null,this.fileBrowserData=null,s.length<=256&&(e.view="normal"),this._syncFileBrowserExpandBtn();const n=e.ownerSessionId||this.activeSessionId;if(!n||this.activeSessionId!==n)return;const i=e.searchEpoch,a=this.fileBrowserShowHidden===!0,r=this.loadFileBrowser(n,{force:!0}),o=e.treeEpoch;if(!r?.then||(await r,!s||s.length>256))return;const l={ownerSessionId:n,showHidden:a,treeEpoch:o,searchEpoch:i,rawInput:t,query:s};this._canContinueFileBrowserHiddenReload(l,e.normalState)&&this.filterFileBrowser(t)},loadFileBrowser(e,{force:t=!1}={}){if(!e)return;const s=this._ensureFileBrowserState(),n=this.$("fileBrowserTree");if(this._syncFileBrowserHiddenBtn(),!n||(s.ownerSessionId||(s.ownerSessionId=e),s.ownerSessionId!==e))return;t&&s.treeEpoch++;const i=this.fileBrowserShowHidden===!0,a=s.treeEpoch,r=s.treeInFlight;if(!t&&this._isFileBrowserNormalCompatible(r,e,i,a))return r.promise;const o=s.normalState;if(!t&&this._isFileBrowserNormalCompatible(o,e,i,a)&&(o.phase==="ready"||o.phase==="error"))return o.phase==="ready"&&(this.fileBrowserData=o.data),this._renderFileBrowserNormalState(o),Promise.resolve(o);const l={sessionId:e,showHidden:i,treeEpoch:a,phase:"loading"};s.normalState=l,this.fileBrowserData=null,this._renderFileBrowserNormalState(l);const c={sessionId:e,showHidden:i,treeEpoch:a,promise:null},h=(async()=>{try{const u=await fetch(`/api/sessions/${encodeURIComponent(e)}/files?depth=5&showHidden=${i}`);if(!u.ok)throw new Error("Failed to load files");const d=await u.json(),p=this._validateFileBrowserTreeEnvelope(d);if(!p){const y=d&&typeof d=="object"&&typeof d.error=="string"?d.error:"Failed to load files";throw new Error(y)}if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const m={sessionId:e,showHidden:i,treeEpoch:a,phase:"ready",data:p};s.normalState=m,this.fileBrowserData=p,this._completeDeferredFileBrowserDirectory?.(m)===!0||this._renderFileBrowserNormalState(m)}catch(u){if(!this._isFileBrowserTreeContextCurrent(c,!0))return;const d={sessionId:e,showHidden:i,treeEpoch:a,phase:"error",error:this._normalizeFileBrowserTreeError(u)};s.normalState=d,this.fileBrowserData=null,this._completeDeferredFileBrowserDirectory?.(d),console.error("Failed to load file browser:",u),this._renderFileBrowserNormalState(d)}})();return c.promise=h.finally(()=>{s.treeInFlight===c&&(s.treeInFlight=null)}),s.treeInFlight=c,c.promise},renderFileBrowserTree(e){const t=this.$("fileBrowserTree");if(!t||!this.fileBrowserData)return;const s=this._ensureFileBrowserState(),n=e||s.normalState?.sessionId||s.ownerSessionId||this.activeSessionId;if(!n)return;const{tree:i}=this.fileBrowserData;if(!i||i.length===0){t.innerHTML='<div class="file-browser-empty">No files found</div>';return}const a=[],r=(o,l)=>{const c=o.type==="directory",h=this.fileBrowserExpandedDirs.has(o.path),u=c?h?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(o.extension),d=c?`<span class="file-tree-expand${h?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',p=!c&&o.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(o.size)}</span>`:"",m=c?"file-tree-name directory":"file-tree-name",g=c?"":`<a class="file-tree-download" href="${escapeHtml(CodemanBase.url(`/api/sessions/${encodeURIComponent(n)}/file-raw?path=${encodeURIComponent(o.path)}&download=true`))}" title="Download" onclick="event.stopPropagation()">⬇</a>`;if(a.push(`
|
|
224
|
-
<div class="file-tree-item" data-path="${escapeHtml(
|
|
225
|
-
${
|
|
226
|
-
<span class="file-tree-icon">${
|
|
227
|
-
<span class="${m}">${escapeHtml(
|
|
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()">⬇</a>`;if(o.push(`
|
|
224
|
+
<div class="file-tree-item" data-path="${escapeHtml(a.path)}" data-type="${escapeHtml(a.type)}" data-depth="${l}">
|
|
225
|
+
${h}
|
|
226
|
+
<span class="file-tree-icon">${d}</span>
|
|
227
|
+
<span class="${m}">${escapeHtml(a.name)}</span>
|
|
228
228
|
${p}
|
|
229
229
|
${g}
|
|
230
230
|
</div>
|
|
231
|
-
`),c&&
|
|
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()">⬇</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">${
|
|
235
|
-
<span class="${
|
|
236
|
-
${
|
|
234
|
+
<span class="file-tree-icon">${u}</span>
|
|
235
|
+
<span class="${h}">${escapeHtml(l.name)}</span>
|
|
236
|
+
${d}
|
|
237
237
|
${p}
|
|
238
238
|
</div>
|
|
239
|
-
`}).join("")}n.querySelectorAll(".file-tree-item").forEach(
|
|
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
240
|
`),f=$.length>500,S=f?$.slice(0,500).join(`
|
|
241
|
-
`):w;if(
|
|
242
|
-
OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFilePreviewEdit(!0);return}if(!
|
|
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 D=this.attachmentCards.keys().next().value;D&&this.closeAttachmentCard(D)}const g=this.ensureAttachmentCardStack(),w=this.sessions.get(t)?.name||t.substring(0,8),b=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=`
|
|
243
243
|
<div class="attachment-thumbnail">
|
|
244
244
|
${f?`<img class="attachment-thumbnail-img" src="${escapeHtml(f)}" alt="">`:""}
|
|
245
245
|
<div class="attachment-thumbnail-fallback ${f?"":"visible"}">${escapeHtml(T)}</div>
|
|
246
246
|
</div>
|
|
247
247
|
<div class="attachment-card-main">
|
|
248
|
-
<div class="attachment-file-name" title="${escapeHtml(
|
|
248
|
+
<div class="attachment-file-name" title="${escapeHtml(h)}">${escapeHtml(n)}</div>
|
|
249
249
|
<div class="attachment-file-meta">
|
|
250
250
|
<span>${escapeHtml(w)}</span>
|
|
251
|
-
<span>${this.formatFileSize(
|
|
251
|
+
<span>${this.formatFileSize(o||0)}</span>
|
|
252
252
|
</div>
|
|
253
253
|
<div class="attachment-actions">
|
|
254
254
|
<button type="button" class="attachment-preview-btn">Preview</button>
|
|
@@ -257,7 +257,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
257
257
|
</div>
|
|
258
258
|
</div>
|
|
259
259
|
<button type="button" class="attachment-close-btn" title="Dismiss">×</button>
|
|
260
|
-
`;const I=v.querySelector(".attachment-thumbnail-img");I&&(I.onerror=()=>{I.remove(),v.querySelector(".attachment-thumbnail-fallback")?.classList.add("visible")}),v.querySelector(".attachment-preview-btn")?.addEventListener("click",()=>{this.openFilePreview(
|
|
260
|
+
`;const I=v.querySelector(".attachment-thumbnail-img");I&&(I.onerror=()=>{I.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=`
|
|
261
261
|
<div class="attachment-history-header">
|
|
262
262
|
<div>
|
|
263
263
|
<div class="attachment-history-title">Attachments</div>
|
|
@@ -269,14 +269,14 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
269
269
|
</div>
|
|
270
270
|
</div>
|
|
271
271
|
<div class="attachment-history-list" id="attachmentHistoryList"></div>
|
|
272
|
-
`,document.body.appendChild(e),e.querySelector("#attachmentHistoryRefreshBtn")?.addEventListener("click",()=>{this.loadAttachmentHistory(this.activeSessionId)}),e.querySelector("#attachmentHistoryCloseBtn")?.addEventListener("click",()=>{this.closeAttachmentHistory()}),e)},async toggleAttachmentHistory(){if(this.attachmentHistoryDrawerOpen){this.closeAttachmentHistory();return}await this.openAttachmentHistory()},async openAttachmentHistory(){const e=this.ensureAttachmentHistoryDrawer();this.attachmentHistoryDrawerOpen=!0,e.classList.add("open"),this.updateAttachmentHistoryBadge(),await this.loadAttachmentHistory(this.activeSessionId)},closeAttachmentHistory(){const e=document.getElementById("attachmentHistoryDrawer");this.attachmentHistoryDrawerOpen=!1,e?.classList.remove("open"),this._debounceTimers?.attachmentHistoryRefresh&&(clearTimeout(this._debounceTimers.attachmentHistoryRefresh),this._debounceTimers.attachmentHistoryRefresh=null),this.updateAttachmentHistoryBadge()},async loadAttachmentHistory(e=this.activeSessionId){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(!e){this.attachmentHistoryItems=[],n.textContent="No session",s.innerHTML='<div class="attachment-history-empty">No active session</div>',this.updateAttachmentHistoryBadge(0);return}s.innerHTML='<div class="attachment-history-empty">Loading...</div>';try{const i=await fetch(`/api/sessions/${e}/attachments`);if(!i.ok)throw new Error("Failed to load attachments");const
|
|
272
|
+
`,document.body.appendChild(e),e.querySelector("#attachmentHistoryRefreshBtn")?.addEventListener("click",()=>{this.loadAttachmentHistory(this.activeSessionId)}),e.querySelector("#attachmentHistoryCloseBtn")?.addEventListener("click",()=>{this.closeAttachmentHistory()}),e)},async toggleAttachmentHistory(){if(this.attachmentHistoryDrawerOpen){this.closeAttachmentHistory();return}await this.openAttachmentHistory()},async openAttachmentHistory(){const e=this.ensureAttachmentHistoryDrawer();this.attachmentHistoryDrawerOpen=!0,e.classList.add("open"),this.updateAttachmentHistoryBadge(),await this.loadAttachmentHistory(this.activeSessionId)},closeAttachmentHistory(){const e=document.getElementById("attachmentHistoryDrawer");this.attachmentHistoryDrawerOpen=!1,e?.classList.remove("open"),this._debounceTimers?.attachmentHistoryRefresh&&(clearTimeout(this._debounceTimers.attachmentHistoryRefresh),this._debounceTimers.attachmentHistoryRefresh=null),this.updateAttachmentHistoryBadge()},async loadAttachmentHistory(e=this.activeSessionId){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(!e){this.attachmentHistoryItems=[],n.textContent="No session",s.innerHTML='<div class="attachment-history-empty">No active session</div>',this.updateAttachmentHistoryBadge(0);return}s.innerHTML='<div class="attachment-history-empty">Loading...</div>';try{const i=await fetch(`/api/sessions/${e}/attachments`);if(!i.ok)throw new Error("Failed to load attachments");const o=await i.json();if(!o.success)throw new Error(o.error||"Failed to load attachments");const r=o.data?.items||[];this.attachmentHistoryItems=r,this.attachmentHistoryCounts.set(e,r.length),this.updateAttachmentHistoryBadge(r.length),this.renderAttachmentHistory(r)}catch(i){console.error("Failed to load attachment history:",i),n.textContent="Unavailable",s.innerHTML=`<div class="attachment-history-empty">Error: ${escapeHtml(i.message)}</div>`}}},renderAttachmentHistory(e=this.attachmentHistoryItems||[]){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(n.textContent=`${e.length} ${e.length===1?"file":"files"}`,e.length===0){s.innerHTML=`
|
|
273
273
|
<div class="attachment-history-empty">
|
|
274
274
|
<div class="attachment-history-empty-title">No attachments yet</div>
|
|
275
275
|
<div>Show a file here by running:</div>
|
|
276
276
|
<code>codeman attach /absolute/path/to/file.pptx</code>
|
|
277
277
|
<div>Supports .pptx, .docx, .pdf, .png, .md, and .txt.</div>
|
|
278
278
|
</div>
|
|
279
|
-
`;return}s.innerHTML=e.map(i=>this.renderAttachmentHistoryItem(i)).join(""),s.querySelectorAll(".attachment-history-thumb-img").forEach(i=>{i.onerror=()=>{i.remove(),i.closest(".attachment-history-thumb")?.querySelector(".attachment-history-thumb-fallback")?.classList.add("visible")}}),s.querySelectorAll("[data-attachment-action]").forEach(i=>{i.addEventListener("click",()=>{const
|
|
279
|
+
`;return}s.innerHTML=e.map(i=>this.renderAttachmentHistoryItem(i)).join(""),s.querySelectorAll(".attachment-history-thumb-img").forEach(i=>{i.onerror=()=>{i.remove(),i.closest(".attachment-history-thumb")?.querySelector(".attachment-history-thumb-fallback")?.classList.add("visible")}}),s.querySelectorAll("[data-attachment-action]").forEach(i=>{i.addEventListener("click",()=>{const o=i.getAttribute("data-history-id"),r=i.getAttribute("data-attachment-action");!o||!r||(r==="preview"&&this.previewAttachmentHistoryItem(o),r==="download"&&this.downloadAttachmentHistoryItem(o),r==="open"&&this.openAttachmentHistoryItem(o),r==="reshow"&&this.reshowAttachmentCard(o))})})}},renderAttachmentHistoryItem(e){const t=(e.extension||e.attachmentType||"file").toUpperCase(),s=[e.source==="external"?"published":"workspace",this.formatFileSize(e.size||0),e.missing?"missing":""].filter(Boolean).join(" \u2022 "),n=e.thumbnailUrl&&!e.missing?`<img class="attachment-history-thumb-img" src="${escapeHtml(CodemanBase.url(e.thumbnailUrl))}" alt="">`:"",i=e.missing?'disabled aria-disabled="true"':"";return`
|
|
280
280
|
<div class="attachment-history-item ${e.missing?"missing":""}" data-history-item="${escapeHtml(e.id)}">
|
|
281
281
|
<div class="attachment-history-thumb">
|
|
282
282
|
${n}
|
|
@@ -293,7 +293,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
293
293
|
</div>
|
|
294
294
|
</div>
|
|
295
295
|
</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
|
|
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=`
|
|
297
297
|
<div class="log-viewer-window-header">
|
|
298
298
|
<div class="log-viewer-window-title" title="${escapeHtml(e)}">
|
|
299
299
|
<span class="icon">\u{1F4C4}</span>
|
|
@@ -307,7 +307,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
307
307
|
<div class="log-viewer-window-body" id="log-viewer-body-${s}">
|
|
308
308
|
<div class="log-info">Connecting to ${escapeHtml(e)}...</div>
|
|
309
309
|
</div>
|
|
310
|
-
`,document.body.appendChild(
|
|
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=`
|
|
311
311
|
<div class="image-popup-header">
|
|
312
312
|
<div class="image-popup-title" title="${escapeHtml(s)}">
|
|
313
313
|
<span class="icon">\u{1F5BC}\uFE0F</span>
|
|
@@ -317,7 +317,7 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
317
317
|
</div>
|
|
318
318
|
<div class="image-popup-actions">
|
|
319
319
|
<button onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(w))})" title="Open in new tab">\u2197</button>
|
|
320
|
-
<button onclick="app.closeImagePopup(${escapeHtml(JSON.stringify(
|
|
320
|
+
<button onclick="app.closeImagePopup(${escapeHtml(JSON.stringify(a))})" title="Close">\xD7</button>
|
|
321
321
|
</div>
|
|
322
322
|
</div>
|
|
323
323
|
<div class="image-popup-body">
|
|
@@ -325,9 +325,9 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
325
325
|
onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
|
|
326
326
|
onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(w))})" />
|
|
327
327
|
</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(
|
|
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.terminal.clear(),this.terminal.reset(),this.toast("All sessions and tmux killed","success")}}else{this.sessions.clear(),this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.terminal.clear(),this.terminal.reset(),this.toast("All tabs removed, tmux still running","info")}}catch(t){console.error("Failed to kill sessions:",t),this.toast("Failed to kill sessions: "+t.message,"error")}},renderMuxSessions(){this._debouncedCall("muxSessions",this._renderMuxSessionsImmediate)},_renderMuxSessionsImmediate(){const e=document.getElementById("muxSessionsBody");if(!this.muxSessions||this.muxSessions.length===0){e.innerHTML='<div class="monitor-empty">No mux sessions</div>';return}let t="";for(const s of this.muxSessions){const n=s.stats||{memoryMB:0,cpuPercent:0,childCount:0},i=this.sessions.get(s.sessionId),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+=`
|
|
329
329
|
<div class="process-item process-item-clickable" onclick="app.selectSession(${b})" title="Switch to session">
|
|
330
|
-
<span class="monitor-status-badge ${l}">${
|
|
330
|
+
<span class="monitor-status-badge ${l}">${a}</span>
|
|
331
331
|
<div class="process-info">
|
|
332
332
|
<div class="process-name">${w} ${escapeHtml(s.name||s.muxName)}</div>
|
|
333
333
|
<div class="process-meta">
|
|
@@ -342,18 +342,18 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
|
|
|
342
342
|
<button class="btn-toolbar btn-sm btn-danger" onclick="event.stopPropagation(); app.killMuxSession(${b})" title="Kill session">Kill</button>
|
|
343
343
|
</div>
|
|
344
344
|
</div>
|
|
345
|
-
`}e.innerHTML=t},renderMonitorSubagents(){const e=document.getElementById("monitorSubagentsBody"),t=document.getElementById("monitorSubagentStats");if(!e)return;const s=Array.from(this.subagents.values()),n=s.filter(
|
|
345
|
+
`}e.innerHTML=t},renderMonitorSubagents(){const e=document.getElementById("monitorSubagentsBody"),t=document.getElementById("monitorSubagentStats");if(!e)return;const s=Array.from(this.subagents.values()),n=s.filter(o=>o.status==="active"||o.status==="idle").length;if(t&&(t.textContent=`${s.length} tracked`+(n>0?`, ${n} active`:"")),s.length===0){e.innerHTML='<div class="monitor-empty">No background agents</div>';return}let i="";for(const o of s){const r=o.status==="active"?"active":o.status==="idle"?"idle":"completed",a=o.modelShort?`<span class="model-badge ${o.modelShort}">${o.modelShort}</span>`:"",l=o.description?escapeHtml(o.description.substring(0,40)):o.agentId;i+=`
|
|
346
346
|
<div class="process-item">
|
|
347
|
-
<span class="process-mode ${r}">${
|
|
347
|
+
<span class="process-mode ${r}">${o.status}</span>
|
|
348
348
|
<div class="process-info">
|
|
349
|
-
<div class="process-name">${
|
|
349
|
+
<div class="process-name">${a} ${l}</div>
|
|
350
350
|
<div class="process-meta">
|
|
351
|
-
<span>ID: ${
|
|
352
|
-
<span>${
|
|
351
|
+
<span>ID: ${o.agentId}</span>
|
|
352
|
+
<span>${o.toolCallCount||0} tools</span>
|
|
353
353
|
</div>
|
|
354
354
|
</div>
|
|
355
355
|
<div class="process-actions">
|
|
356
|
-
${
|
|
356
|
+
${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
357
|
</div>
|
|
358
358
|
</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,
|
|
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.textContent=e,o.appendChild(r),i){const a=document.createElement("button");a.textContent=i.label,a.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",a.onclick=l=>{l.stopPropagation(),i.onClick(),o.remove()},o.appendChild(a)}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")),setTimeout(()=>{o.classList.remove("show"),setTimeout(()=>o.remove(),200)},n)},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()}}});
|