When several sessions are open on a large screen (≥1024px), the terminal
area can split into 1×2 or 2×2 so multiple LIVE, interactive terminals show
at once — a monitoring convenience for vibe-coding several Claude sessions.
Design keeps activeIndex as the single focused pane, so keybar/voice/approval
routing is unchanged; a new gridLayout + derived visible-set lets several
.term-cell wrappers show together inside a CSS-grid #term. The server and WS
protocol are untouched.
- public/grid-layout.ts (new): layout types/capacity, visibleIndices,
matchMedia desktop gate (GRID_MIN_WIDTH=1024), persistence, toolbar toggle.
- tabs.ts: pane→.term-cell wrapper (header + terminal + inline-approve footer);
applyLayout() owns show/hide + grid class + cell order + placeholders;
board-aware activate() (never focuses a hidden pane); setFocused/setGridLayout
delegate to it; renderCell/renderInlineApprove; refitVisible; notification
suppression for on-screen panes (factoring in the home overlay).
- terminal-session.ts: show({focus}) so non-focused quadrants don't steal
keyboard focus; onFocus callback (capture-phase pointerdown).
- main.ts: mount the toggle; window-focus refit → refitVisible.
- style.css: cell/grid model (.term-pane → relative flex child), focus ring,
pending pulse, inline approve, placeholder, toggle + coarse-pointer targets.
- tests: grid-layout.test.ts + split-grid block in tabs.test.ts (+33).
Adversarial review (4 lenses → per-finding verify) caught and fixed a HIGH:
activate() was not board-aware, so opening a tab on a full grid focused a
hidden pane (typing into an invisible session). Verified: typecheck + build:web
clean, 1566 tests pass, grid-layout 95% / tabs 94% coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
import { defineConfig } from 'vitest/config'
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
include: ['test/**/*.test.ts'],
|
|
environment: 'node',
|
|
// jsdom 29's localStorage methods are undefined under this config; a shared
|
|
// in-memory Web Storage polyfill fixes the frontend (@vitest-environment
|
|
// jsdom) tests that call localStorage.clear()/setItem(). No-op in node env.
|
|
setupFiles: ['test/setup/local-storage.ts'],
|
|
// Scaffold has no tests yet; don't fail the script until modules add theirs.
|
|
passWithNoTests: true,
|
|
// Coverage target is the global 80% rule; enforced with `--coverage`.
|
|
// Scope: all backend src/** (the review's core), plus the frontend modules
|
|
// that carry real logic and have unit tests. The remaining public/*.ts are
|
|
// thin DOM-wiring / entry-point glue (main.ts boots the app; dashboard/qr/
|
|
// share/shortcuts/search/keybar/settings/history just build + wire DOM) with
|
|
// no branch logic worth unit-testing in this fix-pass — excluded so the
|
|
// threshold measures tested surface, not untestable wiring. (Browser-driven
|
|
// E2E, not unit tests, is the right tool for those.)
|
|
coverage: {
|
|
provider: 'v8',
|
|
include: [
|
|
'src/**/*.ts',
|
|
'public/terminal-session.ts',
|
|
'public/tabs.ts',
|
|
'public/grid-layout.ts',
|
|
'public/preview-grid.ts',
|
|
'public/title-util.ts',
|
|
'public/voice-commands.ts',
|
|
'public/voice-confirm.ts',
|
|
// Desktop (Electron) shell: only the PURE logic modules are measured.
|
|
// The Electron glue (main/window/tray/menu/notifications/preload/
|
|
// embedded-server/settings-store) is thin, side-effectful wiring around
|
|
// the electron/native/dist APIs — untestable as a unit and excluded on
|
|
// the same principle as the thin DOM-wiring public/*.ts above.
|
|
'desktop/src/port.ts',
|
|
'desktop/src/shell.ts',
|
|
'desktop/src/server-config.ts',
|
|
'desktop/src/deep-link.ts',
|
|
'desktop/src/notify-policy.ts',
|
|
'desktop/src/notifications.ts',
|
|
'desktop/src/live-poll.ts',
|
|
'desktop/src/prefs.ts',
|
|
'desktop/src/settings-store.ts',
|
|
],
|
|
thresholds: {
|
|
lines: 80,
|
|
functions: 80,
|
|
branches: 80,
|
|
statements: 80,
|
|
},
|
|
},
|
|
},
|
|
})
|