Merge master into feat/story-mode

Picks up master's frontend dead-code cleanup. The one conflict was the
z-index list in src/web/CLAUDE.md: master dropped removed layers, the branch
added the Story Mode overlay (2400); both kept. The new
check:frontend-dead passes with Story Mode's scripts in it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-13 21:10:36 +01:00
commit 00ba3637d2
30 files changed
+330 -5531

No files matched your search

-1
View File
@@ -7,7 +7,6 @@
* only session:terminal is filtered by subscription)
* - Tab switching: terminal buffer loading, session creation + switch
* - Terminal data cap / backpressure recovery
* - Lazy teammate terminal lifecycle
* - SSE padding strategy (only latency-sensitive events)
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
-4
View File
@@ -138,23 +138,19 @@ describe('Codeman light skins', () => {
it('switches live terminals between light and dark contrast policies', () => {
const main = { options: {} as Record<string, unknown>, rows: 24, refresh: vi.fn() };
const teammate = { options: {} as Record<string, unknown>, rows: 12, refresh: vi.fn() };
const refreshFont = vi.fn();
const app = {
terminal: main,
teammateTerminals: new Map([['agent-1', { terminal: teammate }]]),
_localEchoOverlay: { refreshFont },
};
terminal.mixin.applyTerminalSkin.call(app, 'paper-gray');
expect(main.options.minimumContrastRatio).toBe(4.5);
expect(teammate.options.minimumContrastRatio).toBe(4.5);
expect((main.options.theme as Record<string, string>).background).toBe('#f6f8fa');
expect(refreshFont).toHaveBeenCalledTimes(1);
terminal.mixin.applyTerminalSkin.call(app, 'daylight-blue');
expect(main.options.minimumContrastRatio).toBe(1);
expect(teammate.options.minimumContrastRatio).toBe(1);
expect((main.options.theme as Record<string, string>).background).toBe('#161b23');
expect(refreshFont).toHaveBeenCalledTimes(2);
});
-1
View File
@@ -277,7 +277,6 @@ describe('vertical rail session search in Chromium', () => {
['ag1', { element: document.getElementById('subWin'), minimized: false, hidden: false }],
]);
app.subagentParentMap = new Map([['ag1', 'alpha']]);
app.planSubagents = new Map();
});
try {
const anchor = await rowAnchor();
-1
View File
@@ -801,7 +801,6 @@ describe('floating windows whose parent row the search hid', () => {
['ag1', { element: document.getElementById('subWin'), minimized: false, hidden: false }],
]);
app.subagentParentMap = new Map([['ag1', 'review']]);
app.planSubagents = new Map();
app.ultracodeWindows = new Map([
['run1', { element: document.getElementById('runWin'), parentSessionId: 'review' }],
]);
+4 -30
View File
@@ -11,11 +11,9 @@
*
* Three things are pinned here because each fails silently:
*
* - A live save reaches the echo overlays and the Agent Teams panes. Both
* cache the weight (the overlays paint it into their spans, the panes read
* their options at construction), so without the propagation the characters
* being typed, or a pane left open across the save, keep the old weight
* beside a repainted terminal.
* - A live save reaches the echo overlays. They cache the weight and paint
* it into their spans, so without the propagation the characters being
* typed keep the old weight beside a repainted terminal.
* - An unchanged save is a no-op, so opening and closing App Settings does not
* churn the terminal.
* - The bundled face is declared over its full axis. The `@font-face`
@@ -65,19 +63,11 @@ function fakeTerminal(options: Record<string, unknown> = {}) {
function makeApp(
opts: {
teammates?: number;
terminal?: ReturnType<typeof fakeTerminal> | null;
splitPane?: { terminal: ReturnType<typeof fakeTerminal>; fit: () => void } | null;
} = {}
) {
const fit = vi.fn();
const teammateFits: ReturnType<typeof vi.fn>[] = [];
const teammateTerminals = new Map<string, { terminal: ReturnType<typeof fakeTerminal>; fitAddon: unknown }>();
for (let i = 0; i < (opts.teammates ?? 0); i++) {
const teammateFit = vi.fn();
teammateFits.push(teammateFit);
teammateTerminals.set(`agent-${i}`, { terminal: fakeTerminal(), fitAddon: { fit: teammateFit } });
}
const app = {
applyTerminalFontWeights: mixin.applyTerminalFontWeights,
// Secondary panes (the split pane's second terminal) are reached through
@@ -99,12 +89,11 @@ function makeApp(
_awaitTerminalFont: vi.fn(() => Promise.resolve()),
terminal: opts.terminal === undefined ? fakeTerminal() : opts.terminal,
fitAddon: { fit },
teammateTerminals,
_localEchoOverlay: { refreshFont: vi.fn() },
_predictiveEcho: { refreshFont: vi.fn() },
_terminalFontReady: null as unknown,
};
return { app, fit, teammateFits, teammateTerminals };
return { app, fit };
}
describe('applyTerminalFontWeights', () => {
@@ -152,21 +141,6 @@ describe('applyTerminalFontWeights', () => {
expect(app._predictiveEcho.refreshFont).toHaveBeenCalledTimes(1);
});
it('reaches Agent Teams panes, which read their options at construction', () => {
const { app, teammateTerminals, teammateFits } = makeApp({ teammates: 2 });
(app as unknown as { applyTerminalFontWeights: (s: unknown) => void }).applyTerminalFontWeights({
terminalFontWeight: '300',
terminalFontWeightBold: '800',
});
for (const [, entry] of teammateTerminals) {
expect(entry.terminal.options.fontWeight).toBe(300);
expect(entry.terminal.options.fontWeightBold).toBe(800);
}
for (const teammateFit of teammateFits) expect(teammateFit).toHaveBeenCalled();
});
it('restores xterm’s own defaults when the setting is cleared', () => {
const { app } = makeApp({ terminal: fakeTerminal({ fontWeight: 300, fontWeightBold: 800 }) });