Commit Graph

1135 Commits

Author SHA1 Message Date
Kit Langton aef16edbac fix(core): preserve keyboard layouts in embedded terminals (#1477)
## Why

In OpenCode's embedded terminal, Dvorak Ctrl+U and Ctrl+D can reach
Neovim as Ctrl+F and Ctrl+H when the child uses Kitty keyboard encoding.
Paging up instead pages down, and paging down moves the cursor left.

The incoming Kitty event correctly separates the active-layout character
from its base-layout alternative. `EmbeddedTerminalRenderable` currently
passes the alternative as the native encoder's unshifted character,
changing the key's identity.

## What Changes

Keep the active-layout character as the primary key, and use the
base-layout alternative only to infer the physical key when an explicit
physical code is unavailable.

| Incoming Dvorak input | Before | After |
| --- | --- | --- |
| Plain u, base-layout F | u (117), byte `75` hex | Unchanged |
| Ctrl+U, base-layout F | Ctrl+F | Ctrl+U |
| Ctrl+D, base-layout H | Ctrl+H | Ctrl+D |
| Ctrl+Shift+U, base-layout F | Ctrl+Shift+F | Ctrl+Shift+U |

If the child requests alternate-key reporting, the original base-layout
alternative remains available separately. Legacy control-byte output
stays unchanged.

This explains why ordinary typing can work while control shortcuts fail:
in the reproduced Kitty mode, plain letters use the translated text,
while control shortcuts use the incorrectly selected key codepoint.
Ctrl+U becomes `ESC[102;5u` instead of `ESC[117;5u`; Ctrl+D becomes
`ESC[104;5u` instead of `ESC[100;5u`.

Regression coverage parses real Kitty sequences and exercises the native
encoder. One table covers plain Dvorak u/d, control shortcuts, Shift,
Cyrillic input, a QWERTY control case, legacy child mode, and
alternate-key reporting. A focused-delivery test covers
press/repeat/release events.

## Demo

Real Neovim 0.12.4 running in the existing `embedded-terminal-demo.ts`
example with the production `EmbeddedTerminalRenderable`. The inspector
shows:

1. The received key, active-layout and base-layout codepoints, and raw
input sequence.
2. The actual `onData` output forwarded unchanged to the PTY, in escaped
and hexadecimal form. The inspector does not re-encode the key.
3. Neovim's independently observed key via `vim.on_key`, plus its
resulting cursor position.

Each case starts at line 80, column 8, with `scroll=8`. Plain u is typed
in Insert mode; Ctrl+D/U are pressed in Normal mode. The clean Neovim
fixture explicitly enables Kitty mode with `CSI > 1 u`; Dvorak Kitty
input sequences are injected at the outer terminal boundary rather than
generated by an OS keyboard layout.

Before uses the unchanged `EmbeddedTerminal.ts` from `fe547ebe`, loaded
through a test-only module override. After uses `03b84c9a`. The three
independent cases are reordered to show plain typing first, slowed to
0.1×, and held for four seconds each; setup and resets are omitted. Both
processes returned cleanly after the check.

**Before:** Plain u stays 117. Ctrl+D changes 100 → 104 and Neovim sees
`<C-H>`; Ctrl+U changes 117 → 102 and Neovim sees `<C-F>`.


https://github.com/user-attachments/assets/6e1b469f-e4d2-4298-a095-8f422e10a8d0

**After:** Plain u still works. Ctrl+D stays 100 and Neovim sees
`<C-D>`; Ctrl+U stays 117 and Neovim sees `<C-U>`.


https://github.com/user-attachments/assets/b825e1f0-b7e6-4f1b-a112-e780a9f34fe8

## Scope

This fixes OpenTUI's embedded-terminal key adapter, without
application-specific remapping or a public API change. OpenCode will
need to adopt an OpenTUI release containing the fix.

## Verification

```sh
cd packages/core
bun run test src/renderables/EmbeddedTerminal.test.ts
bun run typecheck
bun run test:js:node
bun run build:lib
bun run test
cd ../..
bun run fmt:check
bun run lint
```

- Focused suite: seven regressions fail with the baseline encoder, while
plain typing passes; all 31 tests pass afterward. Rechecked after
consolidating the repeated test setup.
- Full Bun suite after building the parser assets: 5,587 passed, 26
skipped, zero failures. The initial unbuilt-worktree run failed only
because `parser.worker.js` was absent.
- Node suite: 4,842 passed, seven skipped, zero failures.
- Typechecking, formatting, and lint pass.
- Real PTY before/after verification shown above.
2026-09-03 20:37:56 -04:00
Kit Langton fe547ebed1 fix(core): realign split diffs after resizing (#1463) 2026-09-02 22:53:33 +02:00
Kit Langton fccce4ec13 fix(core): bound large diff rendering work (#1462)
---------

Co-authored-by: Sebastian <335157+kommander@users.noreply.github.com>
2026-09-02 22:21:24 +02:00
Sebastian 559ccad63f ci: check stacked pull requests (#1472) 2026-09-02 17:48:15 +02:00
OpenCode Agent runtime f6673a04cc Release v0.5.10 v0.5.10 2026-09-01 15:38:50 +00:00
Sebastian 09a0dd695d fix(native): ship stripped libraries with separate release symbols (#1437) 2026-08-31 22:38:28 +02:00
Simon Klee 24ace6a6e7 renderer: preserve native scrollback images (#1470)
Requiring a pinned footer forces scrollback images to fall back to
blocks during startup and after resize replay resets. Allow Kitty and
Sixel images before pinning when they fit above the pinned footer to
preserve image quality while the footer settles.
2026-08-31 22:02:43 +02:00
opencode-agent[bot] c45f25ed24 fix(core): preserve markdown during stream completion (#1469) 2026-08-31 16:19:21 +02:00
Sebastian ac8543c4d0 fix(core): retry the final automatic frame after backpressure (#1457) 2026-08-31 16:18:05 +02:00
Simon Klee c1a52d2114 renderer: reduce image streaming overhead (#1467)
Reduce frame preparation and transport costs so local terminals can
consume image updates with less CPU work. Bound pending frames when
output stalls and fix per-frame GPU resource growth so sustained
rendering does not accumulate work or retain native handles.

Keep raw transport as the default and make file transport opt-in for
terminals that acknowledge image uploads.

#1328 partially superseded, but it has `NativeImage.adoptRgbaFile(path,
width, height)` that adopts an existing producer-created RGBA file
lazily. Eligible renders send that same path without reading, copying,
or rewriting its pixels.
2026-08-30 22:21:36 +02:00
Sebastian 202e1e6a00 docs: improve OpenTUI skill guidance (#1461)
Document compact layouts and companion packages.

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-08-28 12:38:20 +02:00
OpenCode Agent runtime df2fc1594b Release v0.5.9 v0.5.9 2026-08-27 23:00:22 +00:00
Sebastian 20f669256d perf(native): reduce word layout work with eager view metadata (#1456) 2026-08-28 00:48:15 +02:00
Sebastian fbd584b28e fix(native): expose Yoga C API to Zig consumers (#1454) 2026-08-27 15:54:59 +02:00
Sebastian ab1a2aaf9b fix(core): preserve edit cursors across tab widths (#1449) 2026-08-27 15:54:09 +02:00
Sebastian 8d1dceb717 perf(core): skip tab refresh for tab-free roots (#1450) 2026-08-27 15:00:21 +02:00
Sebastian f49c935ccb fix(native): preserve mixed whitespace graphemes (#1447) 2026-08-27 14:33:41 +02:00
Sebastian bd823a02b8 fix(native): preserve truncated copy ranges (#1448) 2026-08-27 14:11:59 +02:00
James Long a57c2e40f4 fix(core): limit embedded terminal selection highlights to text (#1451) 2026-08-27 13:44:08 +02:00
Sebastian de0b5f2091 docs: document shared Zig cache for worktrees (#1452)
Document the opt-in shared Zig local cache workflow for repeated native
builds across worktrees.

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-08-27 09:08:33 +02:00
Simon Klee 7a0dca8d5e web: add rendered terminal examples to docs (#1446)
Show actual OpenTUI output beside documentation examples so readers
can see component behavior without running an application.
2026-08-26 21:04:05 +02:00
Simon Klee 33203b5029 native: prove styled text without JavaScript (#1419) 2026-08-26 17:29:03 +02:00
Simon Klee e1ad51afcd ssh: assert error code for occupied ports (#1444) 2026-08-26 16:41:47 +02:00
Simon Klee c1f2409947 ffi: preserve viewport lookup success result (#1443)
The native viewport lookup reports whether a view exists, but the FFI
binding discarded that result. Preserve the boolean so callers can
distinguish missing views from successful lookups.
2026-08-26 14:36:41 +02:00
m-pa 010c07df29 fix(core): align bordered hit-grid clipping (#1441)
## Summary
- align hit-grid scissor coordinates with bordered content bounds
- preserve correct clipping for buffered, nested, partial-border, and
scrollable containers
- add regression coverage for final-row and final-column hit targets

Fixes anomalyco/opentui#1231

## Verification
`bun test src/tests/scrollbox-hitgrid.test.ts`

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-08-26 11:04:09 +00:00
Simon Klee 352c1fc59b renderer: ignore invalid terminal dimensions (#1440)
Prevent resize signals with unavailable terminal dimensions from
resetting the renderer to an arbitrary 80x24 fallback.

Supersede #687
2026-08-26 10:50:26 +02:00
Sebastian 465c3ccad0 refactor(core): clarify text layout units (#1429) 2026-08-25 22:52:07 +02:00
Sebastian d2089a81ae refactor(core): make wrapped layout byte-accurate (#1428) 2026-08-25 22:33:47 +02:00
Sebastian 8046c846cf feat(native): export audio module to Zig consumers (#1431) 2026-08-25 16:05:01 +02:00
Simon Klee dd3dce90dd renderer: preserve wide grapheme updates (#1424)
Incremental updates could start output within a wide grapheme and corrupt
it. Begin updates at printable cells to preserve the grapheme.
2026-08-25 09:20:22 +02:00
Simon Klee da717b74a1 renderer: resolve snapshot links from snapshot pool (#1418) 2026-08-25 09:16:46 +02:00
Simon Klee 29243aa294 renderer: prevent backpressure revive (#1170)
A backpressured frame could schedule a retry after its callback paused
or
stopped the renderer, reviving a cancelled continuous loop.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-25 06:52:42 +02:00
James Long a7fb4ec78e feat(native): filter Ghostty logs (#1426)
## Summary
- recognize the selected Ghostty `std.log` scopes in OpenTUI's root
logger
- keep all standard-library logs silent by default
- read `OTUI_GHOSTTY_LOG_LEVEL` inside the native `logFn` and forward
enabled messages through the existing `logger.logMessage` path
- support `error`/`err`, `warn`/`warning`, `info`, and `debug`
thresholds
- document the environment variable

## Behavior
Unset or invalid `OTUI_GHOSTTY_LOG_LEVEL` values emit no `std.log`
messages. Recognized Ghostty scopes are forwarded up to the configured
severity. Logs from every other scope are discarded. Explicit OpenTUI
logger calls are unchanged.

## Testing
- release `bun run build` from `packages/core`
- `bun run test:native` from `packages/core` (2066 passed, 8 skipped)
- `zig fmt --check packages/native/src/lib.zig
packages/native/src/logger.zig`
- `oxfmt --check packages/web/src/content/docs/reference/env-vars.mdx`
2026-08-24 16:58:16 -04:00
James Long 189f1007f8 fix(native): silence standard library logs (#1425)
## Summary
- install a root `std_options.logFn` that discards `std.log` output
- leave Zig's default compile-time log level unchanged
- keep explicit OpenTUI `logger.logMessage` output flowing through the
existing JavaScript callback

## Why
OpenTUI imports native dependencies such as Ghostty into its Zig root
module. Their `std.log` calls otherwise use Zig's default stderr logger,
bypass the renderer, and can corrupt the terminal display. OpenTUI
already has a separate explicit logger for messages intended for
JavaScript and file logging, so standard-library logs should not write
directly to the host terminal.

## Testing
- `zig fmt --check packages/native/src/lib.zig`
- release `bun run build` from `packages/core`
- `bun run test:native` from `packages/core` (2066 passed, 8 skipped)
2026-08-24 22:23:04 +02:00
James Long 247199db66 fix(core): normalize embedded terminal keys (#1423)
## Summary
- reject parser escape codes such as `[A` and `[27u` as physical key
identifiers
- infer Web-style physical identities (`ArrowUp`, `KeyC`, `Digit1`,
`Escape`) before calling Ghostty
- derive the unshifted codepoint from that normalized physical identity
- cover raw arrow input and nested Kitty character/escape re-encoding

## Why this is needed
OpenTUI key events use `code` for two different kinds of values. Kitty
and raw escape parsing can produce parser codes such as `[A` for Up or
`[27u` for Kitty Escape, while other events provide Web-style physical
codes such as `KeyA`. Ghostty's key encoder accepts the latter physical
identities, not parser escape fragments. Passing `[A` through as the
physical key leaves Ghostty unable to encode an arrow key.

Embedded terminals also need to re-encode input when the program inside
them enables the Kitty keyboard protocol. In that mode, text alone is
insufficient for modified and disambiguated keys: Ghostty needs a
normalized physical identity and its unshifted codepoint. Raw character
events may not include `code`, so this change infers `KeyA`/`Digit1`;
Kitty escape events whose parser code is `[27u` fall back to `Escape`.
The regression verifies that Ctrl+C and Escape are re-emitted as Kitty
sequences for a nested terminal.

## Testing
- `bun test src/renderables/EmbeddedTerminal.test.ts` (16 passed)
- `oxlint packages/core/src/renderables/EmbeddedTerminal.ts
packages/core/src/renderables/EmbeddedTerminal.test.ts`
- `oxfmt --check packages/core/src/renderables/EmbeddedTerminal.ts
packages/core/src/renderables/EmbeddedTerminal.test.ts`
2026-08-24 13:27:33 -04:00
Simon Klee 21b0021742 Release v0.5.8 v0.5.8 2026-08-24 15:03:23 +02:00
Simon Klee bc94b6e540 ci: include hidden core dump files (#1422)
Upload dot-prefixed core dump files so crash diagnostics are not
silently omitted from CI artifacts.
2026-08-24 14:07:22 +02:00
Simon Klee 403b7637bb terminal: match Ghostty wide grapheme widths (#1415)
Ghostty 1.3 renders certain multi-code-point graphemes two columns wide.
Use its width profile for direct sessions so rendering, wrapping, and
cursor placement stay aligned with the terminal.

Fix #1401
2026-08-24 13:54:37 +02:00
Simon Klee 0b06560ed4 readme: clarify project overview (#1417) 2026-08-24 13:38:24 +02:00
Simon Klee 4d983f032b mouse: track buttons independently (#1416)
Keep left-button selection state until its own release so right-click
interactions do not interrupt dragging.
2026-08-24 09:03:34 +02:00
Simon Klee 6b3a9e46cb ci: save Linux core dumps from failed tests (#1414)
Linux test crashes left no data to investigate after the runner stopped.
The CI system now saves core dumps and program files to help find the
cause.
2026-08-23 13:23:45 +02:00
Simon Klee a6d993da72 utf8: count Thai spacing marks in widths (#1413)
Thai SARA AM occupies an additional cell within a grapheme. Count
positive-width spacing marks to keep output coordinates aligned with
terminals.

Fix #479
2026-08-23 09:52:44 +02:00
Simon Klee 2ba1c36cba ci: run all matrix tests after failures (#1412)
Preserve results from every supported platform when one matrix job
fails.

Trying to avoid it cancelling all jobs when one thing fails.
2026-08-23 09:02:33 +02:00
Simon Klee 5ec3f33e85 Release v0.5.7 v0.5.7 2026-08-23 08:35:31 +02:00
Wu Shuwen fba0ebe10a fix(core): correlate OSC 66 probe responses (#1384)
## Summary

- correlate cursor-position reports with the two emitted OSC 66 probes
- keep the startup cursor report out of capability detection
- require the exact expected columns instead of accepting larger echoed
payload widths

Terminals that echo an unsupported OSC 66 payload can leave the cursor
well past the expected probe column. Treating any row-1 report at or
beyond the expected column therefore enables capabilities the terminal
does not support. The probe state now consumes only the two ordered
responses emitted by `queryTerminalSend`, while an unrelated cursor
report cannot enable text scaling.

Closes #1383.

## Testing

- filtered Zig 0.16 native tests: 2 passed
- full Zig 0.16 native suite: 2,013 passed, 8 skipped
- native hello example
- `bun run build`
- `bun run fmt:check`
- `bun run lint`
- `zig fmt --check packages/native/src/terminal.zig
packages/native/src/tests/terminal_test.zig`

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-08-23 08:07:19 +02:00
Simon Klee a15a383c89 core: test u32 cell attributes on all platforms (#1411)
Node FFI paths can truncate cell attributes when native types differ
across
platforms. Exercise every per-cell entry point under Node on all
runners.
2026-08-23 07:52:20 +02:00
Simon Klee fa14b96ad4 node: support Node.js 26.4 and later (#1410)
Keep 26.4.0 as the minimum supported version while allowing later
releases.
2026-08-23 06:56:30 +02:00
Simon Klee e3937cf0dd core: fix portable boolean FFI calls (#1409)
Model native flags as numeric bytes so Bun and Node share one FFI
contract. Keep split-footer commits within Darwin ARM64's eight argument
registers to prevent stack marshalling from losing frame-finalization
state.
2026-08-23 06:56:08 +02:00
Simon Klee 03f4c14d2a core: add double/triple click support (#1407)
Fix https://github.com/anomalyco/opentui/issues/74 

Double-click selects a word. Triple-click selects the line. A first
click still selects nothing.

Renderer counts left presses. A second press on the same cell within 500
ms asks for a word. A third press asks for a line.

Drag after a word click keeps the spaces between words. Shift+arrow
after a multi-click extends by cell, not by word.

ASCII font and tables stay cell-only. The click interval and the
word-character set are hard-coded for now.
2026-08-23 06:42:00 +02:00
Simon Klee eaf1d41e92 web: add OpenTUI Mono typeface (#1404)
Replace Commit Mono with a project-owned Iosevka build so the site
typeface can evolve with OpenTUI's visual identity. Add a private font
lab to compare future builds before publishing them.
2026-08-22 11:11:30 +02:00