From c561c411463124d881f4c09c399da61d3ad48974 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 20 Feb 2026 15:58:28 +0100 Subject: [PATCH] chore: bump version to 0.1582 --- CLAUDE.md | 2 +- package.json | 2 +- src/web/public/app.js | 29 +++++++++++++++--------- test/perf-browser.test.ts | 47 ++++++++++----------------------------- 4 files changed, 33 insertions(+), 47 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f9a69a03..4c1ec5d0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`) 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1581 (must match `package.json` for npm publish) +**Version**: 0.1582 (must match `package.json` for npm publish) ## Project Overview diff --git a/package.json b/package.json index 9404b0dd..8901402c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1581", + "version": "0.1582", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/app.js b/src/web/public/app.js index 049751fc..147683d0 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -967,22 +967,31 @@ class LocalEchoOverlay { this.overlay.style.display = 'none'; } - // Create a styled line div with opaque background - _makeLine(text, leftPx, topPx, widthPx, cellH) { + // Create a styled line div with per-character grid positioning. + // Each char is placed at exact col*cellW to match xterm's canvas renderer + // (DOM text flow drifts vs canvas due to sub-pixel glyph width differences). + _makeLine(text, leftPx, topPx, widthPx, cellH, cellW) { const el = document.createElement('div'); - el.style.cssText = `position:absolute;white-space:pre;font-kerning:none;pointer-events:none`; - el.style.fontFamily = this._fontFamily; - el.style.fontSize = this._fontSize; - el.style.fontWeight = this._fontWeight; - el.style.letterSpacing = this._letterSpacing; - el.style.color = this._color; + el.style.cssText = `position:absolute;pointer-events:none`; el.style.backgroundColor = this._bg; el.style.left = leftPx + 'px'; el.style.top = topPx + 'px'; el.style.width = widthPx + 'px'; el.style.height = (cellH + 1) + 'px'; el.style.lineHeight = cellH + 'px'; - el.textContent = text; + // Place each character at its exact grid position + for (let i = 0; i < text.length; i++) { + const span = document.createElement('span'); + span.style.cssText = `position:absolute;display:inline-block;text-align:center;pointer-events:none`; + span.style.left = (i * cellW) + 'px'; + span.style.width = cellW + 'px'; + span.style.fontFamily = this._fontFamily; + span.style.fontSize = this._fontSize; + span.style.fontWeight = this._fontWeight; + span.style.color = this._color; + span.textContent = text[i]; + el.appendChild(span); + } return el; } @@ -1038,7 +1047,7 @@ class LocalEchoOverlay { const leftPx = i === 0 ? startCol * cellW : 0; const widthPx = i === 0 ? (fullWidthPx - leftPx) : fullWidthPx; const topPx = i * cellH; - const lineEl = this._makeLine(lines[i], leftPx, topPx, widthPx, cellH); + const lineEl = this._makeLine(lines[i], leftPx, topPx, widthPx, cellH, cellW); this.overlay.appendChild(lineEl); } diff --git a/test/perf-browser.test.ts b/test/perf-browser.test.ts index 0cdf6b5b..c699cf9f 100644 --- a/test/perf-browser.test.ts +++ b/test/perf-browser.test.ts @@ -662,51 +662,28 @@ describe('Subagent window simulation', () => { expect(timing).toBeLessThan(THRESHOLDS.SUBAGENT_WINDOW_OPEN); }); - it('closes subagent window within threshold', async () => { - // Ensure a window is open - const hasWindow = await page.locator('.subagent-window').count().then(c => c > 0); - if (!hasWindow) { - const claudeSessionId = await page.evaluate((sid: string) => { - const app = (window as unknown as { app: { sessions: Map } }).app; - return app.sessions.get(sid)?.claudeSessionId || 'fake-claude-session'; - }, sessionId); - await page.evaluate(({ cSessionId }: { cSessionId: string }) => { - const app = (window as unknown as { - app: { - subagents: Map>; - openSubagentWindow: (id: string) => void; - } - }).app; - app.subagents.set('perf-agent-close', { - agentId: 'perf-agent-close', - sessionId: cSessionId, - status: 'active', - description: 'Close test agent', - startedAt: Date.now(), - lastActivityAt: Date.now(), - toolCallCount: 0, - entryCount: 0, - fileSize: 0, - }); - app.openSubagentWindow('perf-agent-close'); - }, { cSessionId: claudeSessionId }); - await page.waitForSelector('.subagent-window', { timeout: 3000 }); - } + it('closes (minimizes) subagent window within threshold', async () => { + // perf-agent-1 should still be open from the previous test + // Note: closeSubagentWindow minimizes to tab (display:none), doesn't remove from DOM + const isVisible = await page.locator('#subagent-window-perf-agent-1').isVisible(); + expect(isVisible).toBe(true); const timing = await measure(async () => { await page.evaluate(() => { const app = (window as unknown as { app: { closeSubagentWindow: (id: string) => void } }).app; - const win = document.querySelector('.subagent-window'); - const id = win?.id?.replace('subagent-window-', '') || 'perf-agent-close'; - app.closeSubagentWindow(id); + app.closeSubagentWindow('perf-agent-1'); }); + // Wait for element to become hidden (display: none) await page.waitForFunction( - () => document.querySelectorAll('.subagent-window').length === 0, + () => { + const el = document.getElementById('subagent-window-perf-agent-1'); + return el && el.style.display === 'none'; + }, { timeout: 2000 }, ); }); - console.log(`[subagent window] close: ${timing.toFixed(0)}ms`); + console.log(`[subagent window] close (minimize): ${timing.toFixed(0)}ms`); expect(timing).toBeLessThan(THRESHOLDS.SUBAGENT_WINDOW_CLOSE); });