@oh-my-pi/pi-tui 18.0.3 → 18.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.0.5] - 2026-08-25
6
+
7
+ ### Breaking Changes
8
+
9
+ - Renamed the public `TerminalFrameProvider.resetHistory` method to `beginHistoryReplay`.
10
+
11
+ ### Added
12
+
13
+ - Loader messages can now be provided as a function, allowing dynamic labels such as live countdowns to update on each spinner tick while preserving the existing behavior for static strings.
14
+
15
+ ### Changed
16
+
17
+ - Improved history replay and terminal output handling so replayed content is rendered efficiently and complete replay results are written together.
18
+
19
+ ### Fixed
20
+
21
+ - Fixed graceful shutdown so finalized output is correctly retired before handing control back to the shell.
22
+ - Fixed terminal scrollback corruption during shutdown, tmux pane zoom and resize, and destructive screen resets, preventing duplicated frames, lost history, and stale transcript re-streaming.
23
+ - Fixed streaming Markdown rendering at chunk boundaries to preserve CommonMark emphasis behavior for Unicode text and correctly recognize GFM tables as they are completed.
24
+
25
+ ## [18.0.4] - 2026-08-24
26
+
27
+ ### Changed
28
+
29
+ - Significantly improved streaming Markdown rendering performance by caching unchanged rows, resuming boundary walks, and inspecting only text deltas for guard scans and OSC 8 normalization.
30
+
31
+ ### Fixed
32
+
33
+ - Fixed TUI aborting when syntax highlighting fails during Markdown rendering by falling back to unhighlighted text.
34
+ - Fixed Korean IME cursor drift in Orca by properly matching two-cell Hangul Compatibility Jamo rendering.
35
+
5
36
  ## [18.0.3] - 2026-08-23
6
37
 
7
38
  ### Fixed
@@ -14,7 +14,7 @@ export declare class Loader extends Text {
14
14
  private spinnerColorFn;
15
15
  private messageColorFn;
16
16
  private message;
17
- constructor(ui: TUI, spinnerColorFn: ColorFn, messageColorFn: LoaderMessageColorFn, message?: string, spinnerFrames?: string[]);
17
+ constructor(ui: TUI, spinnerColorFn: ColorFn, messageColorFn: LoaderMessageColorFn, message?: string | (() => string), spinnerFrames?: string[]);
18
18
  render(width: number): readonly string[];
19
19
  start(): void;
20
20
  stop(): void;
@@ -6,6 +6,14 @@ export declare function mathStartIndex(src: string): number | undefined;
6
6
  export declare function autolinkSchemeScanIndex(src: string): number | undefined;
7
7
  /** @internal exported for tests — must never return false for a src the built-in url regex matches. */
8
8
  export declare function urlTokenPossible(src: string): boolean;
9
+ /** @internal exported for tests — counts fast-tail splice frames. A future
10
+ * regression that silently disarms the fast path (e.g. an over-broad gate)
11
+ * leaves byte-identity intact but drops the counter to zero. */
12
+ export declare let fastTailSplices: number;
13
+ /** @internal exported for tests — resets the splice counter. */
14
+ export declare function resetFastTailSplices(): void;
15
+ /** @internal exported for tests — the grown-line-start block-kind gate. */
16
+ export declare function fastLineStartHazard(grownLine: string): boolean;
9
17
  /** Drop all L2 cache entries. Call on theme change to prevent stale styled output. */
10
18
  export declare function clearRenderCache(): void;
11
19
  /**
@@ -10,7 +10,7 @@ export declare enum NotifyProtocol {
10
10
  Osc99 = "\u001B]99;;",
11
11
  Osc9 = "\u001B]9;"
12
12
  }
13
- export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "warp" | "base" | "trueColor";
13
+ export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "warp" | "orca" | "base" | "trueColor";
14
14
  /** Terminal capability details used for rendering and protocol selection. */
15
15
  export declare class TerminalInfo {
16
16
  readonly id: TerminalId;
@@ -23,7 +23,7 @@ export declare class TerminalInfo {
23
23
  /** Renders the Kitty OSC 66 text-sizing protocol (scaled spans). Kitty only. */
24
24
  readonly supportsTextSizing: boolean;
25
25
  /**
26
- * Hangul Compatibility Jamo (U+3131..=U+318E) cell width. Ghostty follows
26
+ * Hangul Compatibility Jamo (U+3131..=U+318E) cell width. Ghostty and Orca follow
27
27
  * UAX#11 (2 cells); Warp paints 1; "platform" keeps the OS default
28
28
  * (macOS narrow, otherwise UAX#11).
29
29
  */
@@ -32,7 +32,7 @@ export declare class TerminalInfo {
32
32
  /** Renders the Kitty OSC 66 text-sizing protocol (scaled spans). Kitty only. */
33
33
  supportsTextSizing?: boolean,
34
34
  /**
35
- * Hangul Compatibility Jamo (U+3131..=U+318E) cell width. Ghostty follows
35
+ * Hangul Compatibility Jamo (U+3131..=U+318E) cell width. Ghostty and Orca follow
36
36
  * UAX#11 (2 cells); Warp paints 1; "platform" keeps the OS default
37
37
  * (macOS narrow, otherwise UAX#11).
38
38
  */
@@ -23,12 +23,19 @@ export interface ViewportSize {
23
23
  readonly columns: number;
24
24
  readonly rows: number;
25
25
  }
26
- /** Immutable finalized rows offered until the terminal accepts this identifier. */
26
+ /** Immutable append or complete replay offered until the terminal accepts this identifier. */
27
27
  export interface HistoryBatch {
28
28
  readonly id: number;
29
29
  readonly rows: readonly string[];
30
+ /**
31
+ * `append` (the default) adds finalized or naturally emitted rows. `replay`
32
+ * is the complete logical ledger; the writer bottom-splits it against the
33
+ * leading blank viewport and serializes the remainder plus final viewport in
34
+ * one synchronous terminal write.
35
+ */
36
+ readonly kind?: "append" | "replay";
30
37
  }
31
- /** One history append and the complete mutable viewport for a terminal frame. */
38
+ /** One history append or complete replay plus the mutable viewport for a terminal frame. */
32
39
  export interface TerminalFramePlan {
33
40
  readonly history?: HistoryBatch;
34
41
  readonly viewport: readonly string[];
@@ -40,7 +47,9 @@ export interface TerminalFrameProvider {
40
47
  /** Full semantic viewport used only on the transient resize buffer. */
41
48
  renderResizeFrame?(viewport: ViewportSize): readonly string[];
42
49
  /** Re-offer finalized history after a display reset or resize replay. */
43
- resetHistory?(): void;
50
+ beginHistoryReplay?(): void;
51
+ /** Force every currently eligible finalized prefix to retire before stop. */
52
+ beginHistoryFlush?(): void;
44
53
  }
45
54
  export interface TUIStartOptions {
46
55
  /** Clear saved native scrollback before the first paint. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "18.0.3",
4
+ "version": "18.0.5",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -37,8 +37,8 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-natives": "18.0.3",
41
- "@oh-my-pi/pi-utils": "18.0.3"
40
+ "@oh-my-pi/pi-natives": "18.0.5",
41
+ "@oh-my-pi/pi-utils": "18.0.5"
42
42
  },
43
43
  "devDependencies": {
44
44
  "kitty-vt-wasm": "^0.2.0"
@@ -32,7 +32,7 @@ export class Loader extends Text {
32
32
  ui: TUI,
33
33
  private spinnerColorFn: ColorFn,
34
34
  private messageColorFn: LoaderMessageColorFn,
35
- private message: string = "Loading...",
35
+ private message: string | (() => string) = "Loading...",
36
36
  spinnerFrames?: string[],
37
37
  ) {
38
38
  super("", 1, 0);
@@ -149,11 +149,18 @@ export class Loader extends Text {
149
149
  }, delayMs);
150
150
  this.#intervalId = timer;
151
151
  }
152
- /** Re-wrap the underlying Text only when its message or frame width changes. */
152
+ #resolveMessage(): string {
153
+ return typeof this.message === "function" ? this.message() : this.message;
154
+ }
155
+
156
+ /** Re-wrap the underlying Text only when its message or frame width changes.
157
+ * When {@link message} is a function it is re-evaluated on every spinner
158
+ * tick, so a dynamic label (e.g. a live countdown) advances in sync with
159
+ * the glyph instead of freezing on the initial value. */
153
160
  #syncText(): boolean {
154
161
  const layoutFrame = this.#layoutFrames[this.#currentFrame];
155
162
  this.#layoutFrame = layoutFrame;
156
- return this.setText(`${layoutFrame} ${this.message}`);
163
+ return this.setText(`${layoutFrame} ${this.#resolveMessage()}`);
157
164
  }
158
165
 
159
166
  #requestPaint() {