mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 00:49:41 +02:00
Owner decision 8 ("when I hit the tiles button, open the tiles already!").
A click with the grid closed now opens it straight away, and Ctrl+Shift+G
runs the same function (toggleTileGrid), so the two cannot drift. What
opens comes from one pure helper, tileGridOpenSet (constants.js):
a. the grid this tab last had, if any of its sessions survive, opened
exactly (an open split closes and its sessions do not join);
b. else an open split's two sessions, Pane A focused;
c. else the open sessions in tab order (the picker's list: no detached
ones), up to what the grid takes here (the cap of 6, fewer when the
window fits fewer), the active session always among them and focused.
A click with the grid open still closes it.
The picker moved to right-click (oncontextmenu, browser menu suppressed).
With the grid open it is preselected with the current tiles, and Open
replaces them. Ctrl/Cmd+click on a tab with the grid closed opens the
toggle's set plus that session. The button's title, the Help modal and
the wiki say right-click chooses which sessions.
Docs: decision 8 and an as-built entry in the spec (Entry points too),
CLAUDE.md, the invariants (#tile-grid, Opening), the wiki's Tile Grid and
Keyboard Shortcuts pages.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
84 lines
4.1 KiB
TypeScript
84 lines
4.1 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const INDEX_HTML = readFileSync(join(process.cwd(), 'src/web/public/index.html'), 'utf-8');
|
|
|
|
function normalizedHtml(value: string): string {
|
|
return value.replace(/\s+/g, ' ');
|
|
}
|
|
|
|
function extractElementById(html: string, id: string): string {
|
|
const idIndex = html.indexOf(`id="${id}"`);
|
|
expect(idIndex, `expected #${id} to exist`).toBeGreaterThanOrEqual(0);
|
|
|
|
const start = html.lastIndexOf('<', idIndex);
|
|
expect(start, `expected #${id} start tag`).toBeGreaterThanOrEqual(0);
|
|
|
|
// Bound at the next HTML comment (every following section is comment-labeled) so
|
|
// sections inserted between this element and any fixed marker don't leak into the
|
|
// slice — the cron modal's "Run At" text false-positived the stale-shortcut check.
|
|
const nextSection = html.indexOf('<!--', idIndex);
|
|
expect(nextSection, `expected section marker after #${id}`).toBeGreaterThanOrEqual(0);
|
|
|
|
const end = nextSection;
|
|
return html.slice(start, end);
|
|
}
|
|
|
|
function escapeRegExp(value: string): string {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
function expectShortcut(html: string, keys: string[], label: string): void {
|
|
const keyPattern = keys.map((key) => `<kbd>${escapeRegExp(key)}</kbd>`).join('\\s*\\+\\s*');
|
|
expect(html).toMatch(new RegExp(`${keyPattern}.*?${label}`, 'i'));
|
|
}
|
|
|
|
describe('help modal shortcuts', () => {
|
|
const helpModal = normalizedHtml(extractElementById(INDEX_HTML, 'helpModal'));
|
|
|
|
it('documents implemented global and tab shortcuts', () => {
|
|
// Ctrl+W is NOT an app shortcut: it is delete-word in the terminal, and as
|
|
// Close Session it killed sessions with no confirm (ctrl-w-never-closes.test.ts).
|
|
expect(helpModal).not.toMatch(/<kbd>Ctrl<\/kbd>\s*\+\s*<kbd>W<\/kbd>/i);
|
|
expectShortcut(helpModal, ['Ctrl', 'Tab'], 'Next Session');
|
|
expectShortcut(helpModal, ['Alt/Option', '['], 'Previous / Next Session');
|
|
expectShortcut(helpModal, ['Alt/Option', ']'], 'Previous / Next Session');
|
|
expectShortcut(helpModal, ['Alt/Option', '1-9'], 'Switch to Tab N');
|
|
expectShortcut(helpModal, ['Ctrl', '{'], 'Move Active Tab Left');
|
|
expectShortcut(helpModal, ['Ctrl', '}'], 'Move Active Tab Right');
|
|
expectShortcut(helpModal, ['Ctrl', '?'], 'Show Shortcuts');
|
|
expect(helpModal).not.toMatch(/Ctrl<\/kbd>\s*\+\s*<kbd>\/<\/kbd>.*?Show Shortcuts/i);
|
|
expectShortcut(helpModal, ['Ctrl', 'Shift', 'V'], 'Voice Input');
|
|
expectShortcut(helpModal, ['Escape'], 'Close Panels');
|
|
});
|
|
|
|
it('documents the tile grid chords', () => {
|
|
expectShortcut(helpModal, ['Ctrl', 'Shift', 'G'], 'Toggle Tile Grid');
|
|
expectShortcut(helpModal, ['Alt/Option', 'Shift', 'Arrows'], 'Focus Tile Left / Right / Up / Down');
|
|
expectShortcut(helpModal, ['Alt/Option', 'Shift', 'Enter'], 'Zoom Focused Tile');
|
|
expectShortcut(helpModal, ['Ctrl/Cmd', 'Click'], 'Add the Session to the Tile Grid');
|
|
expectShortcut(helpModal, ['Right-click'], 'Choose Which Sessions to Tile');
|
|
});
|
|
|
|
it('documents terminal input shortcuts without advertising stale run shortcuts', () => {
|
|
expectShortcut(helpModal, ['Ctrl', 'C'], 'Copy Selection');
|
|
expectShortcut(helpModal, ['Ctrl', 'Shift', 'C'], 'Copy Selection');
|
|
expectShortcut(helpModal, ['Ctrl', 'L'], 'Clear Terminal');
|
|
expectShortcut(helpModal, ['Ctrl', '+'], 'Increase Font');
|
|
expectShortcut(helpModal, ['Ctrl', '-'], 'Decrease Font');
|
|
expectShortcut(helpModal, ['Shift', 'Enter'], 'Insert Newline');
|
|
expectShortcut(helpModal, ['Ctrl', 'Enter'], 'Insert Newline');
|
|
// Ctrl+Shift+R (restore terminal size) is still dispatched — keep it documented.
|
|
expectShortcut(helpModal, ['Ctrl', 'Shift', 'R'], 'Restore Terminal Size');
|
|
|
|
expect(helpModal).not.toMatch(/Ctrl<\/kbd>\s*\+\s*<kbd>K<\/kbd>/i);
|
|
expect(helpModal).not.toMatch(/Ctrl<\/kbd>\s*\+\s*<kbd>Enter<\/kbd>.*?(Run|Start)/i);
|
|
});
|
|
|
|
it('does not advertise the removed Ctrl+Enter run binding in launch UI hints', () => {
|
|
expect(INDEX_HTML).not.toContain('Or press <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to start');
|
|
expect(INDEX_HTML).not.toContain('title="Run (Ctrl+Enter)"');
|
|
});
|
|
});
|