OpenTUI 0.9.0
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:
MarkdownRenderableparses with the native engine, andmarkedis no longer a dependency.renderBlock(block, context)replacesrenderNode(token, context), also as a React and Solid prop.blocksandcommittedBlockCountreplaceinternalBlockModeand the parse state. Paragraphs and headings areTextRenderables, so code that setonChunksoronHighlighton them moves toinlineHook. The hook runs when the content or options change, also inside a setter, not when a highlight arrives. See Markdown. (#2079, #2023) - core: A
MarkdownCodeBlockRenderergets aMarkdownCodeBlockinstead ofTokens.Code. Itsclosedfield 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 factoriesBox(),Text(),Input(),Select(),TabSelect(),ASCIIFont(),FrameBuffer(),Code(), andScrollBox(). Create renderables withnewandadd(), use thettemplate instead ofvstyles, and use therenderAfteroption instead ofGeneric(). See Renderables. (#2000) - core:
Renderable.add()andinsertBefore()accept only aRenderable, andTextRenderableaccepts only text children. Narrow a nullable orBaseRenderablevalue before you add it. See Renderables. (#2000) - core: Input always uses the native parser. The
experimental_nativeInputandstdinParserMaxBufferBytesoptions, theOTUI_NATIVE_INPUT_PARSERandOTUI_NATIVE_INPUT_SHADOWvariables, andStdinParserwith its types are removed. Userenderer.keyInput,prependInputHandler, orsubscribeOscinstead. This fixes the F3 bugs that @FasterPHP reported. See Keyboard input. (#1981) - core:
MouseParserandnonAlphanumericKeysare removed.parseKeypress()decodes with the native parser, needs--experimental-ffiunder Node, and returnsnullfor empty input. Use renderable mouse events orcreateMockMouseinstead ofMouseParser, andterminalNamedSingleStrokeKeysinstead ofnonAlphanumericKeys. See Keyboard input. (#2016) - core: React and Solid write a removed prop or
stylekey through the newsetRenderablePropasundefined, notnull, so the setter restores its default. A custom setter that checksvalue === nullmust also acceptundefined. See React bindings. (#2018) - core:
InputRenderableOptionsno longer hasonSubmit, whichInputRenderablenever called. Listen for theenterevent 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’sdestroy()leaves them in place; callclear()to drop them. See Editing buffers and views. (#2104) - core: Siblings with the same
zIndexpaint 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 higherzIndex. 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 nowyoga.yogaNodeGetLayoutChecked(node, &layout, &box).yoga_c.otYogaNodeGetComputedLayouttakes a third argument,nullor a four-float content box. See Zig. (#2090) - native:
opentui.hhas new calls and types, butOT_CONTEXT_ABI_VERSIONstays 2. Build a C or Rust host with the header, bindings, and library from the same release.opentui-sysfails its build when the library’s header differs from its bindings. See C. (#1949, #2049, #2104) - rust: The experimental crates
opentui-sys,opentui, andopentui-sshmoved 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, shipopentui.dllbeside the app. See Rust. (#1946, #1949, #1984, #1992) - rust:
Markdownparses 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 standaloneCodestill does not wrap.Input::max_lengthcounts 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::releasehands the terminal to its task as on Unix.ScrollbackSurface::commit_rowsfails after a change of width or width method until the surface renders again. See Rust. (#1950, #1958, #2043, #2082)
Added
- core:
EditBuffer.stringIndexToCellOffset()andcellOffsetToStringIndex()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:
MarkdownRenderabletakesinlineHookandinlineHookFilterto rewrite the spans of paragraphs, headings, and table cells.rerunInlineHook()runs the hook again without reparsing.targetAt(x, y)returns thetargetof the paragraph or heading span that the last frame drew at that position. See Markdown. (#2023, #2079) - core: With
extensions: ["math"],MarkdownRenderableparses inline math into spans withmathset, and display math into amathcode block. See Markdown. (#2079, #2078) - core: Markdown table columns, header included, follow the alignment in their delimiter row.
TextTableRenderabletakes acolumnAlignmentsoption with"left","center", or"right"for each column. See TextTable. (#2112) - core: The
alignContentandaspectRatiolayout options work on renderables, ScrollBox content, and React and Solid props.getAlignContent()andgetAspectRatio()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 throughcontentOptions. See Layout. (#1989, #1960, #1987) - core:
StyleDefinitionand theme token styles takestrikethrough, 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, overridekeyBindings. Contributed by @remorses. See Textarea. (#817) - ssh: The server accepts
OPENTUI_FORCE_TRUECOLORfrom the client environment. See SSH. (#1967) - native:
OPENTUI_FORCE_TRUECOLOR=1forces 24-bit color, for example inside GNU screen with itstruecolorsetting on. See Environment variables. (#1967) - native:
zig build -Dvideo=falsebuilds 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_LEFTandOT_MD_BLOCK_ALIGN_RIGHTgive the alignment of a table cell. See C. (#2037, #2049, #2112) - native:
ot_edit_buffer_markandot_edit_buffer_get_marksstore marks in an edit buffer, which moves them with edits and restores them on undo.ot_edit_buffer_convert_offsetconverts between bytes, UTF-16 units, and cells. See C. (#2094, #2110) - native:
ot_edit_buffer_set_layerandot_text_buffer_set_layerwrite a highlight layer ofot_highlight_runbyte runs in one call.ot_context_group_setregisters a group set of up toOT_GROUP_SET_NAME_COUNT_MAXstyle names. See C. (#2026, #2091, #2030) - native:
OT_STYLE_ENUM_LAYOUT_MODEwithOT_LAYOUT_MODE_LISTgives a box list layout.ot_scene_get_layoutwithOT_LAYOUT_CONTENTreturns the content rect of a text node inside its padding. See C. (#1960, #2054) - native: New scene calls:
ot_scene_hit_test_excludingskips one node,ot_scene_get_text_byte_atreturns the byte that the last frame drew at a cell, andot_scene_clear_childrendetaches all children in one pass.ot_scene_paintedtells whether the last frame painted a node. See Frames and output. (#2065, #2019, #1943, #2036) - native:
OT_EDITOR_STYLE_ATTRIBUTE_MASKtells 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_PREFIXmakes the input parser read Escape without a timeout, andOT_CONTROL_END_REPLY_SENTINELends the reply sentinel. See Host I/O and time. (#1948) - native:
opentui.hdefines 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_CELLSpaint record draws a run ofot_scene_cellcells in rows of a given width, each as a blended cell draw would. Rust widgets write it withPainter::cells. See Frames and output. (#2128) - rust:
App::on_animation_tickandWidget::on_animation_tickrun 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::followadds a widget to an animation, andWidget::downgradereturns aWeakWidgetthat does not keep the widget alive. See Rust. (#2120, #2134) - rust:
ScrollBox::on_scrollreports 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. SettableOptions.style: "columns"to get the old top-level table style. See Markdown. (#2079) - core: The default Markdown style strikes through
~~text~~unless the theme setsstrikethrough: 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.MockTreeSitterClientin@opentui/core/testinghashighlightAppend(). 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 withnullwhen no OSC 11 reply arrives, andgetPalette()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.
writeToScrollbackcommits stay at 8 per frame. See Renderer. (#2084) - three: Writing
ThreeRenderable.aspectRatiosets the Yoga layout ratio, whichgetAspectRatio()returns; reading it still returns the display ratio. AnaspectRatiopassed at construction no longer throws. See Three.js WebGPU. (#2101) - native: Inside GNU screen, OpenTUI ignores
COLORTERMandWT_SESSIONand writes 256-color SGR, because screen drops 24-bit colors by default. SetOPENTUI_FORCE_TRUECOLOR=1when screen’struecolorsetting is on. See Terminal capabilities. (#1967, #1982) - native:
WT_SESSIONenables OSC 8 hyperlinks on every host OS whenTERMis unset, empty, or starts withxtermand no multiplexer is present. So a Windows Terminal client gets clickable links from a Linux or macOS host when a remote session forwardsWT_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.aincludes 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, andTextareaviews 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 underdisplay: 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
OAor[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
EmbeddedTerminalRenderablesends Ctrl+J to its program when the host terminal does not use the Kitty protocol. See Embedded terminal. (#2022) - core: In an
InputRenderableatmaxLength, typed or pasted text replaces the selection. A cut atmaxLengthno longer splits a character. See Input. (#1999, #2017) - core:
DiffRenderablekeeps 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
changeevent. A click on the track emits onechangeevent. Both fixes also apply to scrollbars. Contributed by @Kidyoh. See Slider. (#2031) - core:
remove()accepts a child from another copy of the Core bundle, asadd()already did. Contributed by @AksharP5 and reported by @letrquan. See Renderables. (#1535) - core:
EditBuffer.getText()andTextBuffer.getPlainText()keep a leading U+FEFF. Contributed by @sean0407. See Editing buffers and views. (#1626) - core: Setting
treeSitterClienton Diff and Markdown,scrollMarginon 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-availableclipboard 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
onErrorinstead ofonEnd. See Video. (#2074) - core: An autoplay video fires its first
onTimeUpdateafteronReady, and reports a failed state read throughonErroronce. 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_modulesagain, 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 underNODE_ENV=productioninstead 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
fruns 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-outat 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 fromTERM, 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()returnsfalsein ConEmu. See Notifications. (#1998)
API changes#
@opentui/core#
Added
enum-member NativeEditCommand.CursorOffsetAtomic = 13enum-member NativeEditCommand.KeepOutside = 14function parseAlignContent(value: string | null | undefined): Alignfunction parseKeypress(s?: Buffer | string, options?: ParseKeypressOptions): ParsedKey | nullfunction parseLayoutMode(value: string | null | undefined): numberfunction setRenderableProp(node: BaseRenderable, name: string, value: any, prev: any): voidinterface MarkdownBlockinterface MarkdownCodeBlock extends MarkdownBlockinterface MarkdownInlineContextinterface MarkdownInlineSpanmethod EditBuffer.cellOffsetToStringIndex(offset: number): numbermethod EditBuffer.stringIndexToCellOffset(index: number): numbermethod FFIRenderLib.contextEditBufferConvertOffset(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, value: number, toCell: boolean): numbermethod FFIRenderLib.contextEditBufferGetMarks(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, id?: number, kind?: number): NativeEditMark[]method FFIRenderLib.contextEditBufferMark(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, operation: NativeEditMarkOperation, mark?: Partial<NativeEditMark>): numbermethod FFIRenderLib.contextGroupSet(context: NativeContextHandle, names: readonly string[]): numbermethod FFIRenderLib.contextTextBufferSetLayer(context: NativeContextHandle, text: ContextTextBufferHandle, layerId: number, start: number, end: number, runs: Uint32Array): voidmethod FFIRenderLib.markdownBind(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number, node: SceneNodeHandle): voidmethod FFIRenderLib.markdownCreate(context: NativeContextHandle, options: NativeMarkdownOptions): ContextMarkdownHandlemethod FFIRenderLib.markdownDestroy(context: NativeContextHandle, doc: ContextMarkdownHandle): voidmethod FFIRenderLib.markdownFinish(context: NativeContextHandle, doc: ContextMarkdownHandle): NativeMarkdownResultmethod FFIRenderLib.markdownGetSource(context: NativeContextHandle, doc: ContextMarkdownHandle, start?: number, end?: number): stringmethod FFIRenderLib.markdownReadChanges(context: NativeContextHandle, doc: ContextMarkdownHandle, count: number): NativeMarkdownBlock[]method FFIRenderLib.markdownReadLeaf(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number): NativeMarkdownLeafmethod FFIRenderLib.markdownReadSource(context: NativeContextHandle, doc: ContextMarkdownHandle, block: number): stringmethod FFIRenderLib.markdownSetOptions(context: NativeContextHandle, doc: ContextMarkdownHandle, options: NativeMarkdownOptions): NativeMarkdownResultmethod FFIRenderLib.markdownWrite(context: NativeContextHandle, doc: ContextMarkdownHandle, bytes: Uint8Array, replace: boolean): NativeMarkdownResultmethod FFIRenderLib.sceneGetTextByteAt(context: NativeContextHandle, node: SceneNodeHandle, x: number, y: number): number | undefinedmethod FFIRenderLib.scenePainted(context: NativeContextHandle, node: SceneNodeHandle): booleanmethod FFIRenderLib.sceneReachesHooks(context: NativeContextHandle, node: SceneNodeHandle): booleanmethod MarkdownRenderable.rerunInlineHook(select: (source: string) => boolean): voidmethod MarkdownRenderable.targetAt(x: number, y: number): string | undefinedmethod Renderable.getAlignContent(): Alignmethod Renderable.getAspectRatio(): numberproperty ASCIIFontRenderable.selectionBg: RGBA | undefinedproperty ASCIIFontRenderable.selectionFg: RGBA | undefinedproperty DiffRenderable.treeSitterClient: TreeSitterClient | undefinedreadonly property EditBuffer.isDestroyed: booleanproperty EditBufferRenderable.scrollMargin: numberproperty LayoutOptions.alignContent?: AlignStringproperty LayoutOptions.aspectRatio?: numberproperty LayoutOptions.layoutMode?: LayoutModeStringproperty 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?: MarkdownInlineHookproperty MarkdownOptions.inlineHookFilter?: readonly string[]property MarkdownOptions.renderBlock?: (block: MarkdownBlock, context: RenderNodeContext) => Renderable | undefined | nullreadonly property MarkdownRenderable.blocks: readonly { kind: MarkdownBlockKind; renderable: Renderable | undefined; }[]readonly property MarkdownRenderable.committedBlockCount: numberproperty MarkdownRenderable.extensions: readonly "math"[]property MarkdownRenderable.inlineHook: MarkdownInlineHook | undefinedproperty MarkdownRenderable.inlineHookFilter: readonly string[] | undefinedproperty MarkdownRenderable.renderBlock: MarkdownOptions["renderBlock"] | undefinedproperty MarkdownRenderable.treeSitterClient: TreeSitterClient | undefinedproperty NativeEditorStyle.suppliedAttributes?: number | nullproperty Renderable.alignContent: AlignString | null | undefinedproperty Renderable.aspectRatio: number | null | undefinedproperty Renderable.layoutMode: LayoutModeString | null | undefinedproperty StyleDefinition.strikethrough?: booleanproperty StyleDefinitionInput.strikethrough?: booleanproperty 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: 8const ATTRIBUTE_BASE_MASK = 255const ATTRIBUTE_BASE_MASK: 255function 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): numberabstract method BaseRenderable.insertBefore(obj: BaseRenderable | unknown, anchor: BaseRenderable | unknown): voidabstract method BaseRenderable.insertBefore(obj: unknown, anchor: unknown): voidmethod FFIRenderLib.sceneGetLayout(context: NativeContextHandle, node: SceneNodeHandle, observation?: boolean | "paint"): NativeSceneLayoutmethod FFIRenderLib.sceneGetLayout(context: NativeContextHandle, node: SceneNodeHandle, observation?: boolean | "paint" | "content"): NativeSceneLayoutmethod FFIRenderLib.sceneHitTest(context: NativeContextHandle, session: SessionHandle, x: number, y: number): numbermethod FFIRenderLib.sceneHitTest(context: NativeContextHandle, session: SessionHandle, x: number, y: number, excluded?: number): numbermethod Renderable.add(obj: Renderable | VNode<any, any[]> | unknown, index?: number): numbermethod Renderable.add(obj: Renderable, index?: number): numbermethod Renderable.insertBefore(obj: Renderable | VNode<any, any[]> | unknown, anchor?: Renderable | unknown): numbermethod Renderable.insertBefore(obj: Renderable, anchor?: Renderable): numbermethod ScrollBoxContent.add(obj: Renderable | VNode<any, any[]>, index?: number): numbermethod ScrollBoxContent.add(obj: Renderable, index?: number): numbermethod ScrollBoxContent.insertBefore(obj: Renderable | VNode<any, any[]> | unknown, anchor?: Renderable | unknown): numbermethod ScrollBoxContent.insertBefore(obj: Renderable, anchor?: Renderable): numbermethod TextRenderable.add(obj: never, index?: number): numbermethod TextRenderable.insertBefore(obj: BaseRenderable | any, anchor?: TextNodeRenderable): numbermethod TextRenderable.insertBefore(obj: TextNodeRenderable | StyledText | string, anchor?: TextNodeRenderable): numbermethod TextRenderable.insertBefore(obj: never, anchor?: never): numberprotected 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 | undefinedproperty TabSelectRenderable.keyBindings: TabSelectKeyBinding[]property TabSelectRenderable.keyBindings: TabSelectKeyBinding[] | null | undefinedproperty TextareaRenderable.keyBindings: KeyBinding[]property TextareaRenderable.keyBindings: KeyBinding[] | null | undefinedproperty 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 | nulltype 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 MouseParserclass StdinParserclass VRenderable extends Renderableconst nonAlphanumericKeys: string[]const parseKeypress: (s?: Buffer | string, options?: ParseKeypressOptions) => ParsedKey | nullconst 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): Renderableconstructor 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): Renderablefunction 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 VNodefunction maybeMakeRenderable(ctx: RenderContext, node: Renderable | VNode<any, any[]> | unknown): Renderable | nullfunction wrapWithDelegates<T extends InstanceType<RenderableConstructor>>(instance: T, delegateMap: Record<string, string> | undefined): Tinterface BlockStateinterface ParseStateinterface PendingCallinterface RenderableConstructor<P extends RenderableOptions<any> = RenderableOptions<any>>interface StdinParserOptionsinterface StdinParserProtocolContextinterface VNode<P = any, C = VChild[]>interface VRenderableOptions extends RenderableOptions<VRenderable>method ExtmarksController.adjustExtmarksAfterDeletion(deleteOffset: number, length: number, updateHighlights?: boolean): voidproperty CliRendererConfig.experimental_nativeInput?: booleanproperty CliRendererConfig.stdinParserMaxBufferBytes?: numberproperty MarkdownOptions.internalBlockMode?: "coalesced" | "top-level"property MarkdownOptions.renderNode?: (token: Token, context: RenderNodeContext) => Renderable | undefined | nullproperty MarkdownRenderable.internalBlockMode: "coalesced" | "top-level"property MarkdownRenderable.renderNode: MarkdownOptions["renderNode"] | undefinedtype 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[]) => VNodetype 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 | falsetype 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 = 255const OT_ATTR_BLINK = 16const OT_ATTR_BOLD = 1const OT_ATTR_DIM = 2const OT_ATTR_HIDDEN = 64const OT_ATTR_INVERSE = 32const OT_ATTR_ITALIC = 4const OT_ATTR_LINK_MASK = 16777215const OT_ATTR_LINK_SHIFT = 8const OT_ATTR_STRIKETHROUGH = 128const OT_ATTR_UNDERLINE = 8const OT_CELL_CHAR_CONTINUATION = 3221225472const OT_CELL_CHAR_GRAPHEME = 2147483648const OT_CELL_CHAR_IMAGE = 1073741824const OT_CELL_CHAR_KIND_MASK = 3221225472const OT_COLOR_ANSI_0 = 0const OT_COLOR_ANSI_1 = 8388608const OT_COLOR_ANSI_10 = 65280const OT_COLOR_ANSI_11 = 16776960const OT_COLOR_ANSI_12 = 255const OT_COLOR_ANSI_13 = 16711935const OT_COLOR_ANSI_14 = 65535const OT_COLOR_ANSI_15 = 16777215const OT_COLOR_ANSI_2 = 32768const OT_COLOR_ANSI_3 = 8421376const OT_COLOR_ANSI_4 = 128const OT_COLOR_ANSI_5 = 8388736const OT_COLOR_ANSI_6 = 32896const OT_COLOR_ANSI_7 = 12632256const OT_COLOR_ANSI_8 = 8421504const OT_COLOR_ANSI_9 = 16711680const OT_COLOR_CUBE_BASE = 55const OT_COLOR_CUBE_STEP = 40const OT_COLOR_GRAY_BASE = 8const OT_COLOR_GRAY_STEP = 10const OT_CONTROL_END_REPLY_SENTINEL = 12const OT_CURSOR_STYLE_BLOCK = 0const OT_CURSOR_STYLE_DEFAULT = 3const OT_CURSOR_STYLE_LINE = 1const OT_CURSOR_STYLE_UNDERLINE = 2const OT_DIAGNOSTIC_QUEUE_MAX = 64const OT_DISPLAY_CONTENTS = 2const OT_EDITOR_STYLE_ATTRIBUTE_MASK = 8const OT_EDIT_ATOMIC = 1const OT_EDIT_BYTE_TO_CELL = 0const OT_EDIT_CELL_TO_BYTE = 1const OT_EDIT_CELL_TO_UTF16 = 3const OT_EDIT_CURSOR_OFFSET_ATOMIC = 13const OT_EDIT_KEEP_OUTSIDE = 14const OT_EDIT_MARK_ADD = 0const OT_EDIT_MARK_ANY_KIND = 4294967295const OT_EDIT_MARK_ATOMIC = 1const OT_EDIT_MARK_CLEAR = 2const OT_EDIT_MARK_REMOVE = 1const OT_EDIT_MARK_STYLE_VALUE = 2const OT_EDIT_UTF16_TO_CELL = 2const OT_EMBEDDED_TERMINAL_BUTTON_FIVE = 5const OT_EMBEDDED_TERMINAL_BUTTON_FOUR = 4const OT_EMBEDDED_TERMINAL_BUTTON_LEFT = 1const OT_EMBEDDED_TERMINAL_BUTTON_MIDDLE = 3const OT_EMBEDDED_TERMINAL_BUTTON_NONE = -1const OT_EMBEDDED_TERMINAL_BUTTON_RIGHT = 2const OT_EMBEDDED_TERMINAL_BUTTON_SEVEN = 7const OT_EMBEDDED_TERMINAL_BUTTON_SIX = 6const OT_EMBEDDED_TERMINAL_BUTTON_UNKNOWN = 0const OT_EMBEDDED_TERMINAL_CURSOR_BAR = 0const OT_EMBEDDED_TERMINAL_CURSOR_BLOCK = 1const OT_EMBEDDED_TERMINAL_CURSOR_BLOCK_HOLLOW = 3const OT_EMBEDDED_TERMINAL_CURSOR_UNDERLINE = 2const OT_EMBEDDED_TERMINAL_INPUT_BYTES_MAX = 1048576const OT_EMBEDDED_TERMINAL_KEY_PRESS = 1const OT_EMBEDDED_TERMINAL_KEY_RELEASE = 0const OT_EMBEDDED_TERMINAL_KEY_REPEAT = 2const OT_EMBEDDED_TERMINAL_MOD_ALT = 4const OT_EMBEDDED_TERMINAL_MOD_CAPS_LOCK = 16const OT_EMBEDDED_TERMINAL_MOD_CTRL = 2const OT_EMBEDDED_TERMINAL_MOD_NUM_LOCK = 32const OT_EMBEDDED_TERMINAL_MOD_SHIFT = 1const OT_EMBEDDED_TERMINAL_MOD_SUPER = 8const OT_EMBEDDED_TERMINAL_MOUSE_MOTION = 2const OT_EMBEDDED_TERMINAL_MOUSE_PRESS = 0const OT_EMBEDDED_TERMINAL_MOUSE_RELEASE = 1const OT_EMBEDDED_TERMINAL_RESPONSE_BYTES_MAX = 1048576const OT_GROUP_SET_NAME_BYTE_COUNT_MAX = 64const OT_GROUP_SET_NAME_COUNT_MAX = 16const OT_IMAGE_ENCODED_BYTES_MAX = 67108864const OT_INPUT_EXPECT_ESCAPE_PREFIX = 16const OT_KITTY_KEYBOARD_ALL = 31const OT_KITTY_KEYBOARD_ALL_KEYS_AS_ESCAPES = 8const OT_KITTY_KEYBOARD_ALTERNATE_KEYS = 4const OT_KITTY_KEYBOARD_DISAMBIGUATE = 1const OT_KITTY_KEYBOARD_EVENT_TYPES = 2const OT_KITTY_KEYBOARD_REPORT_TEXT = 16const OT_LAYOUT_CONTENT = 3const OT_LAYOUT_MODE_FLEX = 0const OT_LAYOUT_MODE_LIST = 1const OT_MD_BLOCK_ALIGN_LEFT = 4096const OT_MD_BLOCK_ALIGN_RIGHT = 8192const OT_MD_BLOCK_BLANK_AFTER = 64const OT_MD_BLOCK_CHECKED = 32const OT_MD_BLOCK_CLOSED = 1const OT_MD_BLOCK_FENCED = 128const OT_MD_BLOCK_HEADER = 1024const OT_MD_BLOCK_HOOKED = 256const OT_MD_BLOCK_MATH = 512const OT_MD_BLOCK_OPEN = 2048const OT_MD_BLOCK_ORDERED = 4const OT_MD_BLOCK_SETEXT = 2const OT_MD_BLOCK_TASK = 16const OT_MD_BLOCK_TIGHT = 8const OT_MD_CODE = 2const OT_MD_CONCEAL = 1const OT_MD_HEADING = 1const OT_MD_HOOK = 8const OT_MD_HOOK_FILTER_BYTE_COUNT_MAX = 256const OT_MD_HTML = 3const OT_MD_HYPERLINKS = 2const OT_MD_ITEM = 10const OT_MD_LIST = 9const OT_MD_MATH = 4const OT_MD_PARAGRAPH = 0const OT_MD_QUOTE = 8const OT_MD_RULE = 4const OT_MD_SPAN_CODE = 1const OT_MD_SPAN_MATH = 2const OT_MD_TABLE = 5const OT_MD_TABLE_CELL = 7const OT_MD_TABLE_ROW = 6const OT_SCENE_RECORD_CELLS = 13const OT_SCENE_SCROLL_PADDING_MAX = 65535const OT_SPLIT_SNAPSHOTS_MAX = 64const OT_STYLE_ENUM_LAYOUT_MODE = 11const OT_STYLE_ENUM_LAYOUT_MODE_MAX = 1const OT_YOGA_ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE = 4const OT_YOGA_ERRATA_ABSOLUTE_POSITION_WITHOUT_INSETS_EXCLUDES_PADDING = 2const OT_YOGA_ERRATA_ALL = 2147483647const OT_YOGA_ERRATA_CLASSIC = 2147483646const OT_YOGA_ERRATA_NONE = 0const OT_YOGA_ERRATA_STRETCH_FLEX_BASIS = 1const OT_YOGA_EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS = 0const OT_YOGA_LOG_LEVEL_DEBUG = 3const OT_YOGA_LOG_LEVEL_ERROR = 0const OT_YOGA_LOG_LEVEL_FATAL = 5const OT_YOGA_LOG_LEVEL_INFO = 2const OT_YOGA_LOG_LEVEL_VERBOSE = 4const OT_YOGA_LOG_LEVEL_WARN = 1const OT_YOGA_NODE_TYPE_DEFAULT = 0const OT_YOGA_NODE_TYPE_TEXT = 1function 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_statusfunction ot_edit_buffer_convert_offset(ot_context *, const ot_handle *edit_buffer, uint32_t conversion, uint32_t value, uint32_t *out_value): ot_statusfunction 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_statusfunction ot_edit_buffer_mark(ot_context *, const ot_handle *, uint32_t operation, const ot_edit_mark *mark, uint32_t *out_id): ot_statusfunction 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_statusfunction ot_markdown_append(ot_context *, const ot_handle *doc, const uint8_t *bytes, uint32_t byte_count, ot_markdown_result *out): ot_statusfunction ot_markdown_bind(ot_context *, const ot_handle *doc, uint32_t block_index, const ot_handle *text_node): ot_statusfunction ot_markdown_create(ot_context *, const ot_markdown_options *, ot_handle *out_doc): ot_statusfunction ot_markdown_destroy(ot_context *, const ot_handle *doc): ot_statusfunction ot_markdown_finish(ot_context *, const ot_handle *doc, ot_markdown_result *out): ot_statusfunction ot_markdown_get_source(ot_context *, const ot_handle *doc, const uint8_t **out_bytes, uint32_t *out_byte_count): ot_statusfunction ot_markdown_read_changes(ot_context *, const ot_handle *doc, ot_markdown_block *blocks, uint32_t block_capacity, uint32_t *out_block_count): ot_statusfunction 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_statusfunction 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_statusfunction ot_markdown_set(ot_context *, const ot_handle *doc, const uint8_t *bytes, uint32_t byte_count, ot_markdown_result *out): ot_statusfunction ot_markdown_set_options(ot_context *, const ot_handle *doc, const ot_markdown_options *, ot_markdown_result *out): ot_statusfunction ot_scene_clear_children(ot_context *, const ot_handle *node): ot_statusfunction ot_scene_get_text_byte_at(ot_context *, const ot_handle *node, int32_t x, int32_t y, uint32_t *out_byte): ot_statusfunction 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_statusfunction ot_scene_painted(ot_context *, const ot_handle *node, uint32_t *out_painted): ot_statusfunction ot_scene_reaches_hooks(ot_context *, const ot_handle *node, uint32_t *out_reached): ot_statusfunction 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_statusstruct ot_edit_markstruct ot_highlight_runstruct ot_markdown_blockstruct ot_markdown_optionsstruct ot_markdown_resultstruct ot_scene_cellstruct ot_scene_record_cells