aicodeman 1.2.1 → 1.3.0
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 +264 -90
- package/dist/attachment-magic.d.ts +13 -0
- package/dist/attachment-magic.d.ts.map +1 -1
- package/dist/attachment-magic.js +47 -0
- package/dist/attachment-magic.js.map +1 -1
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +13 -2
- package/dist/attachment-registry.js.map +1 -1
- package/dist/config/buffer-limits.d.ts +2 -3
- package/dist/config/buffer-limits.d.ts.map +1 -1
- package/dist/config/buffer-limits.js +5 -5
- package/dist/config/buffer-limits.js.map +1 -1
- package/dist/config/map-limits.d.ts +11 -0
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +11 -0
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config/server-timing.d.ts +6 -0
- package/dist/config/server-timing.d.ts.map +1 -1
- package/dist/config/server-timing.js +9 -0
- package/dist/config/server-timing.js.map +1 -1
- package/dist/config/terminal-history.d.ts +30 -0
- package/dist/config/terminal-history.d.ts.map +1 -0
- package/dist/config/terminal-history.js +43 -0
- package/dist/config/terminal-history.js.map +1 -0
- package/dist/cron/cron-input.d.ts +30 -0
- package/dist/cron/cron-input.d.ts.map +1 -0
- package/dist/cron/cron-input.js +7 -0
- package/dist/cron/cron-input.js.map +1 -0
- package/dist/cron/cron-service.d.ts +92 -0
- package/dist/cron/cron-service.d.ts.map +1 -0
- package/dist/cron/cron-service.js +606 -0
- package/dist/cron/cron-service.js.map +1 -0
- package/dist/cron/cron-time.d.ts +29 -0
- package/dist/cron/cron-time.d.ts.map +1 -0
- package/dist/cron/cron-time.js +87 -0
- package/dist/cron/cron-time.js.map +1 -0
- package/dist/document-thumbnailer.d.ts +1 -1
- package/dist/document-thumbnailer.d.ts.map +1 -1
- package/dist/document-thumbnailer.js +11 -2
- package/dist/document-thumbnailer.js.map +1 -1
- package/dist/generated-artifact-attachments.d.ts +26 -0
- package/dist/generated-artifact-attachments.d.ts.map +1 -0
- package/dist/generated-artifact-attachments.js +61 -0
- package/dist/generated-artifact-attachments.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/mux-interface.d.ts +37 -5
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/remote-hosts.d.ts +66 -0
- package/dist/remote-hosts.d.ts.map +1 -0
- package/dist/remote-hosts.js +198 -0
- package/dist/remote-hosts.js.map +1 -0
- package/dist/services/unified-session-service.d.ts +115 -0
- package/dist/services/unified-session-service.d.ts.map +1 -0
- package/dist/services/unified-session-service.js +173 -0
- package/dist/services/unified-session-service.js.map +1 -0
- package/dist/session-cli-builder.d.ts +3 -1
- package/dist/session-cli-builder.d.ts.map +1 -1
- package/dist/session-cli-builder.js +26 -7
- package/dist/session-cli-builder.js.map +1 -1
- package/dist/session-pty-exit-breaker.d.ts +66 -0
- package/dist/session-pty-exit-breaker.d.ts.map +1 -0
- package/dist/session-pty-exit-breaker.js +77 -0
- package/dist/session-pty-exit-breaker.js.map +1 -0
- package/dist/session.d.ts +27 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +102 -11
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +14 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +49 -0
- package/dist/state-store.js.map +1 -1
- package/dist/tmux-manager.d.ts +100 -8
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +249 -26
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/api.d.ts +11 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/types/app-state.d.ts +5 -0
- package/dist/types/app-state.d.ts.map +1 -1
- package/dist/types/app-state.js.map +1 -1
- package/dist/types/cron.d.ts +88 -0
- package/dist/types/cron.d.ts.map +1 -0
- package/dist/types/cron.js +13 -0
- package/dist/types/cron.js.map +1 -0
- package/dist/types/session.d.ts +57 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/web/heic-jpeg-converter.d.ts +24 -0
- package/dist/web/heic-jpeg-converter.d.ts.map +1 -0
- package/dist/web/heic-jpeg-converter.js +76 -0
- package/dist/web/heic-jpeg-converter.js.map +1 -0
- package/dist/web/heic-jpeg-worker.d.ts +44 -0
- package/dist/web/heic-jpeg-worker.d.ts.map +1 -0
- package/dist/web/heic-jpeg-worker.js +78 -0
- package/dist/web/heic-jpeg-worker.js.map +1 -0
- package/dist/web/ports/config-port.d.ts +2 -0
- package/dist/web/ports/config-port.d.ts.map +1 -1
- package/dist/web/ports/cron-port.d.ts +9 -0
- package/dist/web/ports/cron-port.d.ts.map +1 -0
- package/dist/web/ports/cron-port.js +6 -0
- package/dist/web/ports/cron-port.js.map +1 -0
- package/dist/web/ports/index.d.ts +1 -0
- package/dist/web/ports/index.d.ts.map +1 -1
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.b0534652.js +38 -0
- package/dist/web/public/app.b0534652.js.br +0 -0
- package/dist/web/public/app.b0534652.js.gz +0 -0
- package/dist/web/public/{constants.1c779517.js → constants.5b18ed64.js} +65 -0
- package/dist/web/public/constants.5b18ed64.js.br +0 -0
- package/dist/web/public/constants.5b18ed64.js.gz +0 -0
- package/dist/web/public/cron-ui.js +328 -0
- package/dist/web/public/cron-ui.js.br +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +296 -25
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js +1 -0
- package/dist/web/public/input-cjk.63794d0b.js.br +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.0644e3ac.css +1 -0
- package/dist/web/public/mobile.0644e3ac.css.br +0 -0
- package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
- package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.772e92c9.js → panels-ui.ca03853f.js} +25 -17
- package/dist/web/public/panels-ui.ca03853f.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.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/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.b51814ef.js +49 -0
- package/dist/web/public/session-ui.b51814ef.js.br +0 -0
- package/dist/web/public/session-ui.b51814ef.js.gz +0 -0
- package/dist/web/public/settings-ui.6c67c125.js +67 -0
- package/dist/web/public/settings-ui.6c67c125.js.br +0 -0
- package/dist/web/public/settings-ui.6c67c125.js.gz +0 -0
- package/dist/web/public/styles.e1ba7849.css +1 -0
- package/dist/web/public/styles.e1ba7849.css.br +0 -0
- package/dist/web/public/styles.e1ba7849.css.gz +0 -0
- package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.js +3 -0
- package/dist/web/public/terminal-ui.dbf141fa.js.br +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.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-zerolag-input.137ad9f0.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.085e9e73.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +115 -1
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/cron-routes.d.ts +11 -0
- package/dist/web/routes/cron-routes.d.ts.map +1 -0
- package/dist/web/routes/cron-routes.js +70 -0
- package/dist/web/routes/cron-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/respawn-routes.d.ts.map +1 -1
- package/dist/web/routes/respawn-routes.js +3 -0
- package/dist/web/routes/respawn-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +614 -68
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +5 -0
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +56 -17
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +117 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +168 -1
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +9 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +82 -13
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +5 -1
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +23 -1
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/sse-events.d.ts +15 -0
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +17 -0
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/ws-connection-registry.d.ts +77 -0
- package/dist/web/ws-connection-registry.d.ts.map +1 -0
- package/dist/web/ws-connection-registry.js +99 -0
- package/dist/web/ws-connection-registry.js.map +1 -0
- package/package.json +3 -1
- package/dist/web/public/app.a8d73c32.js +0 -38
- package/dist/web/public/app.a8d73c32.js.br +0 -0
- package/dist/web/public/app.a8d73c32.js.gz +0 -0
- package/dist/web/public/constants.1c779517.js.br +0 -0
- package/dist/web/public/constants.1c779517.js.gz +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js +0 -1
- package/dist/web/public/input-cjk.4e950e0a.js.br +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js.gz +0 -0
- package/dist/web/public/mobile.8a9aff7a.css +0 -1
- package/dist/web/public/mobile.8a9aff7a.css.br +0 -0
- package/dist/web/public/mobile.8a9aff7a.css.gz +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.br +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.gz +0 -0
- package/dist/web/public/session-ui.da3e53f2.js +0 -36
- package/dist/web/public/session-ui.da3e53f2.js.br +0 -0
- package/dist/web/public/session-ui.da3e53f2.js.gz +0 -0
- package/dist/web/public/settings-ui.38feb956.js +0 -61
- package/dist/web/public/settings-ui.38feb956.js.br +0 -0
- package/dist/web/public/settings-ui.38feb956.js.gz +0 -0
- package/dist/web/public/styles.1b3e6147.css +0 -1
- package/dist/web/public/styles.1b3e6147.css.br +0 -0
- package/dist/web/public/styles.1b3e6147.css.gz +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js +0 -3
- package/dist/web/public/terminal-ui.865f21f6.js.br +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js.gz +0 -0
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
"use strict";const AWAY_DIGEST_LAST_VIEWED_KEY="codeman-away-digest-last-viewed",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()},_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)},
|
|
1
|
+
"use strict";const AWAY_DIGEST_LAST_VIEWED_KEY="codeman-away-digest-last-viewed",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()},_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(o=>o.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(),a=typeof n.className=="string"?n.className:"";return n.classList?.contains?.("xterm-helper-textarea")||a.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(a=>!this.sessionOrder?.includes?.(a))],n=new Set,i=[];for(const a of s){if(n.has(a))continue;n.add(a);const r=this.sessions?.get?.(a);if(!r)continue;const o=this.getSessionName?.(r)||r.name||r.title||a.slice(0,8),l=[r.workingDir,r.mode,r.status].filter(Boolean),d=[o,r.workingDir,r.mode,r.status,a].filter(Boolean).join(" ").toLowerCase();t&&!d.includes(t)||i.push({id:`session:${a}`,type:"session",sessionId:a,title:o,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"},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=a=>{const r=String(a?.name||"").trim();if(!r)return 0;const o=[r,a?.path,a?.casePath,a?.workingDir,a?.remote?.path,a?.remote?.hostId].filter(Boolean).join(" ").toLowerCase(),l=r.toLowerCase();return l===t?100:l.startsWith(t)?90:l.includes(t)?80:o.includes(t)?60:0};let n=null,i=0;for(const a of this.cases){const r=s(a);r>i&&(n=a,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,a)=>{const r=a===this.commandPaletteActiveIndex?" active":"",o=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="${a}">
|
|
3
|
+
<span class="command-palette-icon" aria-hidden="true">${o}</span>
|
|
4
|
+
<span class="command-palette-text">
|
|
5
|
+
<span class="command-palette-title">${escapeHtml(i.title)}</span>
|
|
6
|
+
<span class="command-palette-subtitle">${escapeHtml(i.subtitle||"")}</span>
|
|
7
|
+
</span>
|
|
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 a=i.data.sessions||[];if(t.replaceChildren(),a.length===0){this._setSessionManagerMessage(t,e?"No sessions match your search":"No sessions found");return}for(const r of a){const o={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||""},l=!!this.sessions?.has?.(r.sessionId),d=this._buildHistoryItem(o,this.cases,{showViewAll:!1,onActivate:()=>{this.closeSessionManager(),l?this.selectSession(r.sessionId):o.workingDir&&this.resumeHistorySession(r.claudeSessionId||r.sessionId,o.workingDir)}});t.appendChild(d)}}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 o=this.readAwayDigestLastViewed();Number.isFinite(o)&&i.set("lastViewed",String(o))}if(n==="custom"){this.ensureAwayDigestCustomDefaults();const o=this.readAwayDigestDateTimeInput("awayDigestCustomSince"),l=this.readAwayDigestDateTimeInput("awayDigestCustomUntil");if(!Number.isFinite(o))throw new Error("Choose a custom start time");i.set("since",String(o)),Number.isFinite(l)&&i.set("until",String(l))}const a=await fetch(`/api/away-digest?${i.toString()}`),r=await a.json();if(!a.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,a=e.totals.outputTokens||0,r=e.totals.estimatedCost||0;t.innerHTML=`
|
|
2
10
|
<div class="away-digest-card">
|
|
3
11
|
<span class="away-digest-card-label">Needs Attention</span>
|
|
4
12
|
<span class="away-digest-card-value">${e.totals.needsAttention}</span>
|
|
@@ -59,7 +67,7 @@
|
|
|
59
67
|
<span class="stat-card-value">${this.formatTokens(c+m)}</span>
|
|
60
68
|
<span class="stat-card-cost">~$${u.toFixed(2)}</span>
|
|
61
69
|
</div>
|
|
62
|
-
`;const p=document.getElementById("statsChart"),
|
|
70
|
+
`;const p=document.getElementById("statsChart"),y=document.getElementById("statsChartDays"),b=[];for(let g=6;g>=0;g--){const S=new Date;S.setDate(S.getDate()-g);const $=S.toISOString().split("T")[0],w=t.find(D=>D.date===$);b.push({date:$,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:w?w.inputTokens+w.outputTokens:0,cost:w?w.estimatedCost:0})}const f=Math.max(...b.map(g=>g.tokens),1);p.innerHTML=b.map(g=>{const S=Math.max(g.tokens/f*100,3),$=`${g.dayName}: ${this.formatTokens(g.tokens)} (~$${g.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${$}"></div>`}).join(""),y.innerHTML=b.map(g=>`<span>${g.dayName}</span>`).join("");const v=document.getElementById("statsTable"),T=t.slice(0,14);T.length===0?v.innerHTML='<div class="stats-no-data">No usage data recorded yet</div>':v.innerHTML=`
|
|
63
71
|
<div class="stats-table-header">
|
|
64
72
|
<span>Date</span>
|
|
65
73
|
<span>Input</span>
|
|
@@ -74,7 +82,7 @@
|
|
|
74
82
|
<span class="cell cell-cost">$${g.estimatedCost.toFixed(2)}</span>
|
|
75
83
|
</div>
|
|
76
84
|
`).join("")}
|
|
77
|
-
`},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,a,r;const o=c=>{if(c.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const m=getEventCoords(c);n=m.clientX,i=m.clientY;const u=e.getBoundingClientRect();a=u.left,r=u.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",d),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",d),c.preventDefault()},l=c=>{if(!s)return;const m=getEventCoords(c),u=m.clientX-n,h=m.clientY-i;let p=a+u,
|
|
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,a,r;const o=c=>{if(c.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const m=getEventCoords(c);n=m.clientX,i=m.clientY;const u=e.getBoundingClientRect();a=u.left,r=u.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",d),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",d),c.preventDefault()},l=c=>{if(!s)return;const m=getEventCoords(c),u=m.clientX-n,h=m.clientY-i;let p=a+u,y=r+h;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),y=Math.max(0,Math.min(window.innerHeight-b.height,y)),e.style.left=p+"px",e.style.top=y+"px"},d=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",d),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",d)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},toggleSubagentsDetach(){const e=document.getElementById("subagentsPanel"),t=document.getElementById("subagentsDetachBtn");e.classList.contains("detached")?(e.classList.remove("detached"),e.style.top="",e.style.left="",e.style.width="",e.style.height="",t&&(t.innerHTML="⧉",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,a,r;const o=c=>{if(c.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const m=getEventCoords(c);n=m.clientX,i=m.clientY;const u=e.getBoundingClientRect();a=u.left,r=u.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",d),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",d),c.preventDefault()},l=c=>{if(!s)return;const m=getEventCoords(c),u=m.clientX-n,h=m.clientY-i;let p=a+u,y=r+h;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),y=Math.max(0,Math.min(window.innerHeight-b.height,y)),e.style.left=p+"px",e.style.top=y+"px"},d=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",d),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",d)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=o,t._touchDragHandler=o,t.addEventListener("mousedown",o),t.addEventListener("touchstart",o,{passive:!1})},renderTaskPanel(){this._debouncedCall("taskPanel",this._renderTaskPanelImmediate)},_renderTaskPanelImmediate(){const e=this.sessions.get(this.activeSessionId),t=document.getElementById("backgroundTasksBody"),s=document.getElementById("taskPanelStats"),n=document.getElementById("backgroundTasksSection");if(!e||!e.taskTree||e.taskTree.length===0){n&&(n.style.display="none"),t.innerHTML="",s.textContent="0 tasks";return}n&&(n.style.display="");const i=e.taskStats||{running:0,completed:0,failed:0,total:0};s.textContent=`${i.running} running, ${i.completed} done`;const a=(l,d)=>{const c=l.status==="running"?"":l.status==="completed"?"✓":"✗",m=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let u="";if(l.children&&l.children.length>0){u='<div class="task-children">';for(const h of l.children){const p=d.find(y=>y.id===h);p&&(u+=`<div class="task-node">${a(p,d)}</div>`)}u+="</div>"}return`
|
|
78
86
|
<div class="task-item">
|
|
79
87
|
<span class="task-status-icon ${l.status}">${c}</span>
|
|
80
88
|
<div class="task-info">
|
|
@@ -86,13 +94,13 @@
|
|
|
86
94
|
</div>
|
|
87
95
|
</div>
|
|
88
96
|
${u}
|
|
89
|
-
`},r=this.flattenTaskTree(e.taskTree);let o='<div class="task-tree">';for(const l of e.taskTree)o+=`<div class="task-node">${a(l,r)}</div>`;o+="</div>",t.innerHTML=o},flattenTaskTree(e,t=[]){for(const s of e)t.push(s);return t},toggleSubagentPanel(){this.toggleSubagentsPanel()},updateSubagentBadge(){const e=this.$("subagentCountBadge"),t=Array.from(this.subagents.values()).filter(s=>s.status==="active"||s.status==="idle").length;e&&(e.textContent=t>0?t:"")},renderSubagentPanel(){this._subagentPanelRenderTimeout&&clearTimeout(this._subagentPanelRenderTimeout),this._subagentPanelRenderTimeout=setTimeout(()=>{scheduleBackground(()=>this._renderSubagentPanelImmediate())},150)},_renderSubagentPanelImmediate(){const e=this.$("subagentList");if(!e||(this.updateSubagentBadge(),this.renderMonitorSubagents(),!this.subagentPanelVisible))return;if(this.subagents.size===0){e.innerHTML='<div class="subagent-empty">No background agents detected</div>';return}const t=[],s=Array.from(this.subagents.values()).sort((n,i)=>n.status==="active"&&i.status!=="active"?-1:i.status==="active"&&n.status!=="active"?1:(i.lastActivityAt||0)-(n.lastActivityAt||0));for(const n of s){const i=this.activeSubagentId===n.agentId,a=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],o=r[r.length-1],l=o?.type==="tool"?o.tool:null,d=this.subagentWindows.has(n.agentId),c=n.status==="active"||n.status==="idle",m=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",u=this.getTeammateInfo(n),h=u?u.name:n.description||n.agentId.substring(0,7),p=this.getTeammateBadgeHtml(n),
|
|
97
|
+
`},r=this.flattenTaskTree(e.taskTree);let o='<div class="task-tree">';for(const l of e.taskTree)o+=`<div class="task-node">${a(l,r)}</div>`;o+="</div>",t.innerHTML=o},flattenTaskTree(e,t=[]){for(const s of e)t.push(s);return t},toggleSubagentPanel(){this.toggleSubagentsPanel()},updateSubagentBadge(){const e=this.$("subagentCountBadge"),t=Array.from(this.subagents.values()).filter(s=>s.status==="active"||s.status==="idle").length;e&&(e.textContent=t>0?t:"")},renderSubagentPanel(){this._subagentPanelRenderTimeout&&clearTimeout(this._subagentPanelRenderTimeout),this._subagentPanelRenderTimeout=setTimeout(()=>{scheduleBackground(()=>this._renderSubagentPanelImmediate())},150)},_renderSubagentPanelImmediate(){const e=this.$("subagentList");if(!e||(this.updateSubagentBadge(),this.renderMonitorSubagents(),!this.subagentPanelVisible))return;if(this.subagents.size===0){e.innerHTML='<div class="subagent-empty">No background agents detected</div>';return}const t=[],s=Array.from(this.subagents.values()).sort((n,i)=>n.status==="active"&&i.status!=="active"?-1:i.status==="active"&&n.status!=="active"?1:(i.lastActivityAt||0)-(n.lastActivityAt||0));for(const n of s){const i=this.activeSubagentId===n.agentId,a=n.status==="active"?"active":n.status==="idle"?"idle":"completed",r=this.subagentActivity.get(n.agentId)||[],o=r[r.length-1],l=o?.type==="tool"?o.tool:null,d=this.subagentWindows.has(n.agentId),c=n.status==="active"||n.status==="idle",m=n.modelShort?`<span class="subagent-model-badge ${escapeHtml(n.modelShort)}">${escapeHtml(n.modelShort)}</span>`:"",u=this.getTeammateInfo(n),h=u?u.name:n.description||n.agentId.substring(0,7),p=this.getTeammateBadgeHtml(n),y=u?`<span class="subagent-icon teammate-dot teammate-color-${u.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
|
|
90
98
|
<div class="subagent-item ${a} ${i?"selected":""}${u?" is-teammate":""}"
|
|
91
99
|
onclick="app.selectSubagent(${escapeHtml(JSON.stringify(n.agentId))})"
|
|
92
100
|
ondblclick="app.openSubagentWindow(${escapeHtml(JSON.stringify(n.agentId))})"
|
|
93
101
|
title="Double-click to open tracking window">
|
|
94
102
|
<div class="subagent-header">
|
|
95
|
-
${
|
|
103
|
+
${y}
|
|
96
104
|
<span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(h.length>40?h.substring(0,40)+"...":h)}</span>
|
|
97
105
|
${p}
|
|
98
106
|
${m}
|
|
@@ -179,7 +187,7 @@
|
|
|
179
187
|
<span class="tool-detail">${escapeHtml(i)}</span>
|
|
180
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">
|
|
181
189
|
<span class="time">${t}</span> \u{1F4AC} ${escapeHtml(s)}
|
|
182
|
-
</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 d=this.getTeammateInfo(t),c=d?d.name:t.description||e.substring(0,7),m=c.length>50?c.substring(0,50)+"...":c;n.textContent=m}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const d=s.querySelector(".subagent-window-title");if(d){const c=document.createElement("span");c.className=`teammate-badge teammate-color-${a.color}`,c.title=`Team: ${a.teamName}`,c.textContent=`@${a.name}`;const m=d.querySelector(".status");m&&m.insertAdjacentElement("beforebegin",c)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const d=s.querySelector(".subagent-window-title .status");d&&d.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},fontFamily:'"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const c=new Unicode11Addon.Unicode11Addon;r.loadAddon(c),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(c){console.warn("[TeammateTerminal] Failed to open terminal:",c),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(c=>c.json()).then(c=>{if(c.success&&c.data?.buffer)try{r.write(c.data.buffer)}catch{}}).catch(c=>console.error("[TeammateTerminal] Failed to fetch buffer:",c));for(const c of a)try{r.write(c)}catch{}a.length=0},100),r.onData(c=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:c})}).catch(m=>console.error("[TeammateTerminal] Failed to send input:",m))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const d=this.teammateTerminals.get(e);d&&(d.terminal=r,d.fitAddon=o,d.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const
|
|
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 d=this.getTeammateInfo(t),c=d?d.name:t.description||e.substring(0,7),m=c.length>50?c.substring(0,50)+"...":c;n.textContent=m}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const d=s.querySelector(".subagent-window-title");if(d){const c=document.createElement("span");c.className=`teammate-badge teammate-color-${a.color}`,c.title=`Team: ${a.teamName}`,c.textContent=`@${a.name}`;const m=d.querySelector(".status");m&&m.insertAdjacentElement("beforebegin",c)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const d=s.querySelector(".subagent-window-title .status");d&&d.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},fontFamily:'"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const c=new Unicode11Addon.Unicode11Addon;r.loadAddon(c),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(c){console.warn("[TeammateTerminal] Failed to open terminal:",c),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(c=>c.json()).then(c=>{if(c.success&&c.data?.buffer)try{r.write(c.data.buffer)}catch{}}).catch(c=>console.error("[TeammateTerminal] Failed to fetch buffer:",c));for(const c of a)try{r.write(c)}catch{}a.length=0},100),r.onData(c=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:c})}).catch(m=>console.error("[TeammateTerminal] Failed to send input:",m))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const d=this.teammateTerminals.get(e);d&&(d.terminal=r,d.fitAddon=o,d.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const w=this.subagentWindows.get(t);w.hidden&&(w.element.style.display="flex",w.hidden=!1),w.element.style.zIndex=++this.subagentWindowZIndex,w.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,a=20,r=window.innerWidth,o=window.innerHeight,l=50,d=120,c=Math.floor((r-l-50)/(n+a))||1,m=Math.floor((o-d-50)/(i+a))||1,u=s%c,h=Math.floor(s/c)%m;let p=l+u*(n+a),y=d+h*(i+a);p=Math.max(10,Math.min(p,r-n-10)),y=Math.max(10,Math.min(y,o-i-10));const b=e.color||"blue",f=document.createElement("div");f.className="subagent-window has-terminal",f.id=`subagent-window-${t}`,f.style.zIndex=++this.subagentWindowZIndex,f.style.left=`${p}px`,f.style.top=`${y}px`,f.style.width=`${n}px`,f.style.height=`${i}px`,f.innerHTML=`
|
|
183
191
|
<div class="subagent-window-header">
|
|
184
192
|
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
|
|
185
193
|
<span class="icon" style="color: var(--team-color-${b}, #339af0)">\u2B24</span>
|
|
@@ -192,7 +200,7 @@
|
|
|
192
200
|
</div>
|
|
193
201
|
<div class="subagent-window-body teammate-terminal-body" id="subagent-window-body-${t}">
|
|
194
202
|
</div>
|
|
195
|
-
`,document.body.appendChild(f);const v=this.makeWindowDraggable(f,f.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(f);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:f,minimized:!1,hidden:S,dragListeners:v,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(f.style.display="none"),f.addEventListener("mousedown",()=>{f.style.zIndex=++this.subagentWindowZIndex});const $=new ResizeObserver(()=>{this.updateConnectionLines()});if($.observe(f),this.subagentWindows.get(t).resizeObserver=$,S){const
|
|
203
|
+
`,document.body.appendChild(f);const v=this.makeWindowDraggable(f,f.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(f);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:f,minimized:!1,hidden:S,dragListeners:v,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(f.style.display="none"),f.addEventListener("mousedown",()=>{f.style.zIndex=++this.subagentWindowZIndex});const $=new ResizeObserver(()=>{this.updateConnectionLines()});if($.observe(f),this.subagentWindows.get(t).resizeObserver=$,S){const w=this.subagentWindows.get(t);w&&(w._lazyTerminal=!0,w._lazyPaneTarget=e.paneTarget,w._lazySessionId=e.sessionId)}else this.initTeammateTerminal(t,e,f);requestAnimationFrame(()=>{f.style.transition="transform 0.3s ease, opacity 0.3s ease",f.style.transform="scale(1)",f.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[h,p]of this.teams)if(p.leadSessionId===this.activeSessionId){t=p,s=h;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 y=e.querySelector(".team-tasks-header");y&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,y))}const i=this.teamTasks.get(s)||[],a=i.filter(h=>h.status==="completed").length,r=i.length,o=r>0?Math.round(a/r*100):0,l=e.querySelector(".team-tasks-header-text");if(l){const h=t.members.filter(p=>p.agentType!=="team-lead").length;l.textContent=`Team Tasks (${h} teammates)`}const d=e.querySelector(".team-tasks-progress-fill");d&&(d.style.width=`${o}%`);const c=e.querySelector(".team-tasks-progress-text");c&&(c.textContent=`${a}/${r}`);const m=e.querySelector(".team-tasks-list");if(!m)return;if(i.length===0){m.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const u=i.map(h=>{const p=h.status==="completed"?"\u2713":h.status==="in_progress"?"\u25C9":"\u25CB",y=h.status.replace("_","-"),b=h.owner?`<span class="team-task-owner teammate-color-${this.getTeammateColor(h.owner)}">${escapeHtml(h.owner)}</span>`:"";return`<div class="team-task-item ${y}">
|
|
196
204
|
<span class="team-task-status">${p}</span>
|
|
197
205
|
<span class="team-task-subject">${escapeHtml(h.subject)}</span>
|
|
198
206
|
${b}
|
|
@@ -205,22 +213,22 @@
|
|
|
205
213
|
${m.timeout?`<span class="project-insight-timeout">${escapeHtml(m.timeout)}</span>`:""}
|
|
206
214
|
</div>
|
|
207
215
|
<div class="project-insight-paths">
|
|
208
|
-
`);for(const p of u){const
|
|
216
|
+
`);for(const p of u){const y=p.split("/").pop();c.push(`
|
|
209
217
|
<span class="project-insight-filepath"
|
|
210
218
|
onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(p))}, ${escapeHtml(JSON.stringify(m.sessionId))})"
|
|
211
|
-
title="${escapeHtml(p)}">${escapeHtml(
|
|
219
|
+
title="${escapeHtml(p)}">${escapeHtml(y)}</span>
|
|
212
220
|
`)}c.push(`
|
|
213
221
|
</div>
|
|
214
222
|
</div>
|
|
215
|
-
`)}t.innerHTML=c.join("")},closeProjectInsightsPanel(){const e=this.$("projectInsightsPanel");e&&(e.classList.remove("visible"),this.projectInsightsPanelVisible=!1)},async loadFileBrowser(e){if(!e)return;const t=this.$("fileBrowserTree"),s=this.$("fileBrowserStatus");if(t){t.innerHTML='<div class="file-browser-loading">Loading files...</div>';try{const n=await fetch(`/api/sessions/${e}/files?depth=5&showHidden=false`);if(!n.ok)throw new Error("Failed to load files");const i=await n.json();if(!i.success)throw new Error(i.error||"Failed to load files");if(this.fileBrowserData=i.data,this.renderFileBrowserTree(),s){const{totalFiles:a,totalDirectories:r,truncated:o}=i.data;s.textContent=`${a} files, ${r} dirs${o?" (truncated)":""}`}}catch(n){console.error("Failed to load file browser:",n),t.innerHTML=`<div class="file-browser-empty">Failed to load files: ${escapeHtml(n.message)}</div>`}}},renderFileBrowserTree(){const e=this.$("fileBrowserTree");if(!e||!this.fileBrowserData)return;const{tree:t}=this.fileBrowserData;if(!t||t.length===0){e.innerHTML='<div class="file-browser-empty">No files found</div>';return}const s=[],n=this.fileBrowserFilter.toLowerCase(),i=(a,r)=>{const o=a.type==="directory",l=this.fileBrowserExpandedDirs.has(a.path),d=!n||a.name.toLowerCase().includes(n);let c=!1;o&&n&&a.children&&(c=this.hasMatchingChild(a,n));const u=!(d||c)&&n?" hidden-by-filter":"",h=o?l?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(a.extension),p=o?`<span class="file-tree-expand${l?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',
|
|
223
|
+
`)}t.innerHTML=c.join("")},closeProjectInsightsPanel(){const e=this.$("projectInsightsPanel");e&&(e.classList.remove("visible"),this.projectInsightsPanelVisible=!1)},async loadFileBrowser(e){if(!e)return;const t=this.$("fileBrowserTree"),s=this.$("fileBrowserStatus");if(t){t.innerHTML='<div class="file-browser-loading">Loading files...</div>';try{const n=await fetch(`/api/sessions/${e}/files?depth=5&showHidden=false`);if(!n.ok)throw new Error("Failed to load files");const i=await n.json();if(!i.success)throw new Error(i.error||"Failed to load files");if(this.fileBrowserData=i.data,this.renderFileBrowserTree(),s){const{totalFiles:a,totalDirectories:r,truncated:o}=i.data;s.textContent=`${a} files, ${r} dirs${o?" (truncated)":""}`}}catch(n){console.error("Failed to load file browser:",n),t.innerHTML=`<div class="file-browser-empty">Failed to load files: ${escapeHtml(n.message)}</div>`}}},renderFileBrowserTree(){const e=this.$("fileBrowserTree");if(!e||!this.fileBrowserData)return;const{tree:t}=this.fileBrowserData;if(!t||t.length===0){e.innerHTML='<div class="file-browser-empty">No files found</div>';return}const s=[],n=this.fileBrowserFilter.toLowerCase(),i=(a,r)=>{const o=a.type==="directory",l=this.fileBrowserExpandedDirs.has(a.path),d=!n||a.name.toLowerCase().includes(n);let c=!1;o&&n&&a.children&&(c=this.hasMatchingChild(a,n));const u=!(d||c)&&n?" hidden-by-filter":"",h=o?l?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(a.extension),p=o?`<span class="file-tree-expand${l?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',y=!o&&a.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(a.size)}</span>`:"",b=o?"file-tree-name directory":"file-tree-name",f=o?"":`<a class="file-tree-download" href="/api/sessions/${this.activeSessionId}/file-raw?path=${encodeURIComponent(a.path)}&download=true" title="Download" onclick="event.stopPropagation()">⬇</a>`;if(s.push(`
|
|
216
224
|
<div class="file-tree-item${u}" data-path="${escapeHtml(a.path)}" data-type="${a.type}" data-depth="${r}">
|
|
217
225
|
${p}
|
|
218
226
|
<span class="file-tree-icon">${h}</span>
|
|
219
227
|
<span class="${b}">${escapeHtml(a.name)}</span>
|
|
220
|
-
${
|
|
228
|
+
${y}
|
|
221
229
|
${f}
|
|
222
230
|
</div>
|
|
223
|
-
`),o&&l&&a.children)for(const v of a.children)i(v,r+1)};for(const a of t)i(a,0);e.innerHTML=s.join(""),e.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const r=a.dataset.path;a.dataset.type==="directory"?this.toggleFileBrowserFolder(r):this.openFilePreview(r)})})},hasMatchingChild(e,t){if(!e.children)return!1;for(const s of e.children)if(s.name.toLowerCase().includes(t)||s.type==="directory"&&this.hasMatchingChild(s,t))return!0;return!1},toggleFileBrowserFolder(e){this.fileBrowserExpandedDirs.has(e)?this.fileBrowserExpandedDirs.delete(e):this.fileBrowserExpandedDirs.add(e),this.renderFileBrowserTree()},filterFileBrowser(e){this.fileBrowserFilter=e,e&&this.expandAllDirectories(this.fileBrowserData?.tree||[]),this.renderFileBrowserTree()},expandAllDirectories(e){for(const t of e)t.type==="directory"&&(this.fileBrowserExpandedDirs.add(t.path),t.children&&this.expandAllDirectories(t.children))},collapseAllDirectories(){this.fileBrowserExpandedDirs.clear()},toggleFileBrowserExpand(){this.fileBrowserAllExpanded=!this.fileBrowserAllExpanded;const e=this.$("fileBrowserExpandBtn");this.fileBrowserAllExpanded?(this.expandAllDirectories(this.fileBrowserData?.tree||[]),e&&(e.innerHTML="\u229F")):(this.collapseAllDirectories(),e&&(e.innerHTML="\u229E")),this.renderFileBrowserTree()},refreshFileBrowser(){if(this.activeSessionId){this.fileBrowserExpandedDirs.clear(),this.fileBrowserFilter="",this.fileBrowserAllExpanded=!1;const e=this.$("fileBrowserSearch");e&&(e.value=""),this.loadFileBrowser(this.activeSessionId)}},closeFileBrowserPanel(){const e=this.$("fileBrowserPanel");if(e&&(e.classList.remove("visible"),e.style.left="",e.style.top="",e.style.bottom="",e.style.right=""),this.fileBrowserDragListeners){const s=this.fileBrowserDragListeners;document.removeEventListener("mousemove",s.move),document.removeEventListener("mouseup",s.up),document.removeEventListener("touchmove",s.touchMove),document.removeEventListener("touchend",s.up),document.removeEventListener("touchcancel",s.up),s.handle&&(s.handle.removeEventListener("mousedown",s.handleMouseDown),s.handle.removeEventListener("touchstart",s.handleTouchStart),s._onFirstDrag&&(s.handle.removeEventListener("mousedown",s._onFirstDrag),s.handle.removeEventListener("touchstart",s._onFirstDrag))),this.fileBrowserDragListeners=null}const t=this.loadAppSettingsFromStorage();t.showFileBrowser=!1,this.saveAppSettingsToStorage(t)},async openFilePreview(e,t=this.activeSessionId,s=null){if(!t||!e)return;const n=this.$("filePreviewOverlay"),i=this.$("filePreviewTitle"),a=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!a)return;n.classList.add("visible"),i.textContent=e,a.innerHTML='<div class="binary-message">Loading...</div>',r.textContent="";const o=(e.split(".").pop()||"").toLowerCase();if(s){const l=`/api/sessions/${t}/attachments/${encodeURIComponent(s)}`,d=new Set(["png","jpg","jpeg","gif","webp","bmp","svg"]);if(r.textContent=o.toUpperCase(),d.has(o))a.innerHTML=`<img src="${escapeHtml(`${l}/raw`)}" alt="${escapeHtml(e)}">`;else if(o==="pdf")a.innerHTML=`<iframe src="${escapeHtml(`${l}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(o==="docx"||o==="pptx")a.innerHTML=`<iframe src="${escapeHtml(`${l}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const c=await fetch(`${l}/raw`);if(!c.ok)throw new Error("Failed to load attachment");const m=await c.text();a.innerHTML=`<pre><code>${escapeHtml(m)}</code></pre>`}catch(c){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(c.message)}</div>`}return}if(o==="docx"||o==="pptx"){r.textContent=o.toUpperCase();const l=`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(l)}" title="${escapeHtml(e)}"></iframe>`;return}if(o==="pdf"){r.textContent="PDF";const l=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(l)}" title="${escapeHtml(e)}"></iframe>`;return}if(o==="svg"){r.textContent="SVG";try{const l=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!l.ok)throw new Error("Failed to load image");const d=URL.createObjectURL(new Blob([await l.text()],{type:"image/svg+xml"}));a.innerHTML=`<img src="${d}" alt="${escapeHtml(e)}">`;const c=a.querySelector("img");c&&(c.onload=()=>URL.revokeObjectURL(d))}catch(l){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(l.message)}</div>`}return}try{const l=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!l.ok)throw new Error("Failed to load file");const d=await l.json();if(!d.success)throw new Error(d.error||"Failed to load file");const c=d.data;if(c.type==="image")a.innerHTML=`<img src="${c.url}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="video")a.innerHTML=`<video src="${c.url}" controls autoplay></video>`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="audio")a.innerHTML=`<audio src="${c.url}" controls autoplay></audio>`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="binary"){const m=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`;a.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(c.size)})<br>Cannot preview<br><a href="${escapeHtml(m)}" download>Download</a></div>`,r.textContent=c.extension||"binary"}else{this.filePreviewContent=c.content,a.innerHTML=`<pre><code>${escapeHtml(c.content)}</code></pre>`;const m=c.truncated?` (showing 500/${c.totalLines} lines)`:"";r.textContent=`${c.totalLines} lines \u2022 ${this.formatFileSize(c.size)}${m}`}}catch(l){console.error("Failed to preview file:",l),a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(l.message)}</div>`}},closeFilePreview(){const e=this.$("filePreviewOverlay");e&&e.classList.remove("visible"),this.filePreviewContent=""},_onAttachmentDetected(e){if(console.log("[Attachment Detected]",e),this.addAttachmentCard(e),e.sessionId){const t=this.attachmentHistoryCounts.get(e.sessionId)??this.sessions.get(e.sessionId)?.attachmentHistory?.length??0;this.attachmentHistoryCounts.set(e.sessionId,Math.min(t+1,100)),e.sessionId===this.activeSessionId&&(this.updateAttachmentHistoryBadge(),this.attachmentHistoryDrawerOpen&&this._debouncedCall("attachmentHistoryRefresh",()=>{this.attachmentHistoryDrawerOpen&&this.activeSessionId===e.sessionId&&this.loadAttachmentHistory(e.sessionId)},250))}},ensureAttachmentCardStack(){let e=this.attachmentCardStack||document.getElementById("attachmentCardStack");return e||(e=document.createElement("div"),e.id="attachmentCardStack",e.className="attachment-card-stack",document.body.appendChild(e)),this.attachmentCardStack=e,e},openAttachmentInNewTab(e,t,s=null){const n=s?`/api/sessions/${e}/attachments/${encodeURIComponent(s)}/raw`:`/api/sessions/${e}/file-raw?path=${encodeURIComponent(t)}`;window.open(n,"_blank")},addAttachmentCard(e){const{sessionId:t,relativePath:s,fileName:n,timestamp:i,size:a,attachmentType:r,extension:o,attachmentId:l,rawUrl:d,previewUrl:c,thumbnailUrl:m}=e,u=s||n,h=l||`${t}-${i}-${n}`;if(this.attachmentCards.has(h)){this.attachmentCards.get(h).element.focus?.();return}if(this.attachmentCards.size>=10){const
|
|
231
|
+
`),o&&l&&a.children)for(const v of a.children)i(v,r+1)};for(const a of t)i(a,0);e.innerHTML=s.join(""),e.querySelectorAll(".file-tree-item").forEach(a=>{a.addEventListener("click",()=>{const r=a.dataset.path;a.dataset.type==="directory"?this.toggleFileBrowserFolder(r):this.openFilePreview(r)})})},hasMatchingChild(e,t){if(!e.children)return!1;for(const s of e.children)if(s.name.toLowerCase().includes(t)||s.type==="directory"&&this.hasMatchingChild(s,t))return!0;return!1},toggleFileBrowserFolder(e){this.fileBrowserExpandedDirs.has(e)?this.fileBrowserExpandedDirs.delete(e):this.fileBrowserExpandedDirs.add(e),this.renderFileBrowserTree()},filterFileBrowser(e){this.fileBrowserFilter=e,e&&this.expandAllDirectories(this.fileBrowserData?.tree||[]),this.renderFileBrowserTree()},expandAllDirectories(e){for(const t of e)t.type==="directory"&&(this.fileBrowserExpandedDirs.add(t.path),t.children&&this.expandAllDirectories(t.children))},collapseAllDirectories(){this.fileBrowserExpandedDirs.clear()},toggleFileBrowserExpand(){this.fileBrowserAllExpanded=!this.fileBrowserAllExpanded;const e=this.$("fileBrowserExpandBtn");this.fileBrowserAllExpanded?(this.expandAllDirectories(this.fileBrowserData?.tree||[]),e&&(e.innerHTML="\u229F")):(this.collapseAllDirectories(),e&&(e.innerHTML="\u229E")),this.renderFileBrowserTree()},refreshFileBrowser(){if(this.activeSessionId){this.fileBrowserExpandedDirs.clear(),this.fileBrowserFilter="",this.fileBrowserAllExpanded=!1;const e=this.$("fileBrowserSearch");e&&(e.value=""),this.loadFileBrowser(this.activeSessionId)}},closeFileBrowserPanel(){const e=this.$("fileBrowserPanel");if(e&&(e.classList.remove("visible"),e.style.left="",e.style.top="",e.style.bottom="",e.style.right=""),this.fileBrowserDragListeners){const s=this.fileBrowserDragListeners;document.removeEventListener("mousemove",s.move),document.removeEventListener("mouseup",s.up),document.removeEventListener("touchmove",s.touchMove),document.removeEventListener("touchend",s.up),document.removeEventListener("touchcancel",s.up),s.handle&&(s.handle.removeEventListener("mousedown",s.handleMouseDown),s.handle.removeEventListener("touchstart",s.handleTouchStart),s._onFirstDrag&&(s.handle.removeEventListener("mousedown",s._onFirstDrag),s.handle.removeEventListener("touchstart",s._onFirstDrag))),this.fileBrowserDragListeners=null}const t=this.loadAppSettingsFromStorage();t.showFileBrowser=!1,this.saveAppSettingsToStorage(t)},async openFilePreview(e,t=this.activeSessionId,s=null){if(!t||!e)return;const n=this.$("filePreviewOverlay"),i=this.$("filePreviewTitle"),a=this.$("filePreviewBody"),r=this.$("filePreviewFooter");if(!n||!a)return;n.classList.add("visible"),i.textContent=e,a.innerHTML='<div class="binary-message">Loading...</div>',r.textContent="";const o=(e.split(".").pop()||"").toLowerCase();if(s){const l=`/api/sessions/${t}/attachments/${encodeURIComponent(s)}`,d=new Set(["png","jpg","jpeg","gif","webp","bmp","svg"]);if(r.textContent=o.toUpperCase(),d.has(o))a.innerHTML=`<img src="${escapeHtml(`${l}/raw`)}" alt="${escapeHtml(e)}">`;else if(o==="pdf")a.innerHTML=`<iframe src="${escapeHtml(`${l}/raw`)}" title="${escapeHtml(e)}"></iframe>`;else if(o==="docx"||o==="pptx")a.innerHTML=`<iframe src="${escapeHtml(`${l}/preview`)}" title="${escapeHtml(e)}"></iframe>`;else try{const c=await fetch(`${l}/raw`);if(!c.ok)throw new Error("Failed to load attachment");const m=await c.text();a.innerHTML=`<pre><code>${escapeHtml(m)}</code></pre>`}catch(c){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(c.message)}</div>`}return}if(o==="docx"||o==="pptx"){r.textContent=o.toUpperCase();const l=`/api/sessions/${t}/file-preview?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(l)}" title="${escapeHtml(e)}"></iframe>`;return}if(o==="pdf"){r.textContent="PDF";const l=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`;a.innerHTML=`<iframe src="${escapeHtml(l)}" title="${escapeHtml(e)}"></iframe>`;return}if(o==="svg"){r.textContent="SVG";try{const l=await fetch(`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}`);if(!l.ok)throw new Error("Failed to load image");const d=URL.createObjectURL(new Blob([await l.text()],{type:"image/svg+xml"}));a.innerHTML=`<img src="${d}" alt="${escapeHtml(e)}">`;const c=a.querySelector("img");c&&(c.onload=()=>URL.revokeObjectURL(d))}catch(l){a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(l.message)}</div>`}return}try{const l=await fetch(`/api/sessions/${t}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!l.ok)throw new Error("Failed to load file");const d=await l.json();if(!d.success)throw new Error(d.error||"Failed to load file");const c=d.data;if(c.type==="image")a.innerHTML=`<img src="${c.url}" alt="${escapeHtml(e)}">`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="video")a.innerHTML=`<video src="${c.url}" controls autoplay></video>`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="audio")a.innerHTML=`<audio src="${c.url}" controls autoplay></audio>`,r.textContent=`${this.formatFileSize(c.size)} \u2022 ${c.extension}`;else if(c.type==="binary"){const m=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(e)}&download=true`;a.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(c.size)})<br>Cannot preview<br><a href="${escapeHtml(m)}" download>Download</a></div>`,r.textContent=c.extension||"binary"}else{this.filePreviewContent=c.content,a.innerHTML=`<pre><code>${escapeHtml(c.content)}</code></pre>`;const m=c.truncated?` (showing 500/${c.totalLines} lines)`:"";r.textContent=`${c.totalLines} lines \u2022 ${this.formatFileSize(c.size)}${m}`}}catch(l){console.error("Failed to preview file:",l),a.innerHTML=`<div class="binary-message">Error: ${escapeHtml(l.message)}</div>`}},closeFilePreview(){const e=this.$("filePreviewOverlay");e&&e.classList.remove("visible"),this.filePreviewContent=""},_onAttachmentDetected(e){if(console.log("[Attachment Detected]",e),this.addAttachmentCard(e),e.sessionId){const t=this.attachmentHistoryCounts.get(e.sessionId)??this.sessions.get(e.sessionId)?.attachmentHistory?.length??0;this.attachmentHistoryCounts.set(e.sessionId,Math.min(t+1,100)),e.sessionId===this.activeSessionId&&(this.updateAttachmentHistoryBadge(),this.attachmentHistoryDrawerOpen&&this._debouncedCall("attachmentHistoryRefresh",()=>{this.attachmentHistoryDrawerOpen&&this.activeSessionId===e.sessionId&&this.loadAttachmentHistory(e.sessionId)},250))}},ensureAttachmentCardStack(){let e=this.attachmentCardStack||document.getElementById("attachmentCardStack");return e||(e=document.createElement("div"),e.id="attachmentCardStack",e.className="attachment-card-stack",document.body.appendChild(e)),this.attachmentCardStack=e,e},openAttachmentInNewTab(e,t,s=null){const n=s?`/api/sessions/${e}/attachments/${encodeURIComponent(s)}/raw`:`/api/sessions/${e}/file-raw?path=${encodeURIComponent(t)}`;window.open(n,"_blank")},addAttachmentCard(e){const{sessionId:t,relativePath:s,fileName:n,timestamp:i,size:a,attachmentType:r,extension:o,attachmentId:l,rawUrl:d,previewUrl:c,thumbnailUrl:m}=e,u=s||n,h=l||`${t}-${i}-${n}`;if(this.attachmentCards.has(h)){this.attachmentCards.get(h).element.focus?.();return}if(this.attachmentCards.size>=10){const C=this.attachmentCards.keys().next().value;C&&this.closeAttachmentCard(C)}const y=this.ensureAttachmentCardStack(),f=this.sessions.get(t)?.name||t.substring(0,8),v=d||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/raw`:`/api/sessions/${t}/file-raw?path=${encodeURIComponent(u)}`),T=c||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/preview`:null),g=m||(l?`/api/sessions/${t}/attachments/${encodeURIComponent(l)}/thumbnail`:`/api/sessions/${t}/file-thumbnail?path=${encodeURIComponent(u)}`),S=l?`${v}?download=true`:`${v}&download=true`,$=(o||r||"file").toUpperCase(),w=document.createElement("article");w.className=`attachment-card attachment-${escapeHtml(r||"file")}`,w.tabIndex=0,w.dataset.attachmentId=h,w.dataset.previewUrl=T||"",w.innerHTML=`
|
|
224
232
|
<div class="attachment-thumbnail">
|
|
225
233
|
${g?`<img class="attachment-thumbnail-img" src="${escapeHtml(g)}" alt="">`:""}
|
|
226
234
|
<div class="attachment-thumbnail-fallback ${g?"":"visible"}">${escapeHtml($)}</div>
|
|
@@ -238,7 +246,7 @@
|
|
|
238
246
|
</div>
|
|
239
247
|
</div>
|
|
240
248
|
<button type="button" class="attachment-close-btn" title="Dismiss">×</button>
|
|
241
|
-
`;const D=
|
|
249
|
+
`;const D=w.querySelector(".attachment-thumbnail-img");D&&(D.onerror=()=>{D.remove(),w.querySelector(".attachment-thumbnail-fallback")?.classList.add("visible")}),w.querySelector(".attachment-preview-btn")?.addEventListener("click",()=>{this.openFilePreview(u,t,l||null)}),w.querySelector(".attachment-open-btn")?.addEventListener("click",()=>{this.openAttachmentInNewTab(t,u,l||null)}),w.querySelector(".attachment-close-btn")?.addEventListener("click",()=>{this.closeAttachmentCard(h)}),y.prepend(w),this.attachmentCards.set(h,{element:w,sessionId:t,filePath:u}),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=`
|
|
242
250
|
<div class="attachment-history-header">
|
|
243
251
|
<div>
|
|
244
252
|
<div class="attachment-history-title">Attachments</div>
|
|
@@ -288,12 +296,12 @@
|
|
|
288
296
|
<div class="log-viewer-window-body" id="log-viewer-body-${s}">
|
|
289
297
|
<div class="log-info">Connecting to ${escapeHtml(e)}...</div>
|
|
290
298
|
</div>
|
|
291
|
-
`,document.body.appendChild(o);const l=this.makeWindowDraggable(o,o.querySelector(".log-viewer-window-header")),d=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);d.onmessage=c=>{const m=JSON.parse(c.data),u=document.getElementById(`log-viewer-body-${s}`);if(u)switch(m.type){case"connected":u.innerHTML="";break;case"data":const h=u.scrollTop+u.clientHeight>=u.scrollHeight-10,p=escapeHtml(m.content);u.innerHTML+=p,h&&(u.scrollTop=u.scrollHeight),u.innerHTML.length>5e5&&(u.innerHTML=u.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":u.innerHTML+=`<div class="log-error">${escapeHtml(m.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},d.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:o,eventSource:d,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:i,timestamp:a,size:r}=e,o=`${t}-${a}`;if(this.imagePopups.has(o)){const g=this.imagePopups.get(o);g.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const g=this.imagePopups.keys().next().value;g&&this.closeImagePopup(g)}const d=this.imagePopups.size,c=(window.innerWidth-600)/2,m=(window.innerHeight-500)/2,u=c+d%5*30,h=m+d%5*30,
|
|
299
|
+
`,document.body.appendChild(o);const l=this.makeWindowDraggable(o,o.querySelector(".log-viewer-window-header")),d=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);d.onmessage=c=>{const m=JSON.parse(c.data),u=document.getElementById(`log-viewer-body-${s}`);if(u)switch(m.type){case"connected":u.innerHTML="";break;case"data":const h=u.scrollTop+u.clientHeight>=u.scrollHeight-10,p=escapeHtml(m.content);u.innerHTML+=p,h&&(u.scrollTop=u.scrollHeight),u.innerHTML.length>5e5&&(u.innerHTML=u.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":u.innerHTML+=`<div class="log-error">${escapeHtml(m.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},d.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:o,eventSource:d,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:i,timestamp:a,size:r}=e,o=`${t}-${a}`;if(this.imagePopups.has(o)){const g=this.imagePopups.get(o);g.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const g=this.imagePopups.keys().next().value;g&&this.closeImagePopup(g)}const d=this.imagePopups.size,c=(window.innerWidth-600)/2,m=(window.innerHeight-500)/2,u=c+d%5*30,h=m+d%5*30,y=this.sessions.get(t)?.name||t.substring(0,8),b=(r/1024).toFixed(1),f=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||i)}`,v=document.createElement("div");v.className="image-popup-window",v.id=`image-popup-${o}`,v.style.left=`${u}px`,v.style.top=`${h}px`,v.style.zIndex=++this.imagePopupZIndex,v.innerHTML=`
|
|
292
300
|
<div class="image-popup-header">
|
|
293
301
|
<div class="image-popup-title" title="${escapeHtml(s)}">
|
|
294
302
|
<span class="icon">\u{1F5BC}\uFE0F</span>
|
|
295
303
|
<span class="filename">${escapeHtml(i)}</span>
|
|
296
|
-
<span class="session-badge">${escapeHtml(
|
|
304
|
+
<span class="session-badge">${escapeHtml(y)}</span>
|
|
297
305
|
<span class="size-badge">${b} KB</span>
|
|
298
306
|
</div>
|
|
299
307
|
<div class="image-popup-actions">
|
|
@@ -306,13 +314,13 @@
|
|
|
306
314
|
onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
|
|
307
315
|
onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(f))})" />
|
|
308
316
|
</div>
|
|
309
|
-
`,document.body.appendChild(v);const T=this.makeWindowDraggable(v,v.querySelector(".image-popup-header"));v.addEventListener("mousedown",()=>{v.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(o,{element:v,sessionId:t,filePath:s,dragListeners:T})},closeImagePopup(e){const t=this.imagePopups.get(e);t&&(t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.touchMove&&(document.removeEventListener("touchmove",t.dragListeners.touchMove),document.removeEventListener("touchend",t.dragListeners.up),document.removeEventListener("touchcancel",t.dragListeners.up)),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.imagePopups.delete(e))},openImageInNewTab(e){window.open(e,"_blank")},closeSessionImagePopups(e){const t=[];for(const[s,n]of this.imagePopups)n.sessionId===e&&t.push(s);for(const s of t)this.closeImagePopup(s)},async loadMuxSessions(){try{const t=await(await fetch("/api/mux-sessions")).json();this.muxSessions=t.data?.sessions||[],this.renderMuxSessions()}catch(e){console.error("Failed to load mux sessions:",e)}},killAllMuxSessions(){const e=this.muxSessions?.length||0;if(e===0){alert("No sessions to kill");return}document.getElementById("killAllCount").textContent=e;const t=document.getElementById("killAllModal");t.classList.add("active"),this.activeFocusTrap=new FocusTrap(t),this.activeFocusTrap.activate()},closeKillAllModal(){document.getElementById("killAllModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},async confirmKillAll(e){this.closeKillAllModal();try{if(e){if((await(await fetch("/api/sessions",{method:"DELETE"})).json()).success){this.sessions.clear(),this.muxSessions=[],this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.renderMuxSessions(),this.terminal.clear(),this.terminal.reset(),this.toast("All sessions and tmux killed","success")}}else{this.sessions.clear(),this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.terminal.clear(),this.terminal.reset(),this.toast("All tabs removed, tmux still running","info")}}catch(t){console.error("Failed to kill sessions:",t),this.toast("Failed to kill sessions: "+t.message,"error")}},renderMuxSessions(){this._debouncedCall("muxSessions",this._renderMuxSessionsImmediate)},_renderMuxSessionsImmediate(){const e=document.getElementById("muxSessionsBody");if(!this.muxSessions||this.muxSessions.length===0){e.innerHTML='<div class="monitor-empty">No mux sessions</div>';return}let t="";for(const s of this.muxSessions){const n=s.stats||{memoryMB:0,cpuPercent:0,childCount:0},i=this.sessions.get(s.sessionId),a=i?i.status:"unknown",r=i?i.isWorking:!1;let o,l;a==="idle"&&!r?(o="IDLE",l="status-idle"):a==="busy"||r?(o="WORKING",l="status-working"):a==="stopped"?(o="STOPPED",l="status-stopped"):(o=a.toUpperCase(),l="");const d=i&&i.tokens?i.tokens:null,c=i?i.totalCost:0,m=i&&i.cliModel||"",u=m.includes("opus")?"opus":m.includes("sonnet")?"sonnet":m.includes("haiku")?"haiku":"",h=i?i.ralphTodoStats:null;let p="";if(h&&h.total>0){const T=Math.round(h.completed/h.total*100);p=`<span class="process-stat todo-progress">${h.completed}/${h.total} (${T}%)</span>`}let
|
|
317
|
+
`,document.body.appendChild(v);const T=this.makeWindowDraggable(v,v.querySelector(".image-popup-header"));v.addEventListener("mousedown",()=>{v.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(o,{element:v,sessionId:t,filePath:s,dragListeners:T})},closeImagePopup(e){const t=this.imagePopups.get(e);t&&(t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.touchMove&&(document.removeEventListener("touchmove",t.dragListeners.touchMove),document.removeEventListener("touchend",t.dragListeners.up),document.removeEventListener("touchcancel",t.dragListeners.up)),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.imagePopups.delete(e))},openImageInNewTab(e){window.open(e,"_blank")},closeSessionImagePopups(e){const t=[];for(const[s,n]of this.imagePopups)n.sessionId===e&&t.push(s);for(const s of t)this.closeImagePopup(s)},async loadMuxSessions(){try{const t=await(await fetch("/api/mux-sessions")).json();this.muxSessions=t.data?.sessions||[],this.renderMuxSessions()}catch(e){console.error("Failed to load mux sessions:",e)}},killAllMuxSessions(){const e=this.muxSessions?.length||0;if(e===0){alert("No sessions to kill");return}document.getElementById("killAllCount").textContent=e;const t=document.getElementById("killAllModal");t.classList.add("active"),this.activeFocusTrap=new FocusTrap(t),this.activeFocusTrap.activate()},closeKillAllModal(){document.getElementById("killAllModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},async confirmKillAll(e){this.closeKillAllModal();try{if(e){if((await(await fetch("/api/sessions",{method:"DELETE"})).json()).success){this.sessions.clear(),this.muxSessions=[],this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.renderMuxSessions(),this.terminal.clear(),this.terminal.reset(),this.toast("All sessions and tmux killed","success")}}else{this.sessions.clear(),this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.renderSessionTabs(),this.terminal.clear(),this.terminal.reset(),this.toast("All tabs removed, tmux still running","info")}}catch(t){console.error("Failed to kill sessions:",t),this.toast("Failed to kill sessions: "+t.message,"error")}},renderMuxSessions(){this._debouncedCall("muxSessions",this._renderMuxSessionsImmediate)},_renderMuxSessionsImmediate(){const e=document.getElementById("muxSessionsBody");if(!this.muxSessions||this.muxSessions.length===0){e.innerHTML='<div class="monitor-empty">No mux sessions</div>';return}let t="";for(const s of this.muxSessions){const n=s.stats||{memoryMB:0,cpuPercent:0,childCount:0},i=this.sessions.get(s.sessionId),a=i?i.status:"unknown",r=i?i.isWorking:!1;let o,l;a==="idle"&&!r?(o="IDLE",l="status-idle"):a==="busy"||r?(o="WORKING",l="status-working"):a==="stopped"?(o="STOPPED",l="status-stopped"):(o=a.toUpperCase(),l="");const d=i&&i.tokens?i.tokens:null,c=i?i.totalCost:0,m=i&&i.cliModel||"",u=m.includes("opus")?"opus":m.includes("sonnet")?"sonnet":m.includes("haiku")?"haiku":"",h=i?i.ralphTodoStats:null;let p="";if(h&&h.total>0){const T=Math.round(h.completed/h.total*100);p=`<span class="process-stat todo-progress">${h.completed}/${h.total} (${T}%)</span>`}let y="";d&&d.total>0&&(y=`<span class="process-stat tokens">${(d.total/1e3).toFixed(1)}k tok</span>`);let b="";c>0&&(b=`<span class="process-stat cost">$${c.toFixed(2)}</span>`);let f="";u&&(f=`<span class="monitor-model-badge ${u}">${u}</span>`);const v=escapeHtml(JSON.stringify(s.sessionId));t+=`
|
|
310
318
|
<div class="process-item process-item-clickable" onclick="app.selectSession(${v})" title="Switch to session">
|
|
311
319
|
<span class="monitor-status-badge ${l}">${o}</span>
|
|
312
320
|
<div class="process-info">
|
|
313
321
|
<div class="process-name">${f} ${escapeHtml(s.name||s.muxName)}</div>
|
|
314
322
|
<div class="process-meta">
|
|
315
|
-
${
|
|
323
|
+
${y}
|
|
316
324
|
${b}
|
|
317
325
|
${p}
|
|
318
326
|
<span class="process-stat memory">${n.memoryMB}MB</span>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";Object.assign(CodemanApp.prototype,{buildEnvOverrides(e,t){const s={};return(e?.agentTeams||t?.agentTeamsEnabled)&&(s.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS="1"),s},getEffortSetting(e){const t=e?.thinkingEffort;return["low","medium","high","xhigh","max","ultracode"].includes(t)?t:void 0},formatCasePickerLabel(e){return e?.location==="remote"&&e.remote?.hostId?`${e.name} @ ${e.remote.hostId}`:e?.name||""},buildCasePickerOptions(e=[]){const t=[],s=new Set;for(const a of e)!a?.name||s.has(a.name)||(s.add(a.name),t.push(a));return s.has("testcase")||t.push({name:"testcase"}),t.map(a=>{const n=this.formatCasePickerLabel(a),o=[a.name,n,a.path,a.location,a.remote?.hostId,a.remote?.label,a.remote?.path].filter(Boolean).join(" ").toLowerCase();return{name:a.name,label:n,case:a,searchText:o}}).sort((a,n)=>a.label.localeCompare(n.label,void 0,{sensitivity:"base",numeric:!0}))},filterCasePickerOptions(e,t){const s=String(t||"").trim().toLowerCase().split(/\s+/).filter(Boolean);return s.length===0?e:e.filter(a=>s.every(n=>a.searchText.includes(n)))},getCasePickerOptions(){return this.buildCasePickerOptions(this.cases||[])},updateCasePickerInput(e){const t=document.getElementById("quickStartCaseSearch");if(!t)return;const s=this.getCasePickerOptions().find(a=>a.name===e);t.value=s?.label||e||"testcase",t.title=s?.label||t.value},renderQuickStartCaseSelectOptions(e,t){e&&(e.innerHTML=t.map(s=>`<option value="${escapeHtml(s.name)}">${escapeHtml(s.label)}</option>`).join(""))},openCasePicker(e=""){const t=document.getElementById("quickStartCaseSearch"),s=document.getElementById("quickStartCaseList");!t||!s||(this._casePickerOpen=!0,this._casePickerFilter=e,this._casePickerActiveIndex=0,t.setAttribute("aria-expanded","true"),this.renderCasePickerList())},closeCasePicker(){const e=document.getElementById("quickStartCaseSearch"),t=document.getElementById("quickStartCaseList");this._casePickerOpen=!1,this._casePickerFilter="",e?.setAttribute("aria-expanded","false"),e?.removeAttribute("aria-activedescendant"),t?.classList.add("hidden")},renderCasePickerList(){const e=document.getElementById("quickStartCaseSearch"),t=document.getElementById("quickStartCaseList"),s=document.getElementById("quickStartCase");if(!e||!t||!s)return;const a=this.filterCasePickerOptions(this.getCasePickerOptions(),this._casePickerFilter||""),n=s.value||"testcase",o=Math.max(0,a.length-1);if(this._casePickerActiveIndex=Math.min(Math.max(this._casePickerActiveIndex||0,0),o),a.length===0){t.innerHTML='<div class="case-combobox-empty">No cases match</div>',t.classList.remove("hidden"),e.removeAttribute("aria-activedescendant");return}t.innerHTML=a.map((i,r)=>{const c=r===this._casePickerActiveIndex,m=i.name===n;return`
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
id="${`quickStartCaseOption-${r}`}"
|
|
5
|
+
class="case-combobox-option ${c?"active":""} ${m?"selected":""}"
|
|
6
|
+
role="option"
|
|
7
|
+
aria-selected="${m?"true":"false"}"
|
|
8
|
+
data-case="${escapeHtml(i.name)}"
|
|
9
|
+
title="${escapeHtml(i.label)}">
|
|
10
|
+
<span class="case-combobox-check">${m?"\u2713":""}</span>
|
|
11
|
+
<span class="case-combobox-option-label">${escapeHtml(i.label)}</span>
|
|
12
|
+
</button>
|
|
13
|
+
`}).join(""),t.classList.remove("hidden"),e.setAttribute("aria-activedescendant",`quickStartCaseOption-${this._casePickerActiveIndex}`)},selectQuickStartCase(e,{save:t=!0}={}){const s=document.getElementById("quickStartCase");s&&(s.value=e||"testcase",this.updateCasePickerInput(s.value),this.closeCasePicker(),this.updateDirDisplayForCase(s.value),this.updateMobileCaseLabel(s.value),t&&this.saveLastUsedCase(s.value))},setupQuickStartCasePicker(){const e=document.getElementById("quickStartCase"),t=document.getElementById("quickStartCaseSearch"),s=document.getElementById("quickStartCaseList"),a=document.getElementById("quickStartCasePicker");!e||!t||!s||!a||t.dataset.listenerAdded||(t.addEventListener("focus",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("click",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("input",()=>{this.openCasePicker(t.value)}),t.addEventListener("keydown",n=>{const o=this.filterCasePickerOptions(this.getCasePickerOptions(),this._casePickerFilter||t.value);if(n.key==="ArrowDown")n.preventDefault(),this._casePickerActiveIndex=Math.min((this._casePickerActiveIndex||0)+1,Math.max(0,o.length-1)),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(n.key==="ArrowUp")n.preventDefault(),this._casePickerActiveIndex=Math.max((this._casePickerActiveIndex||0)-1,0),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(n.key==="Enter"){const i=o[this._casePickerActiveIndex||0];i&&(n.preventDefault(),this.selectQuickStartCase(i.name),this.run?.())}else n.key==="Escape"?(n.preventDefault(),this.updateCasePickerInput(e.value),this.closeCasePicker()):n.key==="Tab"&&(this.updateCasePickerInput(e.value),this.closeCasePicker())}),s.addEventListener("mousedown",n=>n.preventDefault()),s.addEventListener("click",n=>{const o=n.target.closest?.(".case-combobox-option");o?.dataset?.case&&this.selectQuickStartCase(o.dataset.case)}),document.addEventListener&&!this._casePickerDocumentListenerAdded&&(document.addEventListener("pointerdown",n=>{a.contains(n.target)||(this.updateCasePickerInput(e.value),this.closeCasePicker())}),this._casePickerDocumentListenerAdded=!0),t.dataset.listenerAdded="true")},async loadQuickStartCases(e=null,t=null){try{let s=null;try{const r=t?await t:await fetch("/api/settings").then(c=>c.ok?c.json():null).then(c=>c?.data??null);r&&(s=r.lastUsedCase||null)}catch{}const n=(await(await fetch("/api/cases")).json()).data;this.cases=n,console.log("[loadQuickStartCases] Loaded cases:",n.map(r=>r.name),"lastUsedCase:",s);const o=document.getElementById("quickStartCase"),i=this.getCasePickerOptions();if(this.renderQuickStartCaseSelectOptions(o,i),console.log("[loadQuickStartCases] Set options:",o.innerHTML.substring(0,200)),e)o.value=e,this.updateDirDisplayForCase(e),this.updateMobileCaseLabel(e);else if(s&&n.some(r=>r.name===s))o.value=s,this.updateDirDisplayForCase(s),this.updateMobileCaseLabel(s);else if(n.length>0){const r=n.find(c=>c.name==="testcase")||n[0];o.value=r.name,this.updateDirDisplayForCase(r.name),this.updateMobileCaseLabel(r.name)}else o.value="testcase",document.getElementById("dirDisplay").textContent="~/codeman-cases/testcase",this.updateMobileCaseLabel("testcase");this.updateCasePickerInput(o.value),this.renderCasePickerList(),this.closeCasePicker(),o.dataset.listenerAdded||(o.addEventListener("change",()=>{this.updateDirDisplayForCase(o.value),this.saveLastUsedCase(o.value),this.updateMobileCaseLabel(o.value),this.updateCasePickerInput(o.value)}),o.dataset.listenerAdded="true"),this.setupQuickStartCasePicker()}catch(s){console.error("Failed to load cases:",s)}},async updateDirDisplayForCase(e){try{const s=(await(await fetch(`/api/cases/${e}`)).json()).data;s.path&&(document.getElementById("dirDisplay").textContent=s.path,document.getElementById("dirInput").value=s.path)}catch{document.getElementById("dirDisplay").textContent=e}},async saveLastUsedCase(e){try{await fetch("/api/settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({lastUsedCase:e})})}catch(t){console.error("Failed to save last used case:",t)}},async quickStart(){return this.run()},async run(){const e=this._runMode||"claude";return e==="opencode"?this.runOpenCode():e==="codex"?this.runCodex():e==="gemini"?this.runGemini():this.runClaude()},setRunMode(e){this._runMode=e;try{localStorage.setItem("codeman_runMode",e)}catch{}this._applyRunMode(),this._apiPut("/api/settings",{runMode:e}).catch(()=>{}),document.getElementById("runModeMenu")?.classList.remove("active")},toggleRunModeMenu(e){e?.stopPropagation();const t=document.getElementById("runModeMenu");if(t&&(t.classList.toggle("active"),t.querySelectorAll(".run-mode-option").forEach(s=>{s.classList.toggle("selected",s.dataset.mode===this.runMode)}),t.classList.contains("active"))){this._loadRunModeHistory();const s=a=>{t.contains(a.target)||(t.classList.remove("active"),document.removeEventListener("click",s))};setTimeout(()=>document.addEventListener("click",s),0)}},async _loadRunModeHistory(){const e=document.getElementById("runModeHistory");if(e){e.innerHTML='<div class="run-mode-hist-empty">Loading...</div>';try{const t=await this._fetchHistorySessions(10);if(t.length===0){e.innerHTML='<div class="run-mode-hist-empty">No history</div>';return}e.replaceChildren();for(const s of t){const a=new Date(s.lastModified),n=a.toLocaleDateString("en",{month:"short",day:"numeric"})+" "+a.toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit",hour12:!1}),o=s.workingDir.replace(/^\/home\/[^/]+\//,"~/"),i=document.createElement("button");i.className="run-mode-option",i.title=s.workingDir,i.dataset.sessionId=s.sessionId,i.dataset.workingDir=s.workingDir;const r=document.createElement("span");r.className="hist-dir",r.textContent=o;const c=document.createElement("span");c.className="hist-meta",c.textContent=n,i.append(r,c),i.addEventListener("click",m=>{m.stopPropagation(),this.resumeHistorySession(s.sessionId,s.workingDir)}),e.appendChild(i)}}catch{e.innerHTML='<div class="run-mode-hist-empty">Failed to load</div>'}}},_applyRunMode(){const e=this.runMode,t=document.getElementById("runBtn"),s=t?.nextElementSibling,a=document.getElementById("runBtnLabel");t&&(t.className=`btn-toolbar btn-run mode-${e}`),s&&(s.className=`btn-toolbar btn-run-gear mode-${e}`),a&&(a.textContent=e==="opencode"?"Run OC":e==="codex"?"Run CX":e==="gemini"?"Run GM":"Run")},_initRunMode(){try{this._runMode=localStorage.getItem("codeman_runMode")||"claude"}catch{this._runMode="claude"}this._applyRunMode()},incrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},incrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},async runClaude(){const e=document.getElementById("quickStartCase").value||"testcase",t=Math.min(20,Math.max(1,parseInt(document.getElementById("tabCount").value)||1));this.terminal.clear(),this.terminal.writeln(`\x1B[1;32m Starting ${t} Claude session(s) in ${e}...\x1B[0m`),this.terminal.writeln(""),this.terminal.focus();try{let a=(await(await fetch(`/api/cases/${e}`)).json())?.data??{};if(!a.path){const f=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:""})})).json();if(!f.success)throw new Error(f.error||"Failed to create case");a=f.data.case}const n=a.path;if(!n)throw new Error("Case path not found");if(a.location==="remote"){const u=[];for(let f=0;f<t;f++){const w=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"claude"})})).json();if(!w.success)throw new Error(w.error||"Failed to start remote Claude session");u.push(w.data.sessionId)}this.terminal.writeln(`\x1B[90m All ${t} remote session(s) ready\x1B[0m`),u[0]&&(await this.selectSession(u[0]),this.loadQuickStartCases()),this.terminal.focus();return}let o=null,i=1;for(const[,u]of this.sessions){const f=u.name&&u.name.match(/^w(\d+)-([a-zA-Z0-9_-]+)/);if(f&&f[2]===e){const v=parseInt(f[1]);v>=i&&(i=v+1)}}const r=this.isRalphTrackerEnabledByDefault(),c=[];for(let u=0;u<t;u++)c.push(`w${i+u}-${e}`);const m=this.getCaseSettings(e),h=this.loadAppSettingsFromStorage(),p=this.buildEnvOverrides(m,h),y=Object.keys(p).length>0,l=this.getEffortSetting(h),d=m.opusContext1m||h.opusContext1mEnabled,C=h.claudeModel||(d?"opus[1m]":"");this.terminal.writeln(`\x1B[90m Creating ${t} session(s)...\x1B[0m`);const g=c.map(u=>fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({workingDir:n,name:u,...y?{envOverrides:p}:{},...l?{effort:l}:{},...C!==void 0?{modelOverride:C}:{},statusLineTelemetry:h.showPlanUsageLimits===!0})}).then(f=>f.json())),S=await Promise.all(g),b=[];for(const u of S){if(!u.success)throw new Error(u.error);b.push(u.data.session.id)}o=b[0],await Promise.all(b.map(u=>fetch(`/api/sessions/${u}/ralph-config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:r,disableAutoEnable:!r})}))),this.terminal.writeln(`\x1B[90m Starting ${t} session(s) in parallel...\x1B[0m`),await Promise.all(b.map(u=>fetch(`/api/sessions/${u}/interactive`,{method:"POST"}))),this.terminal.writeln(`\x1B[90m All ${t} sessions ready\x1B[0m`),o&&(await this.selectSession(o),this.loadQuickStartCases()),this.terminal.focus()}catch(s){this.terminal.writeln(`\x1B[1;31m Error: ${s.message}\x1B[0m`)}},stopClaude(){if(!this.activeSessionId)return;const e=document.querySelector(".btn-toolbar.btn-stop");e&&(this._stopConfirmTimer?(clearTimeout(this._stopConfirmTimer),this._stopConfirmTimer=null,e.innerHTML=e.dataset.origHtml,delete e.dataset.origHtml,e.classList.remove("confirming"),fetch(`/api/sessions/${this.activeSessionId}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:""})})):(e.dataset.origHtml=e.innerHTML,e.textContent="Tap again",e.classList.add("confirming"),this._stopConfirmTimer=setTimeout(()=>{this._stopConfirmTimer=null,e.dataset.origHtml&&(e.innerHTML=e.dataset.origHtml,delete e.dataset.origHtml),e.classList.remove("confirming")},2e3)))},async runShell(){const e=document.getElementById("quickStartCase").value||"testcase",t=Math.min(20,Math.max(1,parseInt(document.getElementById("shellCount").value)||1));this.terminal.clear(),this.terminal.writeln(`\x1B[1;33m Starting ${t} Shell session(s) in ${e}...\x1B[0m`),this.terminal.writeln("");try{let a=(await(await fetch(`/api/cases/${e}`)).json())?.data??{};if(!a.path){const d=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:""})})).json();if(!d.success)throw new Error(d.error||"Failed to create case");a=d.data.case}const n=(this.cases||[]).find(l=>l.name===e),o=a.location==="remote"||n?.location==="remote",i=a.path;if(!i)throw new Error("Case path not found");if(a.location==="remote"){const l=[];for(let d=0;d<t;d++){const g=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"shell"})})).json();if(!g.success)throw new Error(g.error||"Failed to start remote shell session");l.push(g.data.sessionId)}l[0]&&(this.activeSessionId=l[0],await this.selectSession(l[0])),this.terminal.focus();return}let r=1;for(const[,l]of this.sessions){const d=l.name&&l.name.match(/^s(\d+)-([a-zA-Z0-9_-]+)/);if(d&&d[2]===e){const C=parseInt(d[1]);C>=r&&(r=C+1)}}const c=[];for(let l=0;l<t;l++)c.push(`s${r+l}-${e}`);const m=c.map(l=>fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...o?{caseName:e}:{workingDir:i},mode:"shell",name:l})}).then(d=>d.json())),h=await Promise.all(m),p=[];for(const l of h){if(!l.success)throw new Error(l.error);p.push(l.data.session.id)}await Promise.all(p.map(l=>fetch(`/api/sessions/${l}/shell`,{method:"POST"})));const y=this.getTerminalDimensions();y&&await Promise.all(p.map(l=>fetch(`/api/sessions/${l}/resize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)}))),p.length>0&&(this.activeSessionId=p[0],await this.selectSession(p[0])),this.terminal.focus()}catch(s){this.terminal.writeln(`\x1B[1;31m Error: ${s.message}\x1B[0m`)}},async runOpenCode(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(s=>s.name===e)?.location==="remote";this.terminal.clear(),this.terminal.writeln(`\x1B[1;32m Starting OpenCode session in ${e}...\x1B[0m`),this.terminal.writeln(""),this.terminal.focus();try{if(!t&&!(await(await fetch("/api/opencode/status")).json()).data.available){this.terminal.writeln("\x1B[1;31m OpenCode CLI not found.\x1B[0m"),this.terminal.writeln("\x1B[90m Install with: curl -fsSL https://opencode.ai/install | bash\x1B[0m");return}const s=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),n=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"opencode",...t?{}:{openCodeConfig:{autoAllowTools:!0},...Object.keys(s).length>0?{envOverrides:s}:{}}})})).json();if(!n.success)throw new Error(n.error||"Failed to start OpenCode");n.data.sessionId&&await this.selectSession(n.data.sessionId),this.terminal.focus()}catch(s){this.terminal.writeln(`\x1B[1;31m Error: ${s.message}\x1B[0m`)}},async runCodex(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(s=>s.name===e)?.location==="remote";this.terminal.clear(),this.terminal.writeln(`\x1B[1;32m Starting Codex session in ${e}...\x1B[0m`),this.terminal.writeln(""),this.terminal.focus();try{if(!t&&!(await(await fetch("/api/codex/status")).json()).data.available){this.terminal.writeln("\x1B[1;31m Codex CLI not found.\x1B[0m"),this.terminal.writeln("\x1B[90m Install with: npm install -g @openai/codex\x1B[0m");return}const s=this.loadAppSettingsFromStorage(),a=this.buildEnvOverrides(this.getCaseSettings(e),s),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"codex",...t?{}:{codexConfig:{dangerouslyBypassApprovals:s.codexDangerouslyBypassApprovals??!1,renderMode:"hybrid"},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start Codex");o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(s){this.terminal.writeln(`\x1B[1;31m Error: ${s.message}\x1B[0m`)}},async runGemini(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(s=>s.name===e)?.location==="remote";this.terminal.clear(),this.terminal.writeln(`\x1B[1;32m Starting Gemini session in ${e}...\x1B[0m`),this.terminal.writeln(""),this.terminal.focus();try{if(!t&&!(await(await fetch("/api/gemini/status")).json()).data.available){this.terminal.writeln("\x1B[1;31m Gemini CLI not found.\x1B[0m"),this.terminal.writeln("\x1B[90m Install with: npm install -g @google/gemini-cli\x1B[0m");return}const s=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),n=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"gemini",...t?{}:{geminiConfig:{approvalMode:"yolo"},...Object.keys(s).length>0?{envOverrides:s}:{}}})})).json();if(!n.success)throw new Error(n.error||"Failed to start Gemini");n.data.sessionId&&await this.selectSession(n.data.sessionId),this.terminal.focus()}catch(s){this.terminal.writeln(`\x1B[1;31m Error: ${s.message}\x1B[0m`)}},openSessionOptions(e){const t=this.sessions.get(e);if(!t)return;this.editingSessionId=e;const s=t.mode==="opencode"||t.mode==="codex"||t.mode==="gemini";this.switchOptionsTab(s?"summary":"respawn");const a=document.getElementById("sessionRespawnStatus"),n=document.getElementById("modalEnableRespawnBtn"),o=document.getElementById("modalStopRespawnBtn");this.respawnStatus[e]?(a.classList.add("active"),a.querySelector(".respawn-status-text").textContent=this.respawnStatus[e].state||"Active",n.style.display="none",o.style.display=""):(a.classList.remove("active"),a.querySelector(".respawn-status-text").textContent="Not active",n.style.display="",o.style.display="none");const i=document.getElementById("sessionRespawnSection");t.mode==="claude"&&t.pid?i.style.display="":i.style.display="none";const r=t.mode==="opencode"||t.mode==="codex"||t.mode==="gemini";document.querySelectorAll("[data-claude-only]").forEach(g=>{g.style.display=r?"none":""}),this.selectDurationPreset(""),this.loadSavedRespawnConfig(e),document.getElementById("modalAutoCompactEnabled").checked=t.autoCompactEnabled??!1,document.getElementById("modalAutoCompactThreshold").value=t.autoCompactThreshold??11e4,document.getElementById("modalAutoCompactPrompt").value=t.autoCompactPrompt??"",document.getElementById("modalAutoClearEnabled").checked=t.autoClearEnabled??!1,document.getElementById("modalAutoClearThreshold").value=t.autoClearThreshold??14e4,document.getElementById("modalAutoResumeEnabled").checked=t.autoResumeEnabled??!1,this.updateAutoResumeStatus(e),document.getElementById("modalImageWatcherEnabled").checked=t.imageWatcherEnabled??!0,document.getElementById("modalFlickerFilterEnabled").checked=t.flickerFilterEnabled??!1;const m=parseSessionPrefix(t.name),h=document.getElementById("modalSessionPrefix");m?(h.textContent=m.prefix+": ",h.style.display="",document.getElementById("modalSessionName").value=m.suffix,document.getElementById("modalSessionName").placeholder="Add description..."):(h.style.display="none",h.textContent="",document.getElementById("modalSessionName").value=t.name||"",document.getElementById("modalSessionName").placeholder="Auto (directory name)");const p=t.color||"default";document.getElementById("sessionColorPicker")?.querySelectorAll(".color-swatch").forEach(g=>{g.classList.toggle("selected",g.dataset.color===p)}),this.renderPresetDropdown(),document.getElementById("respawnPresetSelect").value="",document.getElementById("presetDescriptionHint").textContent="";const l=document.querySelector('#sessionOptionsModal .modal-tab-btn[data-tab="ralph"]'),d=document.querySelector('#sessionOptionsModal .modal-tab-btn[data-tab="respawn"]');if(r?(l&&(l.style.display="none"),d&&(d.style.display="none"),this.switchOptionsTab("context")):(l&&(l.style.display=""),d&&(d.style.display="")),!r){const g=this.ralphStates.get(e);this.populateRalphForm({enabled:g?.loop?.enabled??t.ralphLoop?.enabled??!1,completionPhrase:g?.loop?.completionPhrase||t.ralphLoop?.completionPhrase||"",maxIterations:g?.loop?.maxIterations||t.ralphLoop?.maxIterations||0,maxTodos:g?.loop?.maxTodos||t.ralphLoop?.maxTodos,todoExpirationMinutes:g?.loop?.todoExpirationMinutes||t.ralphLoop?.todoExpirationMinutes})}const C=document.getElementById("sessionOptionsModal");C.classList.add("active"),this.activeFocusTrap=new FocusTrap(C),this.activeFocusTrap.activate()},async saveSessionName(){if(!this.editingSessionId)return;const e=this.sessions.get(this.editingSessionId),t=e?parseSessionPrefix(e.name):null,s=document.getElementById("modalSessionName").value.trim();let a;t?a=t.prefix+(s?": "+s:""):a=s;try{await this._apiPut(`/api/sessions/${this.editingSessionId}/name`,{name:a})}catch(n){this.showToast("Failed to save session name: "+n.message,"error")}},async autoSaveAutoCompact(){if(this.editingSessionId)try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-compact`,{enabled:document.getElementById("modalAutoCompactEnabled").checked,threshold:parseInt(document.getElementById("modalAutoCompactThreshold").value)||11e4,prompt:document.getElementById("modalAutoCompactPrompt").value.trim()||void 0})}catch{}},async autoSaveAutoClear(){if(this.editingSessionId)try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-clear`,{enabled:document.getElementById("modalAutoClearEnabled").checked,threshold:parseInt(document.getElementById("modalAutoClearThreshold").value)||14e4})}catch{}},async autoSaveAutoResume(){if(!this.editingSessionId)return;const e=document.getElementById("modalAutoResumeEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-resume`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.autoResumeEnabled=e,e||(t.autoResumeAt=void 0)),this.updateAutoResumeStatus(this.editingSessionId),this.showToast(`Auto-resume on usage limit ${e?"enabled":"disabled"}`,"success")}catch(t){this.showToast("Failed to toggle auto-resume: "+t.message,"error")}},updateAutoResumeStatus(e){const t=document.getElementById("autoResumeStatus");if(!t||this.editingSessionId!==e)return;const s=this.sessions.get(e);if(s?.autoResumeAt&&s.autoResumeAt>Date.now()){const a=new Date(s.autoResumeAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});t.textContent=`Usage limit pause active \u2014 resumes at ${a}`,t.classList.add("active")}else t.textContent="",t.classList.remove("active")},async toggleSessionImageWatcher(){if(!this.editingSessionId)return;const e=document.getElementById("modalImageWatcherEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/image-watcher`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.imageWatcherEnabled=e),this.showToast(`Image watcher ${e?"enabled":"disabled"}`,"success")}catch{this.showToast("Failed to toggle image watcher","error")}},async toggleFlickerFilter(){if(!this.editingSessionId)return;const e=document.getElementById("modalFlickerFilterEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/flicker-filter`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.flickerFilterEnabled=e),this.showToast(`Flicker filter ${e?"enabled":"disabled"}`,"success")}catch{this.showToast("Failed to toggle flicker filter","error")}},async autoSaveRespawnConfig(){if(!this.editingSessionId)return;const e={updatePrompt:document.getElementById("modalRespawnPrompt").value,sendClear:document.getElementById("modalRespawnSendClear").checked,sendInit:document.getElementById("modalRespawnSendInit").checked,kickstartPrompt:document.getElementById("modalRespawnKickstart").value.trim()||void 0,autoAcceptPrompts:document.getElementById("modalRespawnAutoAccept").checked};try{await this._apiPut(`/api/sessions/${this.editingSessionId}/respawn/config`,e)}catch{}},async loadSavedRespawnConfig(e){try{const s=await(await fetch(`/api/sessions/${e}/respawn/config`)).json();if(s.success&&s.data&&s.data.config){const a=s.data.config;document.getElementById("modalRespawnPrompt").value=a.updatePrompt||"update all the docs and CLAUDE.md",document.getElementById("modalRespawnSendClear").checked=a.sendClear??!0,document.getElementById("modalRespawnSendInit").checked=a.sendInit??!0,document.getElementById("modalRespawnKickstart").value=a.kickstartPrompt||"",document.getElementById("modalRespawnAutoAccept").checked=a.autoAcceptPrompts??!0,a.durationMinutes&&(document.querySelector(`.duration-preset-btn[data-minutes="${a.durationMinutes}"]`)?this.selectDurationPreset(String(a.durationMinutes)):(this.selectDurationPreset("custom"),document.getElementById("modalRespawnDuration").value=a.durationMinutes))}}catch{}},selectDurationPreset(e){document.querySelectorAll(".duration-preset-btn").forEach(n=>n.classList.remove("active"));const t=document.querySelector(`.duration-preset-btn[data-minutes="${e}"]`);t&&t.classList.add("active");const s=document.querySelector(".duration-custom-input"),a=document.getElementById("modalRespawnDuration");e==="custom"?(s.classList.add("visible"),a.focus()):(s.classList.remove("visible"),a.value="")},getSelectedDuration(){const e=document.querySelector(".duration-custom-input"),t=document.getElementById("modalRespawnDuration");if(e.classList.contains("visible"))return t.value?parseInt(t.value):null;{const a=document.querySelector(".duration-preset-btn.active")?.dataset.minutes;return a?parseInt(a):null}},switchOptionsTab(e){document.querySelectorAll("#sessionOptionsModal .modal-tab-btn").forEach(t=>{t.classList.toggle("active",t.dataset.tab===e)}),document.getElementById("respawn-tab").classList.toggle("hidden",e!=="respawn"),document.getElementById("context-tab").classList.toggle("hidden",e!=="context"),document.getElementById("ralph-tab").classList.toggle("hidden",e!=="ralph"),document.getElementById("summary-tab").classList.toggle("hidden",e!=="summary"),e==="summary"&&this.editingSessionId&&this.loadRunSummary(this.editingSessionId)},getRalphConfig(){return{enabled:document.getElementById("modalRalphEnabled").checked,completionPhrase:document.getElementById("modalRalphPhrase").value.trim(),maxIterations:parseInt(document.getElementById("modalRalphMaxIterations").value)||0,maxTodos:parseInt(document.getElementById("modalRalphMaxTodos").value)||50,todoExpirationMinutes:parseInt(document.getElementById("modalRalphTodoExpiration").value)||60}},populateRalphForm(e){document.getElementById("modalRalphEnabled").checked=e?.enabled??!1,document.getElementById("modalRalphPhrase").value=e?.completionPhrase||"",document.getElementById("modalRalphMaxIterations").value=e?.maxIterations||0,document.getElementById("modalRalphMaxTodos").value=e?.maxTodos||50,document.getElementById("modalRalphTodoExpiration").value=e?.todoExpirationMinutes||60},async saveRalphConfig(){if(!this.editingSessionId){this.showToast("No session selected","warning");return}const e=this.getRalphConfig();e.enabled&&this.ralphClosedSessions.delete(this.editingSessionId);try{const s=await(await fetch(`/api/sessions/${this.editingSessionId}/ralph-config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json();if(s.error)throw new Error(s.error);this.showToast("Ralph config saved","success")}catch(t){this.showToast("Failed to save Ralph config: "+t.message,"error")}},startInlineRename(e){const t=this.sessions.get(e);if(!t)return;const s=document.querySelector(`.tab-name[data-session-id="${e}"]`);if(!s)return;this._inlineRenameActive=!0;const a=this.getSessionName(t),n=parseSessionPrefix(t.name),o=s.textContent;for(s.textContent="";s.firstChild;)s.removeChild(s.firstChild);if(n){const c=document.createElement("span");c.textContent=n.prefix+": ",c.style.cssText="color: var(--text-muted); font-size: 0.75rem; white-space: nowrap;",s.appendChild(c)}const i=document.createElement("input");i.type="text",i.value=n?n.suffix:t.name||"",i.placeholder=n?"Add description...":a,i.className="tab-rename-input",i.style.cssText="width: 80px; font-size: 0.75rem; padding: 2px 4px; background: var(--bg-input); border: 1px solid var(--accent); border-radius: 3px; color: var(--text); outline: none;",s.appendChild(i),i.focus(),i.select();const r=async({commit:c})=>{if(!this._inlineRenameActive)return;if(this._inlineRenameActive=!1,this._activeRename=null,!c){this.renderSessionTabs();return}const m=i.value.trim(),h=n?n.prefix+(m?": "+m:""):m;if(s.textContent=h||o,this.sessions.has(e)&&h!==t.name)try{await fetch(`/api/sessions/${e}/name`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:h})})}catch{s.textContent=o,this.showToast("Failed to rename","error")}this.renderSessionTabs()};this._activeRename={sessionId:e,cancel:()=>r({commit:!1})},i.addEventListener("blur",()=>r({commit:!0})),i.addEventListener("keydown",c=>{c.isComposing||c.keyCode===229||(c.key==="Enter"?(c.preventDefault(),i.blur()):c.key==="Escape"&&(i.value="",i.blur()))})},toggleCaseSettings(){const e=document.getElementById("caseSettingsPopover");if(e.classList.contains("hidden")){const t=document.getElementById("quickStartCase").value||"testcase",s=this.getCaseSettings(t);document.getElementById("caseAgentTeams").checked=s.agentTeams,document.getElementById("caseOpusContext1m").checked=s.opusContext1m,e.classList.remove("hidden");const a=n=>{!e.contains(n.target)&&!n.target.classList.contains("btn-case-settings")&&(e.classList.add("hidden"),document.removeEventListener("click",a))};setTimeout(()=>document.addEventListener("click",a),0)}else e.classList.add("hidden")},getCaseSettings(e){try{const t=localStorage.getItem("caseSettings_"+e);if(t)return JSON.parse(t)}catch{}return{agentTeams:!1,opusContext1m:!0}},saveCaseSettings(e,t){localStorage.setItem("caseSettings_"+e,JSON.stringify(t))},onCaseSettingChanged(){const e=document.getElementById("quickStartCase").value||"testcase",t=this.getCaseSettings(e);t.agentTeams=document.getElementById("caseAgentTeams").checked,t.opusContext1m=document.getElementById("caseOpusContext1m").checked,this.saveCaseSettings(e,t);const s=document.getElementById("caseAgentTeamsMobile");s&&(s.checked=t.agentTeams);const a=document.getElementById("caseOpusContext1mMobile");a&&(a.checked=t.opusContext1m)},toggleCaseSettingsMobile(){const e=document.getElementById("caseSettingsPopoverMobile");if(e.classList.contains("hidden")){const t=document.getElementById("quickStartCase").value||"testcase",s=this.getCaseSettings(t);document.getElementById("caseAgentTeamsMobile").checked=s.agentTeams,document.getElementById("caseOpusContext1mMobile").checked=s.opusContext1m,e.classList.remove("hidden");const a=n=>{!e.contains(n.target)&&!n.target.classList.contains("btn-case-settings-mobile")&&(e.classList.add("hidden"),document.removeEventListener("click",a))};setTimeout(()=>document.addEventListener("click",a),0)}else e.classList.add("hidden")},onCaseSettingChangedMobile(){const e=document.getElementById("quickStartCase").value||"testcase",t=this.getCaseSettings(e);t.agentTeams=document.getElementById("caseAgentTeamsMobile").checked,t.opusContext1m=document.getElementById("caseOpusContext1mMobile").checked,this.saveCaseSettings(e,t);const s=document.getElementById("caseAgentTeams");s&&(s.checked=t.agentTeams);const a=document.getElementById("caseOpusContext1m");a&&(a.checked=t.opusContext1m)},showCreateCaseModal(){document.getElementById("newCaseName").value="",document.getElementById("newCaseDescription").value="",document.getElementById("linkCaseName").value="",document.getElementById("linkCasePath").value="",["remoteCaseName","remoteCasePath","remoteHostId","remoteHostAddress","remoteHostUsername","remoteHostPort","remoteHostCodexCommand","remoteHostIdentityFile","remoteHostSocksProxy","remoteHostJumpHost","remoteHostExtraSshOptions"].forEach(s=>{const a=document.getElementById(s);a&&(a.value="")}),this.caseModalTab="case-create",this.switchCaseModalTab("case-create");const t=document.getElementById("createCaseModal");t.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(s=>{s.onclick=()=>this.switchCaseModalTab(s.dataset.tab)}),t.querySelectorAll('input[type="text"]').forEach(s=>{s._mobileScrollWired||(s._mobileScrollWired=!0,s.addEventListener("focus",()=>{window.innerWidth<=430&&setTimeout(()=>s.scrollIntoView({behavior:"smooth",block:"center"}),300)}))}),t.classList.add("active"),document.getElementById("newCaseName").focus()},switchCaseModalTab(e){this.caseModalTab=e;const t=document.getElementById("createCaseModal");t.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(a=>{a.classList.toggle("active",a.dataset.tab===e)}),t.querySelectorAll(".modal-tab-content").forEach(a=>{a.classList.toggle("hidden",a.id!==e)});const s=document.getElementById("caseModalSubmit");e==="case-manage"?(s.style.display="none",this.renderCaseManageList()):(s.style.display="",s.textContent=e==="case-create"?"Create":e==="case-remote"?"Link Remote":"Link"),e==="case-create"?document.getElementById("newCaseName").focus():e==="case-link"?document.getElementById("linkCaseName").focus():e==="case-remote"&&document.getElementById("remoteCaseName").focus()},closeCreateCaseModal(){document.getElementById("createCaseModal").classList.remove("active")},async submitCaseModal(){const e=document.getElementById("caseModalSubmit"),t=e.textContent;e.classList.add("loading"),e.textContent=this.caseModalTab==="case-create"?"Creating...":"Linking...";try{this.caseModalTab==="case-create"?await this.createCase():this.caseModalTab==="case-remote"?await this.linkRemoteCase():await this.linkCase()}finally{e.classList.remove("loading"),e.textContent=t}},async createCase(){const e=document.getElementById("newCaseName").value.trim(),t=document.getElementById("newCaseDescription").value.trim();if(!e){this.showToast("Please enter a case name","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}try{const a=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:t})})).json();a.success?(this.closeCreateCaseModal(),this.showToast(`Case "${e}" created`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)):this.showToast(a.error||"Failed to create case","error")}catch(s){console.error("Failed to create case:",s),this.showToast("Failed to create case: "+s.message,"error")}},async linkCase(){const e=document.getElementById("linkCaseName").value.trim(),t=document.getElementById("linkCasePath").value.trim();if(!e){this.showToast("Please enter a case name","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}if(!t){this.showToast("Please enter a folder path","error");return}try{const a=await(await fetch("/api/cases/link",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,path:t})})).json();a.success?(this.closeCreateCaseModal(),this.showToast(`Case "${e}" linked to ${t}`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)):this.showToast(a.error||"Failed to link case","error")}catch(s){console.error("Failed to link case:",s),this.showToast("Failed to link case: "+s.message,"error")}},async linkRemoteCase(){const e=document.getElementById("remoteCaseName").value.trim(),t=document.getElementById("remoteCasePath").value.trim(),s=document.getElementById("remoteHostId").value.trim(),a=document.getElementById("remoteHostAddress").value.trim(),n=document.getElementById("remoteHostUsername").value.trim(),o=document.getElementById("remoteHostCodexCommand").value.trim(),i=document.getElementById("remoteHostPort").value.trim(),r=document.getElementById("remoteHostIdentityFile").value.trim(),c=document.getElementById("remoteHostSocksProxy").value.trim(),m=document.getElementById("remoteHostJumpHost").value.trim(),h=document.getElementById("remoteHostExtraSshOptions").value.split(`
|
|
14
|
+
`).map(y=>y.trim()).filter(y=>y.length>0);if(!e||!t||!s||!a||!n){this.showToast("Please complete all required remote fields","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)||!/^[a-zA-Z0-9_-]+$/.test(s)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}if(!t.startsWith("/")){this.showToast("Remote path must be absolute","error");return}let p;if(i&&(p=Number(i),!Number.isInteger(p)||p<1||p>65535)){this.showToast("SSH port must be a number between 1 and 65535","error");return}try{const y={id:s,label:s,host:a,username:n,...p?{port:p}:{},...r?{identityFile:r}:{},...c?{socksProxy:c}:{},...m?{jumpHost:m}:{},...h.length?{extraSshOptions:h}:{},...o?{commands:{codex:o}}:{}},d=await(await fetch("/api/remote-hosts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)})).json();if(!d.success&&d.errorCode!=="ALREADY_EXISTS")throw new Error(d.error||"Failed to save remote host");const g=await(await fetch("/api/cases/remote-link",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,hostId:s,remotePath:t})})).json();g.success?(this.closeCreateCaseModal(),this.showToast(`Remote case "${e}" linked`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)):this.showToast(g.error||"Failed to link remote case","error")}catch(y){console.error("Failed to link remote case:",y),this.showToast("Failed to link remote case: "+y.message,"error")}},renderCaseManageList(){const e=document.getElementById("caseManageList"),t=this.cases||[];if(t.length===0){e.innerHTML='<div class="form-hint" style="text-align: center; padding: 2rem 0;">No cases yet</div>';return}let s="";t.forEach((a,n)=>{const o=n===0,i=n===t.length-1,r=a.path?a.path.replace(/^\/Users\/[^/]+/,"~"):"";s+=`
|
|
15
|
+
<div class="case-manage-item" data-case="${escapeHtml(a.name)}">
|
|
16
|
+
<div class="case-manage-info">
|
|
17
|
+
<span class="case-manage-name">${escapeHtml(a.name)}</span>
|
|
18
|
+
<span class="case-manage-path">${escapeHtml(r)}</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="case-manage-actions">
|
|
21
|
+
<button class="case-manage-btn" onclick="app.moveCaseUp(${escapeHtml(JSON.stringify(a.name))})"
|
|
22
|
+
title="Move up" ${o?"disabled":""}>▲</button>
|
|
23
|
+
<button class="case-manage-btn" onclick="app.moveCaseDown(${escapeHtml(JSON.stringify(a.name))})"
|
|
24
|
+
title="Move down" ${i?"disabled":""}>▼</button>
|
|
25
|
+
<button class="case-manage-btn case-manage-btn-delete" onclick="app.deleteCase(${escapeHtml(JSON.stringify(a.name))})"
|
|
26
|
+
title="Delete case">✕</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
`}),e.innerHTML=s},async moveCaseUp(e){const t=this.cases||[],s=t.findIndex(n=>n.name===e);if(s<=0)return;const a=[...t];[a[s-1],a[s]]=[a[s],a[s-1]],this.cases=a,this.renderCaseManageList(),await this.saveCaseOrder(a.map(n=>n.name))},async moveCaseDown(e){const t=this.cases||[],s=t.findIndex(n=>n.name===e);if(s<0||s>=t.length-1)return;const a=[...t];[a[s],a[s+1]]=[a[s+1],a[s]],this.cases=a,this.renderCaseManageList(),await this.saveCaseOrder(a.map(n=>n.name))},async deleteCase(e){if(confirm(`Delete case "${e}"? Linked cases will only be unlinked (folder preserved). Created cases will be permanently deleted.`))try{const s=await(await fetch(`/api/cases/${encodeURIComponent(e)}`,{method:"DELETE"})).json();if(s.success){this.showToast(`Case "${e}" ${s.data?.type==="unlinked"?"unlinked":"deleted"}`,"success"),this.cases=(this.cases||[]).filter(o=>o.name!==e),this.renderCaseManageList();const a=document.getElementById("quickStartCase"),n=a.value;n===e&&a.blur?.(),await this.loadQuickStartCases(n===e?null:n),n===e&&await this.saveLastUsedCase(document.getElementById("quickStartCase")?.value||"testcase")}else this.showToast(s.error||"Failed to delete case","error")}catch(t){this.showToast("Failed to delete case: "+t.message,"error")}},async saveCaseOrder(e){try{await fetch("/api/cases/order",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({order:e})});const s=document.getElementById("quickStartCase").value;await this.loadQuickStartCases(s)}catch(t){this.showToast("Failed to save case order: "+t.message,"error")}},showMobileCasePicker(){const e=document.getElementById("mobileCasePickerModal"),t=document.getElementById("mobileCaseList"),a=document.getElementById("quickStartCase").value;let n="";const o=this.getCasePickerOptions();for(const i of o){const r=i.name===a;n+=`
|
|
30
|
+
<button class="mobile-case-item ${r?"selected":""}"
|
|
31
|
+
onclick="app.selectMobileCase(${escapeHtml(JSON.stringify(i.name))})">
|
|
32
|
+
<span class="mobile-case-item-icon">
|
|
33
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
34
|
+
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
|
35
|
+
</svg>
|
|
36
|
+
</span>
|
|
37
|
+
<span class="mobile-case-item-name">${escapeHtml(i.label)}</span>
|
|
38
|
+
<span class="mobile-case-item-delete" onclick="event.stopPropagation(); app.deleteCaseMobile(${escapeHtml(JSON.stringify(i.name))})" title="Delete">
|
|
39
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
40
|
+
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
41
|
+
</svg>
|
|
42
|
+
</span>
|
|
43
|
+
<span class="mobile-case-item-check">
|
|
44
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
45
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
46
|
+
</svg>
|
|
47
|
+
</span>
|
|
48
|
+
</button>
|
|
49
|
+
`}t.innerHTML=n,e.classList.add("active")},closeMobileCasePicker(){document.getElementById("mobileCasePickerModal").classList.remove("active")},selectMobileCase(e){const t=document.getElementById("quickStartCase");t.value=e,this.updateMobileCaseLabel(e),this.updateDirDisplayForCase(e),this.saveLastUsedCase(e),this.closeMobileCasePicker(),this.showToast(`Selected: ${e}`,"success")},updateMobileCaseLabel(e){const t=document.getElementById("mobileCaseName");t&&(t.textContent=e)},async deleteCaseMobile(e){if(confirm(`Delete case "${e}"?`))try{const s=await(await fetch(`/api/cases/${encodeURIComponent(e)}`,{method:"DELETE"})).json();s.success?(this.showToast(`Case "${e}" ${s.data?.type==="unlinked"?"unlinked":"deleted"}`,"success"),this.cases=(this.cases||[]).filter(a=>a.name!==e),this.closeMobileCasePicker(),await this.loadQuickStartCases()):this.showToast(s.error||"Failed to delete case","error")}catch(t){this.showToast("Failed to delete case: "+t.message,"error")}},showCreateCaseFromMobile(){this.closeMobileCasePicker(),this.showCreateCaseModal();const e=document.getElementById("createCaseModal");e.classList.add("from-mobile"),setTimeout(()=>e.classList.remove("from-mobile"),300)}}),Object.defineProperty(CodemanApp.prototype,"runMode",{configurable:!0,enumerable:!0,get(){return this._runMode||"claude"},set(e){this._runMode=e==="opencode"||e==="codex"||e==="gemini"||e==="claude"?e:"claude"}});
|
|
Binary file
|
|
Binary file
|