Commit Graph

24 Commits

Author SHA1 Message Date
zhangheng
fb1a6d5fe7 fix(terminal): retain webgl profiling samples 2026-06-24 18:17:37 +08:00
zhangheng
a039f7fd5f test(terminal): profile webgl render stages 2026-06-24 17:46:54 +08:00
zhangheng
7593561761 feat(terminal): default renderer to canvas 2026-06-24 17:23:13 +08:00
zhangheng
a145a19636 test(terminal): keep renderer bench reports separate 2026-06-24 16:00:32 +08:00
zhangheng
1334685fac feat(terminal): add runtime renderer switch 2026-06-24 15:31:58 +08:00
zhangheng
7ee8d7bde1 test(terminal): fix bench command output wait 2026-06-24 14:54:23 +08:00
zhangheng
a10c638e60 test(terminal): support local browser bench runs 2026-06-24 14:48:32 +08:00
zhangheng
21019d82e0 test(terminal): add renderer bench smoke script 2026-06-24 14:13:09 +08:00
zhangheng
f4c919de40 feat(terminal): recover webgl renderer context 2026-06-24 13:47:31 +08:00
zhangheng
e66dcb6f80 refactor(terminal): split renderer module 2026-06-24 12:20:33 +08:00
zhangheng
05bd106f06 fix(terminal): clean up browser callbacks on unmount 2026-06-24 11:21:06 +08:00
zhangheng
f68ee93c6d docs(terminal): update roadmap after renderer and addon work 2026-06-23 16:49:45 +08:00
zhangheng
69a670939d feat(terminal): add WebGL renderer and addon APIs 2026-06-23 16:48:53 +08:00
zhangheng
f76d341501 feat(terminal): Chinese / IME input (P3)
Implement IME composition support and migrate regular character input to
a hidden textarea, matching the xterm.js approach.

Key changes:
- Add a hidden, absolutely-positioned `<textarea>` inside `.terminal-screen`
  that follows the terminal cursor cell. It receives focus on click, connect,
  and resize so the OS/browser IME candidate window appears at the cursor.
- `on:input` now sends regular printable characters; `on:keydown` only handles
  special keys (arrows, Enter, Esc, Backspace, F-keys, Ctrl combos).
- `on:compositionstart/end` track IME sessions and send the final composed
  string at the end, avoiding partial pinyin/jamo/kana leaks.
- Move `on:paste` and `on:keydown` from `.terminal-screen` to the textarea so
  focus stays on the IME target.
- Replace `.terminal-screen:focus` with `:focus-within` since the screen div
  no longer holds focus.
- Add `is_regular_text_input` helper and unit test.

Web-sys features extended with `InputEvent` and `CompositionEvent`.

Tests: 24/24 pass, clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:09:45 +08:00
zhangheng
843e4e55ab docs(terminal): add performance goal and P4 Canvas/WebGL renderer to roadmap
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 15:37:34 +08:00
zhangheng
6f033253c4 docs(terminal): update coverage assessment after P2 mouse reporting
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 15:09:44 +08:00
zhangheng
b9cc578d28 feat(terminal): mouse reporting (P2)
Implement xterm-compatible mouse reporting so vim, htop, btm, tmux, etc.
can receive click / drag / wheel events.

Core:
- Expose `mouse_protocol_mode()` and `mouse_protocol_encoding()` on
  `TerminalCore`, re-exporting the vt100 enums.

Component:
- Add `MouseAction`, `mouse_button_base`, `with_mouse_modifiers`,
  `encode_mouse_report`, and `pixel_to_grid` pure helpers.
- Support SGR (1006), Default, and UTF-8 encodings.  SGR is fully correct
  and ASCII-only; Default/UTF-8 are correct for the common ≤94 col/row
  range.
- Wire `on:mousedown` / `on:mouseup` / `on:mousemove` to report mouse
  events when the app enabled mouse reporting; otherwise fall back to
  local text selection.
- Shift+click bypasses mouse reporting and forces local selection (xterm
  behaviour).
- Throttle motion reports to one per grid cell.
- Report vertical/horizontal wheel ticks as buttons 64/65/66/67 when
  mouse reporting is active, otherwise keep the existing alt-screen
  arrow-key paging.

Tests: 23/23 pass, clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:57:36 +08:00
zhangheng
c3ddd9df4f fix(terminal): robust Ctrl/paste handling + alt-screen wheel coalescing
Keyboard input fixes addressing reported regressions:

- Pressing Ctrl alone no longer sends SIGINT: key_to_bytes now guards on
  a single-character key, so modifier keys "Control"/"Shift"/"Meta" are
  not misread as Ctrl-C (0x03).
- Ctrl-Shift-C copy restored: the copy branch runs before the generic
  Ctrl+letter suppression so the clipboard user-gesture stays intact.
- Ctrl+C copies when a selection exists (VS Code / Windows Terminal
  behaviour); without a selection it still sends SIGINT.
- Ctrl+V / Ctrl-Shift+V paste via navigator.clipboard.readText() —
  reliable across browsers that don't fire paste on a non-contenteditable
  div; on:paste retained for right-click / menu paste.
- Bare Ctrl+letter (no Shift/Alt/Meta) is preventDefault'd so the browser
  stops intercepting Ctrl-R/F/A etc. (Ctrl-W/T/N stay browser-reserved
  and cannot be intercepted by any web page).
- Filter Ctrl-Z (0x1a) / Ctrl-\ (0x1c) so stty SIGTSTP/SIGQUIT don't
  surprise-kill the foreground process; Ctrl-C (0x03) is kept.

Alt-screen wheel:

- Coalesce multiple wheel events per animation frame into a single
  arrow-key batch (Rc<Cell> accumulator + requestAnimationFrame), so one
  trackpad swipe pages at most one viewport instead of flying past.

Tests: 18/18 pass, clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:32:46 +08:00
zhangheng
511b212334 docs(terminal): mark P1 done in roadmap + document stty control bytes
Ctrl-C/Ctrl-Z/Ctrl-\ reach the PTY as 0x03/0x1a/0x1c and are then
interpreted by bash's stty (SIGINT/SIGTSTP/SIGQUIT), which is real-terminal
behavior and preserved on purpose per user confirmation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 20:38:53 +08:00
zhangheng
ef4396247c feat(terminal): full function keys + application-cursor mode (P1)
Fills out keyboard input per TERMINAL_ROADMAP.md P1: F1–F12,
Home/End, Insert/Delete, PageUp/PageDown, the full Ctrl-A..Z range,
and Alt+key. Arrows / Home / End now respect DECCKM (application-cursor
mode) — emitted as SS3 (ESC O X) when the app enables it, else CSI (ESC [
X). The alt-screen wheel-paging handler uses the same logic so it
matches the app's expectations.

core.rs:
- application_cursor() proxy for vt100's Screen::application_cursor().

component.rs:
- cursor_seq(final_byte, app_cursor) helper: SS3 vs CSI prefix.
- key_to_bytes(key, ctrl, alt, meta, app_cursor) — pure table for the
  whole key map, unit-testable without constructing a KeyboardEvent.
  F1–F4 SS3, F5–F12 CSI~ (with the real xterm 16/22 gaps), paging keys
  always CSI~, Ctrl A..Z → 0x01..0x1a (plus @[\\]^_ variants), Alt+char
  → ESC prefix. map_key_to_terminal_input becomes a thin wrapper.
- handle_keydown reads app_cursor from core_signal before mapping.
- handle_wheel reads app_cursor and uses cursor_seq for arrow paging.

Adds key_to_bytes_table unit test (17 terminal tests total). Clean clippy.

Note: 0x03/0x1a/0x1c (Ctrl-C / Ctrl-Z / Ctrl-\) reach the PTY unchanged
and are interpreted by the shell's stty as SIGINT / SIGTSTP / SIGQUIT —
this is real-terminal behavior (same as xterm / SSH into a real box),
not a bug, and is preserved on purpose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 20:37:58 +08:00
zhangheng
8863977c0d feat(terminal): text selection + copy (custom model)
Drag to select terminal text and copy it with Ctrl-Shift-C. Built as a
custom selection model (xterm.js-style) rather than native selection,
because the virtual scroll only keeps ~60 rows in the DOM and rebuilds
them every frame — native selection would be destroyed and couldn't span
scrolled-out rows.

core.rs:
- selection_text(start, end) extracts a (row,col)→(row,col) range straight
  from history_rows + screen_cache, so copy is correct across the whole
  buffer regardless of what is currently rendered
- TerminalRow::text_in_cols slices a row by grid columns (using the
  segment `cols` widths), trimming trailing whitespace

component.rs:
- Selection { anchor, head } in logical buffer coords; mousedown/move/up
  drag updates it; point_to_cell maps pixels→cell (padding + scrollTop)
- selection_rects computes per-row highlight rects clipped to the rendered
  window; rendered as an overlay inside the translated rows layer so the
  highlight follows scrolling without splitting text segments
- cell_size signal kept in sync with viewport measurement
- Ctrl-Shift-C copies via navigator.clipboard.write_text; Ctrl-C is left
  untouched (still SIGINT); typing clears the selection

style: .terminal-selection overlay, user-select:none on the grid, rows
above the highlight via z-index.

web-sys: add DomRect, MouseEvent, Navigator, Clipboard.

Adds point_to_cell / selection_rects / selection_text unit tests (16
terminal tests total). Marks P0 done in TERMINAL_ROADMAP.md.

Known follow-up: no auto-scroll when dragging past the viewport edge
(scroll first, then select); wide+combining column slicing is approximate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:24:32 +08:00
zhangheng
c886befac7 feat(terminal): clipboard paste with bracketed-paste support
Pasting (Ctrl-V or browser menu) now sends clipboard text to the PTY.

- on:paste reads clipboard_data().get_data("text"); newlines normalized
  to CR (the line-submit byte) via the pure, unit-tested prepare_paste()
- when the app enabled bracketed paste (DECSET 2004, exposed via the new
  TerminalCore::bracketed_paste() proxy), the text is wrapped in
  ESC[200~ / ESC[201~ so vim et al. don't auto-indent each pasted line
- pasting returns the view to live mode + scrolls to bottom, matching the
  keydown handler
- Cargo.toml: add ClipboardEvent/DataTransfer (and explicit WheelEvent)
  to the web-sys feature allowlist
- right-click is left to the browser's default menu (avoids requiring
  clipboard-read permission); Ctrl-V is the primary path

Adds a prepare_paste unit test (14 terminal tests total) and marks paste
done in TERMINAL_ROADMAP.md. Also fixes a clippy manual_contains lint in
an existing test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:59:03 +08:00
zhangheng
a018609a28 refactor(terminal): explicit history buffer + virtual scrolling
Replace the spacer-estimate scrollback model with an explicit history
row buffer owned by TerminalCore, rendered with absolute-positioned
virtual scrolling so huge outputs (cat of 50k-line files) stay smooth.

core.rs:
- TerminalCore owns history_rows + a cached screen, captured after each
  process() via a bounded "conveyor belt" read of vt100 scrollback
- feed bytes in newline-bounded safe slices so a capture never reads
  deeper than viewport_rows (works around vt100 0.15 visible_rows
  subtract-overflow panic)
- handle clear (ESC[3J) by wiping our own history buffer
- TerminalSegment carries grid column count for exact-width rendering
- collect_window() clones only the visible slice

component.rs:
- TerminalCore lives in an RwSignal; render driven by an rAF-coalesced
  render_tick so packet bursts collapse into one frame
- virtual_window pins to the bottom in live mode, follows scroll_top in
  history mode; absolute-positioned rows in a fixed-height sizer keep
  scroll geometry constant (fixes the "keeps drifting up" bounce)
- position-based programmatic-scroll detection replaces the racy
  ignore-next-scroll flag
- alt-screen wheel events translate to arrow keys (vim/less paging)
- try_* signal access guards against disposal during hydration

style: lock row height to 18px, segment width to Nch (exact grid so
fallback-font glyphs like btm braille can't push the row sideways),
absolute-position the virtual rows layer.

Adds 12 unit tests (capture, clear, alt-screen, window slicing, wheel
mapping) and TERMINAL_ROADMAP.md tracking xterm.js parity work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 19:51:06 +08:00
zhangheng
fd056ce502 feat: add rust browser terminal prototype 2026-06-08 16:34:13 +08:00