mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-13 18:29:32 +01:00
Clamp the stored toolbar width to MIN_WIDTH in apply() and step the arrow keys from the width on screen, so a drag to the window edge or a held ArrowRight no longer stores a negative width or leaves an ArrowLeft dead zone (browser test added) Drop Escape as a reset key on the handle: the global capture-phase Escape handler already closes every panel on it (static pin added) Say in the changeset, the wiki and the densityFor comment that the percentages go at the narrowest step in the Compact style only, and drop Escape from the changeset Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
77 lines
3.1 KiB
TypeScript
77 lines
3.1 KiB
TypeScript
/**
|
|
* @fileoverview The draggable divider at the left edge of the header toolbar: the width clamp keeps the tabs
|
|
* some room, the controller is wired into the page and build, and the stored width is device-local.
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import vm from 'node:vm';
|
|
|
|
const publicDir = resolve(import.meta.dirname, '../src/web/public');
|
|
const read = (name: string) => readFileSync(resolve(publicDir, name), 'utf8');
|
|
const buildJs = readFileSync(resolve(import.meta.dirname, '../scripts/build.mjs'), 'utf8');
|
|
|
|
function load() {
|
|
const window: Record<string, unknown> = {};
|
|
const context = vm.createContext({
|
|
window,
|
|
document: { readyState: 'loading', addEventListener: () => {}, querySelector: () => null },
|
|
localStorage: { getItem: () => null, setItem: () => {}, removeItem: () => {} },
|
|
});
|
|
vm.runInContext(read('header-divider.js'), context, { filename: 'header-divider.js' });
|
|
return window.CodemanHeaderDivider as {
|
|
clampWidth: (w: number, header: number, brand: number) => number;
|
|
densityFor: (w: number) => string;
|
|
MIN_WIDTH: number;
|
|
MIN_TABS_ROOM: number;
|
|
};
|
|
}
|
|
|
|
describe('header divider', () => {
|
|
it('clamps the toolbar between its minimum and what leaves the tabs room', () => {
|
|
const { clampWidth, MIN_WIDTH, MIN_TABS_ROOM } = load();
|
|
expect(clampWidth(10, 1600, 120)).toBe(MIN_WIDTH);
|
|
expect(clampWidth(5000, 1600, 120)).toBe(1600 - 120 - MIN_TABS_ROOM);
|
|
expect(clampWidth(420.4, 1600, 120)).toBe(420);
|
|
});
|
|
|
|
it('shows less of the usage readings the narrower the toolbar gets', () => {
|
|
const { densityFor } = load();
|
|
expect([700, 560, 559, 400, 399, 280, 279, 160].map(densityFor)).toEqual([
|
|
'',
|
|
'',
|
|
'tight',
|
|
'tight',
|
|
'dense',
|
|
'dense',
|
|
'tiny',
|
|
'tiny',
|
|
]);
|
|
});
|
|
|
|
it('never goes below the minimum even in a header too narrow for the tabs room', () => {
|
|
const { clampWidth, MIN_WIDTH } = load();
|
|
expect(clampWidth(400, 300, 100)).toBe(MIN_WIDTH);
|
|
});
|
|
|
|
it('is wired into the page and the build, and hides where the tabs are not in the header', () => {
|
|
expect(read('index.html')).toContain('<script defer src="header-divider.js"></script>');
|
|
expect(buildJs).toContain("run('minify header-divider.js'");
|
|
expect(buildJs).toMatch(/HASHABLE[\s\S]{0,1500}'header-divider\.js'/);
|
|
const css = read('styles.css');
|
|
expect(css).toContain('.header-divider');
|
|
expect(css).toMatch(/data-session-list='sidebar'\] \.header-divider/);
|
|
expect(css).toMatch(/data-tab-orientation='vertical'\] \.header-divider/);
|
|
});
|
|
|
|
it('keeps the width on the device only and offers a keyboard reset', () => {
|
|
const js = read('header-divider.js');
|
|
expect(js).toContain('localStorage');
|
|
expect(js).not.toContain('/api/settings');
|
|
expect(js).toMatch(/key === 'Home'/);
|
|
// Escape belongs to the global close-panels handler (app.js), so the handle must not claim it too.
|
|
expect(js).not.toMatch(/'Escape'/);
|
|
expect(js).toContain("addEventListener('dblclick'");
|
|
});
|
|
});
|