Commit Graph
1166 Commits
Author SHA1 Message Date
Simon Klee 8742fa72b9 native: release replaced text buffer ropes (#1544)
Rope nodes live in the text buffer's arena and are never freed one at a
time. setText and setTextFromMemId cleared the rope and built the new
document in the same arena without resetting it, so every call kept the
whole previous rope. A 2000-line text grew RSS by about 1.24 MB per
call; 2000 calls reached 2.5 GB. EditBuffer.setText leaked the same way
even though it clears undo history first. setStyledText did not leak
because it reset the arena itself.

clear() now resets the arena when the rope has no undo or redo history,
so nothing can reference the old nodes. With history it still clears in
place. The arena keeps its capacity, as setStyledText's reset did, so a
document of similar size reuses the same pages. After the reset the rope
points at a static empty document, so the release never allocates and
running out of memory cannot leave the rope dangling. reset() and
setStyledText use the same release instead of resetting the arena and
rebuilding a rope that could fail after the old one was freed.

loadFile stored the file bytes in the rope arena while the memory
registry pointed at them, so a reset would hand those bytes to the next
rope. The registry now owns them.


#1543
2026-09-29 23:02:45 +02:00
Simon Klee 32d67005d0 core: encode draw text into reused storage (#1538)
bufferDrawText and drawBox titles allocated a new Uint8Array through
TextEncoder.encode on every call. Text now encodes into a reused scratch
buffer with encodeInto. A UTF-16 code unit encodes to at most three
bytes,
so text that fits a third of the scratch never overflows it, and longer
text allocates as before.

Text still converts as TextEncoder.encode converted it: undefined draws
nothing and other values use ToString. Both box titles convert before
either encodes, so a title whose toString draws cannot overwrite the
other
title's bytes.

On Node, a 10-character drawText drops from 9,859 to 6,012 user-space
instructions and a box with two titles from 28,849 to 22,154. On Bun,
where encode was already cheap, short text drops about 5%.
2026-09-27 15:43:01 +02:00
Simon Klee 205a48b78a native: round color bytes without a libm call (#1536)
Baseline x86-64 has no rounding instruction, so @round in
rgbaComponentToU8 called roundf for every channel of every blended,
filtered or faded cell. Values are clamped to [0, 255], so truncation
plus
an exact fraction test gives the same result.

The gain benchmark process drops from 37.3 to 19.6 billion user-space
instructions and the colormatrix benchmark from 23.9 to 13.7 billion.
2026-09-27 15:36:36 +02:00
Simon Klee ca25b66882 native: resize buffers in place while the cells fit (#1537)
OptimizedBuffer.resize reallocated all four cell arrays on every size
change. When a reallocation moved, it copied cells that clear() then
overwrote. If one reallocation failed, the arrays were left with
different lengths while width and height kept the old size, so a failed
shrink left a cell array shorter than the buffer's cells.

Resize now reuses the arrays while the new cells fit and the capacity
stays within half again the cells. Growing past the capacity reserves
half
of it again, so a buffer that grows a row at a time or alternates sizes
reallocates rarely. A larger jump or a shrink takes the exact cells. All
four arrays are allocated before any is freed, so a failed resize leaves
the buffer unchanged.
2026-09-27 15:31:30 +02:00
Simon Klee 684d70ce1b core: skip native resets of a text view with no selection (#1539)
TextTable resets the local selection of every cell view on each
selection
update, and almost all of them have no selection. Only TextBufferView
sets
a wrapped view's selection, and either native reset clears the whole
selection, so the wrapper now remembers when its view is clear and skips
the native call. A destroyed view still throws.

A TextTable selection update drops from about 2,960 native resets to 35.
On Node that is 16.4 to 15.2 million user-space instructions per update.
On Bun, where each call is cheap, the change is within noise.
2026-09-25 11:49:41 +02:00
clang.engineer 0450fadc05 terminal: keep tmux capability replies in the pane that asked (#1460)
Inside tmux, startup probes went through DCS passthrough. The outer
terminal's replies carry no pane identity, so a pane switch before a
reply delivered the bytes to the newly active pane as input.

Send all probes unwrapped; tmux answers the ones it implements for the
pane that asked. Skip the Kitty graphics and DA probes under tmux,
remove the passthrough retry after late XTVERSION detection, and ignore
graphics replies once tmux is known.

tmux before 3.6 does not answer DECRQM, but it has reported pane focus
for mode 1004 since 1.8, so enable focus tracking whenever tmux is
detected.

An explicit Sixel protocol is now honored under tmux through
passthrough, like explicit Kitty. Automatic selection still uses
blocks.

Closes #1459
2026-09-25 11:49:07 +02:00
Simon Klee b726707b9b chore: cleanup CODEOWNERS 2026-09-25 10:59:47 +02:00
avrlandSimon Klee a11bf6851e fix portal teardown leak (#1522)
Hi @simonklee | @kommander ,

Fixes: #1518 

This PR fixes the portal destroy leaking its children listeners in the
memory by recursively destroying its children.

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-25 10:09:20 +02:00
Simon Klee 28b27a587e ssh: disable Nagle's algorithm on accepted connections (#1531)
On Node, a server socket keeps Nagle's algorithm enabled. A frame
written as more than one small packet then waits for the client's
delayed ACK. On Linux that adds about 40 ms to each reply. Bun's sockets
are not affected.

Call setNoDelay(true) on each accepted ssh2 connection, after the close
and error handlers are registered, so a failure is reported through the
normal cleanup path.

Loopback ssh2 probe, two small writes per reply, client with TCP_NODELAY
(as interactive OpenSSH sets it), median of 60 replies:

  node 26.4  off 41.011 ms  on 0.107 ms
  node 26.7  off 41.003 ms  on 0.106 ms
  bun 1.4    off  0.048 ms  on 0.045 ms
2026-09-25 09:39:00 +02:00
Simon KleeandLuis H. Moreno 4b1474e009 Fix/kitty z above cell background (#1525)
Supersede #1484, #1505 

Fix #1506

---------

Co-authored-by: Luis H. Moreno <luis@suprnation.com>
2026-09-23 14:26:00 +02:00
Simon Klee 94e19d33db ci: cache native cross-compile bottles (#1528)
Avoid rebuilding unchanged native libraries in every workflow while
verifying their inputs and Zig version before reuse.
2026-09-23 11:31:02 +02:00
Simon Klee 5eeed22a2f native: fix Zig dependency extraction on Windows (#1527)
Avoid interpreting Windows drive paths as remote tar hosts when
extracting
the vendored Zig dependencies.
2026-09-22 21:24:18 +02:00
Simon Klee b4afd3d97f Release v0.5.12 v0.5.12 2026-09-22 10:11:24 +02:00
Simon Klee 4954312d74 chore: packaging and test fixes (#1517)
pPacked parser asset paths, runtime-plugin string-literal rewrites, Node
snapshot parsing, Solid plugin file reads, SSH test retries, Node
example argv forwarding, etc. etc.
2026-09-18 11:09:57 +02:00
Simon Klee c01292fd08 text-buffer: preserve inline continuation alignment (#1511)
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.
2026-09-17 12:47:43 +02:00
JensenandSimon Klee 9b8b4c853a fix(core): support transparent background in embedded terminals (#1502)
Fixes #1501

Adds `transparentBackground` (default `false`) to
`EmbeddedTerminalRenderable`.
Enabled, cells with no explicit background compose with the `.default`
intent
(SGR 49), so the host terminal's background shows through; explicitly
painted
backgrounds stay opaque. Hot-switchable.

Tests: native `tests.zig` (off → `.rgb`, on → `.default`, explicit
unchanged) and
`EmbeddedTerminal.test.ts` (off → on → off). Native suite 2,148 passed /
32 skipped;
core file 32 passed; fmt and lint clean.

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-17 12:47:10 +02:00
avrlandSimon Klee 871119e9e4 Docs website Scrollbar fix (#1510)
make the scrollbar style blend in with the website theme.

Thanks.

<img width="2530" height="1090" alt="image"
src="https://github.com/user-attachments/assets/e0b99842-3582-4160-a157-6fbb9cc661ba"
/>

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-16 11:13:29 +02:00
Simon Klee f3018fa86d docs: document text alignment (#1509)
Clarify that alignment applies after wrapping and uses display-cell
width.
Distinguish it from Yoga layout so measurement and selection behavior
stay
predictable.

0924679971
2026-09-16 10:50:34 +02:00
Simon Klee f015a73f3e core: keep worker error listener on termination (#1508)
A worker can emit an error while termination is pending. Keep its
listener attached so Node does not turn that failure into an uncaught
exception.
2026-09-16 10:20:18 +02:00
avrlandSimon Klee 0924679971 feat: implement per-line text alignment support textAlign (#1507)
Fixes: #916 

Implemented this textAlign feature for left | center | right positions.

Works fine for both wraps (chars and words).

It was straightforward  to add padding = total cols - line_length.
Please let me know if I missed anything.


https://github.com/user-attachments/assets/cd45ffff-9ad7-47a7-b3db-bf6e758f36c6

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-16 07:37:08 +00:00
Yusuf Efe f532fa7b2b fix(core): pass markdown fg through to table cells (#1503)
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).
2026-09-15 20:35:52 +02:00
Simon Klee 0491dc19e9 editor: allow cursor reads after destruction (#1504)
Destroyed listeners may inspect cursor state after native resources are
released. Return an empty cursor so cleanup notifications do not throw.
2026-09-15 14:17:52 +02:00
Simon Klee ac753b48d3 renderer: fix split-footer resize cleanup (#1488)
A width shrink could clear below a settling footer, leaving stale rows
visible. Clear from the visible footer origin while preserving pinned
cleanup.

Supersede #1487
Fix #1486
2026-09-09 12:45:31 +02:00
avrlandSimon Klee 97a20ab249 feat: add comprehensive mouse pointer style support and update related functionality (#1037)
This fixes #1032, but there since there are no standard for supporting
OSC22 across terminals, there are different cursor types unsupported on
different terminal app (ghostty, kitty, etc).

https://github.com/user-attachments/assets/dc0b8399-fe3f-462a-9ca3-8636876c77ca

---------

Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-09 09:14:21 +00:00
OpenCode Agent 6b9863ea7c Release v0.5.11 v0.5.11 2026-09-07 10:42:03 +00:00
62a7c38289 feat(core): support PCM through the shared audio stream interface (#1481)
---------

Co-authored-by: Filip Hejmowski <fhejmowski@simplito.com>
Co-authored-by: neriousy <34747899+neriousy@users.noreply.github.com>
Co-authored-by: kommander <335157+kommander@users.noreply.github.com>
Co-authored-by: Tilda <tilda@localhost>
2026-09-07 12:30:18 +02:00
Simon Klee 7581976f4d markdown: fix hyperlink rendering and detection (#1442)
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
2026-09-04 21:30:50 +02:00
9c55da3a63 feat(native): allow line wrapping between CJK characters (#1453)
Closes https://github.com/anomalyco/opentui/issues/1183.

---------

Co-authored-by: OpenCode Agent runtime <opencode-agent@msg-bot-e4abd35d1d114f2aa16fe1d2.exe.xyz>
Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-09-04 21:10:35 +02:00
Simon Klee 0733b5382c runtime: support Bun 1.4 (#1400) 2026-09-04 21:09:49 +02:00
Simon Klee f1b320aa74 ci: skip symbols in dependent package builds (#1476)
Speedup some ci things.

Releases still publish all eight native symbol archives, including
Windows. The symbol generation, verification, and release-upload steps
are unchanged.
2026-09-04 20:58:03 +02:00
Simon Klee 87271aff19 web: add another color theme (#1478) 2026-09-04 20:33:12 +02:00
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 LangtonandSebastian 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
SebastianandSimon Klee 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