aicodeman 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/instance.d.ts +42 -0
- package/dist/config/instance.d.ts.map +1 -0
- package/dist/config/instance.js +61 -0
- package/dist/config/instance.js.map +1 -0
- package/dist/push-store.d.ts.map +1 -1
- package/dist/push-store.js +2 -2
- package/dist/push-store.js.map +1 -1
- package/dist/session-lifecycle-log.d.ts.map +1 -1
- package/dist/session-lifecycle-log.js +3 -3
- package/dist/session-lifecycle-log.js.map +1 -1
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +6 -3
- package/dist/state-store.js.map +1 -1
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +6 -5
- package/dist/tmux-manager.js.map +1 -1
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +12 -1
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/public/api-client.3adebdc2.js.gz +0 -0
- package/dist/web/public/{app.d044eb65.js → app.cc861c96.js} +9 -7
- package/dist/web/public/app.cc861c96.js.br +0 -0
- package/dist/web/public/app.cc861c96.js.gz +0 -0
- package/dist/web/public/constants.cb6426c4.js.gz +0 -0
- package/dist/web/public/gesture/gesture-codeman.js +4735 -0
- package/dist/web/public/gesture/gesture_recognizer.task +0 -0
- package/dist/web/public/gesture/wasm/vision_wasm_internal.js +20 -0
- package/dist/web/public/gesture/wasm/vision_wasm_internal.wasm +0 -0
- package/dist/web/public/gesture/wasm/vision_wasm_nosimd_internal.js +20 -0
- package/dist/web/public/gesture/wasm/vision_wasm_nosimd_internal.wasm +0 -0
- package/dist/web/public/image-input.7cade6a8.js.gz +0 -0
- package/dist/web/public/index.html +35 -7
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.88082175.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.cdfd8c04.js.gz +0 -0
- package/dist/web/public/mobile-handlers.1e2a8ef8.js.gz +0 -0
- package/dist/web/public/mobile.26dc30d6.css.gz +0 -0
- package/dist/web/public/notification-manager.9c984ac2.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.cf998835.js → panels-ui.3e304caf.js} +16 -16
- package/dist/web/public/panels-ui.3e304caf.js.br +0 -0
- package/dist/web/public/panels-ui.3e304caf.js.gz +0 -0
- package/dist/web/public/ralph-panel.61076370.js.gz +0 -0
- package/dist/web/public/ralph-wizard.52d533d2.js.gz +0 -0
- package/dist/web/public/respawn-ui.5377f958.js.gz +0 -0
- package/dist/web/public/session-ui.3e0cf024.js.gz +0 -0
- package/dist/web/public/settings-ui.c06be9c3.js +55 -0
- package/dist/web/public/settings-ui.c06be9c3.js.br +0 -0
- package/dist/web/public/settings-ui.c06be9c3.js.gz +0 -0
- package/dist/web/public/styles.84a35202.css +1 -0
- package/dist/web/public/styles.84a35202.css.br +0 -0
- package/dist/web/public/styles.84a35202.css.gz +0 -0
- package/dist/web/public/subagent-windows.a366a4ad.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.37caa926.js.gz +0 -0
- package/dist/web/public/upload.html.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-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/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +4 -2
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +4 -3
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +2 -1
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts +7 -0
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +51 -5
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +2 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +3 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +21 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +124 -15
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +14 -0
- package/dist/web/public/app.d044eb65.js.br +0 -0
- package/dist/web/public/app.d044eb65.js.gz +0 -0
- package/dist/web/public/panels-ui.cf998835.js.br +0 -0
- package/dist/web/public/panels-ui.cf998835.js.gz +0 -0
- package/dist/web/public/settings-ui.25a18120.js +0 -55
- package/dist/web/public/settings-ui.25a18120.js.br +0 -0
- package/dist/web/public/settings-ui.25a18120.js.gz +0 -0
- package/dist/web/public/styles.42be1d59.css +0 -1
- package/dist/web/public/styles.42be1d59.css.br +0 -0
- package/dist/web/public/styles.42be1d59.css.gz +0 -0
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
+
<!-- Resolve all relative assets against the site root so the same shell can be
|
|
6
|
+
served at /session/:id (detached single-session window) without 404ing
|
|
7
|
+
on relative <script>/<link> URLs. Must precede the first resource tag. -->
|
|
8
|
+
<base href="/">
|
|
5
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
6
10
|
<meta name="description" content="Claude Code session manager with web interface">
|
|
7
11
|
<meta name="theme-color" content="#0a0a0a">
|
|
@@ -12,7 +16,7 @@
|
|
|
12
16
|
<link rel="manifest" href="manifest.json">
|
|
13
17
|
<title>Codeman</title>
|
|
14
18
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
|
|
15
|
-
<link rel="stylesheet" href="styles.
|
|
19
|
+
<link rel="stylesheet" href="styles.84a35202.css">
|
|
16
20
|
<link rel="stylesheet" href="mobile.26dc30d6.css" media="(max-width: 1023px)">
|
|
17
21
|
<!-- xterm.css loaded async — terminal won't display until xterm.js runs anyway -->
|
|
18
22
|
<link rel="preload" href="vendor/xterm.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
@@ -21,7 +25,7 @@
|
|
|
21
25
|
instead of waiting until <script> tags at bottom-of-body are reached. -->
|
|
22
26
|
<link rel="preload" href="vendor/xterm.min.js" as="script">
|
|
23
27
|
<link rel="preload" href="constants.cb6426c4.js" as="script">
|
|
24
|
-
<link rel="preload" href="app.
|
|
28
|
+
<link rel="preload" href="app.cc861c96.js" as="script">
|
|
25
29
|
<!-- Self-hosted xterm.js — eliminates CDN DNS/TLS latency (~100ms).
|
|
26
30
|
'defer' preserves execution order (xterm loads before fit addon). -->
|
|
27
31
|
<script defer src="vendor/xterm.min.js"></script>
|
|
@@ -55,7 +59,9 @@
|
|
|
55
59
|
<div class="skeleton-toolbar"></div>
|
|
56
60
|
</div>
|
|
57
61
|
<!-- Skip link for keyboard users -->
|
|
58
|
-
<
|
|
62
|
+
<!-- onclick scrolls/focuses directly: with <base href="/"> a bare href="#..." would
|
|
63
|
+
navigate to /#... (the dashboard) from a /session/:id solo window. -->
|
|
64
|
+
<a href="#terminalContainer" class="skip-link" onclick="event.preventDefault(); var t=document.getElementById('terminalContainer'); if(t){t.scrollIntoView(); var f=t.querySelector('textarea,[tabindex]'); (f||t).focus&&(f||t).focus();}">Skip to terminal</a>
|
|
59
65
|
<div class="app">
|
|
60
66
|
<!-- Compact Header with Session Tabs -->
|
|
61
67
|
<header class="header">
|
|
@@ -67,7 +73,11 @@
|
|
|
67
73
|
<div class="session-tabs" id="sessionTabs" role="tablist" aria-label="Session tabs">
|
|
68
74
|
</div>
|
|
69
75
|
|
|
76
|
+
<!-- Detached single-session window title (shown only in solo mode) -->
|
|
77
|
+
<div class="solo-session-title" id="soloSessionTitle" style="display: none;" aria-live="polite"></div>
|
|
78
|
+
|
|
70
79
|
<div class="header-right">
|
|
80
|
+
<button class="btn-icon-header btn-solo-redock" id="soloRedockBtn" style="display: none;" onclick="window.close()" title="Re-dock to dashboard (close window)" aria-label="Re-dock session to dashboard">⊞</button>
|
|
71
81
|
<button class="tunnel-indicator" id="tunnelIndicator" style="display: none;" onclick="app.toggleTunnelPanel()" title="Cloudflare Tunnel" aria-label="Tunnel status">
|
|
72
82
|
<span class="tunnel-dot"></span>
|
|
73
83
|
</button>
|
|
@@ -97,7 +107,8 @@
|
|
|
97
107
|
</div>
|
|
98
108
|
</div>
|
|
99
109
|
<button class="btn-icon-header btn-response-viewer-header" onclick="app.toggleResponseViewer()" title="View last response" aria-label="View last response"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>
|
|
100
|
-
<button class="btn-icon-header btn-
|
|
110
|
+
<button class="btn-icon-header btn-multimonitor btn-multimonitor--hidden" onclick="app.launchMultiMonitor()" title="Open Codeman across all displays" aria-label="Open Codeman across all displays"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="13" height="9" rx="1.5"/><rect x="11" y="9" width="11" height="8" rx="1.5"/></svg></button>
|
|
111
|
+
<button class="btn-icon-header btn-notifications" onclick="app.toggleNotifications()" title="Notifications" aria-label="Toggle notifications" style="display:none;">
|
|
101
112
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
102
113
|
<span class="notification-badge" id="notifBadge" style="display:none;">0</span>
|
|
103
114
|
</button>
|
|
@@ -922,6 +933,16 @@
|
|
|
922
933
|
<span class="slider"></span>
|
|
923
934
|
</label>
|
|
924
935
|
</div>
|
|
936
|
+
<div class="settings-item settings-item-multiline" id="appSettingsGestureControlItem" title="Enable the camera hand-tracking gesture overlay (applied on reload). The instance must run with CODEMAN_GESTURE=1.">
|
|
937
|
+
<div class="settings-item-text">
|
|
938
|
+
<span class="settings-item-label">Gesture Control (beta)</span>
|
|
939
|
+
<span class="settings-item-desc">Camera hand-tracking overlay (applied on reload)</span>
|
|
940
|
+
</div>
|
|
941
|
+
<label class="switch switch-sm">
|
|
942
|
+
<input type="checkbox" id="appSettingsGestureControl">
|
|
943
|
+
<span class="slider"></span>
|
|
944
|
+
</label>
|
|
945
|
+
</div>
|
|
925
946
|
|
|
926
947
|
<!-- Header Displays Section -->
|
|
927
948
|
<div class="settings-section-header">Header Displays</div>
|
|
@@ -960,6 +981,13 @@
|
|
|
960
981
|
<span class="slider"></span>
|
|
961
982
|
</label>
|
|
962
983
|
</div>
|
|
984
|
+
<div class="settings-item" title="Show the multi-monitor button in the header (opens Codeman spanned across all displays)">
|
|
985
|
+
<span class="settings-item-label">Multi-monitor Button</span>
|
|
986
|
+
<label class="switch switch-sm">
|
|
987
|
+
<input type="checkbox" id="appSettingsShowMultiMonitorButton">
|
|
988
|
+
<span class="slider"></span>
|
|
989
|
+
</label>
|
|
990
|
+
</div>
|
|
963
991
|
|
|
964
992
|
<!-- Tab Bar Section -->
|
|
965
993
|
<div class="settings-section-header">Tab Bar</div>
|
|
@@ -1794,13 +1822,13 @@
|
|
|
1794
1822
|
<script defer src="notification-manager.9c984ac2.js"></script>
|
|
1795
1823
|
<script defer src="keyboard-accessory.cdfd8c04.js"></script>
|
|
1796
1824
|
<script defer src="input-cjk.88082175.js"></script>
|
|
1797
|
-
<script defer src="app.
|
|
1825
|
+
<script defer src="app.cc861c96.js"></script>
|
|
1798
1826
|
<script defer src="terminal-ui.37caa926.js"></script>
|
|
1799
1827
|
<script defer src="respawn-ui.5377f958.js"></script>
|
|
1800
1828
|
<script defer src="ralph-panel.61076370.js"></script>
|
|
1801
1829
|
<script defer src="orchestrator-panel.js"></script>
|
|
1802
|
-
<script defer src="settings-ui.
|
|
1803
|
-
<script defer src="panels-ui.
|
|
1830
|
+
<script defer src="settings-ui.c06be9c3.js"></script>
|
|
1831
|
+
<script defer src="panels-ui.3e304caf.js"></script>
|
|
1804
1832
|
<script defer src="session-ui.3e0cf024.js"></script>
|
|
1805
1833
|
<script defer src="ralph-wizard.52d533d2.js"></script>
|
|
1806
1834
|
<script defer src="api-client.3adebdc2.js"></script>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<span class="stat-card-value">${this.formatTokens(d+u)}</span>
|
|
15
15
|
<span class="stat-card-cost">~$${h.toFixed(2)}</span>
|
|
16
16
|
</div>
|
|
17
|
-
`;const p=document.getElementById("statsChart"),f=document.getElementById("statsChartDays"),
|
|
17
|
+
`;const p=document.getElementById("statsChart"),f=document.getElementById("statsChartDays"),b=[];for(let v=6;v>=0;v--){const S=new Date;S.setDate(S.getDate()-v);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 g=Math.max(...b.map(v=>v.tokens),1);p.innerHTML=b.map(v=>{const S=Math.max(v.tokens/g*100,3),$=`${v.dayName}: ${this.formatTokens(v.tokens)} (~$${v.cost.toFixed(2)})`;return`<div class="bar" style="height: ${S}%" data-tooltip="${$}"></div>`}).join(""),f.innerHTML=b.map(v=>`<span>${v.dayName}</span>`).join("");const w=document.getElementById("statsTable"),T=t.slice(0,14);T.length===0?w.innerHTML='<div class="stats-no-data">No usage data recorded yet</div>':w.innerHTML=`
|
|
18
18
|
<div class="stats-table-header">
|
|
19
19
|
<span>Date</span>
|
|
20
20
|
<span>Input</span>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<span class="cell cell-cost">$${v.estimatedCost.toFixed(2)}</span>
|
|
30
30
|
</div>
|
|
31
31
|
`).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")?(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,a,o,r;const i=d=>{if(d.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(d);n=u.clientX,a=u.clientY;const h=e.getBoundingClientRect();o=h.left,r=h.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),d.preventDefault()},l=d=>{if(!s)return;const u=getEventCoords(d),h=u.clientX-n,m=u.clientY-a;let p=o+h,f=r+m;const
|
|
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")?(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,a,o,r;const i=d=>{if(d.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(d);n=u.clientX,a=u.clientY;const h=e.getBoundingClientRect();o=h.left,r=h.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),d.preventDefault()},l=d=>{if(!s)return;const u=getEventCoords(d),h=u.clientX-n,m=u.clientY-a;let p=o+h,f=r+m;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),f=Math.max(0,Math.min(window.innerHeight-b.height,f)),e.style.left=p+"px",e.style.top=f+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=i,t._touchDragHandler=i,t.addEventListener("mousedown",i),t.addEventListener("touchstart",i,{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,a,o,r;const i=d=>{if(d.target.closest("button")||!e.classList.contains("detached"))return;s=!0;const u=getEventCoords(d);n=u.clientX,a=u.clientY;const h=e.getBoundingClientRect();o=h.left,r=h.top,document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c),d.preventDefault()},l=d=>{if(!s)return;const u=getEventCoords(d),h=u.clientX-n,m=u.clientY-a;let p=o+h,f=r+m;const b=e.getBoundingClientRect();p=Math.max(0,Math.min(window.innerWidth-b.width,p)),f=Math.max(0,Math.min(window.innerHeight-b.height,f)),e.style.left=p+"px",e.style.top=f+"px"},c=()=>{s=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",c),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",c)};t.removeEventListener("mousedown",t._dragHandler),t.removeEventListener("touchstart",t._touchDragHandler),t._dragHandler=i,t._touchDragHandler=i,t.addEventListener("mousedown",i),t.addEventListener("touchstart",i,{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 a=e.taskStats||{running:0,completed:0,failed:0,total:0};s.textContent=`${a.running} running, ${a.completed} done`;const o=(l,c)=>{const d=l.status==="running"?"":l.status==="completed"?"✓":"✗",u=l.endTime?`${((l.endTime-l.startTime)/1e3).toFixed(1)}s`:`${((Date.now()-l.startTime)/1e3).toFixed(0)}s...`;let h="";if(l.children&&l.children.length>0){h='<div class="task-children">';for(const m of l.children){const p=c.find(f=>f.id===m);p&&(h+=`<div class="task-node">${o(p,c)}</div>`)}h+="</div>"}return`
|
|
33
33
|
<div class="task-item">
|
|
34
34
|
<span class="task-status-icon ${l.status}">${d}</span>
|
|
35
35
|
<div class="task-info">
|
|
@@ -134,10 +134,10 @@
|
|
|
134
134
|
<span class="tool-detail">${escapeHtml(a)}</span>
|
|
135
135
|
</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
136
|
<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 c=this.getTeammateInfo(t),d=c?c.name:t.description||e.substring(0,7),u=d.length>50?d.substring(0,50)+"...":d;n.textContent=u}let a=s.querySelector(".teammate-badge");const o=this.getTeammateInfo(t);if(o&&!a){const c=s.querySelector(".subagent-window-title");if(c){const d=document.createElement("span");d.className=`teammate-badge teammate-color-${o.color}`,d.title=`Team: ${o.teamName}`,d.textContent=`@${o.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",d)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let i=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!i){i=document.createElement("span"),i.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",i)}i.className=`subagent-model-badge ${t.modelShort}`,i.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 a=t.sessionId,o=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:a,resizeObserver:null,pendingData:o}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{background:"#0d0d0d",foreground:"#e0e0e0",cursor:"#e0e0e0",cursorAccent:"#0d0d0d",selection:"rgba(255, 255, 255, 0.3)",black:"#0d0d0d",red:"#ff6b6b",green:"#51cf66",yellow:"#ffd43b",blue:"#339af0",magenta:"#cc5de8",cyan:"#22b8cf",white:"#e0e0e0",brightBlack:"#495057",brightRed:"#ff8787",brightGreen:"#69db7c",brightYellow:"#ffe066",brightBlue:"#5c7cfa",brightMagenta:"#da77f2",brightCyan:"#66d9e8",brightWhite:"#ffffff"},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}),i=new FitAddon.FitAddon;if(r.loadAddon(i),typeof Unicode11Addon<"u")try{const d=new Unicode11Addon.Unicode11Addon;r.loadAddon(d),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(d){console.warn("[TeammateTerminal] Failed to open terminal:",d),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{i.fit()}catch{}fetch(`/api/sessions/${a}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(d=>d.json()).then(d=>{if(d.success&&d.data?.buffer)try{r.write(d.data.buffer)}catch{}}).catch(d=>console.error("[TeammateTerminal] Failed to fetch buffer:",d));for(const d of o)try{r.write(d)}catch{}o.length=0},100),r.onData(d=>{fetch(`/api/sessions/${a}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:d})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{i.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=i,c.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,a=400,o=20,r=window.innerWidth,i=window.innerHeight,l=50,c=120,d=Math.floor((r-l-50)/(n+o))||1,u=Math.floor((i-c-50)/(a+o))||1,h=s%d,m=Math.floor(s/d)%u;let p=l+h*(n+o),f=c+m*(a+o);p=Math.max(10,Math.min(p,r-n-10)),f=Math.max(10,Math.min(f,i-a-10));const
|
|
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 c=this.getTeammateInfo(t),d=c?c.name:t.description||e.substring(0,7),u=d.length>50?d.substring(0,50)+"...":d;n.textContent=u}let a=s.querySelector(".teammate-badge");const o=this.getTeammateInfo(t);if(o&&!a){const c=s.querySelector(".subagent-window-title");if(c){const d=document.createElement("span");d.className=`teammate-badge teammate-color-${o.color}`,d.title=`Team: ${o.teamName}`,d.textContent=`@${o.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",d)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let i=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!i){i=document.createElement("span"),i.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",i)}i.className=`subagent-model-badge ${t.modelShort}`,i.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 a=t.sessionId,o=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:a,resizeObserver:null,pendingData:o}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{background:"#0d0d0d",foreground:"#e0e0e0",cursor:"#e0e0e0",cursorAccent:"#0d0d0d",selection:"rgba(255, 255, 255, 0.3)",black:"#0d0d0d",red:"#ff6b6b",green:"#51cf66",yellow:"#ffd43b",blue:"#339af0",magenta:"#cc5de8",cyan:"#22b8cf",white:"#e0e0e0",brightBlack:"#495057",brightRed:"#ff8787",brightGreen:"#69db7c",brightYellow:"#ffe066",brightBlue:"#5c7cfa",brightMagenta:"#da77f2",brightCyan:"#66d9e8",brightWhite:"#ffffff"},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}),i=new FitAddon.FitAddon;if(r.loadAddon(i),typeof Unicode11Addon<"u")try{const d=new Unicode11Addon.Unicode11Addon;r.loadAddon(d),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(d){console.warn("[TeammateTerminal] Failed to open terminal:",d),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{i.fit()}catch{}fetch(`/api/sessions/${a}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(d=>d.json()).then(d=>{if(d.success&&d.data?.buffer)try{r.write(d.data.buffer)}catch{}}).catch(d=>console.error("[TeammateTerminal] Failed to fetch buffer:",d));for(const d of o)try{r.write(d)}catch{}o.length=0},100),r.onData(d=>{fetch(`/api/sessions/${a}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:d})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{i.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=i,c.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,a=400,o=20,r=window.innerWidth,i=window.innerHeight,l=50,c=120,d=Math.floor((r-l-50)/(n+o))||1,u=Math.floor((i-c-50)/(a+o))||1,h=s%d,m=Math.floor(s/d)%u;let p=l+h*(n+o),f=c+m*(a+o);p=Math.max(10,Math.min(p,r-n-10)),f=Math.max(10,Math.min(f,i-a-10));const b=e.color||"blue",g=document.createElement("div");g.className="subagent-window has-terminal",g.id=`subagent-window-${t}`,g.style.zIndex=++this.subagentWindowZIndex,g.style.left=`${p}px`,g.style.top=`${f}px`,g.style.width=`${n}px`,g.style.height=`${a}px`,g.innerHTML=`
|
|
138
138
|
<div class="subagent-window-header">
|
|
139
139
|
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
|
|
140
|
-
<span class="icon" style="color: var(--team-color-${
|
|
140
|
+
<span class="icon" style="color: var(--team-color-${b}, #339af0)">\u2B24</span>
|
|
141
141
|
<span class="id">${escapeHtml(e.teammateName)}</span>
|
|
142
142
|
<span class="status running">terminal</span>
|
|
143
143
|
</div>
|
|
@@ -147,10 +147,10 @@
|
|
|
147
147
|
</div>
|
|
148
148
|
<div class="subagent-window-body teammate-terminal-body" id="subagent-window-body-${t}">
|
|
149
149
|
</div>
|
|
150
|
-
`,document.body.appendChild(g);const
|
|
150
|
+
`,document.body.appendChild(g);const w=this.makeWindowDraggable(g,g.querySelector(".subagent-window-header"));typeof this.makeWindowResizable=="function"&&this.makeWindowResizable(g);const S=(this.loadAppSettingsFromStorage().subagentActiveTabOnly??!0)&&e.sessionId!==this.activeSessionId;this.subagentWindows.set(t,{element:g,minimized:!1,hidden:S,dragListeners:w,description:`Teammate: ${e.teammateName}`}),this.subagentParentMap.set(t,e.sessionId),S&&(g.style.display="none"),g.addEventListener("mousedown",()=>{g.style.zIndex=++this.subagentWindowZIndex});const $=new ResizeObserver(()=>{this.updateConnectionLines()});if($.observe(g),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,g);requestAnimationFrame(()=>{g.style.transition="transform 0.3s ease, opacity 0.3s ease",g.style.transform="scale(1)",g.style.opacity="1"})},rebuildTeammateMap(){this.teammateMap.clear();for(const[e,t]of this.teams)for(const s of t.members)s.agentType!=="team-lead"&&this.teammateMap.set(s.name,{name:s.name,color:s.color||"blue",teamName:e,agentId:s.agentId})},getTeammateInfo(e){if(!e?.description)return null;const t=e.description.match(/<teammate-message\s+teammate_id="?([^">\s]+)/);if(!t)return null;const n=t[1].split("@")[0];return this.teammateMap.get(n)||{name:n,color:"blue",teamName:"unknown"}},getTeammateBadgeHtml(e){const t=this.getTeammateInfo(e);return t?`<span class="teammate-badge teammate-color-${t.color}" title="Team: ${escapeHtml(t.teamName)}">@${escapeHtml(t.name)}</span>`:""},renderTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");if(!e)return;let t=null,s=null;if(this.activeSessionId){for(const[m,p]of this.teams)if(p.leadSessionId===this.activeSessionId){t=p,s=m;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 f=e.querySelector(".team-tasks-header");f&&(this.teamTasksDragListeners=this.makeWindowDraggable(e,f))}const a=this.teamTasks.get(s)||[],o=a.filter(m=>m.status==="completed").length,r=a.length,i=r>0?Math.round(o/r*100):0,l=e.querySelector(".team-tasks-header-text");if(l){const m=t.members.filter(p=>p.agentType!=="team-lead").length;l.textContent=`Team Tasks (${m} teammates)`}const c=e.querySelector(".team-tasks-progress-fill");c&&(c.style.width=`${i}%`);const d=e.querySelector(".team-tasks-progress-text");d&&(d.textContent=`${o}/${r}`);const u=e.querySelector(".team-tasks-list");if(!u)return;if(a.length===0){u.innerHTML='<div class="team-task-empty">No tasks yet</div>';return}const h=a.map(m=>{const p=m.status==="completed"?"\u2713":m.status==="in_progress"?"\u25C9":"\u25CB",f=m.status.replace("_","-"),b=m.owner?`<span class="team-task-owner teammate-color-${this.getTeammateColor(m.owner)}">${escapeHtml(m.owner)}</span>`:"";return`<div class="team-task-item ${f}">
|
|
151
151
|
<span class="team-task-status">${p}</span>
|
|
152
152
|
<span class="team-task-subject">${escapeHtml(m.subject)}</span>
|
|
153
|
-
${
|
|
153
|
+
${b}
|
|
154
154
|
</div>`}).join("");u.innerHTML=h},hideTeamTasksPanel(){const e=document.getElementById("teamTasksPanel");e&&(e.style.display="none"),this.teamTasksDragListeners&&(document.removeEventListener("mousemove",this.teamTasksDragListeners.move),document.removeEventListener("mouseup",this.teamTasksDragListeners.up),this.teamTasksDragListeners.touchMove&&(document.removeEventListener("touchmove",this.teamTasksDragListeners.touchMove),document.removeEventListener("touchend",this.teamTasksDragListeners.up),document.removeEventListener("touchcancel",this.teamTasksDragListeners.up)),this.teamTasksDragListeners.handle&&(this.teamTasksDragListeners.handle.removeEventListener("mousedown",this.teamTasksDragListeners.handleMouseDown),this.teamTasksDragListeners.handle.removeEventListener("touchstart",this.teamTasksDragListeners.handleTouchStart)),this.teamTasksDragListeners=null)},getTeammateColor(e){return this.teammateMap.get(e)?.color||"blue"},normalizeFilePath(e,t){if(!e)return"";let s=e.trim();const n="/home/"+(window.USER||"user");s.startsWith("~/")?s=n+s.slice(1):s==="~"&&(s=n),!s.startsWith("/")&&t&&(s=t+"/"+s);const a=s.split("/"),o=[];for(const r of a)r===""||r==="."||(r===".."?o.length>1&&o.pop():o.push(r));return"/"+o.join("/")},getFilename(e){const t=e.split("/");return t[t.length-1]||""},isShallowRootPath(e){return e.startsWith("/")?e.split("/").filter(s=>s!=="").length===1:!1},isPathInWorkingDir(e,t){if(!t)return!1;const s=this.normalizeFilePath(e,t);return s.startsWith(t+"/")||s===t},pathsAreEquivalent(e,t,s){const n=this.normalizeFilePath(e,s),a=this.normalizeFilePath(t,s);if(n===a)return!0;const o=this.getFilename(n),r=this.getFilename(a);if(o!==r)return!1;const i=this.isShallowRootPath(e),l=this.isShallowRootPath(t),c=this.isPathInWorkingDir(n,s),d=this.isPathInWorkingDir(a,s);return!!(i&&d||l&&c)},pickBetterPath(e,t,s){if(s){const o=this.isPathInWorkingDir(e,s),r=this.isPathInWorkingDir(t,s);if(o&&!r)return e;if(r&&!o)return t}const n=e.startsWith("/"),a=t.startsWith("/");return n&&!a?e:a&&!n?t:e.length!==t.length?e.length>t.length?e:t:!e.includes("~")&&t.includes("~")?e:!t.includes("~")&&e.includes("~")?t:e},deduplicateProjectInsightPaths(e,t){const s=[];for(const o of e)for(const r of o.filePaths)s.push({rawPath:r,toolId:o.id});if(s.length<=1){const o=new Map;for(const r of s)o.set(this.normalizeFilePath(r.rawPath,t),r);return o}s.sort((o,r)=>{const i=this.isPathInWorkingDir(o.rawPath,t),l=this.isPathInWorkingDir(r.rawPath,t);return i&&!l?-1:l&&!i?1:r.rawPath.length-o.rawPath.length});const n=new Map,a=new Set;for(const{rawPath:o,toolId:r}of s){const i=this.normalizeFilePath(o,t);let l=!1;for(const[,c]of n)if(this.pathsAreEquivalent(o,c.rawPath,t)){l=!0;break}!l&&!a.has(i)&&(n.set(i,{rawPath:o,toolId:r}),a.add(i))}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(a=>a.id===t.id);n&&(n.status="completed"),this.renderProjectInsightsPanel(),setTimeout(()=>{const a=this.projectInsights.get(e)||[];this.projectInsights.set(e,a.filter(o=>o.id!==t.id)),this.renderProjectInsightsPanel()},2e3)},handleBashToolsUpdate(e,t){this.projectInsights.set(e,t),this.renderProjectInsightsPanel()},renderProjectInsightsPanel(){const e=this.$("projectInsightsPanel"),t=this.$("projectInsightsList");if(!e||!t)return;if(!(this.loadAppSettingsFromStorage().showProjectInsights??!1)){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}const o=(this.projectInsights.get(this.activeSessionId)||[]).filter(u=>u.status==="running");if(o.length===0){e.classList.remove("visible"),this.projectInsightsPanelVisible=!1;return}e.classList.add("visible"),this.projectInsightsPanelVisible=!0;const i=this.sessions.get(this.activeSessionId)?.workingDir||this.currentSessionWorkingDir,l=this.deduplicateProjectInsightPaths(o,i),c=new Set(Array.from(l.values()).map(u=>u.rawPath)),d=[];for(const u of o){const h=u.filePaths.filter(p=>c.has(p));if(h.length===0)continue;const m=u.command.length>50?u.command.substring(0,50)+"...":u.command;d.push(`
|
|
155
155
|
<div class="project-insight-item" data-tool-id="${u.id}">
|
|
156
156
|
<div class="project-insight-command">
|
|
@@ -167,15 +167,15 @@
|
|
|
167
167
|
`)}d.push(`
|
|
168
168
|
</div>
|
|
169
169
|
</div>
|
|
170
|
-
`)}t.innerHTML=d.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 a=await n.json();if(!a.success)throw new Error(a.error||"Failed to load files");if(this.fileBrowserData=a.data,this.renderFileBrowserTree(),s){const{totalFiles:o,totalDirectories:r,truncated:i}=a.data;s.textContent=`${o} files, ${r} dirs${i?" (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(),a=(o,r)=>{const i=o.type==="directory",l=this.fileBrowserExpandedDirs.has(o.path),c=!n||o.name.toLowerCase().includes(n);let d=!1;i&&n&&o.children&&(d=this.hasMatchingChild(o,n));const h=!(c||d)&&n?" hidden-by-filter":"",m=i?l?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(o.extension),p=i?`<span class="file-tree-expand${l?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',f=!i&&o.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(o.size)}</span>`:"",
|
|
170
|
+
`)}t.innerHTML=d.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 a=await n.json();if(!a.success)throw new Error(a.error||"Failed to load files");if(this.fileBrowserData=a.data,this.renderFileBrowserTree(),s){const{totalFiles:o,totalDirectories:r,truncated:i}=a.data;s.textContent=`${o} files, ${r} dirs${i?" (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(),a=(o,r)=>{const i=o.type==="directory",l=this.fileBrowserExpandedDirs.has(o.path),c=!n||o.name.toLowerCase().includes(n);let d=!1;i&&n&&o.children&&(d=this.hasMatchingChild(o,n));const h=!(c||d)&&n?" hidden-by-filter":"",m=i?l?"\u{1F4C2}":"\u{1F4C1}":this.getFileIcon(o.extension),p=i?`<span class="file-tree-expand${l?" expanded":""}">\u25B6</span>`:'<span class="file-tree-expand"></span>',f=!i&&o.size!==void 0?`<span class="file-tree-size">${this.formatFileSize(o.size)}</span>`:"",b=i?"file-tree-name directory":"file-tree-name",g=i?"":`<a class="file-tree-download" href="/api/sessions/${this.activeSessionId}/file-raw?path=${encodeURIComponent(o.path)}&download=true" title="Download" onclick="event.stopPropagation()">⬇</a>`;if(s.push(`
|
|
171
171
|
<div class="file-tree-item${h}" data-path="${escapeHtml(o.path)}" data-type="${o.type}" data-depth="${r}">
|
|
172
172
|
${p}
|
|
173
173
|
<span class="file-tree-icon">${m}</span>
|
|
174
|
-
<span class="${
|
|
174
|
+
<span class="${b}">${escapeHtml(o.name)}</span>
|
|
175
175
|
${f}
|
|
176
176
|
${g}
|
|
177
177
|
</div>
|
|
178
|
-
`),i&&l&&o.children)for(const
|
|
178
|
+
`),i&&l&&o.children)for(const w of o.children)a(w,r+1)};for(const o of t)a(o,0);e.innerHTML=s.join(""),e.querySelectorAll(".file-tree-item").forEach(o=>{o.addEventListener("click",()=>{const r=o.dataset.path;o.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){if(!this.activeSessionId||!e)return;const t=this.$("filePreviewOverlay"),s=this.$("filePreviewTitle"),n=this.$("filePreviewBody"),a=this.$("filePreviewFooter");if(!(!t||!n)){t.classList.add("visible"),s.textContent=e,n.innerHTML='<div class="binary-message">Loading...</div>',a.textContent="";try{const o=await fetch(`/api/sessions/${this.activeSessionId}/file-content?path=${encodeURIComponent(e)}&lines=500`);if(!o.ok)throw new Error("Failed to load file");const r=await o.json();if(!r.success)throw new Error(r.error||"Failed to load file");const i=r.data;if(i.type==="image")n.innerHTML=`<img src="${i.url}" alt="${escapeHtml(e)}">`,a.textContent=`${this.formatFileSize(i.size)} \u2022 ${i.extension}`;else if(i.type==="video")n.innerHTML=`<video src="${i.url}" controls autoplay></video>`,a.textContent=`${this.formatFileSize(i.size)} \u2022 ${i.extension}`;else if(i.type==="binary")n.innerHTML=`<div class="binary-message">Binary file (${this.formatFileSize(i.size)})<br>Cannot preview</div>`,a.textContent=i.extension||"binary";else{this.filePreviewContent=i.content,n.innerHTML=`<pre><code>${escapeHtml(i.content)}</code></pre>`;const l=i.truncated?` (showing 500/${i.totalLines} lines)`:"";a.textContent=`${i.totalLines} lines \u2022 ${this.formatFileSize(i.size)}${l}`}}catch(o){console.error("Failed to preview file:",o),n.innerHTML=`<div class="binary-message">Error: ${escapeHtml(o.message)}</div>`}}},closeFilePreview(){const e=this.$("filePreviewOverlay");e&&e.classList.remove("visible"),this.filePreviewContent=""},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 d=this.logViewerWindows.get(s);d.element.style.zIndex=++this.logViewerWindowZIndex;return}const n=this.logViewerWindows.size,a=100+n%5*30,o=100+n%5*30,r=e.split("/").pop(),i=document.createElement("div");i.className="log-viewer-window",i.id=`log-viewer-window-${s}`,i.style.left=`${a}px`,i.style.top=`${o}px`,i.style.zIndex=++this.logViewerWindowZIndex,i.innerHTML=`
|
|
179
179
|
<div class="log-viewer-window-header">
|
|
180
180
|
<div class="log-viewer-window-title" title="${escapeHtml(e)}">
|
|
181
181
|
<span class="icon">\u{1F4C4}</span>
|
|
@@ -189,13 +189,13 @@
|
|
|
189
189
|
<div class="log-viewer-window-body" id="log-viewer-body-${s}">
|
|
190
190
|
<div class="log-info">Connecting to ${escapeHtml(e)}...</div>
|
|
191
191
|
</div>
|
|
192
|
-
`,document.body.appendChild(i);const l=this.makeWindowDraggable(i,i.querySelector(".log-viewer-window-header")),c=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);c.onmessage=d=>{const u=JSON.parse(d.data),h=document.getElementById(`log-viewer-body-${s}`);if(h)switch(u.type){case"connected":h.innerHTML="";break;case"data":const m=h.scrollTop+h.clientHeight>=h.scrollHeight-10,p=escapeHtml(u.content);h.innerHTML+=p,m&&(h.scrollTop=h.scrollHeight),h.innerHTML.length>5e5&&(h.innerHTML=h.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":h.innerHTML+=`<div class="log-error">${escapeHtml(u.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:i,eventSource:c,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:a,timestamp:o,size:r}=e,i=`${t}-${o}`;if(this.imagePopups.has(i)){const v=this.imagePopups.get(i);v.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const v=this.imagePopups.keys().next().value;v&&this.closeImagePopup(v)}const c=this.imagePopups.size,d=(window.innerWidth-600)/2,u=(window.innerHeight-500)/2,h=d+c%5*30,m=u+c%5*30,f=this.sessions.get(t)?.name||t.substring(0,8),
|
|
192
|
+
`,document.body.appendChild(i);const l=this.makeWindowDraggable(i,i.querySelector(".log-viewer-window-header")),c=new EventSource(`/api/sessions/${t}/tail-file?path=${encodeURIComponent(e)}&lines=50`);c.onmessage=d=>{const u=JSON.parse(d.data),h=document.getElementById(`log-viewer-body-${s}`);if(h)switch(u.type){case"connected":h.innerHTML="";break;case"data":const m=h.scrollTop+h.clientHeight>=h.scrollHeight-10,p=escapeHtml(u.content);h.innerHTML+=p,m&&(h.scrollTop=h.scrollHeight),h.innerHTML.length>5e5&&(h.innerHTML=h.innerHTML.slice(-4e5));break;case"end":this.updateLogViewerStatus(s,"disconnected","ended");break;case"error":h.innerHTML+=`<div class="log-error">${escapeHtml(u.error)}</div>`,this.updateLogViewerStatus(s,"error","error");break}},c.onerror=()=>{this.updateLogViewerStatus(s,"disconnected","connection error")},this.logViewerWindows.set(s,{element:i,eventSource:c,filePath:e,sessionId:t,dragListeners:l})},updateLogViewerStatus(e,t,s){const n=document.querySelector(`#log-viewer-window-${e} .status`);n&&(n.className=`status ${t}`,n.textContent=s)},closeLogViewerWindow(e){const t=this.logViewerWindows.get(e);t&&(t.eventSource&&t.eventSource.close(),t.dragListeners&&(document.removeEventListener("mousemove",t.dragListeners.move),document.removeEventListener("mouseup",t.dragListeners.up),t.dragListeners.handle&&(t.dragListeners.handle.removeEventListener("mousedown",t.dragListeners.handleMouseDown),t.dragListeners.handle.removeEventListener("touchstart",t.dragListeners.handleTouchStart))),t.element.remove(),this.logViewerWindows.delete(e))},closeSessionLogViewerWindows(e){const t=[];for(const[s,n]of this.logViewerWindows)n.sessionId===e&&t.push(s);for(const s of t)this.closeLogViewerWindow(s)},openImagePopup(e){const{sessionId:t,filePath:s,relativePath:n,fileName:a,timestamp:o,size:r}=e,i=`${t}-${o}`;if(this.imagePopups.has(i)){const v=this.imagePopups.get(i);v.element.style.zIndex=++this.imagePopupZIndex;return}if(this.imagePopups.size>=20){const v=this.imagePopups.keys().next().value;v&&this.closeImagePopup(v)}const c=this.imagePopups.size,d=(window.innerWidth-600)/2,u=(window.innerHeight-500)/2,h=d+c%5*30,m=u+c%5*30,f=this.sessions.get(t)?.name||t.substring(0,8),b=(r/1024).toFixed(1),g=`/api/sessions/${t}/file-raw?path=${encodeURIComponent(n||a)}`,w=document.createElement("div");w.className="image-popup-window",w.id=`image-popup-${i}`,w.style.left=`${h}px`,w.style.top=`${m}px`,w.style.zIndex=++this.imagePopupZIndex,w.innerHTML=`
|
|
193
193
|
<div class="image-popup-header">
|
|
194
194
|
<div class="image-popup-title" title="${escapeHtml(s)}">
|
|
195
195
|
<span class="icon">\u{1F5BC}\uFE0F</span>
|
|
196
196
|
<span class="filename">${escapeHtml(a)}</span>
|
|
197
197
|
<span class="session-badge">${escapeHtml(f)}</span>
|
|
198
|
-
<span class="size-badge">${
|
|
198
|
+
<span class="size-badge">${b} KB</span>
|
|
199
199
|
</div>
|
|
200
200
|
<div class="image-popup-actions">
|
|
201
201
|
<button onclick="app.openImageInNewTab('${escapeHtml(g)}')" title="Open in new tab">\u2197</button>
|
|
@@ -207,21 +207,21 @@
|
|
|
207
207
|
onerror="this.parentElement.innerHTML='<div class=\\'image-error\\'>Failed to load image</div>'"
|
|
208
208
|
onclick="app.openImageInNewTab('${escapeHtml(g)}')" />
|
|
209
209
|
</div>
|
|
210
|
-
`,document.body.appendChild(
|
|
211
|
-
<div class="process-item process-item-clickable" onclick="app.selectSession('${
|
|
210
|
+
`,document.body.appendChild(w);const T=this.makeWindowDraggable(w,w.querySelector(".image-popup-header"));w.addEventListener("mousedown",()=>{w.style.zIndex=++this.imagePopupZIndex}),this.imagePopups.set(i,{element:w,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.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},a=this.sessions.get(s.sessionId),o=a?a.status:"unknown",r=a?a.isWorking:!1;let i,l;o==="idle"&&!r?(i="IDLE",l="status-idle"):o==="busy"||r?(i="WORKING",l="status-working"):o==="stopped"?(i="STOPPED",l="status-stopped"):(i=o.toUpperCase(),l="");const c=a&&a.tokens?a.tokens:null,d=a?a.totalCost:0,u=a&&a.cliModel||"",h=u.includes("opus")?"opus":u.includes("sonnet")?"sonnet":u.includes("haiku")?"haiku":"",m=a?a.ralphTodoStats:null;let p="";if(m&&m.total>0){const T=Math.round(m.completed/m.total*100);p=`<span class="process-stat todo-progress">${m.completed}/${m.total} (${T}%)</span>`}let f="";c&&c.total>0&&(f=`<span class="process-stat tokens">${(c.total/1e3).toFixed(1)}k tok</span>`);let b="";d>0&&(b=`<span class="process-stat cost">$${d.toFixed(2)}</span>`);let g="";h&&(g=`<span class="monitor-model-badge ${h}">${h}</span>`);const w=escapeHtml(s.sessionId);t+=`
|
|
211
|
+
<div class="process-item process-item-clickable" onclick="app.selectSession('${w}')" title="Switch to session">
|
|
212
212
|
<span class="monitor-status-badge ${l}">${i}</span>
|
|
213
213
|
<div class="process-info">
|
|
214
214
|
<div class="process-name">${g} ${escapeHtml(s.name||s.muxName)}</div>
|
|
215
215
|
<div class="process-meta">
|
|
216
216
|
${f}
|
|
217
|
-
${
|
|
217
|
+
${b}
|
|
218
218
|
${p}
|
|
219
219
|
<span class="process-stat memory">${n.memoryMB}MB</span>
|
|
220
220
|
<span class="process-stat cpu">${n.cpuPercent}%</span>
|
|
221
221
|
</div>
|
|
222
222
|
</div>
|
|
223
223
|
<div class="process-actions">
|
|
224
|
-
<button class="btn-toolbar btn-sm btn-danger" onclick="event.stopPropagation(); app.killMuxSession('${
|
|
224
|
+
<button class="btn-toolbar btn-sm btn-danger" onclick="event.stopPropagation(); app.killMuxSession('${w}')" title="Kill session">Kill</button>
|
|
225
225
|
</div>
|
|
226
226
|
</div>
|
|
227
227
|
`}e.innerHTML=t},renderMonitorSubagents(){const e=document.getElementById("monitorSubagentsBody"),t=document.getElementById("monitorSubagentStats");if(!e)return;const s=Array.from(this.subagents.values()),n=s.filter(o=>o.status==="active"||o.status==="idle").length;if(t&&(t.textContent=`${s.length} tracked`+(n>0?`, ${n} active`:"")),s.length===0){e.innerHTML='<div class="monitor-empty">No background agents</div>';return}let a="";for(const o of s){const r=o.status==="active"?"active":o.status==="idle"?"idle":"completed",i=o.modelShort?`<span class="model-badge ${o.modelShort}">${o.modelShort}</span>`:"",l=o.description?escapeHtml(o.description.substring(0,40)):o.agentId;a+=`
|
|
@@ -238,4 +238,4 @@
|
|
|
238
238
|
${o.status!=="completed"?`<button class="btn-toolbar btn-sm btn-danger" onclick="app.killSubagent('${escapeHtml(o.agentId)}')" title="Kill agent">Kill</button>`:""}
|
|
239
239
|
</div>
|
|
240
240
|
</div>
|
|
241
|
-
`}e.innerHTML=a},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.dead&&t.dead.length>0?(this.showToast(`Found ${t.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()},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=3e3,action:a}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.textContent=e,o.appendChild(r),a){const i=document.createElement("button");i.textContent=a.label,i.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",i.onclick=l=>{l.stopPropagation(),a.onClick(),o.remove()},o.appendChild(i)}this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),setTimeout(()=>{o.classList.remove("show"),setTimeout(()=>o.remove(),200)},n)},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),a=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&&a){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,a.style.width=`${Math.min(100,e.memory.percent)}%`,a.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(a.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&a.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 a=document.createElement("span");a.style.cssText="color:#cdd6f4;font-weight:600",a.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(a),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const i=document.createElement("button");i.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",i.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(i),t.appendChild(s),document.body.appendChild(t),i.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});
|
|
241
|
+
`}e.innerHTML=a},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.dead&&t.dead.length>0?(this.showToast(`Found ${t.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:a}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.textContent=e,o.appendChild(r),a){const i=document.createElement("button");i.textContent=a.label,i.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",i.onclick=l=>{l.stopPropagation(),a.onClick(),o.remove()},o.appendChild(i)}this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),setTimeout(()=>{o.classList.remove("show"),setTimeout(()=>o.remove(),200)},n)},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),a=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&&a){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,a.style.width=`${Math.min(100,e.memory.percent)}%`,a.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(a.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&a.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 a=document.createElement("span");a.style.cssText="color:#cdd6f4;font-weight:600",a.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(a),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const i=document.createElement("button");i.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",i.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(i),t.appendChild(s),document.body.appendChild(t),i.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|