Skip to content

Release v0.6.7

Choose a tag to compare

@github-actions github-actions released this 23 Oct 03:56
· 17 commits to main since this release
v0.6.7
a8f2745

In 0.6.6, OpenTUI gave the text a\u200Ebcdef seven cells, but the terminal drew it in six. U+200E, the left-to-right mark, is one of Unicode's default-ignorable code points, and terminals draw these in no cell. OpenTUI gave each of them one cell. As a result, it placed the rest of the row one column right of where the terminal drew it. A full frame looked correct, but the next partial update did not: in Ghostty, changing the f to Z showed abcdefZ. Hebrew and Arabic text often carries direction marks, so these rows broke when they were updated. In 0.6.7, every default-ignorable code point takes no cell. Layout, cursor movement, drawing, and encodeUnicode use the same width, so they agree. Two exceptions remain. The soft hyphen U+00AD keeps one cell, because terminals draw it. The conjoining Hangul fillers U+115F and U+1160 keep their widths, because each starts a syllable cluster.

When more code points have zero width, an editor bug becomes more common. In 0.6.6, a Textarea line that started with U+200B trapped the cursor. Right did not move past it from column 0, and Delete did nothing there. No key could delete a line that held only zero-width text. Without a fix, a Hebrew line that starts with U+200F would act the same way. 0.6.7 fixes both cases. A border character must still fill exactly one cell, so a customBorderChars entry such as U+200E or U+FFA0 is now an error. Before, it gave a misaligned border. Replace it with a character that takes one cell, such as a space.

A terminal reports mouse motion only when the pointer enters a new cell. In 0.6.6, the renderer gave a left-button drag to the renderable under the first motion report. A fast drag on a one-column ScrollBox scrollbar thumb often reported its first motion over a neighbor. The bar then did not scroll, and the thumb kept its dragging state. Now the renderable that receives the left press owns the drag. If the press hit nothing, or its renderable was destroyed, the renderable under the pointer is the source, as before. A press that starts or extends a text selection still follows the selection path.

registerBaseLayoutFallback() is for keyboard layouts that cannot type the character of a binding. On a Russian layout, Ctrl+с must still run ctrl+c. In 0.6.6, the addon used the Kitty base-layout key for every chord that had no direct binding. On Dvorak, Ctrl+J then ran a ctrl+c binding, Ctrl+E ran ctrl+d, and Ctrl+; ran ctrl+z. The addon now uses the same rule as Core's key bindings. It falls back only when the typed key is one non-ASCII character, or when the base-layout key is a digit. The digit case is for AZERTY, where the digit keys type & " ' ( - _ and Ctrl+& must still match ctrl+1. Some punctuation on non-Latin layouts still does not fall back, for example Hebrew ' on the W key.

In @opentui/react, each root.render() call created a new reconciler container. It dropped the old tree and did not run its effect cleanups. A later call now updates the mounted tree, as react-dom does:

const root = createRoot(renderer)
root.render(<App theme="dark" />)
root.render(<App theme="light" />) // App keeps its state

An explicit root.render() also resets the internal error boundary. If a tree failed with an uncaught error, the next call tries it again.

OpenCode draws each diff hunk as a separate DiffRenderable. To line up the gutters, it put an invisible sign on line -1 and measured the gutters again after mount. The new gutter option sets that layout directly:

const gutter = { paddingLeft: 2, minDigits: 4 }
const first = new DiffRenderable(renderer, { diff: firstHunk, gutter })
const second = new DiffRenderable(renderer, { diff: secondHunk, gutter })

minDigits always reserves the +/- sign column. Thus both code columns start at the same cell, also when a hunk has no signs. Line -1 still works, for compatibility.

Breaking changes

  • core: A Box with a default-ignorable character in customBorderChars, such as U+200E or U+FFA0, throws InvalidArgument at creation, and drawGrid rejects such a character. Use a border character that takes one cell. See Box. (#1675)

Added

  • core: The gutter option and property of DiffRenderable set paddingLeft, paddingRight, and minDigits for the line-number gutter. Diffs with the same minDigits align their code columns. See Diff. (#1684)
  • core: LineNumberRenderable.getGutterLayout() and setGutterLayout() read and set the gutter padding, minimum digits, and minimum widths as a GutterLayout. See Line number gutter. (#1684)

Changed

  • react: A later root.render() updates the mounted tree, keeps component state, and tries again after a tree that failed with an uncaught error. Effects clean up when the tree is replaced, on root.unmount(), and on renderer.destroy(). See React bindings. (#1692)
  • keymap: registerBaseLayoutFallback() uses the base-layout key only when the typed key is one non-ASCII character or the base-layout key is a digit. Dvorak Ctrl+J now matches ctrl+j, not ctrl+c. See Built-in keymap addons. (#1718)

Fixed

  • core: Bidirectional marks and other default-ignorable code points take no cell in layout, cursor movement, drawing, and encodeUnicode. Before, a partial update of a row with such a mark wrote one column to the right. See Text and terminal cells. (#1675)
  • core: In a Textarea or Input, Right, Shift+Right, and Delete move over zero-width characters at the start of a line or a pasted chunk. A double or triple click at such a line start selects the same text as on a line without the prefix. See Textarea. (#1681)
  • core: Delete, Backspace, Delete Line, and a selection delete remove text that holds only zero-width characters, such as U+200B, U+200D, or a combining mark. Delete Line also keeps a zero-width character that starts the next line. See Textarea. (#1683)
  • core: With word wrap, a combining mark at the start of a typed or appended chunk stays on the line of its base character. Before, the mark moved to the next line and did not draw. See Text and terminal cells. (#1715)
  • core: After a measure provider fails, it gets one more call before the next frame. Before, the node stayed 0 cells wide until invalidateIntrinsicSize(). See Custom renderables. (#1680)
  • core: A left-button drag goes to the renderable that got the press, also when the first motion report is over a different renderable. A release over the drag source or its descendant no longer sends drop and a second up. See Interaction, focus, and selection. (#1716)
  • core: Key bindings fall back to the base-layout key when it is a digit, so AZERTY Ctrl+& matches ctrl+1. They no longer fall back to a space base-layout key. See Keyboard input. (#1718)
  • keymap: registerBaseLayoutFallback() no longer throws or reports an event-match-resolver-error when the base-layout key is a space. See Built-in keymap addons. (#1718)
  • core: On Node, a -0 integer argument goes to native code as 0. Before, calls such as EditBuffer.setCursor, pushScissorRect, and CliRenderer.hitTest threw TypeError: Argument N must be an int32. See Runtime and platform support. (#1709)

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

What's Changed

  • core: add diff gutter padding and digit options by @jlongster in #1684
  • native: count text work in tests and replace the wrap timing test by @simonklee in #1721
  • core: pass -0 as 0 to i32 and u32 arguments on Node by @simonklee in #1709
  • core: specify F3 and cursor-report readings in the parser vectors by @simonklee in #1708
  • native: keep a mark that starts a chunk with its glyph in word wrap by @simonklee in #1715
  • native: measure a failed custom measurement again on the next frame, once by @simonklee in #1680
  • core: capture a left drag to the renderable that got the press by @simonklee in #1716
  • keymap: fall back to the base-layout key only where the typed key cannot match by @simonklee in #1718
  • fix(react): reuse root container across renders by @msmps in #1692
  • native: share one text corpus between the Zig and TypeScript tests by @simonklee in #1729
  • native: step right over zero-width clusters at a line or chunk start by @simonklee in #1681
  • native: delete zero-width text on a line without cells by @simonklee in #1683
  • native: drop the corpus bug entries the zero-width delete fix resolves by @simonklee in #1740
  • native: give default-ignorable code points no cell by @simonklee in #1675

Full Changelog: v0.6.6...v0.6.7