Skip to content

feat(terminal-engine): full xterm.js → alacritty cutover (Phase 1+2+3)#150

Open
alxjrvs wants to merge 46 commits into
devfrom
run/2026-05-16-alacritty-terminal-engine/work
Open

feat(terminal-engine): full xterm.js → alacritty cutover (Phase 1+2+3)#150
alxjrvs wants to merge 46 commits into
devfrom
run/2026-05-16-alacritty-terminal-engine/work

Conversation

@alxjrvs
Copy link
Copy Markdown
Contributor

@alxjrvs alxjrvs commented May 16, 2026

Summary

Complete migration of gnar-term's terminal state engine from xterm.js to alacritty_terminal. Phase 1 (engine + IPC + renderer behind a feature flag), Phase 2 (parity bridges for search, selection/clipboard, links, input, theme/font), and Phase 3 (xterm.js removal + default flip) all shipped in this single branch.

After this PR merges, @xterm/* is gone from src/ and from package-lock.json. AlacrittyTerminalSurface.svelte is the sole terminal renderer. The terminalEngine flag is removed (no escape hatch — the alacritty path is the only path).

What landed (21 cycles, 3 orchestrator hot-fixes)

Phase 1 — engine + IPC + canvas-2d renderer (cycles 1–7):

  • terminal_engine Rust module: TerminalEngine trait, AlacrittyEngine wrapping Term<MyListener> + vte::Processor
  • GridSnapshot/GridDiff IPC types with serde round-trip + TS mirror; Tauri Channel<TerminalChannelMessage> dispatch
  • AlacrittyTerminalSurface.svelte with canvas-2d renderer (16-color palette, SGR attrs, block cursor)
  • Initial terminalEngine feature flag in gnar-term.json (default "xterm" during Phase 1)

Phase 1 remediation (cycle-8 + 2 hot-fixes):

  • Wired the previously-deferred PTY → bridge data pipe so the engine actually receives bytes
  • detach_alacritty_engine Tauri command + kill_pty safety-net cleanup
  • textBaseline survives canvas resize (HTML spec §4.12.5.1 fix); poisoned-mutex logging in pty reader thread

Smoke-note hand-offs (cycles 9–11):

  • 256-color cube + grayscale ramp + ATTR_ITALIC rendering
  • MyListener event surfacing (Bell/Title/PtyWrite/etc. drained and logged)
  • PtyBridge::viewport_dims cached as fields (no more per-feed full-grid snapshot)

Alacritty alignment audit + resolution (cycle-12):

  • PTY write-back routing: Event::PtyWrite and Event::ColorRequest now invoke the formatter against term.colors() and write the response — programs no longer hang on OSC color queries
  • Full xterm key encoding: F1–F12, Home/End/PageUp/PageDown/Insert/Delete, Ctrl-letter combos, Alt-prefix, modifier-encoded variants
  • CursorPos.shape (Block/Beam/Underline/HollowBlock/Hidden) via RenderableCursor::new — neovim insert-mode beam cursor renders correctly
  • Expanded attrs bitfield (DIM, HIDDEN, STRIKEOUT, WIDE_CHAR) + zerowidth combining marks preserved in Cell.ch

Final deferrals closed (cycles 13–15):

  • OSC 52 clipboard read/write routed through tauri-plugin-clipboard-manager via a ClipboardAccess trait (testable independent of Tauri)
  • ColorIndex::Named wire-format variant preserves Foreground/Background/Cursor/Dim*/BrightForeground semantics — themes overriding via OSC 10/11/12 actually reach the renderer
  • Snapshot generation switched to Grid::display_iter so display_offset is honored (scrollback no longer shows the wrong rows)

Phase 2 parity (cycles 16–20):

  • search-bridge.ts wires alacritty_terminal::regex_search_left/right to the search-result API (cycle-16)
  • mouse-handler.ts for selection + middle-click paste + OSC 52 read response (cycle-17)
  • link-overlay.ts + OSC 7 cwd tap per-pane (cycle-18)
  • paste-handler.ts bracketed paste mode + full key-corpus + scroll anchor (cycle-19)
  • theme-bridge.ts runtime theme + font hot-reload (cycle-20)

Phase 3 cutover (cycle-21):

  • @xterm/xterm, @xterm/addon-fit, @xterm/addon-search, @xterm/addon-webgl removed from package.json
  • src/types/xterm.d.ts and src/lib/components/TerminalSurface.svelte deleted
  • terminal-service.ts xterm code paths removed (flushPtyBuffer HWM backpressure, clearTextureAtlas hooks, xterm-style linkHandler, xterm theme mapping) — ~660 lines deleted
  • FindBar.svelte rewired to alacritty's search bridge
  • PaneView.svelte renders AlacrittyTerminalSurface unconditionally; terminalEngine field + getter deleted
  • cutover.test.ts integration test asserts no xterm imports remain and the cutover surface is wired
  • e2e-smoke-notes.md expanded with cutover QA plan
  • package-lock.json scrubbed of the four @xterm/* transitive entries

Stats

  • 28 cycles' worth of commits (21 planned + 1 Phase-1 remediation + 3 orchestrator hot-fixes + 3 smoke-note hand-offs already counted in the 21)
  • ~3,900 insertions / ~660 deletions in src/ net of xterm removal
  • 83 Rust tests passing (cargo) + 2,412 frontend tests passing (vitest, 7 skipped)
  • cargo clippy -- -D warnings clean
  • All pre-commit hooks pass (rust-fmt, prettier, eslint, typecheck, svelte-check, rust-clippy, rust-check) and the pre-push npm test hook passes

Verification sweep

$ rg -n "@xterm/|searchAddon|FitAddon|WebglAddon|xterm\.d\.ts" src/
(zero hits)

$ npm ls @xterm/addon-fit @xterm/addon-search @xterm/addon-webgl @xterm/xterm
gnar-term@0.5.0
└── (empty)

Test plan

  • Pull this branch locally
  • npm install — verify package-lock has no @xterm/* entries
  • npm test — verify 2412 vitest tests pass (7 skipped expected)
  • cargo test --manifest-path src-tauri/Cargo.toml --package gnar-term --lib terminal_engine — 83 tests pass
  • cargo clippy --manifest-path src-tauri/Cargo.toml --lib --tests -- -D warnings — clean
  • npm run build — full Tauri build succeeds
  • macOS smoke — launch the built app:
    • Open a new pane; type a command, see output rendered through the canvas
    • ls --color=auto renders 16-color ANSI + extended palette correctly
    • git log --color=always | head -50 paginates and colors correctly
    • Resize the pane (drag divider): grid reflows, glyph alignment stays correct, cursor lands in the right cell
    • Open nvim (or any program that queries OSC 10/11 colors): app does NOT hang
    • Highlight text → middle-click pastes via OSC 52
    • Open a URL printed in the terminal by clicking it (link overlay)
    • cd somewhere → check the pane title reflects the OSC 7 cwd
    • Ctrl+F opens FindBar → search returns highlighted matches → next/prev cycles through them
    • Change theme in settings → terminal recolors live without a relaunch
    • PageUp scrolls back; PageDown scrolls forward; cursor tracks correctly through wraps
  • Linux smoke (WebKitGTK) — same checklist as macOS where applicable
  • MCP read_output — invoke against an alacritty pane: returns recent terminal text (whatever the bridge plumbing routed it to per cycle-21 prose)

Files of interest

  • src-tauri/src/terminal_engine/ — the entire engine module
  • src-tauri/src/terminal_engine/pty_bridge.rs — bridge between the PTY reader thread and the engine, plus the clipboard/PTY-write event routing
  • src/lib/components/AlacrittyTerminalSurface.svelte — single terminal renderer
  • src/lib/components/alacritty/ — five Phase-2 parity bridges
  • src/lib/components/cutover.test.ts — proves no xterm imports remain
  • docs/implement/2026-05-16-alacritty-terminal-engine/ — full audit trail (intent.md, plan.md, review.md, ship.md, journal.jsonl, per-cycle prose, e2e-smoke-notes.md) — kept locally per project policy (docs/implement/ is gitignored)

🤖 Generated with Claude Code

alxjrvs and others added 24 commits May 16, 2026 10:39
Captures the alacritty_terminal vs libghostty-vt analysis that motivates
this branch's xterm.js → alacritty_terminal conversion. Covers API surface,
feature matrix, maturity/build axes, gnar-term-specific migration impact,
and the TerminalEngine trait escape hatch for a future libghostty-vt swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cycle-1)

Phase 1 MVP cycle-1 — Foundation. Adds alacritty_terminal 0.26 as a
direct Rust dependency, creates the src-tauri/src/terminal_engine/
module skeleton, wires it into lib.rs. The module currently holds only
an extern-crate-form unit test that asserts the dep resolves; cycle-2
will populate it with the TerminalEngine trait and AlacrittyEngine
impl per plan.md.

AC-1: alacritty_terminal is a direct dependency, mac + linux build.

Plan + ADRs: docs/implement/2026-05-16-alacritty-terminal-engine/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ycle-2)

Implements AC-2. Adds the TerminalEngine trait, AlacrittyEngine concrete
impl wrapping alacritty_terminal::Term + vte::ansi::Processor, Phase 1 grid
types in types.rs (cycle-3 will add serde + TS mirror). Four unit tests
exercise feed/damage/resize/snapshot/cursor.

Per ADR-0001 (trait shape) and ADR-0002 (wire format) in
docs/implement/2026-05-16-alacritty-terminal-engine/scaffold/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cycle-2 of 2026-05-16-alacritty-terminal-engine: TerminalEngine trait
+ AlacrittyEngine impl, types, 5 unit tests. SHA a0667ed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…trip (cycle-3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ycle-5)

Adds a pure canvas-2d rendering layer for the Alacritty terminal engine:

- alacritty-renderer.ts: Renderer class with paintSnapshot/paintDiff/paintCursor,
  16-color ANSI palette, SGR bold/underline/inverse, dirty-rect-only repaints.
- alacritty-renderer.test.ts: 16 vitest tests (MockContext proxy records canvas
  operations; asserts call sequences for snapshot, diff, cursor, palette, attrs).
- AlacrittyTerminalSurface.svelte: canvas mount, Tauri Channel subscription,
  Phase-1 key encoding (ASCII, Enter, Backspace, Tab, arrows), detach on destroy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ChannelMessage> (cycle-4)

- Add PtyBridge (pty_bridge.rs): owns AlacrittyEngine per pane, emits
  TerminalChannelMessage::Snapshot on attach and ::Diff on feed.
- Add MessageSink trait for testability; TauriChannelSink is the
  production impl, TestSink (Vec-backed) is used in unit tests.
- Add alacritty_commands.rs: attach_alacritty_engine, feed_alacritty_engine,
  resize_alacritty_engine Tauri commands registered in invoke_handler\!.
- Extend AppState.bridges: HashMap<u32, PtyBridge> — xterm.js path untouched.
- Ordering invariant: Snapshot always precedes any Diff (structural guarantee).
- Resize emits Snapshot (not Diff) because alacritty_terminal reflows entire
  grid on resize.
- Channel drop errors swallowed silently with inline comment explaining rationale.
- Fix pre-existing clippy doc_markdown warnings in alacritty_tests.rs and
  ipc_tests.rs from cycle-3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… (cycle-6)

- Add terminalEngine?: "xterm" | "alacritty" to GnarTermConfig (optional;
  absent defaults to "xterm", preserving existing behavior)
- Export getTerminalEngine() getter matching getMcpSetting() idiom; guards
  defensively so invalid config values fall back to "xterm"
- PaneView.svelte reads $configStore.terminalEngine reactively and mounts
  AlacrittyTerminalSurface when "alacritty", TerminalSurface otherwise;
  xterm branch is byte-identical to prior call site
- 10 tests in terminal-engine-flag.test.ts covering default, explicit values,
  invalid fallback, session stability, and pane dispatch contract

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…otes (cycle-7)

Add e2e_tests.rs with a single integration test that exercises the full
pipeline from cycles 2-4: AlacrittyEngine feed→damage→cursor (cycle-2),
GridDiff construction + serde round-trip (cycle-3), TerminalChannelMessage
JSON shape verification (cycle-4). 20 Rust tests pass; 2174 vitest tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… for clippy

Resolves a clippy::doc-markdown warning surfaced under `-D warnings`
that was not caught in the cycle-7 worktree's local clippy run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xes (cycle-8)

- F-001: add detach_alacritty_engine Tauri command + kill_pty safety net
- F-002: wire PTY reader thread to bridge feed (Arc<Mutex> bridges, xterm first)
- F-003: set textBaseline=top in Renderer constructor and AlacrittyTerminalSurface
- F-004: replace color(display-p3) fallback with #000000 for WebKitGTK compat
- F-005: replace cfg(debug_assertions) eprintln with log::debug in pty_bridge
- F-006: add log::warn for get_size fallback in attach_alacritty_engine
- F-007: console.error + remove stale cycle-4 comment + resize-mismatch message
- F-008: extract encodeKey to alacritty-key-encoder.ts with 20 unit tests
- F-009: add selectTerminalComponent + pane-view-dispatch.test.ts (5 tests)
- F-010: fix void cursor-ordering test — real cursorIdx > textIdx assertion
- F-011: return after resize-mismatch warn; re-trigger snapshot via resize command
- F-012: PaneView uses getTerminalEngine() normalizer instead of raw configStore
- F-013: ResizeObserver wires canvas resize to resize_alacritty_engine invoke
- F-018: Rgb wire-contract test locks array serialisation format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ned mutex (re-review)

Address NF-001 from the cycle-8 re-review: assigning canvasEl.width/height
resets the 2D context state including textBaseline. The snapshot handler
constructed the Renderer before the resize, so its constructor's
textBaseline="top" was wiped before paintSnapshot. The ResizeObserver
path never restored baseline after the resize at all. Both produced
glyph misalignment that F-003 was meant to prevent.

Snapshot handler: assign canvas dimensions first, then construct the
Renderer so its constructor's ctx setup is the last write. ResizeObserver:
re-apply textBaseline + font directly after dimension assignment.

Also address F-002 re-review NEEDS-WORK: replace the silent
`if let Ok(mut bridges) = bridges_arc.lock()` with a match arm that
logs poisoned-mutex errors at error level — without it, a panic in
another thread would silently kill all future bridge feeds with no
diagnostic evidence.

Bumps write_pty keystroke-loss console.warn to console.error for
consistency with the other failure paths in the component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…(cycle-9)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the silent-discard send_event with an Arc<Mutex<VecDeque>> queue.
Add AlacrittyEngine::drain_events and wire it into PtyBridge::feed_and_emit
with debug logging. PtyWrite routing to the PTY writer is deferred (TODO).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…feed snapshot (cycle-11)

Add cols/rows fields to PtyBridge, set at construction and updated in
resize_and_emit. feed_and_emit now reads cached u16 values instead of
calling engine.snapshot() (O(rows x cols) cell iteration + allocation)
on every PTY byte-feed. Delete the viewport_dims helper. Add invariant
test: cached_viewport_dims_survive_resize_and_match_diff_payload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@alxjrvs
Copy link
Copy Markdown
Contributor Author

alxjrvs commented May 16, 2026

Phase 2 hand-off wave landed (wave 7) — three additional cycles addressing the deferred items from e2e-smoke-notes.md:

  • cycle-9 (5a1c47d): 256-color palette cube + ATTR_ITALIC SGR rendering — alacritty-renderer.ts now resolves the full xterm 256-color space (6×6×6 cube + 24-step grayscale) and renders italic glyphs (incl. combined bold+italic). +12 vitest tests.
  • cycle-10 (759308c): MyListener event surfacing — events from alacritty_terminal::Term are now drained per byte-feed and logged. Adapted from PtyWrite-only to the actual emit shape (alacritty uses Event::ColorRequest for OSC color queries). Full PTY write-back routing remains a follow-up (TODO(pty-write-routing) in pty_bridge.rs).
  • cycle-11 (5bc772e): cache viewport dims as PtyBridge fields — removes the per-feed full-grid snapshot just to read 2 u16s. +1 regression test.

Updated stats: 26 cargo + 2209 vitest tests passing; clippy -D warnings clean; all pre-commit/pre-push hooks green.

The test plan above is unchanged — the additions are behind the same feature flag (terminalEngine: "alacritty"), and the xterm.js default path remains byte-identical.

alxjrvs and others added 3 commits May 16, 2026 16:37
…lacritty alignment (cycle-12)

- PtyBridge: add pty_writer field; route PtyWrite and ColorRequest events
  back to the PTY so programs don't hang on OSC color queries
- alacritty-key-encoder: Phase-2 coverage (F1-F12, Home/End/PageUp/PageDown,
  Insert/Delete, Ctrl+letter, Alt-prefix, modifier-encoded sequences)
- cursor_position: use Term::renderable_content().cursor (RenderableCursor);
  add CursorShapeTag enum and shape field to CursorPos; renderer dispatches
  on beam/underline/hollow_block/hidden
- Attr coverage: ATTR_DIM/HIDDEN/STRIKEOUT/WIDE_CHAR constants + flag mapping;
  zerowidth combining marks preserved; renderer handles dim/hidden/strikeout

AC-3: pty_write_event_routed_to_writer
AC-4: cursor_shape_beam_renders_thin_vertical_bar
AC-6: color_request_response_shape_contains_osc_prefix
Tests: 34 Rust, 2248 TypeScript, 0 failing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… after cycle-12

cycle-12 wired the PTY write-back path; this test's doc comment still pointed
at the resolved TODO. Tightens the comment to reflect the layered split:
the test asserts buffering (cycle-10), routing is asserted in pty_bridge_tests
(cycle-12).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alxjrvs
Copy link
Copy Markdown
Contributor Author

alxjrvs commented May 16, 2026

TODO-resolution + alacritty-alignment wave landed (wave 8 — cycle-12):

  • PtyWrite routing wired. PtyBridge now owns a pty_writer (independent take_writer() clone at attach_alacritty_engine time). On Event::PtyWrite, response bytes are written + flushed to the PTY. On Event::ColorRequest(index, formatter), the engine's color slot is looked up and the formatter is evaluated to produce the OSC response. Programs that issue OSC 4/10/11/12 color queries (nvim --termguicolors detection, bat, ls --color=auto) no longer hang.
  • Full key encoding. alacritty-key-encoder.ts now covers F1-F12 (DEC PF1-PF4 + xterm ~-terminated), Home/End/PageUp/PageDown/Insert/Delete, Ctrl-letter combos (Ctrl+A → \x01, etc.), Alt-prefix for printables, and modifier-encoded variants (\x1b[1;5A for Ctrl+Up etc.).
  • Cursor shape preserved. CursorPos carries a shape: { kind: "block" | "beam" | "underline" | "hollow_block" | "hidden" } field sourced from RenderableCursor::new(&term). Renderer branches on shape — neovim's insert-mode beam cursor now renders correctly.
  • Attribute coverage expanded. Added ATTR_DIM, ATTR_HIDDEN, ATTR_STRIKEOUT, ATTR_WIDE_CHAR to the bitfield and the renderer. DIM darkens fg, HIDDEN renders fg=bg, STRIKEOUT draws a midline rect, WIDE_CHAR spaces correctly across two cells.
  • Combining marks no longer dropped. map_row now appends acell.zerowidth() characters to Cell.ch, so accented letters and emoji ZWJ sequences round-trip intact.

Stats: 34 cargo + 2248 vitest tests passing (up from 26 + 2209), all 7 pre-commit hooks green, clippy -D warnings clean.

Intentionally deferred (out-of-scope per intent.md): OSC 52 clipboard plumbing (TODO(clipboard-osc52)), Color::Named wire-format change (would require coordinated TS/Rust schema bump), display_iter for scrollback (Phase 2 has its own scrollback design).

The alignment audit from earlier (F-1 through F-9) is documented in cycles/cycle-12.md alongside the rationale for which findings landed here vs. which remain for the migration's full Phase 2 run.

…aware snapshots (cycle-15)

Fixes audit F-5: snapshot() and damage() now correctly reflect the visible
viewport when display_offset > 0 (user scrolled up into scrollback history).

- snapshot(): replaced Line(0)..Line(rows) direct indexing with Grid::display_iter(),
  which yields cells adjusted for the current display_offset.  Viewport row is
  computed as: point.line.0 + display_offset.
- damage() Partial arm: LineDamageBounds.line from TermDamageIterator is already
  viewport-relative (iterator adds display_offset per upstream source line 208).
  map_row() call now converts back to grid-relative via Line(b.line - display_offset).
- damage() Full arm: viewport rows now mapped with display_offset correction.
- AlacrittyEngine::scroll_up_by(lines): new pub method proxying
  grid_mut().scroll_display(Scroll::Delta(lines as i32)) for test driving.
- Two new tests: snapshot_respects_display_offset_when_scrolled,
  snapshot_viewport_dimensions_invariant_under_scroll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
alxjrvs and others added 18 commits May 16, 2026 16:57
…ipboard (cycle-13)

- Add ClipboardAccess trait (write_text/read_text) to decouple bridge from Tauri
- Add AppHandleClipboard production wrapper in alacritty_commands.rs
- Add clipboard field to PtyBridge; route ClipboardStore/Load events in feed_and_emit
- Set Osc52::CopyPaste in AlacrittyEngine so load queries are not silently dropped
- Add TestClipboard + 3 tests: store routing, load→PTY response, graceful no-access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…palette resolution (cycle-14)

Resolves audit F-1: `map_color` previously collapsed all `Color::Named`
variants to `Indexed(u8)`, erasing semantic identity (Foreground \!= White,
Background \!= Black, Cursor lost entirely, Dim variants wrong).

- Add `NamedSlot` enum (13 variants) covering the semantic `NamedColor`
  range (indices 256-268: Foreground, Background, Cursor, DimBlack..
  DimWhite, BrightForeground, DimForeground).
- Extend `ColorIndex` with `Named(NamedSlot)` third variant; serde shape
  becomes `{ kind: "Named", value: "foreground" }` on the wire.
- Update `map_color`: palette range (Black=0..BrightWhite=15) stays
  `Indexed`; semantic range emits `Named(NamedSlot)`.
- Mirror in `terminal-ipc.ts`: add `NamedSlot` union + `Named` variant.
- Extend `Renderer` with optional `palette` option; `resolveColor` accepts
  a palette for named-slot lookup, falling back to `DEFAULT_NAMED_PALETTE`.
- Verified: cycle-12 `ColorRequest` handler already handles indices 256-268
  correctly via `colors[index]`; no change to `pty_bridge.rs` needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds theme-bridge.ts and cell-metrics.ts under src/lib/components/alacritty/.
theme-bridge subscribes to the Svelte theme store, maps ThemeDef to the
renderer's Palette shape, and notifies the renderer on every theme change.
cell-metrics measures monospace glyph dimensions via off-screen canvas and
re-fires on font-size store changes for PtyBridge resize plumbing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- osc7.rs: parse_osc7() stateless parser (file:// URL decode, host strip)
- osc7.rs: Osc7Emitter callback wrapper + scan() raw-byte scanner for PtyBridge integration
- link-extractor.ts: extractLinks(GridSnapshot) → LinkMatch[] (URL + file path regexes)
- link-overlay.ts: attachLinkOverlay(canvas, opts) → LinkOverlayHandle; debounced hover + modifier-click dispatch via open_url/open_with_default_app Tauri commands
- mod.rs: register pub mod osc7 (minimal required change; cycle-21 owns full mod.rs wiring)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…scroll anchor (cycle-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add SelectionState, SelectionMode, IpcSelectionRange, and
  SelectionTextProvider trait in selection.rs; 8 Rust tests covering
  cell/semantic/lines selection, copy-on-selection-change flag,
  shift-click extend, serde round-trip, and mode mapping.
- Add mouse-handler.ts with attachMouse(): pixel→cell translation,
  single/double/triple click mode dispatch, shift-click extend,
  and outside-canvas clear; 10 TS tests including mouse_drag_selection.
- Add osc52-read.ts with handleClipboardRead() and encodeOsc52Response();
  8 TS tests including osc52_read_round_trip_through_clipboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ge (cycle-16)

- Add search.rs: SearchQuery/SearchMatch types + find_next/find_prev
  wrapping Term::regex_search_right/left with literal-escape, case-
  sensitive (?-i/?i), and ASCII whole-word (?-u:\b) support
- Add search_find_next/prev/clear Tauri command stubs (registration
  deferred to cycle-21)
- Add search-bridge.ts: attachSearch(paneId) -> SearchHandle with
  findNext/findPrev/clear methods invoking the Tauri command surface
- 10 Rust tests (all regex_search-named) + 8 vitest tests; 59 Rust /
  2266 TS passing

AC-1: regex_search_finds_case_sensitive_literal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… default (cycle-21)

Delete TerminalSurface.svelte and xterm.d.ts; update all test fixtures
and mocks to reflect the post-cutover TerminalSurface shape (no terminal,
fitAddon, searchAddon, termElement fields). Sweep verified zero hits for
@xterm/|searchAddon|FitAddon|WebglAddon|xterm.d.ts across src/. Rust
clippy doc_markdown warnings fixed in search.rs, osc7_tests.rs, and
selection_tests.rs. 214 test files passing, 2412 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cycle-21 removed @xterm/* from package.json but didn't regenerate
package-lock.json; running npm install dropped the four orphaned
@xterm transitive nodes (addon-fit, addon-search, addon-webgl, xterm
itself). npm ls is now empty for those names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alxjrvs alxjrvs changed the title feat(terminal-engine): Phase 1 alacritty engine behind terminalEngine flag feat(terminal-engine): full xterm.js → alacritty cutover (Phase 1+2+3) May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant