@oh-my-pi/pi-tui 18.15.0 → 18.16.0

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +36 -18
  2. package/dist/types/app-keybindings.d.ts +5 -0
  3. package/dist/types/apps/session-picker.d.ts +5 -3
  4. package/dist/types/chat/bash-execution.d.ts +4 -1
  5. package/dist/types/chat/eval-execution.d.ts +2 -0
  6. package/dist/types/chat/execution-shared.d.ts +14 -8
  7. package/dist/types/chrome/dynamic-border.d.ts +6 -0
  8. package/dist/types/components/editor.d.ts +15 -6
  9. package/dist/types/components/loader.d.ts +2 -0
  10. package/dist/types/keybinding-matchers.d.ts +7 -0
  11. package/dist/types/overlays/btw-history-panel.d.ts +13 -0
  12. package/dist/types/overlays/btw-history.d.ts +24 -0
  13. package/dist/types/overlays/btw-inject.d.ts +22 -0
  14. package/dist/types/overlays/btw-panel.d.ts +7 -0
  15. package/dist/types/overlays/session-selector.d.ts +24 -3
  16. package/dist/types/render/render-utils.d.ts +4 -1
  17. package/dist/types/status-line/startup.d.ts +0 -15
  18. package/dist/types/theme/symbols.d.ts +3 -1
  19. package/dist/types/theme/theme-class.d.ts +1 -0
  20. package/dist/types/tools/output-meta.d.ts +27 -0
  21. package/dist/types/utils.d.ts +4 -0
  22. package/package.json +9 -9
  23. package/src/app-keybindings.ts +5 -0
  24. package/src/apps/session-picker.ts +7 -3
  25. package/src/chat/bash-execution.ts +20 -9
  26. package/src/chat/chat-transcript-builder.ts +1 -0
  27. package/src/chat/eval-execution.ts +14 -7
  28. package/src/chat/execution-shared.ts +39 -18
  29. package/src/chat/read-tool-group.ts +9 -16
  30. package/src/chat/transcript-render-helpers.ts +16 -4
  31. package/src/chrome/dynamic-border.ts +28 -2
  32. package/src/components/editor.ts +123 -24
  33. package/src/components/key-value-list.ts +9 -9
  34. package/src/components/loader.ts +7 -0
  35. package/src/components/section.ts +1 -9
  36. package/src/components/table.ts +1 -9
  37. package/src/keybinding-matchers.ts +13 -0
  38. package/src/overlays/btw-history-panel.ts +98 -14
  39. package/src/overlays/btw-history.ts +65 -0
  40. package/src/overlays/btw-inject.ts +91 -0
  41. package/src/overlays/btw-panel.ts +53 -12
  42. package/src/overlays/model-browser.ts +65 -8
  43. package/src/overlays/session-selector.ts +131 -26
  44. package/src/overlays/usage-dashboard.ts +2 -2
  45. package/src/render/code-cell.ts +1 -1
  46. package/src/render/render-utils.ts +5 -3
  47. package/src/setup/scenes/glyph.ts +1 -1
  48. package/src/status-line/startup.ts +36 -70
  49. package/src/theme/symbols.ts +16 -9
  50. package/src/theme/theme-class.ts +3 -0
  51. package/src/tools/agent-tree.ts +13 -11
  52. package/src/tools/ask.ts +13 -6
  53. package/src/tools/bash.ts +7 -0
  54. package/src/tools/default-renderer.ts +1 -1
  55. package/src/tools/edit.ts +39 -41
  56. package/src/tools/fetch.ts +2 -1
  57. package/src/tools/github.ts +14 -46
  58. package/src/tools/json-tree.ts +1 -5
  59. package/src/tools/lsp.ts +38 -10
  60. package/src/tools/output-meta.ts +68 -4
  61. package/src/tools/proc-render.ts +1 -1
  62. package/src/tools/task.ts +34 -77
  63. package/src/tools/vibe.ts +4 -4
  64. package/src/tools/wait.ts +2 -2
  65. package/src/tools/write.ts +17 -27
  66. package/src/tools/yield.ts +1 -1
  67. package/src/utils.ts +10 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.16.0] - 2026-11-02
6
+
7
+ ### Breaking Changes
8
+
9
+ - `buildExecutionFrame` sets its loader into the new `bottomBorder` itself, so callers no longer add it as a row and must dispose it; `buildStatusFooter` no longer takes `status` or `exitCode`, and `executionStatusLabel` returns a settled run's `exit N` / `cancelled` marker for that border ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
10
+ - `SessionSelectorOptions.loadAllSessions` and `SessionPickerHost.loadAllSessions` resolve to a `SessionListing`, and `allSessions` takes one, so the all-projects scope can show preliminary rows before its complete listing
11
+ - `Editor.prediction` is replaced by `Editor.predictions`, which returns every prediction for the next prompt, most likely first, and `Editor.onPredictionAccepted` receives the inserted prediction's index in that list ([#17796](https://github.com/can1357/oh-my-pi/pull/17796) by [@wolfiesch](https://github.com/wolfiesch))
12
+
13
+ ### Added
14
+
15
+ - `SessionSelectorOptions.completeSessions` (and `SessionPickerOptions.completeSessions`) replaces the picker's preliminary rows with the complete listing once it settles, keeping the query, the selected session, and deletes/renames made meanwhile
16
+ - `/btw` answers can show a steering prompt as a block behind a pink rail (a toned card in Tern) that `Alt+I` (`app.btw.inject`, remappable) sends to the main session; once sent it turns green with `➤` on each line, without moving ([#17618](https://github.com/can1357/oh-my-pi/pull/17618) by [@apullin](https://github.com/apullin))
17
+ - Added `singleLine` and `finiteWidth` to `utils` for row-confined text and cell widths
18
+ - `Loader.inlineText()` returns the current spinner frame and message as one styled line, and `DynamicBorder.setLabel()` sets a label (or a function re-read every render) into the rule ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
19
+
20
+ ### Changed
21
+
22
+ - The ASCII symbol preset's status glyphs are all three columns wide: success is `[+]`, error `[-]`, aborted `[#]` and pending `[ ]` (alongside `[~]` running, `[!]` warning and `[i]` info), and status spinners animate as `[|] [/] [-] [\]` ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
23
+ - Built-in renderers no longer use `status.done`, `theme.status.done` or the `done` value of `ToolUIStatus`; they remain as aliases of `status.success` / `success` (an explicit `symbols.overrides["status.done"]` still applies to `theme.status.done`) ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
24
+ - Every finished state shows the success glyph (`✔`, ASCII `[+]`). Task rows, verdicts and headers, background-job, launch and supervised-process rows, Eval cells, Vibe, Wait and process job rows, Yield and the generic tool card showed a separate `•` done dot, and a finished Read showed `●` (ASCII `[x]`). Live Task agent rows show the pending or running glyph, and a Task whose merge failed shows the warning glyph ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
25
+ - Edit and Write animate their streaming spinner in the card header, as Eval does, and show their `preview` / `streaming` label in the card's bottom border instead of on a row below the content; `streaming` ends once the content has fully arrived ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
26
+ - `!` shell and `$` Python runs show their spinner, and then `exit N` or `cancelled`, in the frame's bottom border instead of on rows of their own; the spinner is the status spinner tool headers use ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
27
+ - GitHub run-watch job rows use the shared status glyphs: a running job shows the running glyph instead of a warning-colored dot, a queued job the pending glyph instead of a hollow circle (a job in a state omp does not recognize keeps the circle), and a passed job's glyph is green like other success glyphs. Ask's multi-line custom answer lines up under its first line in every symbol preset ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
28
+
29
+ ### Fixed
30
+
31
+ - An LSP result other than diagnostics, such as a raw `request` response, that contains the error glyph (`✘`, ASCII `[-]`) is shown as a response instead of as diagnostics reading "No issues" with the rest of its lines dropped ([#17732](https://github.com/can1357/oh-my-pi/pull/17732) by [@JustasMasiulis](https://github.com/JustasMasiulis))
32
+ - `Editor.onPredictionAccepted` and `Editor.onPredictionShown` report when Tab or Right inserts the rest of the composer prediction and when a frame paints it (at least one cell in the terminal, or the ghost on a native editor node) ([#17739](https://github.com/can1357/oh-my-pi/pull/17739) by [@wolfiesch](https://github.com/wolfiesch))
33
+ - Added `OutputMeta.minimized` (bash minimizer filter, raw and minimized byte counts, raw-output artifact), shown as a `Minimized` fact on bash result cards ([#16586](https://github.com/can1357/oh-my-pi/pull/16586) by [@will-bogusz](https://github.com/will-bogusz))
34
+ - Added `isColumnTruncationNotice` and `hasColumnTruncationClause` to `tools/output-meta`, matching the `[Some lines truncated to …]` clause that `formatOutputNotice` emits alone or inside a longer notice, and `LimitsMeta.columnTruncated.rawByteCap`, which makes that notice name the byte cap a `:raw` read of the first cut line stops at ([#16595](https://github.com/can1357/oh-my-pi/pull/16595) by [@will-bogusz](https://github.com/will-bogusz))
35
+ - Fixed the model browser showing Factory Droid's undiscounted credit rate during a Factory promotion (2× instead of 1.6× for GPT-5.6 Sol); rows now show the rate in force and the model details show the next rate and its date ([#16309](https://github.com/can1357/oh-my-pi/pull/16309) by [@will-bogusz](https://github.com/will-bogusz))
36
+ - With two or more `Editor.predictions`, the ghost text follows the first one the draft is a prefix of; a typed draft that equals a shorter one keeps the ghost of a longer one. Once the buffer equals one of them with no ghost (always right after Tab, Right, or the list inserts one), a dim `⇥ N more` hint appears; press Tab to list them all, then Enter, Tab or Right to swap the whole line (never submitting) or Esc to keep it ([#17796](https://github.com/can1357/oh-my-pi/pull/17796) by [@wolfiesch](https://github.com/wolfiesch))
37
+ - F2 now opens the rename dialog in the resume picker in Ghostty, kitty and other terminals using the kitty keyboard protocol; F1 and F4 shortcuts work there too ([#17812](https://github.com/can1357/oh-my-pi/issues/17812)).
38
+ - Added `Component.nativeTrailers()`, which lets a transcript block add native blocks of its own directly after it. Trailing blocks stay visible while the block is collapsed.
39
+
5
40
  ## [18.15.0] - 2026-11-01
6
41
 
7
42
  ### Breaking Changes
@@ -2489,21 +2524,4 @@
2489
2524
  - Fixed hiding overlays during ED3-risk foreground streaming on unknown-viewport terminals leaving the overlay's transient rows in native scrollback. Overlay visibility reductions now bypass the streaming deferral path and rebuild once, so hidden dialog/notification sentinels are scrubbed immediately.
2490
2525
  - Fixed ED3-risk / unknown-viewport terminals (including WSL fronted by Windows Terminal) keeping the foreground-stream eager-rebuild mode active after the stream had already settled. A later scrolled content shrink or resize-with-append could then bypass the anti-yank deferral and repaint from stale geometry, jumping the viewport or replaying the wrong rows. The eager opt-in now drops immediately when no teardown render is pending, and the one-frame post-checkpoint suffix-suppression path no longer overrides geometry reflow handling.
2491
2526
 
2492
- ## [15.9.2] - 2026-06-05
2493
-
2494
- ### Changed
2495
-
2496
- - Changed foreground-stream rendering on ED3-risk terminals (Ghostty/kitty/Alacritty/VTE/iTerm2 on POSIX) to defer native-scrollback commits for unpinned transient frames: while a turn streams, generic frames repaint only the viewport and suppress `\r\n` scroll growth, so transient output (spinner ticks, partial lines, status rows) never pollutes terminal history. Components that report a `NativeScrollbackLiveRegion` still commit newly sealed prefix rows while keeping the active suffix dirty for checkpoint replay. Native scrollback is reconciled in a single ED3 (`CSI 3 J`) + re-emit at the next checkpoint (prompt submit) or on an explicit user-input/IME opt-in; an erase is never emitted mid-stream under a possibly-scrolled reader. Non-ED3-risk terminals keep their eager live rebuild. ([#1895](https://github.com/can1357/oh-my-pi/pull/1895))
2497
-
2498
- ### Fixed
2499
-
2500
- - Fixed ED3-risk foreground streaming dropping sealed transcript rows above the live block until the next prompt-submit checkpoint, which made scrollback beyond the viewport appear duplicated or out of order. The renderer restores native-scrollback live-region pinning so newly sealed rows are appended once while active live rows remain deferred.
2501
- - Fixed inline images (added in 15.9) rendering as a wall of empty PUA box glyphs and producing laggy scrolling on Kitty-protocol terminals that do not implement Unicode placeholders — most notably WezTerm (per upstream wezterm/wezterm#986, placeholder support is still unchecked) and the tmux/screen `getFallbackImageProtocol` path that forces Kitty mode even on non-supporting outer terminals (Terminal.app, etc.). `unicodePlaceholders` now defaults on only for `kitty` and `ghostty`; everything else falls back to direct `a=p,i=…,p=…` placement, which those paths already render correctly. `PI_NO_KITTY_PLACEHOLDERS=1` is still honored as a hard opt-out, and a new `PI_KITTY_PLACEHOLDERS=1` opts in on otherwise-unsupported terminals (e.g. a wezterm nightly that has merged placeholder support) ([#1877](https://github.com/can1357/oh-my-pi/issues/1877)).
2502
-
2503
- ## [15.9.1] - 2026-06-04
2504
-
2505
- ### Fixed
2506
-
2507
- - Fixed the OSC 11 appearance poll re-querying every 2s forever on terminals that support Mode 2031 but never change theme, whose repeated OSC 11/DA1 writes cleared the user's active text selection (breaking copy every 2 seconds). The poll now stops as soon as DECRQM confirms Mode 2031 support, since push notifications make polling redundant.
2508
-
2509
- Older entries are archived in [packages/tui/CHANGELOG.md@6b354a56a890](https://github.com/can1357/oh-my-pi/blob/6b354a56a890b69b6bc9503c16c510be33675b5a/packages/tui/CHANGELOG.md).
2527
+ Older entries are archived in [packages/tui/CHANGELOG.md@9f40fb535f35](https://github.com/can1357/oh-my-pi/blob/9f40fb535f35f2b38336a4e694ff4c0dc91d4868/packages/tui/CHANGELOG.md).
@@ -24,6 +24,7 @@ interface AppKeybindings {
24
24
  "app.message.followUp": true;
25
25
  "app.retry": true;
26
26
  "app.message.dequeue": true;
27
+ "app.btw.inject": true;
27
28
  "app.clipboard.pasteImage": true;
28
29
  "app.clipboard.pasteTextRaw": true;
29
30
  "app.clipboard.copyLine": true;
@@ -272,6 +273,10 @@ export declare const KEYBINDINGS: {
272
273
  readonly defaultKeys: ["alt+up", "shift+up"];
273
274
  readonly description: "Dequeue message";
274
275
  };
276
+ readonly "app.btw.inject": {
277
+ readonly defaultKeys: "alt+i";
278
+ readonly description: "Send a /btw answer's steering prompt to the main session";
279
+ };
275
280
  readonly "app.clipboard.pasteImage": {
276
281
  readonly defaultKeys: KeyId[];
277
282
  readonly description: "Paste image or text from clipboard";
@@ -1,4 +1,4 @@
1
- import { type SessionSelectorEntry, type SessionHistoryMatcher } from "../overlays/session-selector.js";
1
+ import { type SessionHistoryMatcher, type SessionListing, type SessionSelectorEntry } from "../overlays/session-selector.js";
2
2
  /** Persistence and history capabilities supplied by the session-owning host. */
3
3
  export interface SessionPickerHost<T extends SessionSelectorEntry = SessionSelectorEntry> {
4
4
  loadPinnedIds?(): Promise<ReadonlySet<string>>;
@@ -7,11 +7,13 @@ export interface SessionPickerHost<T extends SessionSelectorEntry = SessionSelec
7
7
  /** Rename the session and resolve with the title as stored, or null when it did not happen. */
8
8
  renameSession?(session: T, title: string): Promise<string | null>;
9
9
  setSessionPinned?(session: T, pinned: boolean): Promise<void>;
10
- loadAllSessions?(): Promise<T[]>;
10
+ loadAllSessions?(): Promise<SessionListing<T>>;
11
11
  }
12
12
  /** Presentation and capability controls for the standalone session picker. */
13
13
  export interface SessionPickerOptions<T extends SessionSelectorEntry = SessionSelectorEntry> {
14
- allSessions?: T[];
14
+ /** Complete listing replacing the preliminary `sessions` once it settles. */
15
+ completeSessions?: Promise<T[]>;
16
+ allSessions?: SessionListing<T>;
15
17
  title?: string;
16
18
  scopeLabel?: string | false;
17
19
  showCwd?: boolean;
@@ -4,7 +4,7 @@
4
4
  import type { ImageContent } from "@oh-my-pi/pi-ai";
5
5
  import { Container, type TUI } from "../tui.js";
6
6
  import type { OutputArtifactError } from "../tools/streaming-output.js";
7
- import type { TruncationMeta } from "../tools/output-meta.js";
7
+ import type { MinimizedMeta, TruncationMeta } from "../tools/output-meta.js";
8
8
  import { type DescribeContext, type NativeNode, type NativeUiEvent } from "../native/node.js";
9
9
  /** PTY size for `!` commands: the execution frame's inner content area. */
10
10
  export declare function bashPtyViewport(ui: TUI): {
@@ -43,9 +43,12 @@ export declare class BashExecutionComponent extends Container {
43
43
  output?: string;
44
44
  truncation?: TruncationMeta;
45
45
  artifactError?: OutputArtifactError;
46
+ minimized?: MinimizedMeta;
46
47
  images?: readonly ImageContent[];
47
48
  showImages?: boolean;
48
49
  }): void;
50
+ /** The loader animates the bottom border from outside the child tree, so teardown stops it here. */
51
+ dispose(): void;
49
52
  render(width: number): readonly string[];
50
53
  /**
51
54
  * Get the raw output for creating BashExecutionMessage.
@@ -32,6 +32,8 @@ export declare class EvalExecutionComponent extends Container {
32
32
  truncation?: TruncationMeta;
33
33
  artifactError?: OutputArtifactError;
34
34
  }): void;
35
+ /** The loader animates the bottom border from outside the child tree, so teardown stops it here. */
36
+ dispose(): void;
35
37
  getOutput(): string;
36
38
  getCode(): string;
37
39
  }
@@ -10,7 +10,8 @@ import { Loader } from "../components/loader.js";
10
10
  import { Text } from "../components/text.js";
11
11
  import { Container, type TUI } from "../tui.js";
12
12
  import type { OutputArtifactError } from "../tools/streaming-output.js";
13
- import { type TruncationMeta } from "../tools/output-meta.js";
13
+ import { type MinimizedMeta, type TruncationMeta } from "../tools/output-meta.js";
14
+ import { DynamicBorder } from "../chrome/dynamic-border.js";
14
15
  import type { TspText } from "@oh-my-pi/pi-wire";
15
16
  import type { NativeChild, NativeNode } from "../native/node.js";
16
17
  /** Output rows shown while an execution is collapsed. */
@@ -25,23 +26,26 @@ export type ExecutionColorKey = "dim" | "bashMode" | "pythonMode";
25
26
  /**
26
27
  * Build the spacer + top border + content container + bottom border scaffold
27
28
  * that bash and eval execution components share. The caller appends the
28
- * header (command vs `>>>` prompt) and the returned loader to
29
- * `contentContainer` so per-mode order is preserved.
29
+ * header (command vs `>>>` prompt) to `contentContainer` and keeps the run's
30
+ * state in `bottomBorder` ({@link executionStatusLabel}); the returned loader
31
+ * animates that label rather than taking a row of its own.
30
32
  */
31
33
  export declare function buildExecutionFrame(parent: Container, ui: TUI, colorKey: ExecutionColorKey): {
32
34
  contentContainer: Container;
33
35
  loader: Loader;
36
+ bottomBorder: DynamicBorder;
34
37
  };
38
+ /** The bottom-border marker of a settled run: `exit N` for a failure, `cancelled` for a cancel. */
39
+ export declare function executionStatusLabel(status: Exclude<ExecutionStatus, "running">, exitCode: number | undefined): string | undefined;
35
40
  /**
36
- * Build the post-run status block (hidden-line hint, exit/cancel marker,
37
- * truncation notice). Returns undefined when there is nothing to display so
38
- * callers can skip appending a stray Text child.
41
+ * Build the post-run status block (hidden-line hint, truncation notice).
42
+ * Returns undefined when there is nothing to display so callers can skip
43
+ * appending a stray Text child.
39
44
  */
40
45
  export declare function buildStatusFooter(opts: {
41
- status: ExecutionStatus;
42
- exitCode: number | undefined;
43
46
  truncation: TruncationMeta | undefined;
44
47
  artifactError?: OutputArtifactError;
48
+ minimized?: MinimizedMeta;
45
49
  hiddenLineCount: number;
46
50
  /** Suppress the "… N more lines" hint (used when sixel passthrough renders the full output). */
47
51
  suppressHiddenCount?: boolean;
@@ -64,6 +68,7 @@ export interface ExecutionCardInput {
64
68
  readonly exitCode: number | undefined;
65
69
  readonly truncation: TruncationMeta | undefined;
66
70
  readonly artifactError?: OutputArtifactError;
71
+ readonly minimized?: MinimizedMeta;
67
72
  }
68
73
  /**
69
74
  * Native card shared by user bash/eval runs: the head with a live `elapsed`
@@ -94,6 +99,7 @@ export interface ExecutionToolInput {
94
99
  readonly exitCode: number | undefined;
95
100
  readonly truncation: TruncationMeta | undefined;
96
101
  readonly artifactError?: OutputArtifactError;
102
+ readonly minimized?: MinimizedMeta;
97
103
  }
98
104
  /**
99
105
  * A user `!`/`$` run as the agent's `tool` frame (§6.6): a head with a `you`
@@ -13,6 +13,12 @@ export declare class DynamicBorder implements Component {
13
13
  #private;
14
14
  constructor(color?: (str: string) => string);
15
15
  invalidate(): void;
16
+ /**
17
+ * Pre-styled text set into the rule after a short lead (`─── label ────`);
18
+ * undefined draws a plain rule. A function label is re-read every render,
19
+ * for live text such as a spinner.
20
+ */
21
+ setLabel(label: string | (() => string) | undefined): void;
16
22
  /** Native terminals draw their own chrome; a border rule describes nothing. */
17
23
  describe(): NativeNode;
18
24
  render(width: number): readonly string[];
@@ -157,12 +157,21 @@ export declare class Editor implements Component, Focusable {
157
157
  * cursor. The host styles it (ANSI allowed). Re-evaluated on every render, so hosts can derive
158
158
  * it from live state. */
159
159
  placeholder?: () => string | undefined;
160
- /** Whole-message prediction for the next prompt, painted as ghost text after the cursor while
161
- * the single-line buffer is a strict prefix of it, the cursor sits at its end, and no
162
- * autocomplete is open. A shown prediction replaces the {@link placeholder}; Tab or Right inserts
163
- * the rest without submitting. Must be a single line. Re-evaluated on every render, so hosts can
164
- * derive it from live state. */
165
- prediction?: () => string | undefined;
160
+ /** Whole-message predictions for the next prompt, most likely first. The first one the single-line
161
+ * buffer is a strict prefix of is painted as ghost text after the cursor while the cursor sits at
162
+ * its end and no autocomplete is open. A shown prediction replaces the {@link placeholder}; Tab or
163
+ * Right inserts the rest without submitting. A buffer the user typed keeps the ghost even when it
164
+ * equals a shorter prediction; once Tab, Right, or the list has inserted one of two or more
165
+ * predictions, no ghost extends it to a longer one, and Tab instead opens a list of all of them
166
+ * that swaps the whole line, also without submitting. Each must be a single line. Re-evaluated on
167
+ * every render, so hosts can derive them from live state. */
168
+ predictions?: () => readonly string[] | undefined;
169
+ /** Called with the prediction's index in {@link predictions} after Tab or Right inserts the rest of
170
+ * a shown prediction, or the alternatives list swaps a different one into the buffer. */
171
+ onPredictionAccepted?: (index: number) => void;
172
+ /** Called for each frame that paints a prediction as ghost text: a terminal frame that draws at
173
+ * least one cell of it, or a native frame that carries it as the editor's ghost. */
174
+ onPredictionShown?: () => void;
166
175
  constructor(theme: EditorTheme);
167
176
  /** Return bounded editor content, cursor, and transient child state for debug inspection. */
168
177
  debugState(): Record<string, unknown>;
@@ -71,6 +71,8 @@ export declare class Loader extends Text {
71
71
  stop(): void;
72
72
  /** Lifecycle teardown: stop the animation timer. Idempotent. */
73
73
  dispose(): void;
74
+ /** The current spinner frame and message as one styled line, for hosts that set it into their own chrome. */
75
+ inlineText(): string;
74
76
  /** Install a lazy right-docked suffix for the spinner row (e.g. a styled
75
77
  * session title). Re-evaluated every paint; dropped when the row leaves
76
78
  * less than a two-cell gap. */
@@ -42,3 +42,10 @@ export declare function matchesAppFollowUp(data: string): boolean;
42
42
  export declare function appApproveKeys(): readonly KeyId[];
43
43
  /** Match the tool-approval key, recognizing modifier-tagged LF as Ctrl+Enter like {@link matchesAppFollowUp}. */
44
44
  export declare function matchesAppApprove(data: string): boolean;
45
+ /**
46
+ * Effective /btw inject key (Alt+I by default). No fallback: it sends text into the main
47
+ * session, so a user who unbinds it gets no key at all.
48
+ */
49
+ export declare function appBtwInjectKey(): KeyId | undefined;
50
+ /** Match the key that sends a /btw answer's steering prompt to the main session. */
51
+ export declare function matchesAppBtwInject(data: string): boolean;
@@ -1,6 +1,7 @@
1
1
  import type { ImageContent } from "@oh-my-pi/pi-ai";
2
2
  import { type Component, type Focusable, Input } from "../index.js";
3
3
  import { type BtwHistoryRecord } from "./btw-history.js";
4
+ import { type BtwInjectState } from "./btw-inject.js";
4
5
  import type { SpaceHoldHandler } from "../space-hold.js";
5
6
  import { type KeyId } from "../app-keybindings.js";
6
7
  import type { NativeNode, NativeUiEvent } from "../native/node.js";
@@ -26,6 +27,18 @@ interface BtwHistoryPanelOptions {
26
27
  * first. For a host with no inline panel to fall back to (Tern).
27
28
  */
28
29
  escapeHides?: boolean;
30
+ /** `btw.inject` hooks, given only while it is on for this view. */
31
+ inject?: BtwHistoryInject;
32
+ }
33
+ interface BtwHistoryInject {
34
+ /**
35
+ * How a finished turn's closed `<inject>` block shows: apart from its prose while pending or
36
+ * sent, plain text when `undefined`. A streaming turn always shows its block.
37
+ */
38
+ state: (record: BtwHistoryRecord, turnIndex: number) => BtwInjectState | undefined;
39
+ /** Whether the inject key may send the record's latest block now. */
40
+ canSend: (record: BtwHistoryRecord) => boolean;
41
+ send: (record: BtwHistoryRecord) => void;
29
42
  }
30
43
  /** Session-local side questions. Selecting or copying never promotes them into chat. */
31
44
  export declare class BtwHistoryPanel implements Component, Focusable {
@@ -20,5 +20,29 @@ export interface BtwHistoryRecord extends BtwHistoryTurn {
20
20
  export declare function getBtwTopicLabel(record: BtwHistoryRecord): string;
21
21
  export declare function getBtwLatestTurn(record: BtwHistoryRecord): BtwHistoryTurn;
22
22
  export declare function getBtwTurns(record: BtwHistoryRecord): readonly BtwHistoryTurn[];
23
+ /** Identifies one turn of a topic, its latest by default: a follow-up gets a new key, so per-answer marks reset. */
24
+ export declare function getBtwTurnKey(record: BtwHistoryRecord, turnIndex?: number): string;
23
25
  /** Copy the most recent nonblank answer, preserving its original whitespace. */
24
26
  export declare function getBtwCopyText(record: BtwHistoryRecord): string | undefined;
27
+ /** An answer around its `<inject>` block: the steering prompt a side answer drafted for the main session. */
28
+ export interface BtwInjectParts {
29
+ before: string;
30
+ inject: string;
31
+ after: string;
32
+ /** False while no opener has a close: the block may still be streaming, so it runs to the end. */
33
+ closed: boolean;
34
+ }
35
+ /**
36
+ * Split an answer at its last closed `<inject>` block: the last opener that has a close
37
+ * after it, paired with the first close after it. A tag merely mentioned in prose,
38
+ * before or after the block, cannot capture it. Without a closed block, the last opener
39
+ * runs to the end (`closed: false`); display it only while the answer still streams.
40
+ * Linear: the last opener before the last close is the last one with any close after it.
41
+ */
42
+ export declare function splitBtwInject(answer: string): BtwInjectParts | undefined;
43
+ /** A block's text as shown and as sent: control sequences stripped, tabs expanded, trimmed. */
44
+ export declare function formatBtwInjectText(inject: string): string;
45
+ /** The steering prompt to send, exactly as the block shows it; undefined while unclosed or blank. */
46
+ export declare function getBtwInject(answer: string): string | undefined;
47
+ /** A topic's steering prompt: only its latest turn's, once that turn completed. */
48
+ export declare function getBtwRecordInject(record: BtwHistoryRecord): string | undefined;
@@ -0,0 +1,22 @@
1
+ import { type Component } from "../index.js";
2
+ import type { NativeNode } from "../native/node.js";
3
+ import { type BtwInjectParts } from "./btw-history.js";
4
+ /** Labels a steering prompt that has not been sent yet. */
5
+ export declare const BTW_INJECT_LABEL = "Inject into main session";
6
+ /** Labels a sent steering prompt. */
7
+ export declare const BTW_INJECTED_LABEL = "Injected into main session";
8
+ /** A displayed `<inject>` block: still sendable, or sent. Blocks in neither state render as plain text. */
9
+ export type BtwInjectState = "pending" | "sent";
10
+ /** Text-mode answer split around its `<inject>` block: prose, the railed block, prose, a blank row apart. */
11
+ export declare class BtwInjectAnswer implements Component {
12
+ #private;
13
+ constructor(parts: BtwInjectParts, sent: boolean);
14
+ render(width: number): string[];
15
+ invalidate(): void;
16
+ }
17
+ /**
18
+ * The same answer for native surfaces. They wrap text themselves, so a per-row gutter cannot
19
+ * follow wrapped lines: a card toned like the rail (`user` pending, `success` sent) marks the
20
+ * whole block instead, with the label as its last row.
21
+ */
22
+ export declare function describeBtwInjectAnswer(parts: BtwInjectParts, sent: boolean, stream: boolean): NativeNode;
@@ -8,6 +8,11 @@ interface BtwPanelComponentOptions {
8
8
  tui: TUI;
9
9
  canBranch?: () => boolean;
10
10
  canFollowUp?: () => boolean;
11
+ /**
12
+ * Given only while `btw.inject` is on: the answer's `<inject>` block shows apart from its
13
+ * prose, and the inject key (`app.btw.inject`) is offered while this returns true.
14
+ */
15
+ canInject?: () => boolean;
11
16
  }
12
17
  export declare class BtwPanelComponent extends OverlayPanel {
13
18
  #private;
@@ -17,6 +22,8 @@ export declare class BtwPanelComponent extends OverlayPanel {
17
22
  markComplete(): void;
18
23
  /** Visual confirmation that `c` copied the answer to the clipboard. */
19
24
  markCopied(): void;
25
+ /** Shows the answer's steering prompt as sent (send arrows, green), or back to pending when a send failed. */
26
+ setInjected(injected: boolean): void;
20
27
  /** Shows that the completed answer is being promoted into the chat session. */
21
28
  markBranching(): void;
22
29
  markAborted(): void;
@@ -18,6 +18,16 @@ export interface SessionSelectorEntry {
18
18
  /** Path to the parent session when this one was forked. */
19
19
  parentSessionPath?: string;
20
20
  }
21
+ /**
22
+ * One picker scope's rows, possibly in two passes: `sessions` show at once
23
+ * (e.g. rows built from a title index), and `complete` settles with the full
24
+ * listing that replaces them, keeping the query, the selected session, and
25
+ * deletes/renames made meanwhile.
26
+ */
27
+ export interface SessionListing<T extends SessionSelectorEntry = SessionSelectorEntry> {
28
+ sessions: T[];
29
+ complete?: Promise<T[]>;
30
+ }
21
31
  import { OverlayPanel } from "../chrome/overlay-box.js";
22
32
  /** The session list's part of the picker props, plus the memo keys it was built from. */
23
33
  interface SessionPickerView {
@@ -76,6 +86,12 @@ declare class SessionList<T extends SessionSelectorEntry> implements Component {
76
86
  /** Re-render hook for async list updates (fuzzy scan completion, history merge). */
77
87
  onRequestRender?: () => void;
78
88
  constructor(sessions: T[], showCwd?: boolean, historyMatcher?: SessionHistoryMatcher, getTerminalRows?: () => number, pinnedIds?: ReadonlySet<string>, currentSessionPath?: string | (() => string | undefined));
89
+ /**
90
+ * Swap in the complete listing of the shown scope (a scan that finished
91
+ * after the picker opened), keeping the query and the selected session:
92
+ * rows arriving above the cursor never move it onto another session.
93
+ */
94
+ refreshSessions(sessions: T[], showCwd: boolean): void;
79
95
  /** Replace the visible dataset, e.g. when toggling folder/all-projects scope. */
80
96
  setSessions(sessions: T[], showCwd: boolean, pinnedIds?: ReadonlySet<string>): void;
81
97
  /** Cancel pending async search work; idempotent, called on every picker exit path. */
@@ -141,10 +157,15 @@ export interface SessionSelectorOptions<T extends SessionSelectorEntry = Session
141
157
  /** Persist the selected session's pin state (pin keys / the picker's Pin action). */
142
158
  onSetPinned?: (session: T, pinned: boolean) => Promise<void>;
143
159
  historyMatcher?: SessionHistoryMatcher;
160
+ /**
161
+ * Complete folder listing when the constructor's `sessions` are a
162
+ * preliminary subset; replaces them once it settles (see {@link SessionListing}).
163
+ */
164
+ completeSessions?: Promise<T[]>;
144
165
  /** Loads sessions across all projects for the all-projects scope toggle (Tab). */
145
- loadAllSessions?: () => Promise<T[]>;
146
- /** Preloaded all-projects list; cached so the first Tab toggle is instant. */
147
- allSessions?: T[];
166
+ loadAllSessions?: () => Promise<SessionListing<T>>;
167
+ /** Preloaded all-projects listing; cached so the first Tab toggle is instant. */
168
+ allSessions?: SessionListing<T>;
148
169
  /** Picker heading; defaults to "Resume Session". */
149
170
  title?: string;
150
171
  /** Primary action label; defaults to "Resume" (ANSI footer defaults to "select"). */
@@ -211,7 +211,10 @@ export declare function formatStyledPath(filePath: string, theme: Theme): string
211
211
  export type CodeFrameMarker = "" | " " | "*" | "+" | "-" | ">";
212
212
  /** Format a code-frame row with a padded line-number gutter. */
213
213
  export declare function formatCodeFrameLine(marker: CodeFrameMarker, lineNumber: string | number, content: string, lineNumberWidth: number): string;
214
- /** Status states supported by shared tool renderer icons. */
214
+ /**
215
+ * Status states supported by shared tool renderer icons. `done` is a deprecated
216
+ * alias of `success`, kept for extension renderers.
217
+ */
215
218
  export type ToolUIStatus = "success" | "done" | "error" | "warning" | "info" | "pending" | "running" | "aborted";
216
219
  /** Semantic foreground colors used by tool renderer labels. */
217
220
  export type ToolUIColor = "success" | "error" | "warning" | "accent" | "muted";
@@ -1,18 +1,3 @@
1
- /**
2
- * Status line for the speculative first frame, before any session exists.
3
- *
4
- * InteractiveMode persists {@link StatusLineStartupData} (settings plus the
5
- * resolved default role model and status inputs); the next launch renders it
6
- * through the real {@link StatusLineComponent} as a fresh, unnamed session at
7
- * the live terminal width. Path and git branch are read live; context usage is
8
- * reported unknown so the gauge shows the window without a percent.
9
- *
10
- * @example
11
- * ```ts ignore
12
- * const statusLine = createStartupStatusLine(cached.statusLine);
13
- * statusLine.attachToEditor(editor, getComposerStyle(shape));
14
- * ```
15
- */
16
1
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
17
2
  import type { Model } from "@oh-my-pi/pi-catalog/types";
18
3
  import { StatusLineComponent } from "./component.js";
@@ -2,7 +2,9 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
2
2
  /**
3
3
  * All available symbol keys organized by category.
4
4
  */
5
- export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "context.content" | "context.messages" | "context.buffer" | "context.free" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "icon.vimReplace" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
5
+ export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted"
6
+ /** @deprecated Alias of `status.success`; kept so extensions and theme overrides that name it keep working. */
7
+ | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "context.content" | "context.messages" | "context.buffer" | "context.free" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "icon.vimReplace" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "btw.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
6
8
  export type SymbolMap = Record<SymbolKey, string>;
7
9
  /**
8
10
  * Icon vocabulary for slash-command autocomplete type indicators. Each name
@@ -129,6 +129,7 @@ export declare class Theme {
129
129
  running: string;
130
130
  shadowed: string;
131
131
  aborted: string;
132
+ /** @deprecated Alias of `success`. */
132
133
  done: string;
133
134
  };
134
135
  get nav(): {
@@ -100,8 +100,21 @@ export interface LimitsMeta {
100
100
  last: number;
101
101
  };
102
102
  selectorBase?: string;
103
+ /** The first cut line exceeds what one `:raw` read returns; that read stops at this many bytes. */
104
+ rawByteCap?: number;
103
105
  };
104
106
  }
107
+ /**
108
+ * The bash minimizer rewrote this output: `filter` is the native filter label
109
+ * (`chain` for a minimized `&&`/`;` chain), the byte counts are the raw capture
110
+ * and the minimized text, and `artifactId` holds the raw capture.
111
+ */
112
+ export interface MinimizedMeta {
113
+ filter: string;
114
+ inputBytes: number;
115
+ outputBytes: number;
116
+ artifactId: string;
117
+ }
105
118
  /**
106
119
  * Structured metadata for tool outputs.
107
120
  */
@@ -114,6 +127,7 @@ export interface OutputMeta {
114
127
  pagedSource?: true;
115
128
  diagnostics?: DiagnosticMeta;
116
129
  limits?: LimitsMeta;
130
+ minimized?: MinimizedMeta;
117
131
  }
118
132
  /**
119
133
  * Reformat pre-formatted diagnostic messages into a multi-level, prefix-folded
@@ -136,6 +150,13 @@ export declare function stripRawOutputArtifactNotice(text: string): {
136
150
  text: string;
137
151
  artifactId?: string;
138
152
  };
153
+ /** Whether `line` is the column-cap notice {@link formatOutputNotice} emits (`[Some lines truncated to 768 chars…]`). */
154
+ export declare function isColumnTruncationNotice(line: string): boolean;
155
+ /**
156
+ * Whether the bracketed notice `line` carries the column-cap clause: alone, or after the parts
157
+ * {@link formatOutputNotice} joins before it (`[Showing lines 1-4 of 5. … Some lines truncated to 768 chars…]`).
158
+ */
159
+ export declare function hasColumnTruncationClause(line: string): boolean;
139
160
  /** Remove a trailing generated output notice when metadata is unavailable. */
140
161
  export declare function stripGeneratedOutputNotice(text: string): string;
141
162
  /** Format truncation ranges and recovery hints. */
@@ -147,6 +168,12 @@ export declare function formatTruncationMetaNotice(truncation: TruncationMeta, s
147
168
  export declare function formatStyledArtifactReference(artifactId: string, theme: Theme): string;
148
169
  /** Describe an incomplete artifact capture. */
149
170
  export declare function formatArtifactErrorNotice(error: OutputArtifactError): string;
171
+ /**
172
+ * `git 120.4KB → 3.2KB`: the minimizer filter that ran and the bytes it took the output from and to.
173
+ * Native labels are short words; a rehydrated session or extension may carry any text, so the label
174
+ * is sanitized to one plain line, its home paths shortened to `~`, and width-bounded.
175
+ */
176
+ export declare function formatMinimizedMeta(minimized: MinimizedMeta): string;
150
177
  /**
151
178
  * Format notices from OutputMeta for LLM consumption.
152
179
  * Returns empty string if no notices needed.
@@ -36,6 +36,10 @@ export declare function truncateToWidth(text: string, maxWidth: number, ellipsis
36
36
  export declare function wrapTextWithAnsi(text: string, width: number): string[];
37
37
  export declare function extractSegments(line: string, beforeEnd: number, afterStart: number, afterLen: number, strictAfter: boolean): ExtractSegmentsResult;
38
38
  export declare function replaceTabs(text: string): string;
39
+ /** Tabs expanded and line breaks collapsed to spaces, for text confined to one rendered row. */
40
+ export declare function singleLine(text: string): string;
41
+ /** A non-negative integer cell width; non-finite input becomes `fallback`. */
42
+ export declare function finiteWidth(width: number, fallback?: number): number;
39
43
  /**
40
44
  * Returns a string of n spaces. Uses a pre-allocated buffer for efficiency.
41
45
  */
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.15.0",
4
+ "version": "18.16.0",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -41,14 +41,14 @@
41
41
  "gen:glyphs": "bun scripts/gen-glyphs.ts"
42
42
  },
43
43
  "dependencies": {
44
- "@oh-my-pi/omptype": "18.15.0",
45
- "@oh-my-pi/pi-agent-core": "18.15.0",
46
- "@oh-my-pi/pi-ai": "18.15.0",
47
- "@oh-my-pi/pi-catalog": "18.15.0",
48
- "@oh-my-pi/pi-natives": "18.15.0",
49
- "@oh-my-pi/pi-utils": "18.15.0",
50
- "@oh-my-pi/pi-wire": "18.15.0",
51
- "@oh-my-pi/snapcompact": "18.15.0"
44
+ "@oh-my-pi/omptype": "18.16.0",
45
+ "@oh-my-pi/pi-agent-core": "18.16.0",
46
+ "@oh-my-pi/pi-ai": "18.16.0",
47
+ "@oh-my-pi/pi-catalog": "18.16.0",
48
+ "@oh-my-pi/pi-natives": "18.16.0",
49
+ "@oh-my-pi/pi-utils": "18.16.0",
50
+ "@oh-my-pi/pi-wire": "18.16.0",
51
+ "@oh-my-pi/snapcompact": "18.16.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "kitty-vt-wasm": "^0.2.0"