mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 20:49:41 +02:00
Review polish on the desktop tab auto-wrap: - Auto-wrap is purely width-driven, but updateTabOverflowMode() was only called at the tail of _renderSessionTabsImmediate (SSE content renders). Window resize — the primary trigger for tabs crossing the one-row overflow threshold — never re-evaluated it, so narrowing/widening the window left the wrap state stale until an unrelated status event fired a render. Call it from the debounced window-resize handler (no-op on mobile/tablet, where the method bails). - Move the re-evaluation into _fullRenderSessionTabs() as well, so the incremental branch's two early `_fullRenderSessionTabs(); return;` paths (badge add/remove, which change tab width) and the manual two-rows toggle (applyTabWrapSettings → _fullRender…) re-evaluate too. The latter also fixes a transient where enabling manual two-rows while auto-wrap was on left both classes set (clipping folder tabs to 96px) until the next render. - Add boundary cases to the policy test: exact fit and the +1 sub-pixel tolerance (no wrap), 2px over (wrap), and a single overflowing tab (no wrap). Verified: tab-overflow test passes; tsc, check:frontend-syntax, check:public-assets, prettier all clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import vm from 'node:vm';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
function loadTabOverflowHelper() {
|
|
const context = vm.createContext({ window: {}, globalThis: {} });
|
|
const source = readFileSync(resolve(import.meta.dirname, '../src/web/public/constants.js'), 'utf8');
|
|
vm.runInContext(source, context, { filename: 'constants.js' });
|
|
return (context.window as { CodemanTabOverflow: { shouldAutoWrapTabs: (input: unknown) => boolean } })
|
|
.CodemanTabOverflow;
|
|
}
|
|
|
|
describe('tab overflow layout policy', () => {
|
|
it('auto-wraps desktop tabs when their rendered width exceeds available tab space', () => {
|
|
const helper = loadTabOverflowHelper();
|
|
|
|
expect(
|
|
helper.shouldAutoWrapTabs({
|
|
deviceType: 'desktop',
|
|
manualTwoRows: false,
|
|
tabCount: 18,
|
|
scrollWidth: 1400,
|
|
clientWidth: 760,
|
|
})
|
|
).toBe(true);
|
|
});
|
|
|
|
it('does not auto-wrap when manual tall tabs are enabled or on mobile/tablet', () => {
|
|
const helper = loadTabOverflowHelper();
|
|
|
|
expect(
|
|
helper.shouldAutoWrapTabs({
|
|
deviceType: 'desktop',
|
|
manualTwoRows: true,
|
|
tabCount: 18,
|
|
scrollWidth: 1400,
|
|
clientWidth: 760,
|
|
})
|
|
).toBe(false);
|
|
expect(
|
|
helper.shouldAutoWrapTabs({
|
|
deviceType: 'mobile',
|
|
manualTwoRows: false,
|
|
tabCount: 18,
|
|
scrollWidth: 1400,
|
|
clientWidth: 320,
|
|
})
|
|
).toBe(false);
|
|
});
|
|
|
|
it('respects the boundary conditions (exact fit, +1 tolerance, and tabCount < 2)', () => {
|
|
const helper = loadTabOverflowHelper();
|
|
const base = { deviceType: 'desktop' as const, manualTwoRows: false, tabCount: 6 };
|
|
|
|
// Exact fit: no overflow, no wrap.
|
|
expect(helper.shouldAutoWrapTabs({ ...base, scrollWidth: 800, clientWidth: 800 })).toBe(false);
|
|
// Within the +1 sub-pixel tolerance: still no wrap.
|
|
expect(helper.shouldAutoWrapTabs({ ...base, scrollWidth: 801, clientWidth: 800 })).toBe(false);
|
|
// 2px over: wrap.
|
|
expect(helper.shouldAutoWrapTabs({ ...base, scrollWidth: 802, clientWidth: 800 })).toBe(true);
|
|
// A single overflowing tab must not wrap (need at least 2 to form a second row).
|
|
expect(helper.shouldAutoWrapTabs({ ...base, tabCount: 1, scrollWidth: 1400, clientWidth: 760 })).toBe(false);
|
|
});
|
|
});
|