- Enable NODE_COMPILE_CACHE in systemd service and npm start for 10-20% faster cold starts
- Lazy-load xterm-addon-webgl.min.js (244KB) only on desktop — mobile never downloads it
- Add <link rel="preload"> hints for critical scripts (xterm, constants, app) in <head>
- Replace per-session tmux subprocess calls with single batch `list-panes -a` call
(N*2+1+M execSync calls → 1 for reconcileSessions)
- Fix CLAUDE.md frontend module count (10 → 11)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-keystroke HTTP POST + SSE terminal output with a single
bidirectional WebSocket connection for dramatically lower input latency.
The existing SSE+POST paths remain fully functional as fallback.
Server-side: ws-routes.ts provides /ws/sessions/:id/terminal with 8ms
micro-batching and 16KB flush threshold. Each batch is wrapped in
DEC 2026 synchronized update markers so xterm.js renders atomically —
Ink's DA capability negotiation fails through the PTY→server→WS proxy
chain, so without server-injected markers, cursor-up redraws flicker.
Frontend: _connectWs/_disconnectWs manage per-session WS lifecycle.
Input and resize use WS fast path with HTTP POST fallback. SSE terminal
events are suppressed when WS is active to prevent double rendering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
xterm.js 6.0.0 natively handles DEC mode 2026 (synchronized output),
which renders Ink's cursor-up redraws atomically at the parser level.
This eliminates split-frame rendering that caused table header loss
and garbled overlapping text in Claude CLI sessions.
- Migrate from xterm/xterm-addon-* to @xterm/* scoped packages
- Update build.mjs and postinstall.js vendor paths
- Remove old xterm 5.x dependencies
`@remotion/compositor-linux-x64-gnu` and `@rspack/binding-linux-x64-gnu`
are Linux x64 binaries that cause npm install to fail on macOS (arm64)
with EBADPLATFORM. Moving them to optionalDependencies allows npm to
skip them gracefully on unsupported platforms.
Fixes#32
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stop toggling WebGL renderer off/on around large buffer writes in
chunkedTerminalWrite(). The dispose+loadAddon cycle caused visible
re-render flashes and (before the deferred fix) synchronous GPU stalls
from ReadPixels blocking the main thread. Instead, keep WebGL active
and rely on 32KB chunked writes to keep per-frame render work under
~5ms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @rollup/rollup-linux-x64-gnu native binding was a hard dependency,
causing npm install to fail on arm64 and macOS platforms. Nothing in the
codebase uses rollup directly (build uses esbuild); rollup is only
pulled in transitively by @remotion/cli and manages its own
platform-specific bindings via its own optionalDependencies.
Closes#28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
require() is not defined in ESM modules. The production build (tsc)
outputs ESM, causing 'require is not defined' → 500 → 'QR unavailable'.
Vitest/tsx shimmed require() so tests passed but production was broken.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@manypkg/get-packages only includes workspace members in its packages list,
not the workspace root. Adding "." to workspaces makes the root package
discoverable, fixing "not in the workspace" errors from changeset version.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: toggleTunnelFromWelcome() sent the entire settings object
back to PUT /api/settings, but the Zod schema uses .strict() which
rejects unknown fields (lastUsedCase, localEchoEnabled, etc.). The PUT
silently failed, so the tunnel never started.
Fix: send only {tunnelEnabled: true/false} instead of the full blob.
Also added polling fallback for tunnel status and server-side re-broadcast
when tunnel is already running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>