These docs describe the main branch, including unreleased changes. Read the 0.9.0 docs.

OpenTUI 0.9.0

npmGitHub release

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#

@opentui/core#

Added

  • enum-member NativeEditCommand.CursorOffsetAtomic = 13
  • enum-member NativeEditCommand.KeepOutside = 14
  • function parseAlignContent(value: string | null | undefined): Align
  • function parseKeypress(s?: Buffer | string, options?: ParseKeypressOptions): ParsedKey | null
  • function parseLayoutMode(value: string | null | undefined): number
  • function setRenderableProp(node: BaseRenderable, name: string, value: any, prev: any): void
  • interface MarkdownBlock
  • interface MarkdownCodeBlock extends MarkdownBlock
  • interface MarkdownInlineContext
  • interface MarkdownInlineSpan
  • method EditBuffer.cellOffsetToStringIndex(offset: number): number
  • method EditBuffer.stringIndexToCellOffset(index: number): number
  • method FFIRenderLib.contextEditBufferConvertOffset(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, value: number, toCell: boolean): number
  • method FFIRenderLib.contextEditBufferGetMarks(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, id?: number, kind?: number): NativeEditMark[]
  • method FFIRenderLib.contextEditBufferMark(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, operation: NativeEditMarkOperation, mark?: Partial<NativeEditMark>): number
  • method FFIRenderLib.contextGroupSet(context: NativeContextHandle, names: readonly string[]): number
  • method FFIRenderLib.contextTextBufferSetLayer(context: NativeContextHandle, text: ContextTextBufferHandle, layerId: number, start: number, end: number, runs: Uint32Array): void
  • method FFIRenderLib.markdownBind(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number, node: SceneNodeHandle): void
  • method FFIRenderLib.markdownCreate(context: NativeContextHandle, options: NativeMarkdownOptions): ContextMarkdownHandle
  • method FFIRenderLib.markdownDestroy(context: NativeContextHandle, doc: ContextMarkdownHandle): void
  • method FFIRenderLib.markdownFinish(context: NativeContextHandle, doc: ContextMarkdownHandle): NativeMarkdownResult
  • method FFIRenderLib.markdownGetSource(context: NativeContextHandle, doc: ContextMarkdownHandle, start?: number, end?: number): string
  • method FFIRenderLib.markdownReadChanges(context: NativeContextHandle, doc: ContextMarkdownHandle, count: number): NativeMarkdownBlock[]
  • method FFIRenderLib.markdownReadLeaf(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number): NativeMarkdownLeaf
  • method FFIRenderLib.markdownReadSource(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number): string
  • method FFIRenderLib.markdownSetOptions(context: NativeContextHandle, doc: ContextMarkdownHandle, options: NativeMarkdownOptions): NativeMarkdownResult
  • method FFIRenderLib.markdownWrite(context: NativeContextHandle, doc: ContextMarkdownHandle, bytes: Uint8Array, replace: boolean): NativeMarkdownResult
  • method FFIRenderLib.sceneGetTextByteAt(context: NativeContextHandle, node: SceneNodeHandle, x: number, y: number): number | undefined
  • method FFIRenderLib.scenePainted(context: NativeContextHandle, node: SceneNodeHandle): boolean
  • method FFIRenderLib.sceneReachesHooks(context: NativeContextHandle, node: SceneNodeHandle): boolean
  • method MarkdownRenderable.rerunInlineHook(select: (source: string) => boolean): void
  • method MarkdownRenderable.targetAt(x: number, y: number): string | undefined
  • method Renderable.getAlignContent(): Align
  • method Renderable.getAspectRatio(): number
  • property ASCIIFontRenderable.selectionBg: RGBA | undefined
  • property ASCIIFontRenderable.selectionFg: RGBA | undefined
  • property DiffRenderable.treeSitterClient: TreeSitterClient | undefined
  • readonly property EditBuffer.isDestroyed: boolean
  • property EditBufferRenderable.scrollMargin: number
  • property LayoutOptions.alignContent?: AlignString
  • property LayoutOptions.aspectRatio?: number
  • property LayoutOptions.layoutMode?: LayoutModeString
  • property LineNumberRenderable.hideLineNumbers: LineNumberOptions["hideLineNumbers"]
  • property LineNumberRenderable.lineColors: LineNumberOptions["lineColors"]
  • property LineNumberRenderable.lineNumbers: LineNumberOptions["lineNumbers"]
  • property LineNumberRenderable.lineSigns: LineNumberOptions["lineSigns"]
  • property MarkdownOptions.extensions?: readonly "math"[]
  • property MarkdownOptions.inlineHook?: MarkdownInlineHook
  • property MarkdownOptions.inlineHookFilter?: readonly string[]
  • property MarkdownOptions.renderBlock?: (block: MarkdownBlock, context: RenderNodeContext) => Renderable | undefined | null
  • readonly property MarkdownRenderable.blocks: readonly { kind: MarkdownBlockKind; renderable: Renderable | undefined; }[]
  • readonly property MarkdownRenderable.committedBlockCount: number
  • property MarkdownRenderable.extensions: readonly "math"[]
  • property MarkdownRenderable.inlineHook: MarkdownInlineHook | undefined
  • property MarkdownRenderable.inlineHookFilter: readonly string[] | undefined
  • property MarkdownRenderable.renderBlock: MarkdownOptions["renderBlock"] | undefined
  • property MarkdownRenderable.treeSitterClient: TreeSitterClient | undefined
  • property NativeEditorStyle.suppliedAttributes?: number | null
  • property Renderable.alignContent: AlignString | null | undefined
  • property Renderable.aspectRatio: number | null | undefined
  • property Renderable.layoutMode: LayoutModeString | null | undefined
  • property StyleDefinition.strikethrough?: boolean
  • property StyleDefinitionInput.strikethrough?: boolean
  • property TextTableOptions.columnAlignments?: TextTableColumnAlignment[]
  • property TextTableRenderable.columnAlignments: TextTableColumnAlignment[]
  • type LayoutModeString = "flex" | "list"
  • type MarkdownBlockKind = "paragraph" | "heading" | "code" | "html" | "rule" | "table" | "row" | "cell" | "quote" | "list" | "item"
  • type MarkdownInlineHook = (spans: MarkdownInlineSpan[], context: MarkdownInlineContext) => MarkdownInlineSpan[]
  • type TextTableColumnAlignment = "left" | "center" | "right"

Changed

  • const ATTRIBUTE_BASE_BITS = 8const ATTRIBUTE_BASE_BITS: 8
  • const ATTRIBUTE_BASE_MASK = 255const ATTRIBUTE_BASE_MASK: 255
  • function createMarkdownCodeBlockRenderer(renderers: MarkdownCodeBlockRendererMap): MarkdownOptions["renderNode"]function createMarkdownCodeBlockRenderer(renderers: MarkdownCodeBlockRendererMap): MarkdownOptions["renderBlock"]
  • interface InputRenderableOptions extends Omit<TextareaOptions, "height" | "minHeight" | "maxHeight" | "initialValue">interface InputRenderableOptions extends Omit<TextareaOptions, "height" | "minHeight" | "maxHeight" | "initialValue" | "onSubmit">
  • abstract method BaseRenderable.add(obj: BaseRenderable | unknown, index?: number): numberabstract method BaseRenderable.add(obj: unknown, index?: number): number
  • abstract method BaseRenderable.insertBefore(obj: BaseRenderable | unknown, anchor: BaseRenderable | unknown): voidabstract method BaseRenderable.insertBefore(obj: unknown, anchor: unknown): void
  • method FFIRenderLib.sceneGetLayout(context: NativeContextHandle, node: SceneNodeHandle, observation?: boolean | "paint"): NativeSceneLayoutmethod FFIRenderLib.sceneGetLayout(context: NativeContextHandle, node: SceneNodeHandle, observation?: boolean | "paint" | "content"): NativeSceneLayout
  • method FFIRenderLib.sceneHitTest(context: NativeContextHandle, session: SessionHandle, x: number, y: number): numbermethod FFIRenderLib.sceneHitTest(context: NativeContextHandle, session: SessionHandle, x: number, y: number, excluded?: number): number
  • method Renderable.add(obj: Renderable | VNode<any, any[]> | unknown, index?: number): numbermethod Renderable.add(obj: Renderable, index?: number): number
  • method Renderable.insertBefore(obj: Renderable | VNode<any, any[]> | unknown, anchor?: Renderable | unknown): numbermethod Renderable.insertBefore(obj: Renderable, anchor?: Renderable): number
  • method ScrollBoxContent.add(obj: Renderable | VNode<any, any[]>, index?: number): numbermethod ScrollBoxContent.add(obj: Renderable, index?: number): number
  • method ScrollBoxContent.insertBefore(obj: Renderable | VNode<any, any[]> | unknown, anchor?: Renderable | unknown): numbermethod ScrollBoxContent.insertBefore(obj: Renderable, anchor?: Renderable): number
  • method TextRenderable.add(obj: never, index?: number): number
  • method TextRenderable.insertBefore(obj: BaseRenderable | any, anchor?: TextNodeRenderable): numbermethod TextRenderable.insertBefore(obj: TextNodeRenderable | StyledText | string, anchor?: TextNodeRenderable): numbermethod TextRenderable.insertBefore(obj: never, anchor?: never): number
  • protected property MarkdownRenderable._contentDefaultOptions: { content: string; conceal: true; concealCode: false; streaming: false; internalBlockMode: "coalesced"; }protected property MarkdownRenderable._contentDefaultOptions: { content: string; conceal: true; concealCode: false; streaming: false; }
  • property SelectRenderable.keyBindings: SelectKeyBinding[]property SelectRenderable.keyBindings: SelectKeyBinding[] | null | undefined
  • property TabSelectRenderable.keyBindings: TabSelectKeyBinding[]property TabSelectRenderable.keyBindings: TabSelectKeyBinding[] | null | undefined
  • property TextareaRenderable.keyBindings: KeyBinding[]property TextareaRenderable.keyBindings: KeyBinding[] | null | undefined
  • property ThemeTokenStyle.style: { foreground?: ColorInput; background?: ColorInput; bold?: boolean; italic?: boolean; underline?: boolean; dim?: boolean; }property ThemeTokenStyle.style: { foreground?: ColorInput; background?: ColorInput; bold?: boolean; italic?: boolean; underline?: boolean; dim?: boolean; strikethrough?: boolean; }
  • type MarkdownCodeBlockRenderer = (token: Tokens.Code, context: RenderNodeContext) => Renderable | undefined | nulltype MarkdownCodeBlockRenderer = (block: MarkdownCodeBlock, context: RenderNodeContext) => Renderable | undefined | null
  • type NativeSessionControl = { kind: "capability-response"; bytes: Uint8Array; } | { kind: "palette-query"; bytes: Uint8Array; } | { kind: "title"; title: string; } | { kind: "mouse"; mode: "disabled" | "drag" | "motion"; } | { kind: "kitty-keyboard-flags"; flags: number; } | { kind: "program-status"; status: ProgramStatus; } | { kind: "query-pixel-resolution"; sentinel?: boolean; } | { kind: "restore-modes" | "query-theme-colors" | "reset-background"; }type NativeSessionControl = { kind: "capability-response"; bytes: Uint8Array; } | { kind: "palette-query"; bytes: Uint8Array; sentinel?: boolean; } | { kind: "title"; title: string; } | { kind: "mouse"; mode: "disabled" | "drag" | "motion"; } | { kind: "kitty-keyboard-flags"; flags: number; } | { kind: "program-status"; status: ProgramStatus; } | { kind: "query-pixel-resolution" | "query-theme-colors"; sentinel?: boolean; } | { kind: "restore-modes" | "reset-background" | "end-reply-sentinel"; }
  • type TreeSitterWorkerRequest = { type: "INIT"; dataPath: string; treeSitterWasmPath?: string; } | { type: "ADD_FILETYPE_PARSER"; filetypeParser: FiletypeParserOptions; } | { type: "PRELOAD_PARSER"; filetype: string; messageId: string; } | { type: "INITIALIZE_PARSER"; bufferId: number; version: number; content: string; filetype: string; messageId: string; } | { type: "HANDLE_EDITS"; bufferId: number; version: number; content: string; edits: Edit[]; } | { type: "GET_PERFORMANCE"; messageId: string; } | { type: "RESET_BUFFER"; bufferId: number; version: number; content: string; edits: Edit[]; } | { type: "DISPOSE_BUFFER"; bufferId: number; } | { type: "ONESHOT_HIGHLIGHT"; content: string; filetype: string; messageId: string; } | { type: "UPDATE_DATA_PATH"; dataPath: string; messageId: string; } | { type: "CLEAR_CACHE"; messageId: string; }type TreeSitterWorkerRequest = { type: "INIT"; dataPath: string; treeSitterWasmPath?: string; } | { type: "ADD_FILETYPE_PARSER"; filetypeParser: FiletypeParserOptions; } | { type: "PRELOAD_PARSER"; filetype: string; messageId: string; } | { type: "INITIALIZE_PARSER"; bufferId: number; version: number; content: string; filetype: string; messageId: string; } | { type: "HANDLE_EDITS"; bufferId: number; version: number; content: string; edits: Edit[]; } | { type: "GET_PERFORMANCE"; messageId: string; } | { type: "RESET_BUFFER"; bufferId: number; version: number; content: string; edits: Edit[]; } | { type: "DISPOSE_BUFFER"; bufferId: number; } | { type: "ONESHOT_HIGHLIGHT"; content: string; filetype: string; messageId: string; } | { type: "APPEND_HIGHLIGHT"; copyId: number; offset: number; text: string; filetype: string; messageId: string; } | { type: "DISPOSE_COPY"; copyId: number; } | { type: "UPDATE_DATA_PATH"; dataPath: string; messageId: string; } | { type: "CLEAR_CACHE"; messageId: string; }

Removed

  • class MouseParser
  • class StdinParser
  • class VRenderable extends Renderable
  • const nonAlphanumericKeys: string[]
  • const parseKeypress: (s?: Buffer | string, options?: ParseKeypressOptions) => ParsedKey | null
  • const vstyles: { bold: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; italic: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; underline: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; dim: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; blink: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; inverse: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; hidden: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; strikethrough: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; boldItalic: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; boldUnderline: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; italicUnderline: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; boldItalicUnderline: (...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; color: (color: string | RGBA, ...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; bgColor: (bgColor: string | RGBA, ...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; fg: (color: string | RGBA, ...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; bg: (bgColor: string | RGBA, ...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; styled: (attributes?: number, ...children: (string | TextNodeRenderable)[]) => TextNodeRenderable; }
  • construct RenderableConstructor(ctx: RenderContext, options: P): Renderable
  • constructor StdinParser(options?: StdinParserOptions)
  • constructor VRenderable(ctx: RenderContext, options: VRenderableOptions)
  • function ASCIIFont(props?: ASCIIFontOptions, ...children: VChild[]): ProxiedVNode<typeof ASCIIFontRenderable>
  • function Box(props?: BoxOptions, ...children: VChild[]): ProxiedVNode<typeof BoxRenderable>
  • function Code(props: CodeOptions, ...children: VChild[]): ProxiedVNode<typeof CodeRenderable>
  • function FrameBuffer(props: FrameBufferOptions, ...children: VChild[]): ProxiedVNode<typeof FrameBufferRenderable>
  • function Generic(props?: VRenderableOptions, ...children: VChild[]): ProxiedVNode<typeof VRenderable>
  • function Input(props?: InputRenderableOptions, ...children: VChild[]): ProxiedVNode<typeof InputRenderable>
  • function ScrollBox(props?: ScrollBoxOptions, ...children: VChild[]): ProxiedVNode<typeof ScrollBoxRenderable>
  • function Select(props?: SelectRenderableOptions, ...children: VChild[]): ProxiedVNode<typeof SelectRenderable>
  • function TabSelect(props?: TabSelectRenderableOptions, ...children: VChild[]): ProxiedVNode<typeof TabSelectRenderable>
  • function Text(props?: TextOptions & { content?: any; }, ...children: VChild[] | TextNodeRenderable[]): ProxiedVNode<typeof TextRenderable>
  • function delegate<ConstructorType extends RenderableConstructor<any>, TargetMap extends Record<keyof InstanceType<ConstructorType>, string>, const Mapping extends Partial<TargetMap>>(mapping: ValidateShape<Mapping, TargetMap>, vnode: ProxiedVNode<ConstructorType>): ProxiedVNode<ConstructorType>function delegate<ConstructorType extends RenderableConstructor<any>, const Mapping extends DelegateMap<InstanceType<ConstructorType>>>(mapping: ValidateShape<Mapping, string>, vnode: VNode & { type: ConstructorType; }): VNodefunction delegate<Factory extends InstantiateFn<any>, InnerNode extends InferNode<Factory>, TargetMap extends Record<keyof InnerNode, string>, const Mapping extends Partial<TargetMap>>(mapping: ValidateShape<Mapping, TargetMap>, vnode: Factory): Renderable
  • function h<P>(type: Construct<P>, props?: P, ...children: VChild[]): VNode<P> | ProxiedVNode<any>function h<P>(type: FunctionalConstruct<P>, props?: P, ...children: VChild[]): VNode<P>function h<TCtor extends RenderableConstructor<any>>(type: TCtor, props?: TCtor extends RenderableConstructor<infer P> ? P : never, ...children: VChild[]): ProxiedVNode<TCtor>
  • function instantiate<NodeType extends VNode | Renderable>(ctx: RenderContext, node: NodeType): InstantiateFn<NodeType>
  • function isVNode(node: any): node is VNode
  • function maybeMakeRenderable(ctx: RenderContext, node: Renderable | VNode<any, any[]> | unknown): Renderable | null
  • function wrapWithDelegates<T extends InstanceType<RenderableConstructor>>(instance: T, delegateMap: Record<string, string> | undefined): T
  • interface BlockState
  • interface ParseState
  • interface PendingCall
  • interface RenderableConstructor<P extends RenderableOptions<any> = RenderableOptions<any>>
  • interface StdinParserOptions
  • interface StdinParserProtocolContext
  • interface VNode<P = any, C = VChild[]>
  • interface VRenderableOptions extends RenderableOptions<VRenderable>
  • method ExtmarksController.adjustExtmarksAfterDeletion(deleteOffset: number, length: number, updateHighlights?: boolean): void
  • property CliRendererConfig.experimental_nativeInput?: boolean
  • property CliRendererConfig.stdinParserMaxBufferBytes?: number
  • property MarkdownOptions.internalBlockMode?: "coalesced" | "top-level"
  • property MarkdownOptions.renderNode?: (token: Token, context: RenderNodeContext) => Renderable | undefined | null
  • property MarkdownRenderable.internalBlockMode: "coalesced" | "top-level"
  • property MarkdownRenderable.renderNode: MarkdownOptions["renderNode"] | undefined
  • type Construct<P = any> = RenderableConstructor<P extends RenderableOptions<any> ? P : never> | FunctionalConstruct<P>
  • type DelegateMap<T> = Partial<Record<keyof T, string>>
  • type FunctionalConstruct<P = any> = (props: P, children?: VChild[]) => VNode
  • type InstantiateFn<NodeType extends VNode | Renderable> = Renderable & { __node?: NodeType; }
  • type ProxiedVNode<TCtor extends RenderableConstructor<any>> = VNode<TCtor extends RenderableConstructor<infer P> ? P : any> & { [K in keyof InstanceType<TCtor>]: InstanceType<TCtor>[K] extends (...args: infer Args) => any ? (...args: Args) => ProxiedVNode<TCtor> : InstanceType<TCtor>[K]; }
  • type StdinEvent = { type: "key"; raw: string; key: ParsedKey; } | { type: "mouse"; raw: string; encoding: "sgr" | "x10"; event: RawMouseEvent; } | { type: "paste"; bytes: Uint8Array; metadata?: PasteMetadata; } | { type: "response"; protocol: StdinResponseProtocol; sequence: string; }
  • type StdinResponseProtocol = "csi" | "cpr" | "osc" | "dcs" | "apc" | "unknown"
  • type VChild = VNode | Renderable | VChild[] | null | undefined | false
  • type ValidateShape<Given, AllowedKeys> = { [K in keyof Given]: K extends keyof AllowedKeys ? NonNullable<Given[K]> : never; }

@opentui/core/testing#

Added

  • method MockTreeSitterClient.highlightAppend(_copyId: number, _offset: number, text: string, filetype: string): Promise<{ highlights?: SimpleHighlight[]; warning?: string; error?: string; }>

@opentui/react#

Changed

  • type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" | "bottomTitle" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : TConstructor extends RenderableConstructor<TextareaRenderable> ? NonStyledProps | "placeholder" | "initialValue" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" | "conceal" | "drawUnstyledText" : TConstructor extends RenderableConstructor<MarkdownRenderable> ? NonStyledProps | "content" | "syntaxStyle" | "treeSitterClient" | "conceal" | "renderNode" : TConstructor extends RenderableConstructor<ImageRenderable> ? NonStyledProps | "source" : TConstructor extends RenderableConstructor<VideoRenderable> ? NonStyledProps | "source" | "autoplay" | "loop" | "audio" | "muted" | "volume" | "avSyncOffsetMs" | "maxFps" : NonStyledPropstype GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" | "bottomTitle" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : TConstructor extends RenderableConstructor<TextareaRenderable> ? NonStyledProps | "placeholder" | "initialValue" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" | "conceal" | "drawUnstyledText" : TConstructor extends RenderableConstructor<MarkdownRenderable> ? NonStyledProps | "content" | "syntaxStyle" | "treeSitterClient" | "conceal" | "renderBlock" : TConstructor extends RenderableConstructor<ImageRenderable> ? NonStyledProps | "source" : TConstructor extends RenderableConstructor<VideoRenderable> ? NonStyledProps | "source" | "autoplay" | "loop" | "audio" | "muted" | "volume" | "avSyncOffsetMs" | "maxFps" : NonStyledProps

@opentui/solid#

Changed

  • type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" | "bottomTitle" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "minLength" | "maxLength" | "placeholder" | "value" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" : TConstructor extends RenderableConstructor<MarkdownRenderable> ? NonStyledProps | "content" | "syntaxStyle" | "treeSitterClient" | "conceal" | "renderNode" : TConstructor extends RenderableConstructor<ImageRenderable> ? NonStyledProps | "source" : TConstructor extends RenderableConstructor<VideoRenderable> ? NonStyledProps | "source" | "autoplay" | "loop" | "audio" | "muted" | "volume" | "avSyncOffsetMs" | "maxFps" : NonStyledPropstype GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" | "bottomTitle" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "minLength" | "maxLength" | "placeholder" | "value" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" : TConstructor extends RenderableConstructor<MarkdownRenderable> ? NonStyledProps | "content" | "syntaxStyle" | "treeSitterClient" | "conceal" | "renderBlock" : TConstructor extends RenderableConstructor<ImageRenderable> ? NonStyledProps | "source" : TConstructor extends RenderableConstructor<VideoRenderable> ? NonStyledProps | "source" | "autoplay" | "loop" | "audio" | "muted" | "volume" | "avSyncOffsetMs" | "maxFps" : NonStyledProps

@opentui/three#

Changed

  • readonly property ThreeRenderable.aspectRatio: numberproperty ThreeRenderable.aspectRatio: number

opentui.h#

Added

  • const OT_ATTR_BASE_MASK = 255
  • const OT_ATTR_BLINK = 16
  • const OT_ATTR_BOLD = 1
  • const OT_ATTR_DIM = 2
  • const OT_ATTR_HIDDEN = 64
  • const OT_ATTR_INVERSE = 32
  • const OT_ATTR_ITALIC = 4
  • const OT_ATTR_LINK_MASK = 16777215
  • const OT_ATTR_LINK_SHIFT = 8
  • const OT_ATTR_STRIKETHROUGH = 128
  • const OT_ATTR_UNDERLINE = 8
  • const OT_CELL_CHAR_CONTINUATION = 3221225472
  • const OT_CELL_CHAR_GRAPHEME = 2147483648
  • const OT_CELL_CHAR_IMAGE = 1073741824
  • const OT_CELL_CHAR_KIND_MASK = 3221225472
  • const OT_COLOR_ANSI_0 = 0
  • const OT_COLOR_ANSI_1 = 8388608
  • const OT_COLOR_ANSI_10 = 65280
  • const OT_COLOR_ANSI_11 = 16776960
  • const OT_COLOR_ANSI_12 = 255
  • const OT_COLOR_ANSI_13 = 16711935
  • const OT_COLOR_ANSI_14 = 65535
  • const OT_COLOR_ANSI_15 = 16777215
  • const OT_COLOR_ANSI_2 = 32768
  • const OT_COLOR_ANSI_3 = 8421376
  • const OT_COLOR_ANSI_4 = 128
  • const OT_COLOR_ANSI_5 = 8388736
  • const OT_COLOR_ANSI_6 = 32896
  • const OT_COLOR_ANSI_7 = 12632256
  • const OT_COLOR_ANSI_8 = 8421504
  • const OT_COLOR_ANSI_9 = 16711680
  • const OT_COLOR_CUBE_BASE = 55
  • const OT_COLOR_CUBE_STEP = 40
  • const OT_COLOR_GRAY_BASE = 8
  • const OT_COLOR_GRAY_STEP = 10
  • const OT_CONTROL_END_REPLY_SENTINEL = 12
  • const OT_CURSOR_STYLE_BLOCK = 0
  • const OT_CURSOR_STYLE_DEFAULT = 3
  • const OT_CURSOR_STYLE_LINE = 1
  • const OT_CURSOR_STYLE_UNDERLINE = 2
  • const OT_DIAGNOSTIC_QUEUE_MAX = 64
  • const OT_DISPLAY_CONTENTS = 2
  • const OT_EDITOR_STYLE_ATTRIBUTE_MASK = 8
  • const OT_EDIT_ATOMIC = 1
  • const OT_EDIT_BYTE_TO_CELL = 0
  • const OT_EDIT_CELL_TO_BYTE = 1
  • const OT_EDIT_CELL_TO_UTF16 = 3
  • const OT_EDIT_CURSOR_OFFSET_ATOMIC = 13
  • const OT_EDIT_KEEP_OUTSIDE = 14
  • const OT_EDIT_MARK_ADD = 0
  • const OT_EDIT_MARK_ANY_KIND = 4294967295
  • const OT_EDIT_MARK_ATOMIC = 1
  • const OT_EDIT_MARK_CLEAR = 2
  • const OT_EDIT_MARK_REMOVE = 1
  • const OT_EDIT_MARK_STYLE_VALUE = 2
  • const OT_EDIT_UTF16_TO_CELL = 2
  • const OT_EMBEDDED_TERMINAL_BUTTON_FIVE = 5
  • const OT_EMBEDDED_TERMINAL_BUTTON_FOUR = 4
  • const OT_EMBEDDED_TERMINAL_BUTTON_LEFT = 1
  • const OT_EMBEDDED_TERMINAL_BUTTON_MIDDLE = 3
  • const OT_EMBEDDED_TERMINAL_BUTTON_NONE = -1
  • const OT_EMBEDDED_TERMINAL_BUTTON_RIGHT = 2
  • const OT_EMBEDDED_TERMINAL_BUTTON_SEVEN = 7
  • const OT_EMBEDDED_TERMINAL_BUTTON_SIX = 6
  • const OT_EMBEDDED_TERMINAL_BUTTON_UNKNOWN = 0
  • const OT_EMBEDDED_TERMINAL_CURSOR_BAR = 0
  • const OT_EMBEDDED_TERMINAL_CURSOR_BLOCK = 1
  • const OT_EMBEDDED_TERMINAL_CURSOR_BLOCK_HOLLOW = 3
  • const OT_EMBEDDED_TERMINAL_CURSOR_UNDERLINE = 2
  • const OT_EMBEDDED_TERMINAL_INPUT_BYTES_MAX = 1048576
  • const OT_EMBEDDED_TERMINAL_KEY_PRESS = 1
  • const OT_EMBEDDED_TERMINAL_KEY_RELEASE = 0
  • const OT_EMBEDDED_TERMINAL_KEY_REPEAT = 2
  • const OT_EMBEDDED_TERMINAL_MOD_ALT = 4
  • const OT_EMBEDDED_TERMINAL_MOD_CAPS_LOCK = 16
  • const OT_EMBEDDED_TERMINAL_MOD_CTRL = 2
  • const OT_EMBEDDED_TERMINAL_MOD_NUM_LOCK = 32
  • const OT_EMBEDDED_TERMINAL_MOD_SHIFT = 1
  • const OT_EMBEDDED_TERMINAL_MOD_SUPER = 8
  • const OT_EMBEDDED_TERMINAL_MOUSE_MOTION = 2
  • const OT_EMBEDDED_TERMINAL_MOUSE_PRESS = 0
  • const OT_EMBEDDED_TERMINAL_MOUSE_RELEASE = 1
  • const OT_EMBEDDED_TERMINAL_RESPONSE_BYTES_MAX = 1048576
  • const OT_GROUP_SET_NAME_BYTE_COUNT_MAX = 64
  • const OT_GROUP_SET_NAME_COUNT_MAX = 16
  • const OT_IMAGE_ENCODED_BYTES_MAX = 67108864
  • const OT_INPUT_EXPECT_ESCAPE_PREFIX = 16
  • const OT_KITTY_KEYBOARD_ALL = 31
  • const OT_KITTY_KEYBOARD_ALL_KEYS_AS_ESCAPES = 8
  • const OT_KITTY_KEYBOARD_ALTERNATE_KEYS = 4
  • const OT_KITTY_KEYBOARD_DISAMBIGUATE = 1
  • const OT_KITTY_KEYBOARD_EVENT_TYPES = 2
  • const OT_KITTY_KEYBOARD_REPORT_TEXT = 16
  • const OT_LAYOUT_CONTENT = 3
  • const OT_LAYOUT_MODE_FLEX = 0
  • const OT_LAYOUT_MODE_LIST = 1
  • const OT_MD_BLOCK_ALIGN_LEFT = 4096
  • const OT_MD_BLOCK_ALIGN_RIGHT = 8192
  • const OT_MD_BLOCK_BLANK_AFTER = 64
  • const OT_MD_BLOCK_CHECKED = 32
  • const OT_MD_BLOCK_CLOSED = 1
  • const OT_MD_BLOCK_FENCED = 128
  • const OT_MD_BLOCK_HEADER = 1024
  • const OT_MD_BLOCK_HOOKED = 256
  • const OT_MD_BLOCK_MATH = 512
  • const OT_MD_BLOCK_OPEN = 2048
  • const OT_MD_BLOCK_ORDERED = 4
  • const OT_MD_BLOCK_SETEXT = 2
  • const OT_MD_BLOCK_TASK = 16
  • const OT_MD_BLOCK_TIGHT = 8
  • const OT_MD_CODE = 2
  • const OT_MD_CONCEAL = 1
  • const OT_MD_HEADING = 1
  • const OT_MD_HOOK = 8
  • const OT_MD_HOOK_FILTER_BYTE_COUNT_MAX = 256
  • const OT_MD_HTML = 3
  • const OT_MD_HYPERLINKS = 2
  • const OT_MD_ITEM = 10
  • const OT_MD_LIST = 9
  • const OT_MD_MATH = 4
  • const OT_MD_PARAGRAPH = 0
  • const OT_MD_QUOTE = 8
  • const OT_MD_RULE = 4
  • const OT_MD_SPAN_CODE = 1
  • const OT_MD_SPAN_MATH = 2
  • const OT_MD_TABLE = 5
  • const OT_MD_TABLE_CELL = 7
  • const OT_MD_TABLE_ROW = 6
  • const OT_SCENE_RECORD_CELLS = 13
  • const OT_SCENE_SCROLL_PADDING_MAX = 65535
  • const OT_SPLIT_SNAPSHOTS_MAX = 64
  • const OT_STYLE_ENUM_LAYOUT_MODE = 11
  • const OT_STYLE_ENUM_LAYOUT_MODE_MAX = 1
  • const OT_YOGA_ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE = 4
  • const OT_YOGA_ERRATA_ABSOLUTE_POSITION_WITHOUT_INSETS_EXCLUDES_PADDING = 2
  • const OT_YOGA_ERRATA_ALL = 2147483647
  • const OT_YOGA_ERRATA_CLASSIC = 2147483646
  • const OT_YOGA_ERRATA_NONE = 0
  • const OT_YOGA_ERRATA_STRETCH_FLEX_BASIS = 1
  • const OT_YOGA_EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS = 0
  • const OT_YOGA_LOG_LEVEL_DEBUG = 3
  • const OT_YOGA_LOG_LEVEL_ERROR = 0
  • const OT_YOGA_LOG_LEVEL_FATAL = 5
  • const OT_YOGA_LOG_LEVEL_INFO = 2
  • const OT_YOGA_LOG_LEVEL_VERBOSE = 4
  • const OT_YOGA_LOG_LEVEL_WARN = 1
  • const OT_YOGA_NODE_TYPE_DEFAULT = 0
  • const OT_YOGA_NODE_TYPE_TEXT = 1
  • function ot_context_group_set(ot_context *, const uint8_t *names, uint32_t name_byte_count, const uint32_t *name_end_bytes, uint32_t name_count, uint32_t *out_group_set): ot_status
  • function ot_edit_buffer_convert_offset(ot_context *, const ot_handle *edit_buffer, uint32_t conversion, uint32_t value, uint32_t *out_value): ot_status
  • function ot_edit_buffer_get_marks(ot_context *, const ot_handle *, uint32_t id, uint32_t kind, ot_edit_mark *marks, uint32_t capacity, uint32_t *out_count): ot_status
  • function ot_edit_buffer_mark(ot_context *, const ot_handle *, uint32_t operation, const ot_edit_mark *mark, uint32_t *out_id): ot_status
  • function ot_edit_buffer_set_layer(ot_context *, const ot_handle *, uint32_t layer_id, uint32_t start_byte, uint32_t end_byte, const ot_highlight_run *runs, uint32_t run_count): ot_status
  • function ot_markdown_append(ot_context *, const ot_handle *doc, const uint8_t *bytes, uint32_t byte_count, ot_markdown_result *out): ot_status
  • function ot_markdown_bind(ot_context *, const ot_handle *doc, uint32_t block_index, const ot_handle *text_node): ot_status
  • function ot_markdown_create(ot_context *, const ot_markdown_options *, ot_handle *out_doc): ot_status
  • function ot_markdown_destroy(ot_context *, const ot_handle *doc): ot_status
  • function ot_markdown_finish(ot_context *, const ot_handle *doc, ot_markdown_result *out): ot_status
  • function ot_markdown_get_source(ot_context *, const ot_handle *doc, const uint8_t **out_bytes, uint32_t *out_byte_count): ot_status
  • function ot_markdown_read_changes(ot_context *, const ot_handle *doc, ot_markdown_block *blocks, uint32_t block_capacity, uint32_t *out_block_count): ot_status
  • function ot_markdown_read_leaf(ot_context *, const ot_handle *doc, uint32_t block_index, uint8_t *bytes, uint32_t byte_capacity, uint32_t *out_byte_count, ot_styled_text_chunk *chunks, uint32_t *spans, uint32_t chunk_capacity, uint32_t *out_chunk_count, uint8_t *urls, uint32_t url_byte_capacity, uint32_t *out_url_byte_count): ot_status
  • function ot_markdown_read_source(ot_context *, const ot_handle *doc, uint32_t block_index, uint8_t *bytes, uint32_t byte_capacity, uint32_t *out_byte_count): ot_status
  • function ot_markdown_set(ot_context *, const ot_handle *doc, const uint8_t *bytes, uint32_t byte_count, ot_markdown_result *out): ot_status
  • function ot_markdown_set_options(ot_context *, const ot_handle *doc, const ot_markdown_options *, ot_markdown_result *out): ot_status
  • function ot_scene_clear_children(ot_context *, const ot_handle *node): ot_status
  • function ot_scene_get_text_byte_at(ot_context *, const ot_handle *node, int32_t x, int32_t y, uint32_t *out_byte): ot_status
  • function ot_scene_hit_test_excluding(ot_context *, const ot_handle *session, int32_t x, int32_t y, uint32_t excluded_num, uint32_t *out_num): ot_status
  • function ot_scene_painted(ot_context *, const ot_handle *node, uint32_t *out_painted): ot_status
  • function ot_scene_reaches_hooks(ot_context *, const ot_handle *node, uint32_t *out_reached): ot_status
  • function ot_text_buffer_set_layer(ot_context *, const ot_handle *, uint32_t layer_id, uint32_t start_byte, uint32_t end_byte, const ot_highlight_run *runs, uint32_t run_count): ot_status
  • struct ot_edit_mark
  • struct ot_highlight_run
  • struct ot_markdown_block
  • struct ot_markdown_options
  • struct ot_markdown_result
  • struct ot_scene_cell
  • struct ot_scene_record_cells