aicodeman 1.1.17 → 1.2.1
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/dist/config/dependency-registry.d.ts.map +1 -1
- package/dist/config/dependency-registry.js +8 -0
- package/dist/config/dependency-registry.js.map +1 -1
- package/dist/mux-interface.d.ts +3 -1
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/ralph-tracker.d.ts +19 -1
- package/dist/ralph-tracker.d.ts.map +1 -1
- package/dist/ralph-tracker.js +44 -3
- package/dist/ralph-tracker.js.map +1 -1
- package/dist/search-service.d.ts +78 -0
- package/dist/search-service.d.ts.map +1 -0
- package/dist/search-service.js +155 -0
- package/dist/search-service.js.map +1 -0
- package/dist/session.d.ts +12 -6
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +36 -8
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +4 -0
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +92 -5
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/ralph.d.ts +4 -0
- package/dist/types/ralph.d.ts.map +1 -1
- package/dist/types/ralph.js.map +1 -1
- package/dist/types/search.d.ts +73 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +20 -0
- package/dist/types/search.js.map +1 -0
- package/dist/types/session.d.ts +15 -2
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +3 -1
- package/dist/types/session.js.map +1 -1
- package/dist/utils/gemini-cli-resolver.d.ts +20 -0
- package/dist/utils/gemini-cli-resolver.d.ts.map +1 -0
- package/dist/utils/gemini-cli-resolver.js +62 -0
- package/dist/utils/gemini-cli-resolver.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/web/away-digest.d.ts +91 -0
- package/dist/web/away-digest.d.ts.map +1 -0
- package/dist/web/away-digest.js +244 -0
- package/dist/web/away-digest.js.map +1 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/{app.d42616c5.js → app.a8d73c32.js} +2 -2
- package/dist/web/public/{app.d42616c5.js.br → app.a8d73c32.js.br} +0 -0
- package/dist/web/public/{app.d42616c5.js.gz → app.a8d73c32.js.gz} +0 -0
- package/dist/web/public/constants.1c779517.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +93 -8
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.4e950e0a.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.8a9aff7a.css +1 -0
- package/dist/web/public/mobile.8a9aff7a.css.br +0 -0
- package/dist/web/public/mobile.8a9aff7a.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.aedcf302.js → panels-ui.772e92c9.js} +131 -86
- 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/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.da3e53f2.js +36 -0
- 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.gz +0 -0
- package/dist/web/public/styles.1b3e6147.css +1 -0
- package/dist/web/public/styles.1b3e6147.css.br +0 -0
- package/dist/web/public/styles.1b3e6147.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.865f21f6.js +3 -0
- package/dist/web/public/terminal-ui.865f21f6.js.br +0 -0
- package/dist/web/public/terminal-ui.865f21f6.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/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/ralph-routes.d.ts.map +1 -1
- package/dist/web/routes/ralph-routes.js +7 -1
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/search-routes.d.ts +27 -0
- package/dist/web/routes/search-routes.d.ts.map +1 -0
- package/dist/web/routes/search-routes.js +123 -0
- package/dist/web/routes/search-routes.js.map +1 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +41 -13
- 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 +56 -1
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +39 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +49 -4
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +5 -1
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/mobile.38346326.css +0 -1
- package/dist/web/public/mobile.38346326.css.br +0 -0
- package/dist/web/public/mobile.38346326.css.gz +0 -0
- package/dist/web/public/panels-ui.aedcf302.js.br +0 -0
- package/dist/web/public/panels-ui.aedcf302.js.gz +0 -0
- package/dist/web/public/session-ui.0ee6c334.js +0 -36
- package/dist/web/public/session-ui.0ee6c334.js.br +0 -0
- package/dist/web/public/session-ui.0ee6c334.js.gz +0 -0
- package/dist/web/public/styles.4d2984bd.css +0 -1
- package/dist/web/public/styles.4d2984bd.css.br +0 -0
- package/dist/web/public/styles.4d2984bd.css.gz +0 -0
- package/dist/web/public/terminal-ui.f4f3b686.js +0 -3
- package/dist/web/public/terminal-ui.f4f3b686.js.br +0 -0
- package/dist/web/public/terminal-ui.f4f3b686.js.gz +0 -0
|
@@ -1,20 +1,65 @@
|
|
|
1
|
-
"use strict";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)},async
|
|
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)},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")},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
|
+
<div class="away-digest-card">
|
|
3
|
+
<span class="away-digest-card-label">Needs Attention</span>
|
|
4
|
+
<span class="away-digest-card-value">${e.totals.needsAttention}</span>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="away-digest-card">
|
|
7
|
+
<span class="away-digest-card-label">Completed</span>
|
|
8
|
+
<span class="away-digest-card-value">${e.totals.completed}</span>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="away-digest-card">
|
|
11
|
+
<span class="away-digest-card-label">Active Sessions</span>
|
|
12
|
+
<span class="away-digest-card-value">${e.totals.activeSessions}</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="away-digest-card">
|
|
15
|
+
<span class="away-digest-card-label">Tokens</span>
|
|
16
|
+
<span class="away-digest-card-value">${this.formatTokens(i+a)}</span>
|
|
17
|
+
<span class="away-digest-card-cost">~$${r.toFixed(2)}</span>
|
|
18
|
+
</div>
|
|
19
|
+
`;const o=[];(e.dataFreshness.runSummariesLiveOnly||e.dataFreshness.subagentsLiveOnly)&&o.push("Run summaries and subagent completions use recent live state; lifecycle and token stats are persisted."),e.totals.tokenWindowPrecision==="day"&&o.push("Token totals are aggregated at day precision."),s.textContent=o.join(" "),n.innerHTML=AWAY_DIGEST_SECTIONS.map(([l,d])=>this.renderAwayDigestSection(d,e.sections[l]||[])).join(""),this.attachAwayDigestActions()},renderAwayDigestSection(e,t){const s=t.length,n=s?t.map(i=>this.renderAwayDigestItem(i)).join(""):'<div class="away-digest-empty">No items</div>';return`
|
|
20
|
+
<section class="away-digest-section">
|
|
21
|
+
<div class="away-digest-section-title">
|
|
22
|
+
<h4>${escapeHtml(e)}</h4>
|
|
23
|
+
<span>${s}</span>
|
|
24
|
+
</div>
|
|
25
|
+
${n}
|
|
26
|
+
</section>
|
|
27
|
+
`},renderAwayDigestItem(e){const t=this.formatAwayDigestSource(e.source),s=e.sessionName||e.sessionId||"",n=e.detail?`<div class="away-digest-item-detail">${escapeHtml(e.detail)}</div>`:"",i=e.link?`
|
|
28
|
+
<button class="away-digest-action"
|
|
29
|
+
data-away-link-type="${escapeHtml(e.link.type)}"
|
|
30
|
+
data-away-session-id="${escapeHtml(e.link.sessionId||"")}">
|
|
31
|
+
Open
|
|
32
|
+
</button>
|
|
33
|
+
`:"";return`
|
|
34
|
+
<article class="away-digest-item away-digest-${escapeHtml(e.severity)}">
|
|
35
|
+
<div class="away-digest-item-main">
|
|
36
|
+
<div class="away-digest-item-meta">
|
|
37
|
+
<span>${escapeHtml(this.formatAwayDigestTimestamp(e.timestamp))}</span>
|
|
38
|
+
<span>${escapeHtml(t)}</span>
|
|
39
|
+
${s?`<span>${escapeHtml(s)}</span>`:""}
|
|
40
|
+
</div>
|
|
41
|
+
<div class="away-digest-item-title">${escapeHtml(e.title)}</div>
|
|
42
|
+
${n}
|
|
43
|
+
</div>
|
|
44
|
+
${i}
|
|
45
|
+
</article>
|
|
46
|
+
`},attachAwayDigestActions(){const e=document.getElementById("awayDigestSections");e&&e.querySelectorAll("[data-away-link-type]").forEach(t=>{t.addEventListener("click",()=>{this.openAwayDigestItem(t.dataset.awayLinkType,t.dataset.awaySessionId||void 0)})})},async openAwayDigestItem(e,t){if(e==="session"&&t){await this.selectSession(t),this.closeAwayDigest();return}if(e==="run_summary"&&t){await this.openRunSummary(t),this.closeAwayDigest();return}e==="lifecycle"&&(this.openLifecycleLog(),this.closeAwayDigest())},formatAwayDigestTimestamp(e){return Number.isFinite(e)?new Date(e).toLocaleString([],{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}):""},formatAwayDigestSource(e){return{lifecycle:"Lifecycle",run_summary:"Run Summary",status:"Status",token_stats:"Token Stats",subagent:"Subagent"}[e]||e},async openTokenStats(){try{const t=await(await fetch("/api/token-stats")).json();t.success?(this.renderTokenStats(t.data),document.getElementById("tokenStatsModal").classList.add("active")):this.showToast("Failed to load token stats","error")}catch(e){console.error("Failed to fetch token stats:",e),this.showToast("Failed to load token stats","error")}},renderTokenStats(e){const{daily:t,totals:s}=e,n=new Date().toISOString().split("T")[0],i=t.find(g=>g.date===n)||{inputTokens:0,outputTokens:0,estimatedCost:0},a=new Date;a.setDate(a.getDate()-7);const r=t.filter(g=>new Date(g.date)>=a),o=r.reduce((g,S)=>g+S.inputTokens,0),l=r.reduce((g,S)=>g+S.outputTokens,0),d=this.estimateCost(o,l),c=s.totalInputTokens,m=s.totalOutputTokens,u=this.estimateCost(c,m),h=document.getElementById("statsSummary");h.innerHTML=`
|
|
2
47
|
<div class="stat-card">
|
|
3
48
|
<span class="stat-card-label">Today</span>
|
|
4
|
-
<span class="stat-card-value">${this.formatTokens(
|
|
5
|
-
<span class="stat-card-cost">~$${
|
|
49
|
+
<span class="stat-card-value">${this.formatTokens(i.inputTokens+i.outputTokens)}</span>
|
|
50
|
+
<span class="stat-card-cost">~$${i.estimatedCost.toFixed(2)}</span>
|
|
6
51
|
</div>
|
|
7
52
|
<div class="stat-card">
|
|
8
53
|
<span class="stat-card-label">7 Days</span>
|
|
9
|
-
<span class="stat-card-value">${this.formatTokens(
|
|
54
|
+
<span class="stat-card-value">${this.formatTokens(o+l)}</span>
|
|
10
55
|
<span class="stat-card-cost">~$${d.toFixed(2)}</span>
|
|
11
56
|
</div>
|
|
12
57
|
<div class="stat-card">
|
|
13
58
|
<span class="stat-card-label">Lifetime</span>
|
|
14
|
-
<span class="stat-card-value">${this.formatTokens(c+
|
|
15
|
-
<span class="stat-card-cost">~$${
|
|
59
|
+
<span class="stat-card-value">${this.formatTokens(c+m)}</span>
|
|
60
|
+
<span class="stat-card-cost">~$${u.toFixed(2)}</span>
|
|
16
61
|
</div>
|
|
17
|
-
`;const p=document.getElementById("statsChart"),
|
|
62
|
+
`;const p=document.getElementById("statsChart"),w=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],y=t.find(D=>D.date===$);b.push({date:$,dayName:S.toLocaleDateString("en-US",{weekday:"short"}),tokens:y?y.inputTokens+y.outputTokens:0,cost:y?y.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(""),w.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=`
|
|
18
63
|
<div class="stats-table-header">
|
|
19
64
|
<span>Date</span>
|
|
20
65
|
<span>Input</span>
|
|
@@ -29,29 +74,29 @@
|
|
|
29
74
|
<span class="cell cell-cost">$${g.estimatedCost.toFixed(2)}</span>
|
|
30
75
|
</div>
|
|
31
76
|
`).join("")}
|
|
32
|
-
`},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,
|
|
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,w=r+h;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),w=Math.max(0,Math.min(window.innerHeight-b.height,w)),e.style.left=p+"px",e.style.top=w+"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,w=r+h;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),w=Math.max(0,Math.min(window.innerHeight-b.height,w)),e.style.left=p+"px",e.style.top=w+"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(w=>w.id===h);p&&(u+=`<div class="task-node">${a(p,d)}</div>`)}u+="</div>"}return`
|
|
33
78
|
<div class="task-item">
|
|
34
79
|
<span class="task-status-icon ${l.status}">${c}</span>
|
|
35
80
|
<div class="task-info">
|
|
36
81
|
<div class="task-description">${escapeHtml(l.description)}</div>
|
|
37
82
|
<div class="task-meta">
|
|
38
83
|
<span class="task-type">${l.subagentType}</span>
|
|
39
|
-
<span>${
|
|
84
|
+
<span>${m}</span>
|
|
40
85
|
</div>
|
|
41
86
|
</div>
|
|
42
87
|
</div>
|
|
43
|
-
${
|
|
44
|
-
`},r=this.flattenTaskTree(e.taskTree);let
|
|
45
|
-
<div class="subagent-item ${
|
|
88
|
+
${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),w=u?`<span class="subagent-icon teammate-dot teammate-color-${u.color}">\u25CF</span>`:'<span class="subagent-icon">\u{1F916}</span>';t.push(`
|
|
90
|
+
<div class="subagent-item ${a} ${i?"selected":""}${u?" is-teammate":""}"
|
|
46
91
|
onclick="app.selectSubagent(${escapeHtml(JSON.stringify(n.agentId))})"
|
|
47
92
|
ondblclick="app.openSubagentWindow(${escapeHtml(JSON.stringify(n.agentId))})"
|
|
48
93
|
title="Double-click to open tracking window">
|
|
49
94
|
<div class="subagent-header">
|
|
50
|
-
${
|
|
51
|
-
<span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(
|
|
95
|
+
${w}
|
|
96
|
+
<span class="subagent-id" title="${escapeHtml(n.description||n.agentId)}">${escapeHtml(h.length>40?h.substring(0,40)+"...":h)}</span>
|
|
52
97
|
${p}
|
|
53
|
-
${
|
|
54
|
-
<span class="subagent-status ${
|
|
98
|
+
${m}
|
|
99
|
+
<span class="subagent-status ${a}">${n.status}</span>
|
|
55
100
|
${c?`<button class="subagent-kill-btn" onclick="event.stopPropagation(); app.killSubagent(${escapeHtml(JSON.stringify(n.agentId))})" title="Kill agent">✕</button>`:""}
|
|
56
101
|
<button class="subagent-window-btn" onclick="event.stopPropagation(); app.${d?"closeSubagentWindow":"openSubagentWindow"}(${escapeHtml(JSON.stringify(n.agentId))})" title="${d?"Close window":"Open in window"}">
|
|
57
102
|
${d?"\u2715":"\u29C9"}
|
|
@@ -62,30 +107,30 @@
|
|
|
62
107
|
${l?`<span class="subagent-last-tool">${this.getToolIcon(l)} ${l}</span>`:""}
|
|
63
108
|
</div>
|
|
64
109
|
</div>
|
|
65
|
-
`)}e.innerHTML=t.join("")},selectSubagent(e){this.activeSubagentId=e,this.renderSubagentPanel(),this.renderSubagentDetail()},renderSubagentDetail(){const e=this.$("subagentDetail");if(!e)return;if(!this.activeSubagentId){e.innerHTML='<div class="subagent-empty">Select an agent to view details</div>';return}const t=this.subagents.get(this.activeSubagentId),s=this.subagentActivity.get(this.activeSubagentId)||[];if(!t){e.innerHTML='<div class="subagent-empty">Agent not found</div>';return}const n=s.slice(-30).map(
|
|
110
|
+
`)}e.innerHTML=t.join("")},selectSubagent(e){this.activeSubagentId=e,this.renderSubagentPanel(),this.renderSubagentDetail()},renderSubagentDetail(){const e=this.$("subagentDetail");if(!e)return;if(!this.activeSubagentId){e.innerHTML='<div class="subagent-empty">Select an agent to view details</div>';return}const t=this.subagents.get(this.activeSubagentId),s=this.subagentActivity.get(this.activeSubagentId)||[];if(!t){e.innerHTML='<div class="subagent-empty">Agent not found</div>';return}const n=s.slice(-30).map(o=>{const l=new Date(o.timestamp).toLocaleTimeString("en-US",{hour12:!1});if(o.type==="tool"){const d=this.getToolDetailExpanded(o.tool,o.input,o.fullInput,o.toolUseId);return`<div class="subagent-activity tool" data-tool-use-id="${escapeHtml(o.toolUseId||"")}">
|
|
66
111
|
<span class="time">${l}</span>
|
|
67
|
-
<span class="icon">${this.getToolIcon(
|
|
68
|
-
<span class="name">${escapeHtml(
|
|
112
|
+
<span class="icon">${this.getToolIcon(o.tool)}</span>
|
|
113
|
+
<span class="name">${escapeHtml(o.tool)}</span>
|
|
69
114
|
<span class="detail">${escapeHtml(d.primary)}</span>
|
|
70
|
-
${d.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(
|
|
71
|
-
${d.hasMore?`<div class="tool-params-expanded" id="tool-params-${escapeHtml(
|
|
72
|
-
</div>`}else if(
|
|
115
|
+
${d.hasMore?`<button class="tool-expand-btn" onclick="app.toggleToolParams(${escapeHtml(JSON.stringify(o.toolUseId))})">\u25B6</button>`:""}
|
|
116
|
+
${d.hasMore?`<div class="tool-params-expanded" id="tool-params-${escapeHtml(o.toolUseId)}" style="display:none;"><pre>${escapeHtml(JSON.stringify(o.fullInput||o.input,null,2))}</pre></div>`:""}
|
|
117
|
+
</div>`}else if(o.type==="tool_result"){const d=o.isError?"\u274C":"\u{1F4C4}",c=o.isError?"error":"",m=o.contentLength>500?` (${this.formatBytes(o.contentLength)})`:"",u=o.preview.length>80?o.preview.substring(0,80)+"...":o.preview;return`<div class="subagent-activity tool-result ${c}">
|
|
73
118
|
<span class="time">${l}</span>
|
|
74
119
|
<span class="icon">${d}</span>
|
|
75
|
-
<span class="name">${escapeHtml(
|
|
76
|
-
<span class="detail">${escapeHtml(
|
|
77
|
-
</div>`}else if(
|
|
120
|
+
<span class="name">${escapeHtml(o.tool||"result")}</span>
|
|
121
|
+
<span class="detail">${escapeHtml(u)}${m}</span>
|
|
122
|
+
</div>`}else if(o.type==="progress"){const d=o.hookEvent||o.hookName,c=d?"\u{1FA9D}":o.progressType==="query_update"?"\u27F3":"\u2713",m=d?" hook":"",u=d?o.hookName||o.hookEvent:o.query||o.progressType;return`<div class="subagent-activity progress${m}">
|
|
78
123
|
<span class="time">${l}</span>
|
|
79
124
|
<span class="icon">${c}</span>
|
|
80
|
-
<span class="detail">${escapeHtml(
|
|
81
|
-
</div>`}else if(
|
|
125
|
+
<span class="detail">${escapeHtml(u)}</span>
|
|
126
|
+
</div>`}else if(o.type==="message"){const d=o.text.length>100?o.text.substring(0,100)+"...":o.text;return`<div class="subagent-activity message">
|
|
82
127
|
<span class="time">${l}</span>
|
|
83
128
|
<span class="icon">\u{1F4AC}</span>
|
|
84
129
|
<span class="detail">${escapeHtml(d)}</span>
|
|
85
|
-
</div>`}return""}).join(""),
|
|
130
|
+
</div>`}return""}).join(""),i=t.description||`Agent ${t.agentId}`,a=t.modelShort?`<span class="subagent-model-badge ${escapeHtml(t.modelShort)}">${escapeHtml(t.modelShort)}</span>`:"",r=t.totalInputTokens||t.totalOutputTokens?`<span>Tokens: ${this.formatTokenCount(t.totalInputTokens||0)}\u2193 ${this.formatTokenCount(t.totalOutputTokens||0)}\u2191</span>`:"";e.innerHTML=`
|
|
86
131
|
<div class="subagent-detail-header">
|
|
87
|
-
<span class="subagent-id" title="${escapeHtml(t.description||t.agentId)}">${escapeHtml(
|
|
88
|
-
${
|
|
132
|
+
<span class="subagent-id" title="${escapeHtml(t.description||t.agentId)}">${escapeHtml(i.length>60?i.substring(0,60)+"...":i)}</span>
|
|
133
|
+
${a}
|
|
89
134
|
<span class="subagent-status ${t.status}">${t.status}</span>
|
|
90
135
|
<button class="subagent-transcript-btn" onclick="app.viewSubagentTranscript(${escapeHtml(JSON.stringify(t.agentId))})">
|
|
91
136
|
View Full Transcript
|
|
@@ -100,7 +145,7 @@
|
|
|
100
145
|
<div class="subagent-activity-log">
|
|
101
146
|
${n||'<div class="subagent-empty">No activity yet</div>'}
|
|
102
147
|
</div>
|
|
103
|
-
`},toggleToolParams(e){const t=document.getElementById(`tool-params-${e}`);if(!t)return;const s=t.previousElementSibling;t.style.display==="none"?(t.style.display="block",s&&(s.textContent="\u25BC")):(t.style.display="none",s&&(s.textContent="\u25B6"))},formatTokenCount(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"k":e.toString()},formatBytes(e){return e>=1024*1024?(e/(1024*1024)).toFixed(1)+"MB":e>=1024?(e/1024).toFixed(1)+"KB":e+"B"},getToolIcon(e){return{WebSearch:"\u{1F50D}",WebFetch:"\u{1F310}",Read:"\u{1F4D6}",Write:"\u{1F4DD}",Edit:"\u270F\uFE0F",Bash:"\u{1F4BB}",Glob:"\u{1F4C1}",Grep:"\u{1F50E}",Task:"\u{1F916}"}[e]||"\u{1F527}"},getToolDetail(e,t){if(!t)return"";if(e==="WebSearch"&&t.query)return`"${t.query}"`;if(e==="WebFetch"&&t.url)return t.url;if(e==="Read"&&t.file_path||(e==="Write"||e==="Edit")&&t.file_path)return t.file_path;if(e==="Bash"&&t.command){const s=t.command;return s.length>40?s.substring(0,40)+"...":s}return e==="Glob"&&t.pattern||e==="Grep"&&t.pattern?t.pattern:""},getToolDetailExpanded(e,t,s,n){const
|
|
148
|
+
`},toggleToolParams(e){const t=document.getElementById(`tool-params-${e}`);if(!t)return;const s=t.previousElementSibling;t.style.display==="none"?(t.style.display="block",s&&(s.textContent="\u25BC")):(t.style.display="none",s&&(s.textContent="\u25B6"))},formatTokenCount(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"k":e.toString()},formatBytes(e){return e>=1024*1024?(e/(1024*1024)).toFixed(1)+"MB":e>=1024?(e/1024).toFixed(1)+"KB":e+"B"},getToolIcon(e){return{WebSearch:"\u{1F50D}",WebFetch:"\u{1F310}",Read:"\u{1F4D6}",Write:"\u{1F4DD}",Edit:"\u270F\uFE0F",Bash:"\u{1F4BB}",Glob:"\u{1F4C1}",Grep:"\u{1F50E}",Task:"\u{1F916}"}[e]||"\u{1F527}"},getToolDetail(e,t){if(!t)return"";if(e==="WebSearch"&&t.query)return`"${t.query}"`;if(e==="WebFetch"&&t.url)return t.url;if(e==="Read"&&t.file_path||(e==="Write"||e==="Edit")&&t.file_path)return t.file_path;if(e==="Bash"&&t.command){const s=t.command;return s.length>40?s.substring(0,40)+"...":s}return e==="Glob"&&t.pattern||e==="Grep"&&t.pattern?t.pattern:""},getToolDetailExpanded(e,t,s,n){const i=this.getToolDetail(e,t),a=["query","url","file_path","command","pattern"],l=Object.keys(s||t||{}).filter(d=>!a.includes(d)).length>0||s&&JSON.stringify(s).length>100;return{primary:i,hasMore:l,fullInput:s||t}},async killSubagent(e){try{const s=await(await this._apiDelete(`/api/subagents/${e}`))?.json();if(s?.success){const n=this.subagents.get(e);n&&(n.status="completed",this.subagents.set(e,n)),this.renderSubagentPanel(),this.renderSubagentDetail(),this.updateSubagentWindows(),this.showToast(`Subagent ${e.substring(0,7)} killed`,"success")}else this.showToast(s.error||"Failed to kill subagent","error")}catch(t){console.error("Failed to kill subagent:",t),this.showToast("Failed to kill subagent: "+t.message,"error")}},async viewSubagentTranscript(e){try{const s=await(await fetch(`/api/subagents/${e}/transcript?format=formatted`)).json();if(!s.success){alert("Failed to load transcript");return}const n=s.data.formatted.join(`
|
|
104
149
|
`);window.open("","_blank","width=800,height=600").document.write(`
|
|
105
150
|
<html>
|
|
106
151
|
<head>
|
|
@@ -115,29 +160,29 @@
|
|
|
115
160
|
<pre>${escapeHtml(n)}</pre>
|
|
116
161
|
</body>
|
|
117
162
|
</html>
|
|
118
|
-
`)}catch(t){console.error("Failed to load transcript:",t),alert("Failed to load transcript: "+t.message)}},findParentSessionForSubagent(e){if(this.subagentParentMap.has(e)){const s=this.subagentParentMap.get(e);if(this.sessions.has(s)){const n=this.subagents.get(e);if(n&&!n.parentSessionId){n.parentSessionId=s;const
|
|
163
|
+
`)}catch(t){console.error("Failed to load transcript:",t),alert("Failed to load transcript: "+t.message)}},findParentSessionForSubagent(e){if(this.subagentParentMap.has(e)){const s=this.subagentParentMap.get(e);if(this.sessions.has(s)){const n=this.subagents.get(e);if(n&&!n.parentSessionId){n.parentSessionId=s;const i=this.sessions.get(s);i&&(n.parentSessionName=this.getSessionName(i)),this.subagents.set(e,n),this.updateSubagentWindowParent(e)}return}this.subagentParentMap.delete(e)}const t=this.subagents.get(e);if(t){if(t.sessionId){for(const[s,n]of this.sessions)if(n.claudeSessionId===t.sessionId){this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}}if(this.activeSessionId&&this.sessions.has(this.activeSessionId)){this.setAgentParentSessionId(e,this.activeSessionId),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines();return}if(this.sessions.size>0){const s=this.sessions.keys().next().value;this.setAgentParentSessionId(e,s),this.updateSubagentWindowParent(e),this.updateSubagentWindowVisibility(),this.updateConnectionLines()}}},recheckOrphanSubagents(){let e=!1;for(const[t,s]of this.subagents)if(!this.subagentParentMap.has(t))this.findParentSessionForSubagent(t),this.subagentParentMap.has(t)&&(e=!0);else if(s.sessionId){const n=this.subagentParentMap.get(t),i=this.sessions.get(n);if(i&&i.claudeSessionId!==s.sessionId){for(const[a,r]of this.sessions)if(r.claudeSessionId===s.sessionId){this.subagentParentMap.set(t,a),s.parentSessionId=a,s.parentSessionName=this.getSessionName(r),this.subagents.set(t,s),this.updateSubagentWindowParent(t),e=!0;break}}}e&&(this.saveSubagentParentMap(),this.updateConnectionLines())},updateSubagentParentNames(e){const t=this.sessions.get(e);if(!t)return;const s=this.getSessionName(t);if(this._parentNameCache?.get(e)!==s){this._parentNameCache||(this._parentNameCache=new Map),this._parentNameCache.set(e,s);for(const[i,a]of this.subagentParentMap)if(a===e){const r=this.subagents.get(i);if(r){r.parentSessionName=s,this.subagents.set(i,r);const o=this.subagentWindows.get(i);if(o){const l=o.element.querySelector(".subagent-window-parent .parent-name");l&&(l.textContent=s)}}}}},updateSubagentWindowParent(e){const t=this.subagentWindows.get(e);if(!t)return;const s=this.subagentParentMap.get(e);if(!s)return;const n=this.sessions.get(s),i=n?this.getSessionName(n):"Unknown",a=t.element,r=a.querySelector(".subagent-window-parent");if(r){r.dataset.parentSession=s;const l=r.querySelector(".parent-name");l&&(l.textContent=i,l.onclick=()=>this.selectSession(s));return}const o=a.querySelector(".subagent-window-header");if(o){const l=document.createElement("div");l.className="subagent-window-parent",l.dataset.parentSession=s,l.innerHTML=`
|
|
119
164
|
<span class="parent-label">from</span>
|
|
120
|
-
<span class="parent-name" onclick="app.selectSession(${escapeHtml(JSON.stringify(s))})">${escapeHtml(
|
|
121
|
-
`,
|
|
165
|
+
<span class="parent-name" onclick="app.selectSession(${escapeHtml(JSON.stringify(s))})">${escapeHtml(i)}</span>
|
|
166
|
+
`,o.insertAdjacentElement("afterend",l)}},updateSubagentWindowVisibility(){const t=this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0;for(const[s,n]of this.subagentWindows){const i=this.subagentParentMap.get(s),a=this.subagents.get(s),r=i||a?.parentSessionId;let o;t?o=!!!r||r===this.activeSessionId:o=!0,o?(n.minimized||(n.element.style.display="flex",n._lazyTerminal&&this._restoreTeammateTerminalFromLazy(s)),n.hidden=!1):(this._disposeTeammateTerminalForMinimize(s),n.element.style.display="none",n.hidden=!0)}this.updateConnectionLines(),this.relayoutMobileSubagentWindows()},closeSessionSubagentWindows(e,t=!1){const s=[];for(const[n,i]of this.subagentWindows){const a=this.subagents.get(n),r=this.subagentParentMap.get(n);(a?.parentSessionId===e||r===e)&&s.push(n)}for(const n of s)this.forceCloseSubagentWindow(n),t&&(this.subagents.delete(n),this.subagentActivity.delete(n),this.subagentToolResults.delete(n),this.subagentParentMap.delete(n));this.minimizedSubagents.delete(e),this.renderSessionTabs()},forceCloseSubagentWindow(e){const t=this.subagentWindows.get(e);t&&(t.resizeObserver&&t.resizeObserver.disconnect(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.touchMove&&(document.removeEventListener("touchmove",t.dragListeners.touchMove),document.removeEventListener("touchend",t.dragListeners.up),document.removeEventListener("touchcancel",t.dragListeners.up)),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.subagentWindows.delete(e));const s=this.teammateTerminals.get(e);if(s){if(s.resizeObserver&&s.resizeObserver.disconnect(),s.terminal)try{s.terminal.dispose()}catch{}this.teammateTerminals.delete(e)}},minimizeSubagentWindow(e){const t=this.subagentWindows.get(e);t&&(this._disposeTeammateTerminalForMinimize(e),t.element.style.display="none",t.minimized=!0,this.updateConnectionLines())},scheduleSubagentWindowRender(e){this.subagentWindows.get(e)?.minimized||(this._subagentWindowRenderTimeouts||(this._subagentWindowRenderTimeouts=new Map),this._subagentWindowRenderTimeouts.has(e)&&clearTimeout(this._subagentWindowRenderTimeouts.get(e)),this._subagentWindowRenderTimeouts.set(e,setTimeout(()=>{this._subagentWindowRenderTimeouts.delete(e),scheduleBackground(()=>this.renderSubagentWindowContent(e))},100)))},renderSubagentWindowContent(e){if(this.teammateTerminals.has(e)||this.subagentWindows.get(e)?._lazyTerminal)return;const s=document.getElementById(`subagent-window-body-${e}`);if(!s)return;const n=this.subagentActivity.get(e)||[];if(n.length===0){s.innerHTML='<div class="subagent-empty">No activity yet</div>';return}const i=s.dataset.renderedCount?parseInt(s.dataset.renderedCount,10):0,a=100,r=n.slice(-a);if(i===0||i>r.length||s.children.length===0||s.children.length===1&&s.querySelector(".subagent-empty")){const o=r.map(l=>this._renderActivityItem(l)).join("");s.innerHTML=o,s.dataset.renderedCount=String(r.length)}else{const o=r.slice(i);if(o.length>0){const l=o.map(d=>this._renderActivityItem(d)).join("");for(s.insertAdjacentHTML("beforeend",l),s.dataset.renderedCount=String(r.length);s.children.length>a;)s.removeChild(s.firstChild)}}s.scrollTop=s.scrollHeight},_renderActivityItem(e){const t=new Date(e.timestamp).toLocaleTimeString("en-US",{hour12:!1});if(e.type==="tool")return`<div class="activity-line">
|
|
122
167
|
<span class="time">${t}</span>
|
|
123
168
|
<span class="tool-icon">${this.getToolIcon(e.tool)}</span>
|
|
124
169
|
<span class="tool-name">${escapeHtml(e.tool)}</span>
|
|
125
170
|
<span class="tool-detail">${escapeHtml(this.getToolDetail(e.tool,e.input))}</span>
|
|
126
|
-
</div>`;if(e.type==="tool_result"){const s=e.isError?"\u274C":"\u{1F4C4}",n=e.isError?" error":"",
|
|
171
|
+
</div>`;if(e.type==="tool_result"){const s=e.isError?"\u274C":"\u{1F4C4}",n=e.isError?" error":"",i=e.contentLength>500?` (${this.formatBytes(e.contentLength)})`:"",a=e.preview.length>60?e.preview.substring(0,60)+"...":e.preview;return`<div class="activity-line result-line${n}">
|
|
127
172
|
<span class="time">${t}</span>
|
|
128
173
|
<span class="tool-icon">${s}</span>
|
|
129
174
|
<span class="tool-name">${escapeHtml(e.tool||"\u2192")}</span>
|
|
130
|
-
<span class="tool-detail">${escapeHtml(
|
|
131
|
-
</div>`}else if(e.type==="progress"){const s=e.hookEvent||e.hookName,n=s?"\u{1FA9D}":e.progressType==="query_update"?"\u27F3":"\u2713",
|
|
175
|
+
<span class="tool-detail">${escapeHtml(a)}${i}</span>
|
|
176
|
+
</div>`}else if(e.type==="progress"){const s=e.hookEvent||e.hookName,n=s?"\u{1FA9D}":e.progressType==="query_update"?"\u27F3":"\u2713",i=s?e.hookName||e.hookEvent:e.query||e.progressType;return`<div class="activity-line progress-line${s?" hook-line":""}">
|
|
132
177
|
<span class="time">${t}</span>
|
|
133
178
|
<span class="tool-icon">${n}</span>
|
|
134
|
-
<span class="tool-detail">${escapeHtml(
|
|
179
|
+
<span class="tool-detail">${escapeHtml(i)}</span>
|
|
135
180
|
</div>`}else if(e.type==="message"){const s=e.text.length>150?e.text.substring(0,150)+"...":e.text;return`<div class="message-line">
|
|
136
181
|
<span class="time">${t}</span> \u{1F4AC} ${escapeHtml(s)}
|
|
137
|
-
</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),
|
|
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 y=this.subagentWindows.get(t);y.hidden&&(y.element.style.display="flex",y.hidden=!1),y.element.style.zIndex=++this.subagentWindowZIndex,y.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,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),w=d+h*(i+a);p=Math.max(10,Math.min(p,r-n-10)),w=Math.max(10,Math.min(w,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=`${w}px`,f.style.width=`${n}px`,f.style.height=`${i}px`,f.innerHTML=`
|
|
138
183
|
<div class="subagent-window-header">
|
|
139
184
|
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
|
|
140
|
-
<span class="icon" style="color: var(--team-color-${
|
|
185
|
+
<span class="icon" style="color: var(--team-color-${b}, #339af0)">\u2B24</span>
|
|
141
186
|
<span class="id">${escapeHtml(e.teammateName)}</span>
|
|
142
187
|
<span class="status running">terminal</span>
|
|
143
188
|
</div>
|
|
@@ -147,44 +192,44 @@
|
|
|
147
192
|
</div>
|
|
148
193
|
<div class="subagent-window-body teammate-terminal-body" id="subagent-window-body-${t}">
|
|
149
194
|
</div>
|
|
150
|
-
`,document.body.appendChild(f);const
|
|
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 y=this.subagentWindows.get(t);y&&(y._lazyTerminal=!0,y._lazyPaneTarget=e.paneTarget,y._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 w=e.querySelector(".team-tasks-header");w&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,w))}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",w=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 ${w}">
|
|
151
196
|
<span class="team-task-status">${p}</span>
|
|
152
|
-
<span class="team-task-subject">${escapeHtml(
|
|
153
|
-
${
|
|
154
|
-
</div>`}).join("");
|
|
155
|
-
<div class="project-insight-item" data-tool-id="${
|
|
197
|
+
<span class="team-task-subject">${escapeHtml(h.subject)}</span>
|
|
198
|
+
${b}
|
|
199
|
+
</div>`}).join("");m.innerHTML=u},hideTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");e&&(e.style.display="none"),this.teamTasksDragListeners&&(document.removeEventListener("mousemove",this.teamTasksDragListeners.move),document.removeEventListener("mouseup",this.teamTasksDragListeners.up),this.teamTasksDragListeners.touchMove&&(document.removeEventListener("touchmove",this.teamTasksDragListeners.touchMove),document.removeEventListener("touchend",this.teamTasksDragListeners.up),document.removeEventListener("touchcancel",this.teamTasksDragListeners.up)),this.teamTasksDragListeners.handle&&(this.teamTasksDragListeners.handle.removeEventListener("mousedown",this.teamTasksDragListeners.handleMouseDown),this.teamTasksDragListeners.handle.removeEventListener("touchstart",this.teamTasksDragListeners.handleTouchStart)),this.teamTasksDragListeners=null)},getTeammateColor(e){return this.teammateMap.get(e)?.color||"blue"},normalizeFilePath(e,t){if(!e)return"";let s=e.trim();const n="/home/"+(window.USER||"user");s.startsWith("~/")?s=n+s.slice(1):s==="~"&&(s=n),!s.startsWith("/")&&t&&(s=t+"/"+s);const i=s.split("/"),a=[];for(const r of i)r===""||r==="."||(r===".."?a.length>1&&a.pop():a.push(r));return"/"+a.join("/")},getFilename(e){const t=e.split("/");return t[t.length-1]||""},isShallowRootPath(e){return e.startsWith("/")?e.split("/").filter(s=>s!=="").length===1:!1},isPathInWorkingDir(e,t){if(!t)return!1;const s=this.normalizeFilePath(e,t);return s.startsWith(t+"/")||s===t},pathsAreEquivalent(e,t,s){const n=this.normalizeFilePath(e,s),i=this.normalizeFilePath(t,s);if(n===i)return!0;const a=this.getFilename(n),r=this.getFilename(i);if(a!==r)return!1;const o=this.isShallowRootPath(e),l=this.isShallowRootPath(t),d=this.isPathInWorkingDir(n,s),c=this.isPathInWorkingDir(i,s);return!!(o&&c||l&&d)},pickBetterPath(e,t,s){if(s){const a=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(a&&!r)return e;if(r&&!a)return t}const n=e.startsWith("/"),i=t.startsWith("/");return n&&!i?e:i&&!n?t:e.length!==t.length?e.length>t.length?e:t:!e.includes("~")&&t.includes("~")?e:!t.includes("~")&&e.includes("~")?t:e},deduplicateProjectInsightPaths(e,t){const s=[];for(const a of e)for(const r of a.filePaths)s.push({rawPath:r,toolId:a.id});if(s.length<=1){const a=new Map;for(const r of s)a.set(this.normalizeFilePath(r.rawPath,t),r);return a}s.sort((a,r)=>{const o=this.isPathInWorkingDir(a.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return o&&!l?-1:l&&!o?1:r.rawPath.length-a.rawPath.length});const n=new Map,i=new Set;for(const{rawPath:a,toolId:r}of s){const o=this.normalizeFilePath(a,t);let l=!1;for(const[,d]of n)if(this.pathsAreEquivalent(a,d.rawPath,t)){l=!0;break}!l&&!i.has(o)&&(n.set(o,{rawPath:a,toolId:r}),i.add(o))}return n},handleBashToolStart(e,t){let s=this.projectInsights.get(e)||[];s=s.filter(n=>n.id!==t.id),s.push(t),this.projectInsights.set(e,s),this.renderProjectInsightsPanel()},handleBashToolEnd(e,t){const n=(this.projectInsights.get(e)||[]).find(i=>i.id===t.id);n&&(n.status="completed"),this.renderProjectInsightsPanel(),setTimeout(()=>{const i=this.projectInsights.get(e)||[];this.projectInsights.set(e,i.filter(a=>a.id!==t.id)),this.renderProjectInsightsPanel()},2e3)},handleBashToolsUpdate(e,t){this.projectInsights.set(e,t),this.renderProjectInsightsPanel()},renderProjectInsightsPanel(){const e=this.$("projectInsightsPanel"),t=this.$("projectInsightsList");if(!e||!t)return;if(!(this.loadAppSettingsFromStorage().showProjectInsights??!1)){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}const a=(this.projectInsights.get(this.activeSessionId)||[]).filter(m=>m.status==="running");if(a.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const o=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(a,o),d=new Set(Array.from(l.values()).map(m=>m.rawPath)),c=[];for(const m of a){const u=m.filePaths.filter(p=>d.has(p));if(u.length===0)continue;const h=m.command.length>50?m.command.substring(0,50)+"...":m.command;c.push(`
|
|
200
|
+
<div class="project-insight-item" data-tool-id="${m.id}">
|
|
156
201
|
<div class="project-insight-command">
|
|
157
202
|
<span class="icon">\u{1F4BB}</span>
|
|
158
|
-
<span class="cmd" title="${escapeHtml(
|
|
159
|
-
<span class="project-insight-status ${
|
|
160
|
-
${
|
|
203
|
+
<span class="cmd" title="${escapeHtml(m.command)}">${escapeHtml(h)}</span>
|
|
204
|
+
<span class="project-insight-status ${m.status}">${m.status}</span>
|
|
205
|
+
${m.timeout?`<span class="project-insight-timeout">${escapeHtml(m.timeout)}</span>`:""}
|
|
161
206
|
</div>
|
|
162
207
|
<div class="project-insight-paths">
|
|
163
|
-
`);for(const p of
|
|
208
|
+
`);for(const p of u){const w=p.split("/").pop();c.push(`
|
|
164
209
|
<span class="project-insight-filepath"
|
|
165
|
-
onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(p))}, ${escapeHtml(JSON.stringify(
|
|
166
|
-
title="${escapeHtml(p)}">${escapeHtml(
|
|
210
|
+
onclick="app.openLogViewerWindow(${escapeHtml(JSON.stringify(p))}, ${escapeHtml(JSON.stringify(m.sessionId))})"
|
|
211
|
+
title="${escapeHtml(p)}">${escapeHtml(w)}</span>
|
|
167
212
|
`)}c.push(`
|
|
168
213
|
</div>
|
|
169
214
|
</div>
|
|
170
|
-
`)}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
|
|
171
|
-
<div class="file-tree-item${
|
|
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>',w=!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
|
+
<div class="file-tree-item${u}" data-path="${escapeHtml(a.path)}" data-type="${a.type}" data-depth="${r}">
|
|
172
217
|
${p}
|
|
173
|
-
<span class="file-tree-icon">${
|
|
174
|
-
<span class="${
|
|
175
|
-
${
|
|
218
|
+
<span class="file-tree-icon">${h}</span>
|
|
219
|
+
<span class="${b}">${escapeHtml(a.name)}</span>
|
|
220
|
+
${w}
|
|
176
221
|
${f}
|
|
177
222
|
</div>
|
|
178
|
-
`),
|
|
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 I=this.attachmentCards.keys().next().value;I&&this.closeAttachmentCard(I)}const w=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(),y=document.createElement("article");y.className=`attachment-card attachment-${escapeHtml(r||"file")}`,y.tabIndex=0,y.dataset.attachmentId=h,y.dataset.previewUrl=T||"",y.innerHTML=`
|
|
179
224
|
<div class="attachment-thumbnail">
|
|
180
225
|
${g?`<img class="attachment-thumbnail-img" src="${escapeHtml(g)}" alt="">`:""}
|
|
181
226
|
<div class="attachment-thumbnail-fallback ${g?"":"visible"}">${escapeHtml($)}</div>
|
|
182
227
|
</div>
|
|
183
228
|
<div class="attachment-card-main">
|
|
184
|
-
<div class="attachment-file-name" title="${escapeHtml(
|
|
229
|
+
<div class="attachment-file-name" title="${escapeHtml(u)}">${escapeHtml(n)}</div>
|
|
185
230
|
<div class="attachment-file-meta">
|
|
186
231
|
<span>${escapeHtml(f)}</span>
|
|
187
|
-
<span>${this.formatFileSize(
|
|
232
|
+
<span>${this.formatFileSize(a||0)}</span>
|
|
188
233
|
</div>
|
|
189
234
|
<div class="attachment-actions">
|
|
190
235
|
<button type="button" class="attachment-preview-btn">Preview</button>
|
|
@@ -193,7 +238,7 @@
|
|
|
193
238
|
</div>
|
|
194
239
|
</div>
|
|
195
240
|
<button type="button" class="attachment-close-btn" title="Dismiss">×</button>
|
|
196
|
-
`;const
|
|
241
|
+
`;const D=y.querySelector(".attachment-thumbnail-img");D&&(D.onerror=()=>{D.remove(),y.querySelector(".attachment-thumbnail-fallback")?.classList.add("visible")}),y.querySelector(".attachment-preview-btn")?.addEventListener("click",()=>{this.openFilePreview(u,t,l||null)}),y.querySelector(".attachment-open-btn")?.addEventListener("click",()=>{this.openAttachmentInNewTab(t,u,l||null)}),y.querySelector(".attachment-close-btn")?.addEventListener("click",()=>{this.closeAttachmentCard(h)}),w.prepend(y),this.attachmentCards.set(h,{element:y,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=`
|
|
197
242
|
<div class="attachment-history-header">
|
|
198
243
|
<div>
|
|
199
244
|
<div class="attachment-history-title">Attachments</div>
|
|
@@ -205,14 +250,14 @@
|
|
|
205
250
|
</div>
|
|
206
251
|
</div>
|
|
207
252
|
<div class="attachment-history-list" id="attachmentHistoryList"></div>
|
|
208
|
-
`,document.body.appendChild(e),e.querySelector("#attachmentHistoryRefreshBtn")?.addEventListener("click",()=>{this.loadAttachmentHistory(this.activeSessionId)}),e.querySelector("#attachmentHistoryCloseBtn")?.addEventListener("click",()=>{this.closeAttachmentHistory()}),e)},async toggleAttachmentHistory(){if(this.attachmentHistoryDrawerOpen){this.closeAttachmentHistory();return}await this.openAttachmentHistory()},async openAttachmentHistory(){const e=this.ensureAttachmentHistoryDrawer();this.attachmentHistoryDrawerOpen=!0,e.classList.add("open"),this.updateAttachmentHistoryBadge(),await this.loadAttachmentHistory(this.activeSessionId)},closeAttachmentHistory(){const e=document.getElementById("attachmentHistoryDrawer");this.attachmentHistoryDrawerOpen=!1,e?.classList.remove("open"),this._debounceTimers?.attachmentHistoryRefresh&&(clearTimeout(this._debounceTimers.attachmentHistoryRefresh),this._debounceTimers.attachmentHistoryRefresh=null),this.updateAttachmentHistoryBadge()},async loadAttachmentHistory(e=this.activeSessionId){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(!e){this.attachmentHistoryItems=[],n.textContent="No session",s.innerHTML='<div class="attachment-history-empty">No active session</div>',this.updateAttachmentHistoryBadge(0);return}s.innerHTML='<div class="attachment-history-empty">Loading...</div>';try{const
|
|
253
|
+
`,document.body.appendChild(e),e.querySelector("#attachmentHistoryRefreshBtn")?.addEventListener("click",()=>{this.loadAttachmentHistory(this.activeSessionId)}),e.querySelector("#attachmentHistoryCloseBtn")?.addEventListener("click",()=>{this.closeAttachmentHistory()}),e)},async toggleAttachmentHistory(){if(this.attachmentHistoryDrawerOpen){this.closeAttachmentHistory();return}await this.openAttachmentHistory()},async openAttachmentHistory(){const e=this.ensureAttachmentHistoryDrawer();this.attachmentHistoryDrawerOpen=!0,e.classList.add("open"),this.updateAttachmentHistoryBadge(),await this.loadAttachmentHistory(this.activeSessionId)},closeAttachmentHistory(){const e=document.getElementById("attachmentHistoryDrawer");this.attachmentHistoryDrawerOpen=!1,e?.classList.remove("open"),this._debounceTimers?.attachmentHistoryRefresh&&(clearTimeout(this._debounceTimers.attachmentHistoryRefresh),this._debounceTimers.attachmentHistoryRefresh=null),this.updateAttachmentHistoryBadge()},async loadAttachmentHistory(e=this.activeSessionId){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(!e){this.attachmentHistoryItems=[],n.textContent="No session",s.innerHTML='<div class="attachment-history-empty">No active session</div>',this.updateAttachmentHistoryBadge(0);return}s.innerHTML='<div class="attachment-history-empty">Loading...</div>';try{const i=await fetch(`/api/sessions/${e}/attachments`);if(!i.ok)throw new Error("Failed to load attachments");const a=await i.json();if(!a.success)throw new Error(a.error||"Failed to load attachments");const r=a.data?.items||[];this.attachmentHistoryItems=r,this.attachmentHistoryCounts.set(e,r.length),this.updateAttachmentHistoryBadge(r.length),this.renderAttachmentHistory(r)}catch(i){console.error("Failed to load attachment history:",i),n.textContent="Unavailable",s.innerHTML=`<div class="attachment-history-empty">Error: ${escapeHtml(i.message)}</div>`}}},renderAttachmentHistory(e=this.attachmentHistoryItems||[]){const t=this.ensureAttachmentHistoryDrawer(),s=t.querySelector("#attachmentHistoryList"),n=t.querySelector("#attachmentHistorySubtitle");if(!(!s||!n)){if(n.textContent=`${e.length} ${e.length===1?"file":"files"}`,e.length===0){s.innerHTML=`
|
|
209
254
|
<div class="attachment-history-empty">
|
|
210
255
|
<div class="attachment-history-empty-title">No attachments yet</div>
|
|
211
256
|
<div>Show a file here by running:</div>
|
|
212
257
|
<code>codeman attach /absolute/path/to/file.pptx</code>
|
|
213
258
|
<div>Supports .pptx, .docx, .pdf, .png, .md, and .txt.</div>
|
|
214
259
|
</div>
|
|
215
|
-
`;return}s.innerHTML=e.map(
|
|
260
|
+
`;return}s.innerHTML=e.map(i=>this.renderAttachmentHistoryItem(i)).join(""),s.querySelectorAll(".attachment-history-thumb-img").forEach(i=>{i.onerror=()=>{i.remove(),i.closest(".attachment-history-thumb")?.querySelector(".attachment-history-thumb-fallback")?.classList.add("visible")}}),s.querySelectorAll("[data-attachment-action]").forEach(i=>{i.addEventListener("click",()=>{const a=i.getAttribute("data-history-id"),r=i.getAttribute("data-attachment-action");!a||!r||(r==="preview"&&this.previewAttachmentHistoryItem(a),r==="download"&&this.downloadAttachmentHistoryItem(a),r==="open"&&this.openAttachmentHistoryItem(a),r==="reshow"&&this.reshowAttachmentCard(a))})})}},renderAttachmentHistoryItem(e){const t=(e.extension||e.attachmentType||"file").toUpperCase(),s=[e.source==="external"?"published":"workspace",this.formatFileSize(e.size||0),e.missing?"missing":""].filter(Boolean).join(" \u2022 "),n=e.thumbnailUrl&&!e.missing?`<img class="attachment-history-thumb-img" src="${escapeHtml(e.thumbnailUrl)}" alt="">`:"",i=e.missing?'disabled aria-disabled="true"':"";return`
|
|
216
261
|
<div class="attachment-history-item ${e.missing?"missing":""}" data-history-item="${escapeHtml(e.id)}">
|
|
217
262
|
<div class="attachment-history-thumb">
|
|
218
263
|
${n}
|
|
@@ -222,14 +267,14 @@
|
|
|
222
267
|
<div class="attachment-history-file-name" title="${escapeHtml(e.fileName)}">${escapeHtml(e.fileName)}</div>
|
|
223
268
|
<div class="attachment-history-meta">${escapeHtml(s)}</div>
|
|
224
269
|
<div class="attachment-history-actions">
|
|
225
|
-
<button type="button" data-attachment-action="preview" data-history-id="${escapeHtml(e.id)}" ${
|
|
226
|
-
<button type="button" data-attachment-action="download" data-history-id="${escapeHtml(e.id)}" ${
|
|
227
|
-
<button type="button" data-attachment-action="open" data-history-id="${escapeHtml(e.id)}" ${
|
|
228
|
-
<button type="button" data-attachment-action="reshow" data-history-id="${escapeHtml(e.id)}" ${
|
|
270
|
+
<button type="button" data-attachment-action="preview" data-history-id="${escapeHtml(e.id)}" ${i}>Preview</button>
|
|
271
|
+
<button type="button" data-attachment-action="download" data-history-id="${escapeHtml(e.id)}" ${i}>Download</button>
|
|
272
|
+
<button type="button" data-attachment-action="open" data-history-id="${escapeHtml(e.id)}" ${i}>Open</button>
|
|
273
|
+
<button type="button" data-attachment-action="reshow" data-history-id="${escapeHtml(e.id)}" ${i}>Card</button>
|
|
229
274
|
</div>
|
|
230
275
|
</div>
|
|
231
276
|
</div>
|
|
232
|
-
`},getAttachmentHistoryItem(e){return(this.attachmentHistoryItems||[]).find(t=>t.id===e)||null},previewAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!t||t.missing)return;const s=t.relativePath||t.fileName;this.openFilePreview(s,t.sessionId,t.attachmentId||null),this.closeAttachmentHistory()},openAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!(!t||t.missing)){if(t.rawUrl||t.url){window.open(t.rawUrl||t.url,"_blank");return}this.openAttachmentInNewTab(t.sessionId,t.relativePath||t.fileName,t.attachmentId||null)}},downloadAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||!t.downloadUrl||window.open(t.downloadUrl,"_blank")},reshowAttachmentCard(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||this.addAttachmentCard({sessionId:t.sessionId,relativePath:t.relativePath,fileName:t.fileName,timestamp:t.timestamp??Date.now(),size:t.size,attachmentType:t.attachmentType,extension:t.extension,attachmentId:t.attachmentId,rawUrl:t.rawUrl,previewUrl:t.previewUrl,thumbnailUrl:t.thumbnailUrl})},copyFilePreviewContent(){this.filePreviewContent&&navigator.clipboard.writeText(this.filePreviewContent).then(()=>{this.showToast("Copied to clipboard","success")}).catch(()=>{this.showToast("Failed to copy","error")})},getFileIcon(e){return e&&{ts:"\u{1F4D8}",tsx:"\u{1F4D8}",js:"\u{1F4D2}",jsx:"\u{1F4D2}",mjs:"\u{1F4D2}",cjs:"\u{1F4D2}",py:"\u{1F40D}",pyx:"\u{1F40D}",pyw:"\u{1F40D}",rs:"\u{1F980}",go:"\u{1F439}",c:"\u2699\uFE0F",cpp:"\u2699\uFE0F",h:"\u2699\uFE0F",hpp:"\u2699\uFE0F",html:"\u{1F310}",htm:"\u{1F310}",css:"\u{1F3A8}",scss:"\u{1F3A8}",sass:"\u{1F3A8}",less:"\u{1F3A8}",json:"\u{1F4CB}",yaml:"\u{1F4CB}",yml:"\u{1F4CB}",xml:"\u{1F4CB}",toml:"\u{1F4CB}",csv:"\u{1F4CB}",md:"\u{1F4DD}",markdown:"\u{1F4DD}",txt:"\u{1F4DD}",rst:"\u{1F4DD}",png:"\u{1F5BC}\uFE0F",jpg:"\u{1F5BC}\uFE0F",jpeg:"\u{1F5BC}\uFE0F",gif:"\u{1F5BC}\uFE0F",svg:"\u{1F5BC}\uFE0F",webp:"\u{1F5BC}\uFE0F",ico:"\u{1F5BC}\uFE0F",bmp:"\u{1F5BC}\uFE0F",mp4:"\u{1F3AC}",webm:"\u{1F3AC}",mov:"\u{1F3AC}",mp3:"\u{1F3B5}",wav:"\u{1F3B5}",ogg:"\u{1F3B5}",sh:"\u{1F4BB}",bash:"\u{1F4BB}",zsh:"\u{1F4BB}",env:"\u{1F510}",gitignore:"\u{1F6AB}",dockerfile:"\u{1F433}",lock:"\u{1F512}"}[e.toLowerCase()]||"\u{1F4C4}"},formatFileSize(e){return e==null?"":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(1)} GB`},openLogViewerWindow(e,t){if(t=t||this.activeSessionId,!t)return;const s=`${t}-${e.replace(/[^a-zA-Z0-9]/g,"_")}`;if(this.logViewerWindows.has(s)){const c=this.logViewerWindows.get(s);c.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,
|
|
277
|
+
`},getAttachmentHistoryItem(e){return(this.attachmentHistoryItems||[]).find(t=>t.id===e)||null},previewAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!t||t.missing)return;const s=t.relativePath||t.fileName;this.openFilePreview(s,t.sessionId,t.attachmentId||null),this.closeAttachmentHistory()},openAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);if(!(!t||t.missing)){if(t.rawUrl||t.url){window.open(t.rawUrl||t.url,"_blank");return}this.openAttachmentInNewTab(t.sessionId,t.relativePath||t.fileName,t.attachmentId||null)}},downloadAttachmentHistoryItem(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||!t.downloadUrl||window.open(t.downloadUrl,"_blank")},reshowAttachmentCard(e){const t=this.getAttachmentHistoryItem(e);!t||t.missing||this.addAttachmentCard({sessionId:t.sessionId,relativePath:t.relativePath,fileName:t.fileName,timestamp:t.timestamp??Date.now(),size:t.size,attachmentType:t.attachmentType,extension:t.extension,attachmentId:t.attachmentId,rawUrl:t.rawUrl,previewUrl:t.previewUrl,thumbnailUrl:t.thumbnailUrl})},copyFilePreviewContent(){this.filePreviewContent&&navigator.clipboard.writeText(this.filePreviewContent).then(()=>{this.showToast("Copied to clipboard","success")}).catch(()=>{this.showToast("Failed to copy","error")})},getFileIcon(e){return e&&{ts:"\u{1F4D8}",tsx:"\u{1F4D8}",js:"\u{1F4D2}",jsx:"\u{1F4D2}",mjs:"\u{1F4D2}",cjs:"\u{1F4D2}",py:"\u{1F40D}",pyx:"\u{1F40D}",pyw:"\u{1F40D}",rs:"\u{1F980}",go:"\u{1F439}",c:"\u2699\uFE0F",cpp:"\u2699\uFE0F",h:"\u2699\uFE0F",hpp:"\u2699\uFE0F",html:"\u{1F310}",htm:"\u{1F310}",css:"\u{1F3A8}",scss:"\u{1F3A8}",sass:"\u{1F3A8}",less:"\u{1F3A8}",json:"\u{1F4CB}",yaml:"\u{1F4CB}",yml:"\u{1F4CB}",xml:"\u{1F4CB}",toml:"\u{1F4CB}",csv:"\u{1F4CB}",md:"\u{1F4DD}",markdown:"\u{1F4DD}",txt:"\u{1F4DD}",rst:"\u{1F4DD}",png:"\u{1F5BC}\uFE0F",jpg:"\u{1F5BC}\uFE0F",jpeg:"\u{1F5BC}\uFE0F",gif:"\u{1F5BC}\uFE0F",svg:"\u{1F5BC}\uFE0F",webp:"\u{1F5BC}\uFE0F",ico:"\u{1F5BC}\uFE0F",bmp:"\u{1F5BC}\uFE0F",mp4:"\u{1F3AC}",webm:"\u{1F3AC}",mov:"\u{1F3AC}",mp3:"\u{1F3B5}",wav:"\u{1F3B5}",ogg:"\u{1F3B5}",sh:"\u{1F4BB}",bash:"\u{1F4BB}",zsh:"\u{1F4BB}",env:"\u{1F510}",gitignore:"\u{1F6AB}",dockerfile:"\u{1F433}",lock:"\u{1F512}"}[e.toLowerCase()]||"\u{1F4C4}"},formatFileSize(e){return e==null?"":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(1)} GB`},openLogViewerWindow(e,t){if(t=t||this.activeSessionId,!t)return;const s=`${t}-${e.replace(/[^a-zA-Z0-9]/g,"_")}`;if(this.logViewerWindows.has(s)){const c=this.logViewerWindows.get(s);c.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,i=100+n%5*30,a=100+n%5*30,r=e.split("/").pop(),o=document.createElement("div");o.className="log-viewer-window",o.id=`log-viewer-window-${s}`,o.style.left=`${i}px`,o.style.top=`${a}px`,o.style.zIndex=++this.logViewerWindowZIndex,o.innerHTML=`
|
|
233
278
|
<div class="log-viewer-window-header">
|
|
234
279
|
<div class="log-viewer-window-title" title="${escapeHtml(e)}">
|
|
235
280
|
<span class="icon">\u{1F4C4}</span>
|
|
@@ -243,53 +288,53 @@
|
|
|
243
288
|
<div class="log-viewer-window-body" id="log-viewer-body-${s}">
|
|
244
289
|
<div class="log-info">Connecting to ${escapeHtml(e)}...</div>
|
|
245
290
|
</div>
|
|
246
|
-
`,document.body.appendChild(
|
|
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,w=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=`
|
|
247
292
|
<div class="image-popup-header">
|
|
248
293
|
<div class="image-popup-title" title="${escapeHtml(s)}">
|
|
249
294
|
<span class="icon">\u{1F5BC}\uFE0F</span>
|
|
250
|
-
<span class="filename">${escapeHtml(
|
|
251
|
-
<span class="session-badge">${escapeHtml(
|
|
252
|
-
<span class="size-badge">${
|
|
295
|
+
<span class="filename">${escapeHtml(i)}</span>
|
|
296
|
+
<span class="session-badge">${escapeHtml(w)}</span>
|
|
297
|
+
<span class="size-badge">${b} KB</span>
|
|
253
298
|
</div>
|
|
254
299
|
<div class="image-popup-actions">
|
|
255
300
|
<button onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(f))})" title="Open in new tab">\u2197</button>
|
|
256
|
-
<button onclick="app.closeImagePopup(${escapeHtml(JSON.stringify(
|
|
301
|
+
<button onclick="app.closeImagePopup(${escapeHtml(JSON.stringify(o))})" title="Close">\xD7</button>
|
|
257
302
|
</div>
|
|
258
303
|
</div>
|
|
259
304
|
<div class="image-popup-body">
|
|
260
|
-
<img src="${f}" alt="${escapeHtml(
|
|
305
|
+
<img src="${f}" alt="${escapeHtml(i)}"
|
|
261
306
|
onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
|
|
262
307
|
onclick="app.openImageInNewTab(${escapeHtml(JSON.stringify(f))})" />
|
|
263
308
|
</div>
|
|
264
|
-
`,document.body.appendChild(
|
|
265
|
-
<div class="process-item process-item-clickable" onclick="app.selectSession(${
|
|
266
|
-
<span class="monitor-status-badge ${l}">${
|
|
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 w="";d&&d.total>0&&(w=`<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
|
+
<div class="process-item process-item-clickable" onclick="app.selectSession(${v})" title="Switch to session">
|
|
311
|
+
<span class="monitor-status-badge ${l}">${o}</span>
|
|
267
312
|
<div class="process-info">
|
|
268
313
|
<div class="process-name">${f} ${escapeHtml(s.name||s.muxName)}</div>
|
|
269
314
|
<div class="process-meta">
|
|
270
|
-
${
|
|
271
|
-
${
|
|
315
|
+
${w}
|
|
316
|
+
${b}
|
|
272
317
|
${p}
|
|
273
318
|
<span class="process-stat memory">${n.memoryMB}MB</span>
|
|
274
319
|
<span class="process-stat cpu">${n.cpuPercent}%</span>
|
|
275
320
|
</div>
|
|
276
321
|
</div>
|
|
277
322
|
<div class="process-actions">
|
|
278
|
-
<button class="btn-toolbar btn-sm btn-danger" onclick="event.stopPropagation(); app.killMuxSession(${
|
|
323
|
+
<button class="btn-toolbar btn-sm btn-danger" onclick="event.stopPropagation(); app.killMuxSession(${v})" title="Kill session">Kill</button>
|
|
279
324
|
</div>
|
|
280
325
|
</div>
|
|
281
|
-
`}e.innerHTML=t},renderMonitorSubagents(){const e=document.getElementById("monitorSubagentsBody"),t=document.getElementById("monitorSubagentStats");if(!e)return;const s=Array.from(this.subagents.values()),n=s.filter(
|
|
326
|
+
`}e.innerHTML=t},renderMonitorSubagents(){const e=document.getElementById("monitorSubagentsBody"),t=document.getElementById("monitorSubagentStats");if(!e)return;const s=Array.from(this.subagents.values()),n=s.filter(a=>a.status==="active"||a.status==="idle").length;if(t&&(t.textContent=`${s.length} tracked`+(n>0?`, ${n} active`:"")),s.length===0){e.innerHTML='<div class="monitor-empty">No background agents</div>';return}let i="";for(const a of s){const r=a.status==="active"?"active":a.status==="idle"?"idle":"completed",o=a.modelShort?`<span class="model-badge ${a.modelShort}">${a.modelShort}</span>`:"",l=a.description?escapeHtml(a.description.substring(0,40)):a.agentId;i+=`
|
|
282
327
|
<div class="process-item">
|
|
283
|
-
<span class="process-mode ${r}">${
|
|
328
|
+
<span class="process-mode ${r}">${a.status}</span>
|
|
284
329
|
<div class="process-info">
|
|
285
|
-
<div class="process-name">${
|
|
330
|
+
<div class="process-name">${o} ${l}</div>
|
|
286
331
|
<div class="process-meta">
|
|
287
|
-
<span>ID: ${
|
|
288
|
-
<span>${
|
|
332
|
+
<span>ID: ${a.agentId}</span>
|
|
333
|
+
<span>${a.toolCallCount||0} tools</span>
|
|
289
334
|
</div>
|
|
290
335
|
</div>
|
|
291
336
|
<div class="process-actions">
|
|
292
|
-
${
|
|
337
|
+
${a.status!=="completed"?`<button class="btn-toolbar btn-sm btn-danger" onclick="app.killSubagent(${escapeHtml(JSON.stringify(a.agentId))})" title="Kill agent">Kill</button>`:""}
|
|
293
338
|
</div>
|
|
294
339
|
</div>
|
|
295
|
-
`}e.innerHTML=
|
|
340
|
+
`}e.innerHTML=i},async killMuxSession(e){if(confirm("Kill this mux session?")){try{await this.closeSession(e,!0)}catch{try{await fetch(`/api/mux-sessions/${e}`,{method:"DELETE"})}catch{}this.showToast("Tmux session killed","success")}this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e),this.renderMuxSessions()}},async reconcileMuxSessions(){try{const t=await(await fetch("/api/mux-sessions/reconcile",{method:"POST"})).json();t.data?.dead&&t.data.dead.length>0?(this.showToast(`Found ${t.data.dead.length} dead mux session(s)`,"warning"),await this.loadMuxSessions()):this.showToast("All mux sessions are alive","success")}catch{this.showToast("Failed to reconcile mux sessions","error")}},toggleNotifications(){this.notificationManager?.toggleDrawer()},async launchMultiMonitor(){try{const e=await fetch("/api/system/span-displays",{method:"POST"}),t=await e.json().catch(()=>({}));e.ok&&t.success?this.showToast("Opening Codeman across all displays\u2026","success"):this.showToast(t.error||"Could not open spanning window","error")}catch(e){this.showToast("Could not open spanning window: "+(e?.message||e),"error")}},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=3e3,action:i}=s,a=document.createElement("div");a.className=`toast toast-${t}`;const r=document.createElement("span");if(r.textContent=e,a.appendChild(r),i){const o=document.createElement("button");o.textContent=i.label,o.style.cssText="margin-left:12px;padding:2px 10px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:3px;color:inherit;cursor:pointer;font-size:12px",o.onclick=l=>{l.stopPropagation(),i.onClick(),a.remove()},a.appendChild(o)}this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(a),requestAnimationFrame(()=>a.classList.add("show")),setTimeout(()=>{a.classList.remove("show"),setTimeout(()=>a.remove(),200)},n)},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s.data)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),i=this.$("statMemBar");if(t&&s&&(t.textContent=`${e.cpu}%`,s.style.width=`${Math.min(100,e.cpu)}%`,s.classList.remove("medium","high"),t.classList.remove("high"),e.cpu>80?(s.classList.add("high"),t.classList.add("high")):e.cpu>50&&s.classList.add("medium")),n&&i){const a=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${a}G`,i.style.width=`${Math.min(100,e.memory.percent)}%`,i.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(i.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&i.classList.add("medium")}},async _onClipboardWrite(e){const t=e?.text;if(typeof t=="string")try{await navigator.clipboard.writeText(t),this.showToast(`Copied to clipboard (${t.length} chars)`,"success")}catch{this._showClipboardFallback(t)}},_showClipboardFallback(e){const t=document.createElement("div");t.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;display:flex;align-items:center;justify-content:center";const s=document.createElement("div");s.style.cssText="background:#1e1e2e;border:1px solid #444;border-radius:8px;padding:16px;max-width:600px;width:90%;max-height:60vh;display:flex;flex-direction:column;gap:12px";const n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center";const i=document.createElement("span");i.style.cssText="color:#cdd6f4;font-weight:600",i.textContent="Clipboard (browser blocked auto-copy)";const a=document.createElement("button");a.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",a.textContent="\xD7",n.appendChild(i),n.appendChild(a);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const o=document.createElement("button");o.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",o.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(o),t.appendChild(s),document.body.appendChild(t),o.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();a.onclick=l,t.onclick=d=>{d.target===t&&l()}}});
|