Skip to content

Commit 5d59c17

Browse files
committed
feat(ultracode): in-page agent transcript windows + minimize-to-tab (1.1.14)
Clicking an agent card opens its live transcript as an in-page connected floating window instead of a detached browser popup. The "−" button on both run and agent windows now minimizes into the originating session tab as a restorable ULTRA badge (🧬 runs, 📄 transcripts). Removes the old collapse-to-header behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cfcd9d2 commit 5d59c17

9 files changed

Lines changed: 548 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# aicodeman
22

3+
## 1.1.14
4+
5+
### Patch Changes
6+
7+
- Ultracode (Workflow-tool) floating windows — agent transcripts in-page, and minimize-to-tab.
8+
- **Agent transcripts open in-page, connected, instead of a detached browser popup.** Clicking an agent card (in a run window or the dock panel) now opens the agent's live transcript as its own draggable floating window, tied by a connector line to its parent run window (falling back to the run's session tab if that window has since closed) — the same line idiom the run windows use. Re-clicking a card focuses the existing window; closing it removes the window and its line. (Previously this spawned a separate `window.open` browser popup.)
9+
- **The window "−" button now minimizes into the originating session tab**, mirroring the subagent-window idiom. The window genie-animates into its tab and is tracked there; the tab shows an `ULTRA` badge whose hover/click dropdown lists each minimized item (🧬 run windows, 📄 agent transcripts). Click an item to restore its floating window, or dismiss it with ×. A run minimized while still active keeps tracking in the background and its badge auto-clears shortly after the run finishes. Both run windows and agent-transcript windows minimize into the same merged badge.
10+
- Removed the old collapse-to-header behavior that the "−" button previously triggered (now superseded by minimize-to-tab).
11+
312
## 1.1.13
413

514
### Patch Changes

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ When user says "COM":
5656
5757
CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed.
5858
59-
**Version**: 1.1.13 (must match `package.json`)
59+
**Version**: 1.1.14 (must match `package.json`)
6060
6161
## Project Overview
6262

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aicodeman",
3-
"version": "1.1.13",
3+
"version": "1.1.14",
44
"description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
55
"type": "module",
66
"main": "dist/index.js",

src/web/public/app.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,10 @@ class CodemanApp {
27732773
const minimizedCount = minimizedAgents?.size || 0;
27742774
const subagentBadge = minimizedCount > 0 ? this.renderSubagentTabBadge(id, minimizedAgents) : '';
27752775

2776+
// Ultracode runs + agent transcripts minimized to this tab (ultracode-windows.js
2777+
// renders one merged ULTRA badge; returns '' when nothing is minimized).
2778+
const ultracodeBadge = this.renderUltracodeTabBadge ? this.renderUltracodeTabBadge(id) : '';
2779+
27762780
// Show folder name if session has a custom name AND tall tabs setting is enabled
27772781
const folderName = session.workingDir ? session.workingDir.split('/').pop() || '' : '';
27782782
const tallTabsEnabled = this._tallTabsEnabled ?? false;
@@ -2792,6 +2796,7 @@ class CodemanApp {
27922796
</span>
27932797
${hasRunningTasks ? `<span class="tab-badge" onclick="event.stopPropagation(); app.toggleTaskPanel()" aria-label="${taskStats.running} running tasks">${taskStats.running}</span>` : ''}
27942798
${subagentBadge}
2799+
${ultracodeBadge}
27952800
<span class="tab-gear" onclick="event.stopPropagation(); app.openSessionOptions('${escapeHtml(id)}')" title="Session options" aria-label="Session options" tabindex="0">&#x2699;</span>
27962801
<span class="tab-detach" onclick="event.stopPropagation(); app.detachSession('${escapeHtml(id)}')" title="Open in a new window" aria-label="Open session in a new window" tabindex="0">&#x29C9;</span>
27972802
<span class="tab-close" onclick="event.stopPropagation(); app.requestCloseSession('${escapeHtml(id)}')" title="Close session" aria-label="Close session" tabindex="0">&times;</span>

src/web/public/styles.css

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5573,6 +5573,45 @@ kbd {
55735573
letter-spacing: 0.03em;
55745574
}
55755575

5576+
/* Ultracode run minimized-to-tab badge — same chip shape as the subagent badge but a
5577+
distinct purple (matches the ultracode window accent), and a SEPARATE class so the
5578+
incremental tab-update path (which keys on .tab-subagent-badge) never touches it.
5579+
Reuses the shared .subagent-dropdown for its restore/dismiss list. */
5580+
.session-tab .tab-ultracode-badge {
5581+
position: relative;
5582+
display: inline-flex;
5583+
align-items: center;
5584+
justify-content: center;
5585+
gap: 2px;
5586+
height: 16px;
5587+
padding: 0 5px;
5588+
border-radius: 8px;
5589+
font-size: 0.6rem;
5590+
cursor: pointer;
5591+
background: #a855f7;
5592+
color: white;
5593+
margin-left: 4px;
5594+
transition: transform 0.1s, background 0.15s;
5595+
}
5596+
@media (hover: hover) {
5597+
.session-tab .tab-ultracode-badge:hover {
5598+
background: #9333ea;
5599+
transform: scale(1.05);
5600+
}
5601+
}
5602+
.session-tab .tab-ultracode-badge .subagent-label {
5603+
font-size: 0.55rem;
5604+
font-weight: 600;
5605+
letter-spacing: 0.03em;
5606+
}
5607+
/* Per-item type glyph in the ULTRA dropdown (🧬 run vs 📄 transcript). */
5608+
.subagent-dropdown-item .ultracode-dd-icon {
5609+
font-size: 0.7rem;
5610+
line-height: 1;
5611+
flex-shrink: 0;
5612+
margin-right: 1px;
5613+
}
5614+
55765615
/* Subagent Dropdown - compact, hover-triggered */
55775616
.subagent-dropdown {
55785617
display: none;
@@ -8647,13 +8686,6 @@ kbd {
86478686
transform: scale(0.92);
86488687
opacity: 0;
86498688
}
8650-
.ultracode-window.collapsed {
8651-
height: auto !important;
8652-
resize: none;
8653-
}
8654-
.ultracode-window.collapsed .ultracode-window-body {
8655-
display: none;
8656-
}
86578689
.ultracode-window-header {
86588690
display: flex;
86598691
align-items: center;
@@ -8744,6 +8776,32 @@ kbd {
87448776
50% { opacity: 1; }
87458777
}
87468778

8779+
/* Agent-transcript window: spawned from an agent card (in a run window or the dock
8780+
panel), tied to its parent run window by its own connector line. Reuses the
8781+
.ultracode-window chrome; wider with a monospace transcript body. */
8782+
.ultracode-agent-window {
8783+
width: 470px;
8784+
height: 420px;
8785+
border-color: #38bdf8;
8786+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(56, 189, 248, 0.18);
8787+
}
8788+
.ultracode-agent-window .ultracode-window-body .uw-transcript {
8789+
margin: 0;
8790+
white-space: pre-wrap;
8791+
word-break: break-word;
8792+
font-family: var(--font-mono);
8793+
font-size: 0.68rem;
8794+
line-height: 1.45;
8795+
color: var(--text);
8796+
}
8797+
/* Distinguish the parent→agent line from the tab→run line: solid cyan, no dashes.
8798+
Must follow .ultracode-connection (equal specificity → source order wins). */
8799+
.connection-line.ultracode-agent-connection {
8800+
stroke: #38bdf8;
8801+
stroke-dasharray: none;
8802+
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.8)) drop-shadow(0 0 5px rgba(56, 189, 248, 0.85));
8803+
}
8804+
87478805
/* Plan-usage chip (App Settings → Display → "Plan Usage Limits"). Shows the
87488806
live 5-hour + weekly plan limits parsed from the Claude statusline. Hidden by
87498807
default via the marker class below; the server strips it at render when the

src/web/public/subagent-windows.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,10 @@ Object.assign(CodemanApp.prototype, {
461461
if (typeof this._appendUltracodeConnectionLines === 'function') {
462462
this._appendUltracodeConnectionLines(svg, rects);
463463
}
464+
// Agent-transcript windows → their run window / tab (ultracode-windows.js).
465+
if (typeof this._appendUltracodeAgentConnectionLines === 'function') {
466+
this._appendUltracodeAgentConnectionLines(svg, rects);
467+
}
464468
},
465469

466470
// ═══════════════════════════════════════════════════════════════

src/web/public/ultracode-panel.js

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,15 @@ Object.assign(CodemanApp.prototype, {
131131
}
132132
},
133133

134-
// Phase 4: open an agent's live transcript by agentId. The workflow agent's
134+
// Phase 4: fetch an agent's live transcript by agentId. The workflow agent's
135135
// agentId is byte-identical to the agent-<id>.jsonl stem already tracked by
136136
// subagent-watcher, so we reuse the existing transcript route — no watcher edits.
137-
// Graceful when the agent isn't tracked yet / aged out / tracking disabled.
138-
async openWorkflowAgentTranscript(agentId) {
139-
if (!agentId) return;
137+
// Returns { formatted: string[], entryCount } or null when nothing is available
138+
// (queued / aged out of tracking / tracking disabled). Rendering into a connected
139+
// in-page floating window lives in ultracode-windows.js (openUltracodeAgentWindow) —
140+
// we no longer spawn a detached browser popup.
141+
async _fetchWorkflowAgentTranscript(agentId) {
142+
if (!agentId) return null;
140143
let data = null;
141144
try {
142145
const res = await fetch(`/api/subagents/${encodeURIComponent(agentId)}/transcript?format=formatted`);
@@ -147,21 +150,8 @@ Object.assign(CodemanApp.prototype, {
147150
const ok = data && data.success && data.data;
148151
const formatted = ok ? data.data.formatted : null;
149152
const entryCount = ok ? data.data.entryCount || 0 : 0;
150-
if (!formatted || !entryCount) {
151-
alert(
152-
'No transcript available for this agent yet — it may be queued, aged out of tracking, or subagent tracking is disabled.'
153-
);
154-
return;
155-
}
156-
const win = window.open('', '_blank', 'width=860,height=640');
157-
if (!win) return; // popup blocked
158-
win.document.write(
159-
`<html><head><title>Workflow agent ${escapeHtml(agentId)} transcript</title>` +
160-
`<style>body{background:#1a1a2e;color:#eee;font-family:monospace;padding:20px}pre{white-space:pre-wrap;word-wrap:break-word}</style>` +
161-
`</head><body><h2>Workflow agent ${escapeHtml(agentId)} (${entryCount} entries)</h2>` +
162-
`<pre>${escapeHtml(formatted.join('\n'))}</pre></body></html>`
163-
);
164-
win.document.close();
153+
if (!formatted || !entryCount) return null;
154+
return { formatted, entryCount };
165155
},
166156

167157
// ----- Render (debounced) -----
@@ -262,13 +252,13 @@ Object.assign(CodemanApp.prototype, {
262252
const header =
263253
`<div class="ultracode-phase-header"><span>${escapeHtml(title)}</span>` +
264254
`<span class="ultracode-phase-sub">${this._fmtNum(tok)} tok · ${tools} tools</span></div>`;
265-
return header + group.map((a) => this._workflowAgentCardHtml(a)).join('');
255+
return header + group.map((a) => this._workflowAgentCardHtml(a, runId)).join('');
266256
})
267257
.join('');
268258
detail.innerHTML = html;
269259
},
270260

271-
_workflowAgentCardHtml(a) {
261+
_workflowAgentCardHtml(a, runId) {
272262
const state = String(a.state || 'start');
273263
const stateCls = this._workflowAgentStateClass(state);
274264
const stateLabel = state === 'start' ? 'queued' : state === 'progress' ? 'running' : state;
@@ -289,7 +279,7 @@ Object.assign(CodemanApp.prototype, {
289279
const cardStateCls = state === 'done' ? ' uw-state-done' : state === 'progress' ? ' uw-state-working' : '';
290280
const cardAttrs = clickable
291281
? ` class="ultracode-agent-card ultracode-agent-card--clickable${cardStateCls}" role="button" tabindex="0"` +
292-
` title="View transcript" onclick="app.openWorkflowAgentTranscript('${escapeHtml(a.agentId)}')"`
282+
` title="View transcript" onclick="app.openUltracodeAgentWindow('${escapeHtml(a.agentId)}','${escapeHtml(runId || '')}')"`
293283
: ` class="ultracode-agent-card${cardStateCls}"`;
294284
return (
295285
`<div${cardAttrs}>` +

0 commit comments

Comments
 (0)