Skip to content

chore(deps): update opencode host runtime to v0.9.0 - #21

Merged
renovate[bot] merged 1 commit into
masterfrom
renovate/opencode-host-runtime
Nov 14, 2026
Merged

renovate[bot] merged 1 commit into
masterfrom
renovate/opencode-host-runtime

Conversation

@renovate

@renovate renovate Bot commented Nov 14, 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.8.4 → 0.9.0 age confidence
@opentui/solid (source) 0.8.4 → 0.9.0 age confidence

Release Notes

anomalyco/opentui (@​opentui/core)

v0.9.0

Compare Source

In 0.9.0, Markdown runs on OpenTUI's native engine, the same engine the Rust widget uses. In 0.8.3, MarkdownRenderable parsed with marked in JavaScript and lexed the open end of the document again on every chunk. In a growing list, the open end is the whole list, so streaming one list took 21,430 million instructions. MarkdownRenderable also sent all prose to the Tree-sitter worker for styling. The native engine reads each chunk once and writes paragraphs and headings into their text nodes itself. Streaming the same list now takes 5.9 million instructions. Over one streamed answer, JavaScript's styled-text writes for prose went from 1,595 to 0, and the prose characters sent to the highlighter went from 56,223 to 0.

Because the engine follows CommonMark 0.31.2 and GFM, some documents look different. While streaming, a line that can still change kind, such as a possible table row or a heading underline, waits for its end before it shows. A table appears when its delimiter row arrives, and task items show ☐ and ☑. The token tree is gone, so renderNode(token, context) becomes renderBlock(block, context), and you switch on block.kind instead of token.type:

// 0.8.3
renderNode: (token) => {
  if (token.type !== "heading") return undefined
  return new TextRenderable(renderer, { content: token.text.toUpperCase() })
}
// 0.9.0
renderBlock: (block) => {
  if (block.kind !== "heading") return undefined
  return new TextRenderable(renderer, { content: block.text.toUpperCase() })
}

internalBlockMode is gone: every top-level block is its own renderable, one blank row apart. Code that read the parse state reads blocks and committedBlockCount instead, and the React and Solid prop is renderBlock too. The Markdown page has the full migration list.

The new inlineHook option replaces the private chunk callback that apps had to override to show IDs as titled links. That callback missed the streaming preview and table cells, so on a typical answer 9 of 18 frames showed the raw ID. The hook gets the spans of every paragraph, heading, and table cell and returns the spans to show. A span with a target in a paragraph or heading becomes clickable through markdown.targetAt(x, y). inlineHookFilter runs the hook only on blocks that contain one of its strings, so other blocks stay on the native path:

const markdown = new MarkdownRenderable(renderer, {
  content,
  syntaxStyle,
  inlineHookFilter: ["ses_"],
  inlineHook: (spans) =>
    spans.map((span) => {
      const title = span.code ? titles.get(span.text) : undefined
      return title ? { ...span, text: title, target: span.text } : span
    }),
})

Input now has one stdin parser, the native one. 0.8 had two: the TypeScript parser was the default, and the native parser was opt-in through experimental_nativeInput. They did not always return the same keys for the same bytes. The TypeScript parser gave ESC , no key name, dropped CSI 1;2 R instead of reporting Shift+F3, and read an SGR button-32 report as a move instead of a drag. Remove experimental_nativeInput and stdinParserMaxBufferBytes from your renderer config. To report a parser bug, attach an OTUI_STDIN_LOG capture. parseKeypress() keeps its signature but decodes with the native parser, so under Node it needs --experimental-ffi. You can feel one change: once a terminal confirms the Kitty keyboard protocol, Escape arrives at once instead of after a 20 ms wait.

The declarative composition layer is gone too: h(), delegate(), vstyles, and the construct factories such as Box() and Text(). Create renderables with their classes and add their children:

// 0.8.3
renderer.root.add(Box({ border: true }, Text({ content: "Done" })))
// 0.9.0
const box = new BoxRenderable(renderer, { border: true })
box.add(new TextRenderable(renderer, { content: "Done" }))
renderer.root.add(box)

Use the t template instead of vstyles, for example t`${bold("Done")}` , and the renderAfter option instead of Generic(). Renderable.add() now accepts only a Renderable, so narrow a nullable value before you add it.

React and Solid now handle a removed prop the same way. They used to map JSX props to renderables in separate code, and the two disagreed. In Solid, a key removed from style kept its old value, so style={on ? { border: true } : {}} left the border on screen. React wrote null for a removed prop, and opacity = null means 0, so removing opacity hid the box. Both reconcilers now call setRenderableProp in Core and write a removed prop as undefined, which restores the default. If your custom setter checks value === null to detect removal, make it accept undefined too.

Two new edit buffer methods convert between JavaScript string indexes and cell offsets. Edit buffer offsets count display cells, but JavaScript indexes a string in UTF-16 units, and the two diverge at the first tab or wide character. In ab\tcd with the default tab width of 2, d is at string index 4 but at cell offset 5, so a plugin that passed the string index deleted c. Neither method moves the cursor:

buffer.setText("ab\tcd")
const offset = buffer.stringIndexToCellOffset(buffer.getText().indexOf("d")) // 5
buffer.setCursorByOffset(offset)
buffer.cellOffsetToStringIndex(buffer.getCursorPosition().offset) // 4

The new layoutMode: "list" makes long columns cheaper to lay out. Before, a long column, such as a transcript, made Yoga run its flex pass over every row when one row changed. A list lays out its rows as a column and reuses the size of each row that did not change. Rows never grow or shrink; otherwise, a list lays out like a flex column with overflow: "scroll". For ScrollBox content, set contentOptions: { layoutMode: "list" }. In a 1,000-row select, a frame that appends one row costs 1.54 million instructions in list mode. Before list mode reused row sizes, the same frame cost 22.39 million.

Breaking changes

  • core: MarkdownRenderable parses with the native engine, and marked is no longer a dependency. renderBlock(block, context) replaces renderNode(token, context), also as a React and Solid prop. blocks and committedBlockCount replace internalBlockMode and the parse state. Paragraphs and headings are TextRenderables, so code that set onChunks or onHighlight on them moves to inlineHook. The hook runs when the content or options change, also inside a setter, not when a highlight arrives. See Markdown. (#​2079, #​2023)
  • core: A MarkdownCodeBlockRenderer gets a MarkdownCodeBlock instead of Tokens.Code. Its closed field tells whether the closing fence has arrived. See Markdown. (#​2079)
  • core: The vnode composition API is removed: h(), VNode, delegate(), instantiate(), Generic(), VRenderable, vstyles, and the factories Box(), Text(), Input(), Select(), TabSelect(), ASCIIFont(), FrameBuffer(), Code(), and ScrollBox(). Create renderables with new and add(), use the t template instead of vstyles, and use the renderAfter option instead of Generic(). See Renderables. (#​2000)
  • core: Renderable.add() and insertBefore() accept only a Renderable, and TextRenderable accepts only text children. Narrow a nullable or BaseRenderable value before you add it. See Renderables. (#​2000)
  • core: Input always uses the native parser. The experimental_nativeInput and stdinParserMaxBufferBytes options, the OTUI_NATIVE_INPUT_PARSER and OTUI_NATIVE_INPUT_SHADOW variables, and StdinParser with its types are removed. Use renderer.keyInput, prependInputHandler, or subscribeOsc instead. This fixes the F3 bugs that @​FasterPHP reported. See Keyboard input. (#​1981)
  • core: MouseParser and nonAlphanumericKeys are removed. parseKeypress() decodes with the native parser, needs --experimental-ffi under Node, and returns null for empty input. Use renderable mouse events or createMockMouse instead of MouseParser, and terminalNamedSingleStrokeKeys instead of nonAlphanumericKeys. See Keyboard input. (#​2016)
  • core: React and Solid write a removed prop or style key through the new setRenderableProp as undefined, not null, so the setter restores its default. A custom setter that checks value === null must also accept undefined. See React bindings. (#​2018)
  • core: InputRenderableOptions no longer has onSubmit, which InputRenderable never called. Listen for the enter event instead. Contributed by @​posva. See Input. (#​1216)
  • core: ExtmarksController.adjustExtmarksAfterDeletion() is removed; the edit buffer now moves extmarks itself. For a malformed range, create() returns 0 instead of throwing an error. Because the buffer owns its extmarks, a controller's destroy() leaves them in place; call clear() to drop them. See Editing buffers and views. (#​2104)
  • core: Siblings with the same zIndex paint in child order instead of the order they were added. A renderable inserted or moved before a sibling paints below it, and where they overlap, the later sibling gets the click. This also applies to a Solid <Show> before an overlapping sibling. To keep a renderable on top, give it a higher zIndex. See Renderables. (#​2119)
  • native: You need Zig 0.17.1 to build the native library and the Zig module. This version fixes an LLVM miscompile of casts from signed to narrower unsigned values. See Zig. (#​2045)
  • native: In the Zig module, yoga.yogaNodeGetContentBoxChecked(node, &box) is now yoga.yogaNodeGetLayoutChecked(node, &layout, &box). yoga_c.otYogaNodeGetComputedLayout takes a third argument, null or a four-float content box. See Zig. (#​2090)
  • native: opentui.h has new calls and types, but OT_CONTEXT_ABI_VERSION stays 2. Build a C or Rust host with the header, bindings, and library from the same release. opentui-sys fails its build when the library's header differs from its bindings. See C. (#​1949, #​2049, #​2104)
  • rust: The experimental crates opentui-sys, opentui, and opentui-ssh moved into the OpenTUI repository and are not published, so depend on them by Git revision. On Linux and macOS they link the static library by default; for the shared library, turn off default features in every crate that depends on them. On Windows, ship opentui.dll beside the app. See Rust. (#​1946, #​1949, #​1984, #​1992)
  • rust: Markdown parses with the native engine and follows CommonMark and GFM, so rules and setext headings can render differently. Its code fences now wrap by word; a standalone Code still does not wrap. Input::max_length counts UTF-16 units, as Core does. See Rust. (#​2049, #​2089, #​2017)
  • rust: An app's priority-0 highlight paints under syntax colors. Textareas that share one style table share one syntax theme: the one set last on any of them. After clear_highlights, a theme change brings syntax colors back only at the next edit. See Rust. (#​2026, #​2030)
  • rust: On Unix, Ctrl+Z stops the app's whole process group, and a suspend or release drops the rest of the input from that read. On Windows, Host::release hands the terminal to its task as on Unix. ScrollbackSurface::commit_rows fails after a change of width or width method until the surface renders again. See Rust. (#​1950, #​1958, #​2043, #​2082)

Added

  • core: EditBuffer.stringIndexToCellOffset() and cellOffsetToStringIndex() convert between a JavaScript string index and a cell offset in the buffer. They do not move the cursor. Reported by @​cameronr. See Editing buffers and views. (#​2110)
  • core: MarkdownRenderable takes inlineHook and inlineHookFilter to rewrite the spans of paragraphs, headings, and table cells. rerunInlineHook() runs the hook again without reparsing. targetAt(x, y) returns the target of the paragraph or heading span that the last frame drew at that position. See Markdown. (#​2023, #​2079)
  • core: With extensions: ["math"], MarkdownRenderable parses inline math into spans with math set, and display math into a math code block. See Markdown. (#​2079, #​2078)
  • core: Markdown table columns, header included, follow the alignment in their delimiter row. TextTableRenderable takes a columnAlignments option with "left", "center", or "right" for each column. See TextTable. (#​2112)
  • core: The alignContent and aspectRatio layout options work on renderables, ScrollBox content, and React and Solid props. getAlignContent() and getAspectRatio() read them. Requested by @​nwidynski. See Layout. (#​2096)
  • core: layoutMode: "list" lays out children as a column without a flex pass over each row, so a change to one row lays out only that row. ScrollBox content takes it through contentOptions. See Layout. (#​1989, #​1960, #​1987)
  • core: StyleDefinition and theme token styles take strikethrough, and Code, Markdown, and Tree-sitter styles draw it. See Code. (#​2027)
  • core: In a Textarea, Cmd+Backspace (super+backspace) deletes to the start of the line in terminals that report Super. If your app uses this key, override keyBindings. Contributed by @​remorses. See Textarea. (#​817)
  • ssh: The server accepts OPENTUI_FORCE_TRUECOLOR from the client environment. See SSH. (#​1967)
  • native: OPENTUI_FORCE_TRUECOLOR=1 forces 24-bit color, for example inside GNU screen with its truecolor setting on. See Environment variables. (#​1967)
  • native: zig build -Dvideo=false builds the same native API without FFmpeg. A video in such a build reports that decoding is unavailable. Requested by @​AaronFeledy. See Deploy an OpenTUI application. (#​2098)
  • native: The ot_markdown_* functions expose the streaming Markdown engine. They create a document, append or set its source, read changed blocks and leaves, and bind paragraphs to text nodes. OT_MD_BLOCK_ALIGN_LEFT and OT_MD_BLOCK_ALIGN_RIGHT give the alignment of a table cell. See C. (#​2037, #​2049, #​2112)
  • native: ot_edit_buffer_mark and ot_edit_buffer_get_marks store marks in an edit buffer, which moves them with edits and restores them on undo. ot_edit_buffer_convert_offset converts between bytes, UTF-16 units, and cells. See C. (#​2094, #​2110)
  • native: ot_edit_buffer_set_layer and ot_text_buffer_set_layer write a highlight layer of ot_highlight_run byte runs in one call. ot_context_group_set registers a group set of up to OT_GROUP_SET_NAME_COUNT_MAX style names. See C. (#​2026, #​2091, #​2030)
  • native: OT_STYLE_ENUM_LAYOUT_MODE with OT_LAYOUT_MODE_LIST gives a box list layout. ot_scene_get_layout with OT_LAYOUT_CONTENT returns the content rect of a text node inside its padding. See C. (#​1960, #​2054)
  • native: New scene calls: ot_scene_hit_test_excluding skips one node, ot_scene_get_text_byte_at returns the byte that the last frame drew at a cell, and ot_scene_clear_children detaches all children in one pass. ot_scene_painted tells whether the last frame painted a node. See Frames and output. (#​2065, #​2019, #​1943, #​2036)
  • native: OT_EDITOR_STYLE_ATTRIBUTE_MASK tells which attribute bits a syntax style supplies, so a later style name can clear bold as well as set it. See C. (#​2027)
  • native: OT_INPUT_EXPECT_ESCAPE_PREFIX makes the input parser read Escape without a timeout, and OT_CONTROL_END_REPLY_SENTINEL ends the reply sentinel. See Host I/O and time. (#​1948)
  • native: opentui.h defines macros for values it used to describe only in comments: text attributes, Kitty keyboard flags, cell kinds, the indexed palette, embedded terminal codes, cursor styles, limits, and Yoga enums. The values do not change. See C. (#​2020)
  • native: The OT_SCENE_RECORD_CELLS paint record draws a run of ot_scene_cell cells in rows of a given width, each as a blended cell draw would. Rust widgets write it with Painter::cells. See Frames and output. (#​2128)
  • rust: App::on_animation_tick and Widget::on_animation_tick run animations on Core's shared clock, so animations with equal rates tick in the same frames. An animation bound to a widget parks while the widget is hidden, detached, or clipped out of view. AnimationTick::follow adds a widget to an animation, and Widget::downgrade returns a WeakWidget that does not keep the widget alive. See Rust. (#​2120, #​2134)
  • rust: ScrollBox::on_scroll reports each move of a scroll box once, with its position and cause. See Rust. (#​2129)
  • rust: Code::finish() frees the parse tree of a view whose text stopped growing, and the next change parses again. A transcript with 100 written files uses 36 MB less heap. See Rust. (#​2126)

Changed

  • core: Markdown follows CommonMark 0.31.2 and GFM. While streaming, a line that can still change kind waits for its end, and a table shows when its delimiter row arrives. Task items show ☐ and ☑. See Markdown. (#​2079, #​2078)
  • core: Each top-level Markdown block is its own renderable, one blank row apart, so apps on the old "coalesced" default now see blank rows between some blocks. Set tableOptions.style: "columns" to get the old top-level table style. See Markdown. (#​2079)
  • core: The default Markdown style strikes through ~~text~~ unless the theme sets strikethrough: false. See Markdown. (#​2027)
  • core: A streaming code fence in Markdown sends only its new text to the Tree-sitter worker. New text shows plain until its highlight arrives, and getLineHighlights() on a streaming fence returns no syntax records. MockTreeSitterClient in @opentui/core/testing has highlightAppend(). See Syntax highlighting with Tree-sitter. (#​2102)
  • core: Extmarks live in the native edit buffer. Undo and redo restore them, IDs are never reused, queries return extmarks in (start, id) order, and an edit that changes part of a virtual extmark drops it. Extmarks no longer clear the buffer's other highlights on each edit. See Editing buffers and views. (#​2104, #​2094)
  • core: When a terminal confirms the Kitty keyboard protocol, Escape arrives at once instead of after 20 ms. Kitty, Ghostty, foot, and Alacritty confirm it, but WezTerm by default and tmux do not. See Keyboard input. (#​1948, #​1971)
  • core: Theme and palette queries end at the DA1 reply instead of on timers. waitForThemeMode() resolves with null when no OSC 11 reply arrives, and getPalette() reports the default colors of a terminal that does not answer OSC 4. See Terminal capabilities. (#​1969)
  • core: In split-footer mode, a frame writes up to 64 captured stdout rows instead of 8, so output at 2,000 rows per second no longer falls behind. writeToScrollback commits stay at 8 per frame. See Renderer. (#​2084)
  • three: Writing ThreeRenderable.aspectRatio sets the Yoga layout ratio, which getAspectRatio() returns; reading it still returns the display ratio. An aspectRatio passed at construction no longer throws. See Three.js WebGPU. (#​2101)
  • native: Inside GNU screen, OpenTUI ignores COLORTERM and WT_SESSION and writes 256-color SGR, because screen drops 24-bit colors by default. Set OPENTUI_FORCE_TRUECOLOR=1 when screen's truecolor setting is on. See Terminal capabilities. (#​1967, #​1982)
  • native: WT_SESSION enables OSC 8 hyperlinks on every host OS when TERM is unset, empty, or starts with xterm and no multiplexer is present. So a Windows Terminal client gets clickable links from a Linux or macOS host when a remote session forwards WT_SESSION. See Terminal capabilities. (#​1976)
  • native: Notifications in tmux and GNU screen use the same passthrough as OSC 52. Inside screen, each non-ASCII character in OSC 777 and OSC 9 text becomes a space. See Notifications. (#​2021)
  • native: Each Linux libopentui.a includes a private libc++ and libc++abi. A static link needs no system libc++ and also works on musl. See C. (#​1991)
  • native: When two highlight ranges with the same priority cover a cell, the later range wins. Before, the winner depended on hash-map order. See Editing buffers and views. (#​2025)
  • native: Layout does less work per frame. Rounding visits only the subtrees that a solve laid out, a larger height cap counts as a cache hit, and Linux builds call Yoga's inline helpers directly. A status glyph beside 10,000 rows costs 30.6% fewer instructions per frame. See Rendering pipeline. (#​1951, #​1966, #​2040, #​2090)
  • native: OpenTUI builds highlight spans at paint time, only for the lines that a view draws, and a layer write splices only the runs that change. 4,000 ranges on one 24,000-byte line cost about 47 million instructions instead of 5.95 billion. See Rendering pipeline. (#​2025, #​2100, #​2111)
  • native: A text or Display write that changes nothing does no work, and removing a first or last child takes one search step. Destroying 40,000 children in reverse order takes 0.84 billion instructions instead of 13.2 billion. See Renderables. (#​1942, #​1943)
  • rust: Code, Diff, and Textarea views no longer wrap their text at width 0 before their first frame, and Code views and Diff rows share their theme and signs instead of copying them. 100 Code views mounted before their first draw take 20 MB instead of 438 MB. See Rust. (#​2124, #​2123)

Fixed

  • core: In a ScrollBox with 16 or more children, a Code or Markdown block that changes in the same tick that moves it out of view measures its new content at once. Descendants of a culled child report their solved layout. See ScrollBox. (#​2003)
  • core: A streaming Code block keeps its highlight on screen when it scrolls away during a highlight, when rows above it go away, and through a theme or conceal change. See Code. (#​2036)
  • core: Committed Markdown blocks stay final while streaming. A split-footer transcript no longer loses the paragraph after a list or near link reference definitions. See Markdown. (#​2029, #​2039)
  • core: Text with padding paints, wraps, hits, and selects inside its padding. Before, it painted at the outer edge of its box and wrapped at the full width. Remove any indent that you added to make up for this. See Text. (#​2054)
  • core: Layout after a change now matches a fresh layout. This fixes text widened by minWidth, children one cell off in a cached box, a column in a baseline-aligned row, stale wraps after small width changes, and nodes added under display: none, which now read 0 × 0. See Layout. (#​1947, #​1968, #​1995, #​2009)
  • core: Without wrapping, the visual line end (Meta+E or Cmd+Right) moves the cursor to the end of a line that is wider than the field. See Input. (#​2053)
  • core: A wheel step that moves the Textarea cursor onto a wrapped row keeps the cursor on that row, so the wheel can show the middle rows of a long paragraph. See Textarea. (#​2076)
  • core: Left from the end of a virtual extmark goes to the character before it, or to its start when another virtual extmark ends there, also after a CJK character, emoji, or tab. A wheel scroll, click, or drag no longer leaves the cursor inside an extmark. See Editing buffers and views. (#​2050, #​2064)
  • core: A left press during a held gesture means the terminal dropped the release, so OpenTUI ends that gesture first. Clicks work again after a lost release, and a ScrollBox stops its auto-scroll. Reported by @​mrbubble62. See Interaction, focus, and selection. (#​2065)
  • core: Under the Kitty protocol, text typed quickly after Escape, such as OA or [TODO], arrives as typed. A bracketed paste arrives whole even when a pause splits its start marker. See Keyboard input. (#​2046, #​1971)
  • core: With reportText, Kitty key text longer than 8 characters arrives whole, up to about 42 CJK characters. See Keyboard input. (#​1972)
  • core: A focused EmbeddedTerminalRenderable sends Ctrl+J to its program when the host terminal does not use the Kitty protocol. See Embedded terminal. (#​2022)
  • core: In an InputRenderable at maxLength, typed or pasted text replaces the selection. A cut at maxLength no longer splits a character. See Input. (#​1999, #​2017)
  • core: DiffRenderable keeps a blank context line and numbers the lines after it correctly. See Diff. (#​2017)
  • core: A click on a Slider thumb without pointer movement keeps the value and emits no change event. A click on the track emits one change event. Both fixes also apply to scrollbars. Contributed by @​Kidyoh. See Slider. (#​2031)
  • core: remove() accepts a child from another copy of the Core bundle, as add() already did. Contributed by @​AksharP5 and reported by @​letrquan. See Renderables. (#​1535)
  • core: EditBuffer.getText() and TextBuffer.getPlainText() keep a leading U+FEFF. Contributed by @​sean0407. See Editing buffers and views. (#​1626)
  • core: Setting treeSitterClient on Diff and Markdown, scrollMargin on editors, the ASCIIFont selection colors, or the line-number gutter props after construction now takes effect, so these props work in Solid JSX. Reported by @​filipsobol. See Solid bindings. (#​2097)
  • core: The Tree-sitter worker frees replaced syntax trees and failed parsers and runs the messages of each buffer in order. 200 full edits of a large file no longer grow the worker from 32 MiB to 166 MiB, and a buffer removed during createBuffer() stays removed. Reported by @​JosXa, @​RaphaelFakhri, and @​Osraka. See Syntax highlighting with Tree-sitter. (#​1978, #​2002, #​2073)
  • core: suspend() keeps the exit-signal handlers until it settles. A SIGTERM or SIGHUP during a suspension restores the terminal. See Lifecycle and cleanup. (#​2083)
  • core: With externalOutputMode: "capture-stdout", a combining mark or VS16 at the start of a write joins the previous character. Captured rows use the renderer's width method, so text after a wide cluster stays visible. See Renderer. (#​2067, #​1929)
  • core: A split-footer app keeps its history across an alternate-screen round trip. When the footer grows in xterm.js terminals such as VS Code, the top rows go to scrollback. Based on work by @​viralcodex, and reported by @​cevr. See Renderer. (#​2041, #​2042)
  • core: The all-available clipboard writes OSC 52 after the host write settles, so the terminal keeps the copy on Wayland and X11. Contributed by @​clemenswasser. See Clipboard. (#​2028)
  • core: A linked plugin package that imports a host module by name gets the host's copy, so Solid ownership, memos, and stores work across it. Reported by @​cevr. See Load plugins and modules at runtime. (#​2095)
  • core: A video decode failure at the end of a clip or at a loop fires onError instead of onEnd. See Video. (#​2074)
  • core: An autoplay video fires its first onTimeUpdate after onReady, and reports a failed state read through onError once. See Video. (#​2127)
  • core: setCursorByOffset() with an offset past the end of the text moves the cursor to the end instead of the start. Contributed by @​latent-9. See Editing buffers and views. (#​2131)
  • solid: The Solid transform compiles JSX files in node_modules again, so runtime plugins from npm or git get reactive props. See Load plugins and modules at runtime. (#​2051)
  • react: The published bundle uses react/jsx-runtime, so the error boundary shows its fallback under NODE_ENV=production instead of a blank screen. Reported by @​nwidynski. See React bindings. (#​2092)
  • keymap: A leader or Neovim-style sequence gives queued input a turn before it expires. Ctrl+X then f runs the shortcut even after an input stall. Reported by @​cevr. See Built-in keymap addons. (#​2099)
  • native: On Windows and macOS, a clipboard write or clear that blocks in the OS call ends as timed-out at its deadline. The text can still land later. See Clipboard. (#​2038)
  • native: In kitty, the OSC 66 startup probe no longer overwrites the top two history rows of a split-footer app. The main screen gets no drawing probe, and OpenTUI recognizes kitty 0.40+ and foot 1.21+ from their XTVERSION reply. See Terminal capabilities. (#​2056)
  • native: Inside GNU screen, startup no longer prints +q4d73. A remote session detects screen from TERM, so OSC 52 copies are wrapped and emoji widths match. See Terminal capabilities. (#​1945, #​1961)
  • native: When a reply selects wcwidth, OpenTUI turns off mode 2027, and teardown restores the mode as it was before startup. Reported by @​mmick66. See Terminal capabilities. (#​1986)
  • native: On a Windows host, remote sessions ask the client terminal about 24-bit color and skip the ConPTY close teardown. See Terminal capabilities. (#​1957, #​1959)
  • native: An image whose Kitty upload does not fit one frame of output shows as Kitty graphics instead of half blocks: OpenTUI sends one scaled copy, and scrolling or resizing only places it again. Half-block cells drawn over a Kitty image go away with their image. See Image. (#​2135, #​2130)
  • rust: Timers, deferred tasks, and quit() calls that a frame starts run on time in an otherwise idle app. Before, they waited for the next unrelated wakeup, so a headless app could fail to end. See Rust. (#​2118)
  • rust: A split-footer app whose terminal does not answer the cursor-position query sleeps between startup replies instead of using a full core for five seconds. See Rust. (#​2122)
  • rust: An app that ends with deferred tasks, timers, handlers, or spawned tasks still queued no longer panics or hangs in teardown. See Rust. (#​2132, #​2120)

Security

  • native: An OSC 8 link target must be non-empty UTF-8 of at most 512 bytes with no control characters. Before, an ESC or BEL in a link target ended the sequence, and the terminal ran the rest as commands. Reported by @​carsteneu, who also wrote the first fix. See Terminal capabilities. (#​1996)
  • native: Notification text must be UTF-8, and C0, DEL, and C1 characters become spaces. OSC 9 text that starts with a digit gets a leading space, and triggerNotification() returns false in ConEmu. See Notifications. (#​1998)

API changes and documentation: https://opentui.com/docs/releases/0.9.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: Enabled.

♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


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

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

@renovate
renovate Bot enabled auto-merge (squash) November 14, 2026 15:00
@renovate
renovate Bot merged commit 3b09a35 into master Nov 14, 2026
4 checks passed
@renovate
renovate Bot deleted the renovate/opencode-host-runtime branch November 14, 2026 17:08
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