When a scrollback snapshot starts in the middle of a line, its first
fragment continues text already written to the terminal. Keep it flush
with the preceding tail so rendering and hit testing identify the same
cells.
Clarify that alignment applies after wrapping and uses display-cell
width.
Distinguish it from Yoga layout so measurement and selection behavior
stay
predictable.
0924679971
Fixes#1432.
`MarkdownRenderable` passes its `fg` to prose, code blocks and quotes,
but not to tables, so table text always comes out in
`TextTableRenderable`'s `#FFFFFF` default.
- pass `fg` when the table is created and when table options are
re-applied
- add an `fg` setter to `TextTableRenderable` that updates existing
cells, so changing `fg` on the markdown later recolours the table too
(the style rerender reuses the table, so a constructor option alone
isn't enough)
Added a test that checks header and body cells use the markdown `fg`,
then changes it. It fails on `main` and passes with this. `bun test
src/renderables` and `bun run typecheck` pass locally (Windows, prebuilt
0.5.11 native lib).
A width shrink could clear below a settling footer, leaving stale rows
visible. Clear from the visible footer origin while preserving pinned
cleanup.
Supersede #1487Fix#1486
Make named and bare links clickable without exposing their destinations
when the terminal supports OSC 8. Preserve visible URL fallbacks
elsewhere and keep links intact across formatting, streaming, wrapping,
and late terminal capability detection.
Fix#1439
Speedup some ci things.
Releases still publish all eight native symbol archives, including
Windows. The symbol generation, verification, and release-upload steps
are unchanged.
## 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.
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.
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.
Document the opt-in shared Zig local cache workflow for repeated native
builds across worktrees.
---------
Co-authored-by: Simon Klee <hello@simonklee.dk>
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.
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>
## 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`