Skip to content

chore(deps): update opentui - #22

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/opentui
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/opentui

Conversation

@renovate

@renovate renovate Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
@opentui/core (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-darwin-arm64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-darwin-x64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-linux-arm64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-linux-x64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-win32-arm64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/core-win32-x64 (source) 0.5.14 → 0.6.6 age confidence
@opentui/solid (source) 0.5.14 → 0.6.6 age confidence
solid-js (source) 1.9.15 → 1.9.17 age confidence

Release Notes

anomalyco/opentui (@​opentui/core)

v0.6.6

Compare Source

Previously, most layout setters ignored null, undefined, or both, so the node kept its old style. As a result, a React or Solid component that removed a style prop kept the old layout. A plugin that read Unit.Undefined from a getter also could not write that value back. In 0.6.6, a nullish write restores the default that the constructor uses when the option is omitted:

const box = new BoxRenderable(renderer, { paddingX: 2 })
box.paddingLeft = 5 // pads the left side by 5
box.paddingLeft = undefined // pads it by 2 again, from paddingX

Margin, padding, gap, min and max sizes, and flexBasis become unset, so an edge falls back to its shorthand again. top, right, bottom, and left also unset the native edge. Before, the getter reported undefined while layout kept the old offset. width and height become "auto", position becomes "relative", overflow becomes "visible", and visible becomes true. Invalid values are still ignored. Not every default comes back. Class defaults such as the row direction of ScrollBoxRenderable stay as they are, and flexShrink becomes 1.

If a custom renderable passes its own width or height to super(), it should override the new protected defaultDimension(kind) method. A nullish write then restores that size instead of "auto". Built-in classes that size themselves, such as InputRenderable, TabSelectRenderable, and ASCIIFontRenderable, already override it.

Added

  • core: A custom renderable can override protected defaultDimension(kind) to set the width or height that a nullish write restores. See Custom renderables. (#​1678)

Changed

  • core: Assigning null or undefined to a layout property restores the default for an omitted option, and removing a React or Solid style prop does the same. See Layout. (#​1678)
  • core: Assigning null or undefined to visible shows the node. Before, null hid the node, so assign false to hide it. (#​1678)

Fixed

  • core: fillRect, drawBox, setCell, drawChar, and other calls with u32 fields now say "must be a non-negative integer" for NaN, Infinity, fractional, and negative values. Before, the error said that the value exceeded the u32 limit. See Buffer API. (#​1674)
  • core: A box title or bottomTitle over 64 KiB throws the same RangeError as drawText instead of a NativeError with the InvalidArgument status. See Box. (#​1674)
  • core: Nesting deeper than 256 levels, counting the root, throws a NativeError with the message "Scene nesting depth exceeds the native limit". Before, the message gave only the ObjectLimit status. See Layout. (#​1674)

API changes and documentation: https://opentui.com/docs/releases/0.6.6

What's Changed

Full Changelog: anomalyco/opentui@v0.6.5...v0.6.6

v0.6.5

Compare Source

OpenTUI 0.6 removed Renderable.getLayoutNode(). Before 0.6, plugins used it to read the layout styles of host renderables that they did not create. Some plugins saved a container's getMaxWidth() and getAlignSelf() so they could restore the container after they restyled it. Others checked the host layout with getFlexDirection(), or sized a panel inside a padded sidebar with getPadding(). 0.6 kept only getWidth() and getMargin(edge), so this code had nothing to call.

In 0.6.5, renderables have the other style getters of a Yoga Node, with the same names and return types. To move, remove the getLayoutNode() call:

// Before 0.6
const saved = host.getLayoutNode().getMaxWidth()

// 0.6.5
const saved = host.getMaxWidth()

These getters return the declared style, not the computed layout. A write shows up at once, before the next layout pass. After box.maxWidth = 40, box.getMaxWidth() returns { unit: Unit.Point, value: 40 }, but box.width still holds the computed width from the last pass. After destroy(), the getters do not throw. They return Yoga's unset values, as getWidth() does.

An edge getter reads only the value set for that exact edge, so this case needs care. The padding option sets Edge.All, paddingX sets Edge.Horizontal, and paddingLeft sets Edge.Left. After padding: 2, getPadding(Edge.Left) returns a Unit.Undefined value, but layout still pads the left side by 2. That is because Yoga falls back from Edge.Left to Edge.Horizontal and then to Edge.All. To get the declared left padding, check the edges in that same order:

import { Edge, Unit } from "@opentui/core/yoga"

const left = [Edge.Left, Edge.Horizontal, Edge.All]
  .map((edge) => sidebar.getPadding(edge))
  .find((value) => value.unit !== Unit.Undefined)

Renderables still have no getter for computed padding, margin, or border. For padding declared in points, the declared value and the computed value are the same.

Added

  • core: Renderables have the remaining Yoga Node style getters, such as getHeight(), getMaxWidth(), getPadding(edge), getGap(gutter), getFlexDirection(), and getAlignSelf(). Use them instead of style reads through getLayoutNode(). See Layout. (#​1667)

API changes and documentation: https://opentui.com/docs/releases/0.6.5

What's Changed

Full Changelog: anomalyco/opentui@v0.6.4...v0.6.5

v0.6.4

Compare Source

Fixed

  • core: Kitty images in iTerm2 no longer go black or blank after a move, a scroll, or a layout change. The renderer now deletes each old placement by its placement id, because iTerm2 also frees the image data when it gets an image-wide delete. (#​1662)

API changes and documentation: https://opentui.com/docs/releases/0.6.4

What's Changed

Full Changelog: anomalyco/opentui@v0.6.3...v0.6.4

v0.6.3

Compare Source

A coding agent such as opencode can wait for minutes at a permission prompt in a
background tab. Before this release, the terminal could not tell the user about it.
In 0.6.3, the agent can report its state to the terminal:

renderer.setProgramStatus({ state: "blocked", kind: "permission", app: "opencode", message: "Allow bash?" })

This call writes one report in the
Program Status Protocol,
OSC 7501. The terminal decides how to show the report. It can use a tab
indicator, an inbox entry, or a desktop notification. A terminal that does not
implement the protocol ignores the sequence, so setProgramStatus() does not
wait for capability detection. capabilities.program_status only tells you that
a reply arrived. A false value can also mean that the reply has not arrived
yet.

Each report replaces one record, and a field that a report omits is gone from
the record. Send app and title with every report that needs them. A program
that runs several tasks can give each task its own record with an id. It can
then remove that record with clear:

renderer.setProgramStatus({ state: "working", id: "subagent-1", title: "Explore", progress: 40 })
renderer.setProgramStatus({ state: "clear", id: "subagent-1" })

Records belong to the terminal, not to the renderer. OpenTUI does not clear them
when the renderer is destroyed or suspended. When the process exits, the
terminal drops working and blocked records. It keeps done and error
records for the user to find. If the user should see the result, report done or
error before the program exits. OpenTUI also does not wrap reports in tmux or
GNU Screen passthrough. The outer terminal cannot tell panes apart or see the
program exit, so a working record from a pane would never clear.

C hosts send the same report with ot_session_control() and
OT_CONTROL_PROGRAM_STATUS. The payload is an ot_session_program_status struct,
followed by the id, app, title, and message bytes. Native code checks every
report on both paths before it writes anything. A report that a terminal would
discard or only partly read fails at the call.

Added

  • core: renderer.setProgramStatus(status) and
    NativeSession.setProgramStatus(status) send one OSC 7501 program status
    report. They return false when OpenTUI does not queue the report, and they
    throw for an invalid report. See
    Program status.
    (#​1657)
  • core: TerminalCapabilities.program_status is true when the terminal
    replies to the OSC 7501 ; ? query. The renderer sends this query at startup
    with the other capability queries, but not to foot. See
    Terminal capabilities.
    (#​1657)
  • native: ot_session_control() with OT_CONTROL_PROGRAM_STATUS and an
    ot_session_program_status payload sends a program status report.
    OT_CAP_PROGRAM_STATUS is set when the terminal replies to the query. See
    Program status.
    (#​1657)

Changed

  • native: ot_session_control() now checks the payload before the terminal
    phase. While the terminal is not active, an invalid payload returns
    OT_INVALID_ARGUMENT instead of OT_INVALID_PHASE. A title with control
    characters is one example. See Frames and output.
    (#​1657)

API changes and documentation: https://opentui.com/docs/releases/0.6.3

What's Changed

Full Changelog: anomalyco/opentui@v0.6.2...v0.6.3

v0.6.2

Compare Source

In 0.6.1, editors started to show control characters as notation. As a result,
an editor offset is no longer the same as a JavaScript string width. The string
"\f" has a width of 0, but the editor shows it as ^L in two cells.
EditBuffer.measureText() gives the correct offset, but only for a buffer you
already have. To get the offset of text that was not in an editor yet, code had
to create an EditBuffer only to measure that text.

In 0.6.2, measureEditText() measures text without a buffer. It returns the
same value as measureText() on an edit buffer with the same width method and
tab width. Pass the settings of the editor that will hold the text. Textarea
and Input use the renderer's width method and the default tab width, which is
2:

import { createCliRenderer, measureEditText } from "@opentui/core"

const renderer = await createCliRenderer()
measureEditText("\f@file", { widthMethod: renderer.widthMethod }) // 7: ^L takes two cells
measureEditText("a\tb", { widthMethod: renderer.widthMethod, tabWidth: 4 }) // 6

C programs get the same measurement from ot_edit_measure_text(). It takes a
context, but no edit buffer handle.

Added

  • core: measureEditText(text, { widthMethod, tabWidth }) returns the
    editor offset at the end of text without an EditBuffer. tabWidth
    defaults to the tab width of a new edit buffer. See
    Editing buffers and views.
    (3ab0b954a)
  • native: ot_edit_measure_text() gives the same measurement through the C
    interface. A tab_width over 255 fails. See
    Editing buffers and views.
    (3ab0b954a)

Fixed

  • native: A bold, dim, italic, underlined, blinking, inverse, hidden, or
    struck-through cell in the last column of a row no longer gives its
    attributes to the next cells that the renderer draws. A row that ends in such
    a cell writes 4 more bytes. See Frames and output.
    (#​1652)
  • native: If you type an emoji with a skin-tone modifier, a ZWJ sequence,
    Hangul jamo, or U+FE0F in an editor, the editor now stores its correct width.
    Before, 👍🏽 took 4 cells instead of 2, so the cursor, wrapping, selection, and
    Right key were wrong. See Textarea.
    (#​1650)
  • native: A combining mark that starts a new text chunk now draws on the
    character before it, for example U+0301 typed after abc in an editor or
    added with TextBuffer.append. The renderer drops a mark that would make a
    1-cell character 2 cells wide, such as U+FE0F. See
    Text and terminal cells.
    (#​1651)
  • native: An embedded terminal redraws all rows when the child changes the
    default foreground or background color (OSC 10, 11, 110, or 111) or sets
    reverse video on or off (CSI ? 5 h or CSI ? 5 l). Before, rows on the
    screen kept the old colors. See
    Embedded terminal.
    (#​1649)
  • core: If the host terminal uses the Kitty keyboard protocol, an embedded
    terminal now sends keypad Enter, keypad navigation, lock, Print Screen, Pause,
    and left and right modifier keys to the child. Keypad digits reach a Kitty
    child as keypad keys, and Ctrl+keypad 0 no longer arrives as a bare 0. See
    Embedded terminal.
    (#​1648)
  • core: parseKeypress() no longer changes the caller's Buffer when it
    decodes a single byte above 127 as a meta key. The input parser of the
    renderer did not have this problem. See
    Keyboard input.
    (#​1653)

API changes and documentation: https://opentui.com/docs/releases/0.6.2

What's Changed

Full Changelog: anomalyco/opentui@v0.6.1...v0.6.2

v0.6.1

Compare Source

In 0.6.0, EditBuffer.setText("a\fb") throws NativeError. The editor places
its cursor by display cell, and a control character took no cells, so the
cursor could not move past it or delete it. In 0.5.14, which accepted
controls, the cursor in "a\fb" could not reach column 0, Backspace stopped at
"a\f", and rendering "A\x07B\x1bC\fD" wrote raw BEL and ESC bytes to the
terminal. Rejecting the text stopped that, but every application then had to
remove these characters before it could edit a string.

In 0.6.1, editors keep control characters and show them the way Vim and
Neovim do. The text "a\fb\0c\x1bd\x85e\x7ff" shows as a^Lb^@c^[d<85>e^?f. A
C0 control or DEL is caret notation in two cells, and a C1 control is
lowercase hex in four cells. The notation changes only how the text looks. The
cursor stops before each control, one Backspace deletes it, and getText(),
undo, and selection copy return the original characters. Tab, carriage
return, and line feed work as before. Read-only text, such as
TextRenderable, still gives controls no cells.

This has one cost. An editor offset is no longer the same as a JavaScript
string width. Bun.stringWidth("\f") is 0, but ^L takes two cells. Code
that calculates editor offsets from string widths, such as code that places an
extmark after a match, must ask the buffer instead. measureText() measures
text the way the buffer lays it out, and getTextRange() converts an offset
back to a string index:

buffer.setText("\f@file")
const text = buffer.getText()
const offset = buffer.measureText(text.slice(0, text.indexOf("@"))) // 2: ^L takes two cells
buffer.getTextRange(0, offset).length // 1: the index of "@"

Added

Changed

  • core: EditBuffer.setText(), insertText(), and replaceText(),
    Textarea initialValue, Input.value, and placeholders accept control
    characters and do not throw NativeError. An editor shows C0 controls and
    DEL as ^@ to ^_ and ^? in two cells, and C1 controls as <80> to
    <9f> in four cells. See Editing buffers and views.
    (#​1636)
  • core: Paste in Textarea and Input keeps control characters. It
    removes terminal escape sequences, including a lone ESC and an incomplete
    sequence, the same way on Bun and Node. Core no longer depends on
    strip-ansi. See Editing buffers and views.
    (#​1636)

Fixed

  • core: Extmarks and the Input cursor use native display offsets. As a
    result, they keep the correct position in text that contains control
    characters. See Editing buffers and views.
    (#​1636)

API changes and documentation: https://opentui.com/docs/releases/0.6.1

What's Changed

Full Changelog: anomalyco/opentui@v0.6.0...v0.6.1

v0.6.0

Compare Source

OpenTUI 0.5 kept the renderable tree in JavaScript. On each frame, JavaScript
walked the tree and read each node's position back from Yoga. It then made one
native call for each draw, so the cost of a frame grew with the number of
nodes and draws. In 0.6.0, native code owns the tree. Each Renderable has a
node in a native scene, which holds the node's Yoga node and paint state.
Layout and paint setters stage their changes, and Core sends all staged
changes to native code in one call before each frame step. Native code then
lays out, clips, and paints the scene and fills the hit grid. It draws most
built-in renderables without a call to JavaScript. When no visible node has a
JavaScript hook, one native frame step lays out and paints the whole frame.
Rendering pipeline follows a
complete frame.

The built-in renderables and the React and Solid bindings keep their option
names, but a few option values now throw, such as enableLayout: false.
Custom renderables may need a little work. Renderable no longer has
render(), getLayoutNode(), or a protected yogaNode. Draw in
renderSelf(), renderBefore, or renderAfter, and report content size with
setMeasureProvider(). Paint hooks also work differently. A hook of an
unbuffered renderable now receives a recording buffer: OpenTUI stores the
hook's draw calls, and native code plays them back at the node's place in
paint order. Painting never stops to call JavaScript, so a hook cannot tear
the frame. But a hook can no longer read the frame's cells. To read cells, a
hook must draw into a buffer that the renderable owns and compose that
buffer, or the work must move to a post-process function. With
buffered: true, the hooks draw into such a buffer, and OpenTUI composes it.
Custom renderables shows a complete
example.

Native rendering resources now belong to one owner, a native Context.
Buffers, text buffers, views, syntax styles, images, and scene nodes cross the
native boundary as generational handles instead of raw pointers. A stale
handle fails with an error instead of touching freed memory, and destroying a
Context releases everything that it owns. The factories that used to create
resources without an owner now take one. For a resource that a renderer
draws, pass renderer.nativeScene:

// 0.5: SyntaxStyle.fromStyles(styles)
const syntaxStyle = SyntaxStyle.fromStyles(styles, renderer.nativeScene)

// 0.5: OptimizedBuffer.create(40, 10, "unicode")
const buffer = OptimizedBuffer.create(40, 10, "unicode", {
  owner: renderer.nativeScene,
})

Image factories take only a ResourceContext as their owner. For the
renderer's Context, pass renderer.nativeScene.resourceContext. Without a
renderer, create a Context with
new ResourceContext({ objectCapacity, renderCellsMax }), and call its
destroy() method when you are done. objectCapacity is the initial number
of object slots, and renderCellsMax limits the cells in each buffer.
API and symbol index lists each removed or
changed API with its replacement.

Terminal transitions are now asynchronous. suspend() and resume() return
promises. destroy() still returns at once, but the terminal restoration and
the remaining output finish later. Before you reuse the streams, or before a
deliberate exit, await renderer.closed:

await renderer.suspend()
// Run an editor or a shell in the terminal.
await renderer.resume()

renderer.destroy()
await renderer.closed

The native library now has a C interface. opentui.h declares a C ABI of
ot_* functions, and the opentui Zig module gains the same Context, scene,
and Session model. A C or Zig program can build a scene and write frames with
no JavaScript runtime. The ABI is experimental, so use the header, library,
and bindings from the same revision.
Native rendering starts with a box and a label.

One fix changes colors in some SSH sessions. Inside an SSH or mosh login,
OpenTUI detects remote mode automatically. In that mode, it used to stop
reading the environment before it read TERM and COLORTERM. As a result,
every remote terminal got truecolor output, and a 256-color terminal such as
GNU screen 4.x showed the UI without color. OpenTUI now reads these two keys.
This has a cost. A truecolor terminal that reaches the server with
TERM=xterm-256color and no COLORTERM used to get exact colors by accident.
Examples are iTerm2 and WezTerm over a stock macOS ssh client, which does not
forward COLORTERM. They now get 256-color approximations. A Kitty XTVERSION
reply still turns on truecolor. To keep truecolor in other terminals, export
COLORTERM=truecolor on the server, forward it with SendEnv COLORTERM and
AcceptEnv COLORTERM, or pass it in the renderer's environment option.

Breaking changes

  • core: Renderable.render(), updateLayout(), updateFromLayout(),
    canReuseRenderCommandList(), getLayoutNode(), the protected yogaNode,
    the RenderCommand type, and RootRenderable.calculateLayout() are
    removed. Draw in renderSelf(), renderBefore, or renderAfter, and use
    setMeasureProvider(), invalidateIntrinsicSize(), and getLayout() for
    size and layout. Protected members such as _x, _y, getScissorRect(),
    and _getVisibleChildren() are also removed. Use x, y, screenX, and
    screenY, and the viewportCulling option of ScrollBox. See
    Custom renderables.
    (#​1479)
  • core: The enableLayout: false option throws. on("resized") and
    on("layout-changed") throw on a renderable that is not the root, where
    these listeners never ran. Use onSizeChange instead. See
    Layout.
    (#​1479)
  • core: Paint hooks of an unbuffered renderable get a recording buffer
    that cannot read cells, and renderer.nextRenderBuffer accepts drawing only
    in a post-process function. buffer.buffers works only in a post-process
    function. Elsewhere, use buffer.withBuffers(callback). To read or change
    cells in a hook, set buffered: true, or draw into a buffer that you own and
    compose it. For the whole frame, use a post-process function. See
    Rendering pipeline.
    (#​1479)
  • core: addToHitGrid(), pushHitGridScissorRect(),
    popHitGridScissorRect(), and clearHitGridScissorRects() are removed from
    CliRenderer and RenderContext. Native code adds a hit area for each
    visible node, and renderer.hitTest(x, y) queries the hit grid. A custom
    RenderContext must implement nativeScene, requestAnimationFrame(), and
    cancelAnimationFrame(). See
    Interaction, focus, and selection.
    (#​1479)
  • core: OptimizedBuffer.create(), TextBuffer.create(),
    EditBuffer.create(), SyntaxStyle.create(), SyntaxStyle.fromStyles(),
    and SyntaxStyle.fromTheme() require an owner, and the ptr properties of
    buffers, views, and styles are removed. Pass renderer.nativeScene, or a
    ResourceContext when no renderer exists. Resources from different owners
    cannot be used together, and the resources that a renderer owns stop
    working when the renderer is destroyed. See
    How Core uses native.
    (#​1479)
  • core: suspend() and resume() return a Promise<void>, and
    destroy() restores the terminal asynchronously. Await the transitions, and
    await renderer.closed before you reuse the streams. Both methods throw
    while another transition is pending, and when the terminal is not set up,
    for example on a test renderer or after destroy(). See
    Lifecycle and cleanup.
    (#​1479)
  • core: The useThread option, renderer.useThread,
    renderer.rendererPtr, dumpBuffers(), dumpOutputBuffer(), and the
    OTUI_NO_NATIVE_RENDER environment variable are removed. Remove
    useThread. Instead of dumpBuffers(), use test frame capture or
    renderer.currentRenderBuffer.withBuffers(). Instead of
    dumpOutputBuffer(), give the renderer a custom stdout. Instead of
    OTUI_NO_NATIVE_RENDER, use createTestRenderer(). See
    Rendering diagnostics.
    (#​1479)
  • core: The Clipboard class is removed. Use
    renderer.copyToClipboardOSC52() and renderer.clearClipboardOSC52(), or
    createClipboard() with createHostClipboard() and
    createRendererClipboardAdapter(renderer). See
    Clipboard.
    (#​1479)
  • core: OpenTUI no longer installs a process-global
    requestAnimationFrame, cancelAnimationFrame, or window. Use
    renderer.requestAnimationFrame() and renderer.cancelAnimationFrame().
    See Animation and Timeline.
    (#​1479)
  • core: After destroy(), renderable constructors, the timeline
    engine.attach(), and most renderer methods throw, for example
    setTerminalTitle(), copyToClipboardOSC52(), getStats(), and
    hitTest(). requestRender() and resize() do nothing. Check
    renderer.isDestroyed first. See
    Lifecycle and cleanup.
    (#​1479)
  • core: When the output stream emits error, or closes or finishes before
    the renderer closes it, the renderer destroys itself and renderer.closed
    rejects. Handle the rejection of closed. See
    Lifecycle and cleanup.
    (#​1479)
  • core: Drawing methods throw RangeError for fractional, NaN, or
    infinite coordinates and sizes. drawText() and box titles throw
    RangeError for text larger than 64 KiB of UTF-8. setCell(),
    setCellWithAlphaBlending(), drawChar(), and drawText() throw
    NativeError for attribute bits 8 to 31. In a paint hook, the frame fails
    instead. See Buffer API.
    (#​1479)
  • core: EditBuffer.setText(), insertText(), insertChar(), and
    replaceText(), the same Textarea and Input methods, Textarea
    initialValue, Input.value, and both placeholder options throw
    NativeError for control characters other than tab, carriage return, and
    line feed. DEL and C1 control characters also throw. Remove these
    characters first. See Editing buffers and views.
    (#​1479)
  • core: The minimum Bun version is 1.3.14 (was 1.3.0), the oldest version
    that CI tests. Bun 1.3.0 to 1.3.3 pass some FFI pointer arguments as wrong
    addresses, Bun 1.3.4 to 1.3.6 can crash after a Worker exits, and Bun 1.3.7
    can hang while it loads modules. See
    Runtime and platform support.
    (#​1479)
  • core: The pointer-based RenderLib methods, such as createRenderer
    and bufferDrawText, and the handle types, such as RendererHandle and
    OptimizedBufferHandle, are removed. Use the public classes and the
    Context*Handle, SessionHandle, and SceneNodeHandle types. See
    How Core uses native.
    (#​1479)
  • core: Standalone Yoga throws YogaError for invalid arguments, and
    Config.free() throws while nodes from that config remain. Measure and
    dirtied functions must return synchronously and must not change Yoga nodes.
    See Yoga API.
    (#​1479)
  • ssh: session.write() can throw OutputPressureError or RangeError.
    cols and rows hold the requested size until the renderer attaches, and
    onClose runs at logical close, before the renderer finishes closing. See
    SSH.
    (#​1479)
  • native: The legacy native exports, such as createRenderer and
    bufferDrawText, are removed. Use the ot_* functions in opentui.h. In
    Zig, the global grapheme and link pools are removed, so
    OptimizedBuffer.init() and CliRenderer.create() require a link_pool.
    GraphemePool.acquire() and LinkPool.acquire() replace alloc(). They
    return an ID that holds one reference, which decref() releases. See
    C and Zig.
    (#​1479)

Added

  • core: renderer.requestAnimationFrame() and
    renderer.cancelAnimationFrame() schedule one-shot frame callbacks for one
    renderer. getTimelineEngine(renderer) and
    createTimeline(options, renderer) give each renderer its own timeline
    engine. See Animation and Timeline.
    (#​1479)
  • core: renderer.closed resolves after destruction writes the remaining
    output and native teardown finishes. It rejects when the output fails or
    does not finish within one second. See
    Lifecycle and cleanup.
    (#​1479)
  • core: renderer.requestResize(width, height) debounces size changes
    from a custom terminal and applies only the latest size. See
    Renderer.
    (#​1479)
  • core: The nativeSceneWorkBudget option lets a frame yield to the event
    loop during preparation. Painting still runs in one native call. See
    Renderer.
    (#​1479)
  • core: The environment option gives terminal environment values to one
    renderer, for example { TERM: pty.term } from an SSH client. These values
    replace the forwardEnvKeys values with the same names. In an automatically
    detected SSH login, only TERM and COLORTERM take effect unless you set
    remote: true. See
    Terminal capabilities.
    (#​1603)
  • core: ResourceContext owns native resources without a renderer, and
    NativeError reports a failed native call with its operation and
    status. Image factories accept a ResourceContext in their owner
    option, such as renderer.nativeScene.resourceContext. See
    How Core uses native and
    NativeImage.
    (#​1479)
  • core: Custom renderables get setMeasureProvider(),
    invalidateIntrinsicSize(), getLayout(), refreshHooks(), and
    defineNativeIntegration(). TextRenderable and CodeRenderable get
    drawToBuffer(), and OptimizedBuffer gets withBuffers() for scoped cell
    access. See Custom renderables.
    (#​1479)
  • core: TestRendererSetup.dispose() and [Symbol.asyncDispose]()
    destroy the test renderer and wait for renderer.closed.
    ManualClock.pendingTimerCount counts the scheduled timeouts and active
    intervals. See Testing.
    (#​1479)
  • native: opentui.h declares an experimental C ABI, and the opentui
    Zig module gains the same Context, scene, and Session model. A
    Context's object table starts at object_capacity slots and doubles when it
    is full, up to 4,194,304 slots or object_capacity, whichever is larger.
    Past that limit, object creation fails with OT_OBJECT_LIMIT. See
    Native rendering.
    (#​1479,
    #​1622)
  • native: Terminology gets OSC 8 hyperlinks and OSC 777 notifications when
    its XTVERSION reply names it. See
    Terminal capabilities.
    (#​1566)
  • ssh: The server accepts COLORTERM, TERM_PROGRAM, and
    TERM_PROGRAM_VERSION env requests that arrive before the shell request,
    with values of at most 256 bytes. It rejects other keys. The renderer gets
    these values with the client's PTY TERM, so capability detection sees the
    client's color depth and hyperlink support, and GNU Screen or tmux from the
    TERM prefix. A client with TERM=xterm-256color and no COLORTERM now
    gets 256-color output instead of truecolor. To keep truecolor, add
    SendEnv COLORTERM to the client's ssh configuration. See
    SSH.
    (#​1603)

Changed

  • core: Native code lays out, culls, and paints the scene and fills the
    hit grid, so JavaScript no longer walks the tree on each frame. JavaScript
    work in a frame now grows with changes and hooks, not with the number of
    renderables. Some built-in renderables, such as Select and ScrollBox, still
    run JavaScript hooks. See
    Rendering pipeline.
    (#​1479)
  • native: Text buffers build rope slices in one allocation. Setting and
    appending text use less time and memory. See
    Editing buffers and views.
    (#​1582)
  • native: Buffers write cells that are not grapheme clusters on a faster
    path, and drawText() writes printable ASCII without copying it. See
    Buffer API.
    (#​1591)
  • core: EmbeddedTerminalRenderable encodes meta as Alt, and only
    super encodes as Super. Core's key parser sets meta for Alt, so Alt+b
    now reaches a legacy child as ESC b, not b. On macOS, an Alt modifier
    reaches the child as Alt, because the host terminal already applied its
    Option setting. See Embedded terminal.
    (#​1594,
    1b25377b6,
    f0d7d1d3b,
    be14991e6)
  • react: root.render() does nothing when the renderer is destroyed, for
    example after exitOnCtrlC. See React bindings.
    (#​1604)
  • solid: render() does not mount when the renderer is destroyed, for
    example after exitOnCtrlC. See Solid bindings.
    (#​1604)

Fixed

  • native: In an SSH or mosh login, automatically detected remote mode
    reads the color depth from TERM and COLORTERM. A 256-color terminal such
    as GNU screen 4.x gets 256-color output instead of truecolor that it cannot
    show. See Terminal capabilities.
    (#​1624)
  • core: In split-footer mode, a write that continues a scrollback row no
    longer loses or overwrites a wide character at the end of the row. Tabs in
    captured stdout count from the start of the terminal row and stop at the
    last column. See Renderer.
    (#​1597,
    #​1617)
  • native: In split-footer mode, a scrollback row that fills the terminal
    width keeps its last cell. Output no longer erases the footer when fewer
    than two rows remain above it, for example with the default footer in a
    terminal of 13 rows or fewer. See Renderer.
    (#​1581,
    #​1618)
  • native: A highlight at the end of a line no longer colors text that an
    append, insert, or undo adds to that line later. setText() and
    replaceText() drop highlights on lines past the new line count, so
    getHighlightCount() and getLineHighlights() no longer report them. See
    Editing buffers and views.
    (#​1621,
    #​1601)
  • native: Up and Down move the cursor to the start of a wide character or
    tab, not inside it. This applies to EditBuffer.moveCursorUp() and
    moveCursorDown(), EditorView.moveUpVisual() and moveDownVisual(), the
    Textarea arrow keys, and EditorView.setViewport() with moveCursor. See
    Textarea.
    (#​1596)
  • native: Down does nothing in an empty Textarea while it shows a
    placeholder of more than one row. The next edit no longer fails with
    InvalidArgument. See Textarea.
    (#​1619)
  • native: A width change in word-wrap mode releases the layout memory of a
    narrower wrap. Before this fix, a 94 KiB text wrapped at width 1 and then at
    width 80 kept about 46 MB. See Text.
    (#​1602)
  • core: Key bindings use the Kitty base-layout key only when the typed key
    is one non-ASCII character. On Dvorak, Ctrl+J no longer matches
    exitOnCtrlC, and Ctrl+ㅊ still matches Ctrl+C. See
    Keyboard input.
    (#​1599)
  • core: .tsx files and tsx Markdown fences use the tree-sitter TSX
    grammar through a separate typescriptreact parser. JSX closing tags no
    longer highlight as regular expressions. See
    Syntax highlighting with Tree-sitter.
    (#​1598)
  • core: The tree-sitter worker changes #lua-match? predicates to
    #match? in bundled and registered queries when it can translate the
    pattern. TypeScript and Zig identifiers no longer all get the type and
    constant captures. See
    Syntax highlighting with Tree-sitter.
    (#​1620)
  • core: EmbeddedTerminalRenderable sends F1 to F12 with or without
    modifiers, keys that the host terminal sends in SS3 form, and the keypad
    Begin key. It sends F13 to F25 to a child in Kitty keyboard mode. See
    Embedded terminal.
    (#​1616)
  • native: The embedded terminal answers OSC 10, 11, and 12 and Kitty OSC
    21 color queries, and it applies an OSC 10 or OSC 11 that sets only one
    default color. OSC 110 resets the foreground to white, and OSC 111 resets the
    background to black. See
    Embedded terminal.
    (#​1615)
  • native: The embedded terminal answers Primary, Secondary, and Tertiary
    Device Attributes queries. fish 4.1 and later no longer stall for about two
    seconds at startup. See
    Embedded terminal.
    (#​1595)

API changes and documentation: https://opentui.com/docs/releases/0.6.0

What's Changed

[!IMPORTANT]
✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@pkg-pr-new

pkg-pr-new Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@faststats/cli@8d111fe

commit: 8d111fe

@renovate
renovate Bot force-pushed the renovate/opentui branch from b25a572 to f8f5214 Compare July 17, 2026 14:27
@renovate
renovate Bot force-pushed the renovate/opentui branch 2 times, most recently from 3801e87 to 98c897c Compare August 4, 2026 06:39
@renovate
renovate Bot force-pushed the renovate/opentui branch 4 times, most recently from 167d32f to d1f33f6 Compare August 18, 2026 15:17
@renovate
renovate Bot force-pushed the renovate/opentui branch 3 times, most recently from 33a33c2 to 3596eb8 Compare August 25, 2026 12:56
@renovate
renovate Bot force-pushed the renovate/opentui branch 2 times, most recently from e5f8841 to c38a2b3 Compare September 1, 2026 19:02
@renovate
renovate Bot force-pushed the renovate/opentui branch from c38a2b3 to 8c1a7b0 Compare September 7, 2026 13:08
@renovate
renovate Bot force-pushed the renovate/opentui branch from 8c1a7b0 to bd331c4 Compare September 22, 2026 18:50
@renovate
renovate Bot force-pushed the renovate/opentui branch from bd331c4 to 80484bd Compare October 1, 2026 03:17
@renovate renovate Bot changed the title chore(deps): update opentui chore(deps): update opentui - autoclosed Oct 4, 2026
@renovate renovate Bot closed this Oct 4, 2026
@renovate
renovate Bot deleted the renovate/opentui branch October 4, 2026 18:35
@renovate renovate Bot changed the title chore(deps): update opentui - autoclosed chore(deps): update dependency solid-js to v1.9.16 Oct 7, 2026
@renovate renovate Bot reopened this Oct 7, 2026
@renovate
renovate Bot force-pushed the renovate/opentui branch 2 times, most recently from 46c4570 to febd6be Compare October 7, 2026 11:14
@renovate renovate Bot changed the title chore(deps): update dependency solid-js to v1.9.16 chore(deps): update opentui Oct 7, 2026
@renovate
renovate Bot force-pushed the renovate/opentui branch 6 times, most recently from 287c721 to 1b35cb9 Compare October 11, 2026 21:25
@renovate
renovate Bot force-pushed the renovate/opentui branch 4 times, most recently from c9da71d to ae06eef Compare October 14, 2026 12:57
@renovate
renovate Bot force-pushed the renovate/opentui branch from ae06eef to 8d111fe Compare October 14, 2026 19:41
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.

0 participants