@oh-my-pi/pi-tui 18.11.2 → 18.12.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 (61) hide show
  1. package/CHANGELOG.md +37 -100
  2. package/README.md +36 -0
  3. package/dist/types/components/markdown.d.ts +4 -3
  4. package/dist/types/overlays/approval-dialog.d.ts +25 -15
  5. package/dist/types/overlays/model-picker.d.ts +2 -0
  6. package/dist/types/overlays/session-selector.d.ts +2 -0
  7. package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
  8. package/dist/types/render/hanging-wrap.d.ts +2 -0
  9. package/dist/types/render/render-utils.d.ts +9 -1
  10. package/dist/types/status-line/types.d.ts +3 -0
  11. package/dist/types/terminal-capabilities.d.ts +8 -1
  12. package/dist/types/terminal-multiplexer.d.ts +6 -0
  13. package/dist/types/theme/symbols.d.ts +1 -1
  14. package/dist/types/theme/theme.d.ts +11 -0
  15. package/dist/types/tools/ast-edit.d.ts +2 -2
  16. package/dist/types/tools/ast-grep.d.ts +2 -2
  17. package/dist/types/tools/edit.d.ts +2 -0
  18. package/dist/types/tools/find.d.ts +14 -4
  19. package/dist/types/tools/glob.d.ts +3 -3
  20. package/dist/types/tools/grep.d.ts +2 -2
  21. package/dist/types/tools/read.d.ts +13 -5
  22. package/dist/types/tools/task.d.ts +8 -0
  23. package/dist/types/tools/write.d.ts +2 -0
  24. package/package.json +9 -9
  25. package/src/chat/assistant-message.ts +3 -1
  26. package/src/chat/read-tool-group.ts +17 -9
  27. package/src/chat/tool-execution.ts +23 -6
  28. package/src/components/image.ts +31 -14
  29. package/src/components/markdown.ts +74 -5
  30. package/src/overlays/approval-dialog.ts +104 -36
  31. package/src/overlays/model-picker.ts +58 -21
  32. package/src/overlays/session-selector.ts +14 -9
  33. package/src/prompt/internal-url-autocomplete.ts +1 -0
  34. package/src/render/hanging-wrap.ts +28 -1
  35. package/src/render/render-utils.ts +16 -2
  36. package/src/status-line/component.ts +1 -0
  37. package/src/status-line/segments.ts +16 -5
  38. package/src/status-line/types.ts +1 -0
  39. package/src/terminal-capabilities.ts +35 -21
  40. package/src/terminal-multiplexer.ts +14 -0
  41. package/src/terminal.ts +11 -2
  42. package/src/theme/symbols.ts +1 -0
  43. package/src/theme/theme-class.ts +1 -0
  44. package/src/theme/theme.ts +46 -14
  45. package/src/tmux.ts +7 -1
  46. package/src/tools/ast-edit.ts +28 -4
  47. package/src/tools/ast-grep.ts +24 -4
  48. package/src/tools/debug.ts +2 -1
  49. package/src/tools/default-renderer.ts +3 -2
  50. package/src/tools/edit.ts +77 -16
  51. package/src/tools/find.ts +65 -15
  52. package/src/tools/glob.ts +51 -17
  53. package/src/tools/grep.ts +94 -11
  54. package/src/tools/lsp.ts +23 -18
  55. package/src/tools/mcp.ts +2 -2
  56. package/src/tools/read.ts +52 -22
  57. package/src/tools/task.ts +17 -7
  58. package/src/tools/web-search.ts +2 -1
  59. package/src/tools/write.ts +17 -5
  60. package/src/tools/yield.ts +2 -2
  61. package/src/tui.ts +36 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.12.0] - 2026-10-17
6
+
7
+ ### Breaking Changes
8
+
9
+ - `FindStats.errors` is replaced by `failed` (judge requests that failed) and `unanswered` (unusable answers), and `FindToolDetails` requires a `coverage` status; the find renderer shows `N/M requests failed` ([#15744](https://github.com/can1357/oh-my-pi/issues/15744))
10
+
11
+ ### Added
12
+
13
+ - Added `pi.showProfile` to `StatusLineSegmentOptions`: `false` keeps the `pi` segment's bare glyph under a named profile, in the rendered bar and in `describe` ([#16063](https://github.com/can1357/oh-my-pi/pull/16063) by [@shgew](https://github.com/shgew))
14
+
15
+ ### Fixed
16
+
17
+ - Fixed sessions run inside tmux silently falling back to the terminal's true-color profile when the client terminal-type probe answered slowly on a loaded machine ([#16020](https://github.com/can1357/oh-my-pi/pull/16020) by [@CharleoYang](https://github.com/CharleoYang))
18
+ - Fixed the `read` result notice printing the absolute resolved path, so a file under the home directory reads `Resolved path: ~/notes.txt` instead of `/home/you/notes.txt` ([#16055](https://github.com/can1357/oh-my-pi/pull/16055) by [@adi-IL](https://github.com/adi-IL)).
19
+ - Fixed grep/read previews and other tool cards passing raw escape sequences from file contents to the terminal, which could switch it to the alternate screen with mouse reporting on, live and again on every resume ([#15963](https://github.com/can1357/oh-my-pi/issues/15963))
20
+ - Fixed a final status-line render after shutdown scheduling another usage refresh ([#15845](https://github.com/can1357/oh-my-pi/issues/15845)).
21
+ - Fixed Ctrl+O on LSP status and other plain LSP responses still clipping every row at 80 columns; expanded rows now wrap to show the full text ([#15824](https://github.com/can1357/oh-my-pi/issues/15824))
22
+ - Fixed Ctrl+O leaving long rows clipped in expanded LSP, debug, MCP, web search fallback, yield, task and custom-tool results; expanded rows now wrap to show the full text, expanded LSP references list every location, and the LSP expand hint no longer appears when nothing is hidden ([#15824](https://github.com/can1357/oh-my-pi/issues/15824))
23
+ - Fixed jagged text and disappearing translucent details in SIXEL images, including re-encoding when the terminal reports a changed background color ([#15891](https://github.com/can1357/oh-my-pi/pull/15891) by [@Bouska](https://github.com/Bouska)).
24
+ - Fixed stale SIXEL backgrounds after terminal restarts, native appearance changes, and failed TSP handshakes, including background changes during the handshake, fixed themes, missing or invalid startup themes, or identical light/dark mappings ([#15891](https://github.com/can1357/oh-my-pi/pull/15891) by [@Bouska](https://github.com/Bouska)).
25
+
26
+ ## [18.11.3] - 2026-10-16
27
+
28
+ ### Added
29
+
30
+ - Added selection-only session and model picker actions for `/seance`, so choosing a source or model leaves the active session and model unchanged ([#15036](https://github.com/can1357/oh-my-pi/pull/15036) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy))
31
+
32
+ ### Changed
33
+
34
+ - `ApprovalDialogComponent` shows a choice list (Approve, an optional `session` row, Deny): the arrow keys move between them and Enter (armed after `APPROVE_ARM_MS`) takes the highlighted one; while a composer draft holds Enter the highlight sits on Deny, relabeled "Deny and send message"; Tern clicks on a choice take it; Page Up/Down scroll the details
35
+
36
+ ### Fixed
37
+
38
+ - Fixed a streamed Markdown list keeping every item out of native scrollback until the turn ended, so rows that scrolled off screen were unreachable mid-stream; items in front of the newest one now retire as soon as it begins ([#13676](https://github.com/can1357/oh-my-pi/issues/13676), [#15993](https://github.com/can1357/oh-my-pi/pull/15993) by [@shgew](https://github.com/shgew))
39
+ - Fixed SIXEL charts and SVG figures being resampled before display, which softened their text, on terminals whose cell height is not a multiple of 6 such as Windows Terminal ([#16007](https://github.com/can1357/oh-my-pi/issues/16007))
40
+
5
41
  ## [18.11.2] - 2026-10-16
6
42
 
7
43
  ### Changed
@@ -2486,103 +2522,4 @@
2486
2522
  - Added `getIndentationNoescape` wrapper that uses `process.cwd()` as the project root for relative file paths
2487
2523
  - Re-export `getDefaultTabWidth`, `getIndentation`, and `setDefaultTabWidth` from `@oh-my-pi/pi-utils`; native text helpers still receive tab width via wrappers that read the JS default
2488
2524
 
2489
- ## [13.16.1] - 2026-03-27
2490
-
2491
- ### Added
2492
-
2493
- - Support for optional SearchDb parameter in CombinedAutocompleteProvider constructor for improved fuzzy search performance
2494
- - Fuzzy matching filter for autocomplete suggestions to improve relevance of results
2495
-
2496
- ### Changed
2497
-
2498
- - Fuzzy discovery now applies fuzzy matching filter to results for improved relevance of autocomplete suggestions
2499
- - Autocomplete fuzzy discovery now accepts optional SearchDb instance for faster searches
2500
-
2501
- ## [13.16.0] - 2026-03-27
2502
-
2503
- ### Changed
2504
-
2505
- - Updated tab replacement in editor text sanitization to respect configured tab width setting
2506
-
2507
- ## [13.15.0] - 2026-03-23
2508
-
2509
- ### Added
2510
-
2511
- - Added `renderInlineMarkdown()` function to render inline markdown (bold, italic, code, links, strikethrough) to styled strings
2512
-
2513
- ### Fixed
2514
-
2515
- - Fixed editor consuming user-rebound copy keys, preventing custom keybindings from working in the editor
2516
-
2517
- ## [13.14.1] - 2026-03-21
2518
-
2519
- ### Added
2520
-
2521
- - Added Ctrl+_ as an additional default shortcut for undo
2522
-
2523
- ### Fixed
2524
-
2525
- - Ensured undo functionality respects user-configured keybindings
2526
-
2527
- ## [13.12.0] - 2026-03-14
2528
-
2529
- ### Added
2530
-
2531
- - Added `moveToMessageStart()` and `moveToMessageEnd()` methods to move cursor to the beginning and end of the entire message
2532
-
2533
- ### Fixed
2534
-
2535
- - Fixed autocomplete to preserve `./` prefix when completing relative file and directory paths
2536
- - Fixed paste marker expansion to handle special regex replacement tokens ($1, $2, $&, $$, $`, $') literally in pasted content
2537
-
2538
- ## [13.11.0] - 2026-03-12
2539
-
2540
- ### Fixed
2541
-
2542
- - Fixed OSC 11 background color detection to correctly handle partial escape sequences that arrive mid-buffer, preventing user input from being swallowed
2543
- - Fixed race condition where overlapping OSC 11 queries would be incorrectly cancelled by DA1 sentinels from previous queries
2544
-
2545
- ## [13.7.5] - 2026-03-04
2546
-
2547
- ### Changed
2548
-
2549
- - Extracted word navigation logic into reusable `moveWordLeft` and `moveWordRight` utility functions for consistent cursor movement across components
2550
-
2551
- ## [13.6.2] - 2026-03-03
2552
-
2553
- ### Fixed
2554
-
2555
- - Fixed cursor positioning when content shrinks to empty without clearOnShrink enabled
2556
-
2557
- ## [13.5.4] - 2026-03-01
2558
-
2559
- ### Fixed
2560
-
2561
- - Fixed viewport repaint scrollback accounting during resize oscillation to avoid double-scrolling on height shrink and added exact-row scrollback assertions in overlay regression coverage ([#228](https://github.com/can1357/oh-my-pi/issues/228), [#234](https://github.com/can1357/oh-my-pi/issues/234))
2562
-
2563
- ## [13.5.3] - 2026-03-01
2564
-
2565
- ### Fixed
2566
-
2567
- - Fixed append rendering logic to correctly handle offscreen header changes during content overflow growth, preserving scroll history integrity
2568
- - Fixed visible tail line updates when appending new content during viewport overflow conditions
2569
- - Fixed cursor positioning instability when appending content under external cursor relocation by using absolute screen addressing instead of relative cursor movement
2570
-
2571
- ## [13.5.2] - 2026-03-01
2572
-
2573
- ### Breaking Changes
2574
-
2575
- - Removed `getMermaidImage` callback from MarkdownTheme; replaced with `getMermaidAscii` that accepts ASCII string instead of image data
2576
- - Removed mermaid module exports (`renderMermaidToPng`, `extractMermaidBlocks`, `prerenderMermaidBlocks`, `MermaidImage` interface)
2577
-
2578
- ### Changed
2579
-
2580
- - Mermaid diagrams now render as ASCII text instead of terminal graphics protocol images
2581
-
2582
- ## [13.5.1] - 2026-03-01
2583
-
2584
- ### Fixed
2585
-
2586
- - Fixed viewport shift handling to prevent stale content when mixed updates remap screen rows
2587
-
2588
- Older entries are archived in [packages/tui/CHANGELOG.md@9748d65121ba](https://github.com/can1357/oh-my-pi/blob/9748d65121bac9589d3b136a6d839d76ec52dc2e/packages/tui/CHANGELOG.md).
2525
+ Older entries are archived in [packages/tui/CHANGELOG.md@691b3d448f3d](https://github.com/can1357/oh-my-pi/blob/691b3d448f3da1f99010854dea4a562395fd190d/packages/tui/CHANGELOG.md).
package/README.md CHANGED
@@ -38,6 +38,42 @@ tui.addChild(editor);
38
38
  tui.start();
39
39
  ```
40
40
 
41
+ ## SIXEL transparency and terminal backgrounds
42
+
43
+ SIXEL has binary transparency, not fractional alpha. omp blends partially
44
+ transparent image pixels against the terminal's reported OSC 11 background
45
+ before quantization, preserving antialiased text and translucent details.
46
+ Images with alpha are resized in premultiplied form before background compositing
47
+ to prevent dark fringes and hidden-color bleed along scaled transparent edges.
48
+ Fully transparent pixels stay transparent. Kitty and iTerm2 retain their
49
+ existing image paths.
50
+
51
+ When a new OSC 11 report changes the background RGB, SIXEL images are
52
+ re-encoded even if the terminal stays dark or stays light. Terminals supporting
53
+ Mode 2031 trigger this automatically. Otherwise, press Ctrl+L to refresh the
54
+ background after changing it. This introduces no periodic polling.
55
+ Direct terminals replay transcript history using the existing theme-change
56
+ repaint policy; multiplexers repaint the viewport without clearing their native
57
+ scrollback.
58
+
59
+ Cached RGB is cleared when terminal ownership starts or ends, so a restarted
60
+ session without an OSC 11 response cannot inherit the previous terminal's RGB.
61
+ If the terminal cannot report its RGB, or an active multiplexer marks background
62
+ queries as unreliable on the current platform, omp approximates the background
63
+ with black for detected dark appearance or white for light. Untrusted reports
64
+ do not override the existing appearance fallback. Exact edge-color matching is
65
+ not guaranteed with that fallback, transparent terminal windows, or graphics
66
+ overlapping a differently colored background.
67
+
68
+ On known-broken background-query paths, such as macOS inside Zellij, native
69
+ appearance changes also repaint SIXEL images when using a fixed theme or
70
+ identical light/dark theme mappings. `COLORFGBG` still takes precedence over the
71
+ native fallback; reports that leave the effective background unchanged do not
72
+ trigger a repaint.
73
+
74
+ If native TSP negotiation fails, the row renderer resumes these SIXEL refreshes.
75
+ Expecting native support alone does not disable repainting after the failure.
76
+
41
77
  ## Core API
42
78
 
43
79
  ### TUI
@@ -185,9 +185,10 @@ export declare class Markdown implements Component {
185
185
  */
186
186
  describe(_cx: DescribeContext): NativeNode;
187
187
  /**
188
- * Width-independent source prefix of the last render ending at a frozen
189
- * Markdown block boundary that no append can move: it stops in front of a
190
- * display-math opener whose block an append could still close. Only
188
+ * Width-independent source prefix of the last render that no append can
189
+ * change: up to a frozen Markdown block boundary, stopping in front of a
190
+ * display-math opener whose block an append could still close, or, while a
191
+ * list streams as the last block, up to its newest top-level item. Only
191
192
  * meaningful while streaming (transient render cache on); grows
192
193
  * monotonically under append-only `setText`.
193
194
  */
@@ -1,19 +1,23 @@
1
1
  /**
2
2
  * Tool-approval prompt in the composer's place, presented like the ask
3
- * dialog: the approval title and its details over an action bar. Approving
4
- * takes a deliberate chord (`app.approval.approve`, Ctrl+Enter by default),
5
- * so an Enter typed for the composer as the prompt appears never approves a
6
- * call, and the chord itself only arms {@link APPROVE_ARM_MS} after mount: it
7
- * is also the follow-up key, pressed mid-run exactly when prompts appear.
8
- * Esc denies. With an {@link ApprovalDialogDraft}, every other key edits the
9
- * composer mounted beneath the dialog, so a message can steer the turn while
10
- * the call waits; an empty composer ignores Enter and arrows scroll the details.
11
- * The prompt stays a few rows so the composer keeps its room; with a full
12
- * prompt, the expand key (`app.tools.expand`) swaps it in, wrapped.
3
+ * dialog: the approval title and its details over a list of choices —
4
+ * Approve, an optional "approve for this session", and Deny — and an action
5
+ * bar. The arrows move between the choices and Enter takes the highlighted
6
+ * one; `app.approval.approve` (Ctrl+Enter by default) approves this call
7
+ * outright and Esc denies. Enter and the approve chord only arm
8
+ * {@link APPROVE_ARM_MS} after mount, so a key meant for the composer (Enter,
9
+ * or the follow-up chord pressed mid-run) as the prompt appears never answers
10
+ * it. With an {@link ApprovalDialogDraft}, every other key edits the composer
11
+ * mounted beneath the dialog, so a message can deny the call and steer the
12
+ * turn instead: while the composer holds a draft, the arrows and Enter edit and
13
+ * send it, and the highlight sits on Deny, relabeled to say Enter sends.
14
+ * The prompt stays a few rows so the composer keeps its room; Page Up/Down
15
+ * scroll the details, and with a full prompt the expand key
16
+ * (`app.tools.expand`) swaps it in, wrapped.
13
17
  */
14
18
  import { type Component, type TUI } from "../index.js";
15
- import type { NativeNode, NativeUiEvent } from "../native/node.js";
16
- /** The approve key is ignored this long after mount, so a follow-up sent as the prompt appears cannot approve it. */
19
+ import { type NativeNode, type NativeUiEvent } from "../native/node.js";
20
+ /** Enter and the approve key are ignored this long after mount, so a key sent as the prompt appears cannot answer it. */
17
21
  export declare const APPROVE_ARM_MS = 500;
18
22
  interface ApprovalDialogCallbacks {
19
23
  onApprove(): void;
@@ -39,6 +43,11 @@ interface ApprovalDialogOptions {
39
43
  onTimeoutReset?: () => void;
40
44
  /** Route typing to the composer instead of dropping it. */
41
45
  draft?: ApprovalDialogDraft;
46
+ /** Offer to approve this and later like calls for the rest of the session, as the row between Approve and Deny. */
47
+ session?: {
48
+ label: string;
49
+ onApprove(): void;
50
+ };
42
51
  }
43
52
  export declare class ApprovalDialogComponent implements Component {
44
53
  #private;
@@ -59,11 +68,12 @@ export declare class ApprovalDialogComponent implements Component {
59
68
  /**
60
69
  * In the dock, in the composer's place and framed as the composer is (an
61
70
  * `omp.editor.*` root role), like the ask dialog, rather than a card over
62
- * the transcript: the title, the details, then Details (with a full prompt),
63
- * Deny, and the primary Approve, whose keys show in their tooltips.
71
+ * the transcript: the title, the details, the choices with the keyboard's
72
+ * highlight (a click takes one), then Details (with a full prompt), Deny,
73
+ * and the primary Approve, whose keys show in their tooltips.
64
74
  */
65
75
  describe(): NativeNode;
66
- /** Deny, Approve, and Details run exactly what Esc, the approve key, and the expand key run. */
76
+ /** Deny, Approve, and Details run exactly what Esc, the approve key, and the expand key run; a click on a choice takes it. */
67
77
  handleNativeEvent(event: NativeUiEvent): void;
68
78
  }
69
79
  export {};
@@ -57,6 +57,8 @@ export interface ModelPickerOptions {
57
57
  taskModeKeys?: readonly KeyId[];
58
58
  /** `provider/id` highlighted and preselected in task mode (current Task subagent model). */
59
59
  taskSelector?: string;
60
+ /** Present selection-only chrome; consumers handle the chosen selector without mutating a session. */
61
+ selectOnly?: boolean;
60
62
  }
61
63
  /** Rows the browser renders around its list window (search + blank, blank + two detail rows). */
62
64
  export declare const BROWSER_FRAME_ROWS = 5;
@@ -127,6 +127,8 @@ export interface SessionSelectorOptions<T extends SessionSelectorEntry = Session
127
127
  allSessions?: T[];
128
128
  /** Picker heading; defaults to "Resume Session". */
129
129
  title?: string;
130
+ /** Primary action label; defaults to "Resume" (ANSI footer defaults to "select"). */
131
+ actionLabel?: string;
130
132
  /** Fixed scope label, or false to omit the scope suffix. */
131
133
  scopeLabel?: string | false;
132
134
  /**
@@ -31,6 +31,7 @@ export interface InternalUrlCallerContext {
31
31
  cwd?: string;
32
32
  sessionFile?: string;
33
33
  sessionId?: string;
34
+ seanceSourceArtifactsDir?: string;
34
35
  }
35
36
  /**
36
37
  * Suggestions for the internal-url token ending at the cursor, or `null` when
@@ -3,3 +3,5 @@ import type { Theme } from "../theme/theme.js";
3
3
  export declare function wrapPrefixedLine(prefix: string, continuationPrefix: string, content: string, width: number): string[];
4
4
  /** Preserve a tree connector's ancestor guides while hanging a long node beneath its label. */
5
5
  export declare function wrapTreeLine(line: string, width: number, theme: Theme): string[] | undefined;
6
+ /** Hang a tree row beneath its connector, or a connector-less detail row beneath its guide rails. */
7
+ export declare function wrapGuidedLine(line: string, width: number, theme: Theme): string[];
@@ -105,14 +105,22 @@ export declare function cappedHeadLines(lines: readonly string[], max: number):
105
105
  };
106
106
  /** Get the first nonblank preview lines, trimmed and width-capped. */
107
107
  export declare function getPreviewLines(text: string, maxLines: number, maxLineLen: number, ellipsis?: Ellipsis): string[];
108
+ /** Collapse a possibly multi-line string into one line; whitespace runs (tabs included) become one space. */
109
+ export declare function flattenLine(text: string): string;
108
110
  /**
109
111
  * Collapse a possibly multi-line string into a single line, then truncate it to
110
112
  * `maxWidth` display cells. {@link truncateToWidth} alone caps width but
111
113
  * newlines are zero-width, so multi-line content (markdown briefs, tool args,
112
114
  * provider errors) would otherwise spill a single status row across several
113
- * visual lines. Whitespace runs collapse to one space, so tabs are handled too.
115
+ * visual lines.
114
116
  */
115
117
  export declare function previewLine(text: string, maxWidth: number, ellipsis?: Ellipsis): string;
118
+ /**
119
+ * Clip a result row to `maxWidth` only while collapsed. Expanded rows stay whole
120
+ * so the frame wraps them: the expand key ({@link formatExpandHint}) must reveal
121
+ * text, not re-clip it.
122
+ */
123
+ export declare function clipCollapsed(text: string, maxWidth: number, expanded: boolean | undefined): string;
116
124
  /**
117
125
  * Extract domain from URL, stripping www. prefix.
118
126
  */
@@ -31,6 +31,9 @@ export interface CollabStatus {
31
31
  stateOverride?: CollabSessionState | null;
32
32
  }
33
33
  export interface StatusLineSegmentOptions {
34
+ pi?: {
35
+ showProfile?: boolean;
36
+ };
34
37
  model?: {
35
38
  showThinkingLevel?: boolean;
36
39
  };
@@ -1,6 +1,13 @@
1
1
  import type { HangulCompatibilityJamoWidth } from "./utils.js";
2
2
  export * from "./terminal-multiplexer.js";
3
3
  export { wrapTmuxPassthrough } from "./tmux.js";
4
+ /**
5
+ * Trusted terminal background when reported; otherwise black for detected dark
6
+ * appearance or white for detected light appearance, using theme auto-detection.
7
+ */
8
+ export declare function getTerminalBackgroundColor(): number;
9
+ /** Store an OSC 11 background, or reset to appearance detection (also for tests). */
10
+ export declare function setTerminalBackgroundColor(color: number | undefined): void;
4
11
  export declare enum ImageProtocol {
5
12
  Kitty = "\u001B_G",
6
13
  Iterm2 = "\u001B]1337;File=",
@@ -393,7 +400,7 @@ export declare function getImageDimensions(base64Data: string, mimeType: string)
393
400
  * thread; `null` when the encode fails. For output that cannot wait for an
394
401
  * off-thread encode.
395
402
  */
396
- export declare function encodeSixelNow(base64Data: string, widthPx: number, heightPx: number): string | null;
403
+ export declare function encodeSixelNow(base64Data: string, widthPx: number, heightPx: number, backgroundRgb?: number): string | null;
397
404
  export declare function renderImage(base64Data: string, imageDimensions: ImageDimensions, options?: ImageRenderOptions): {
398
405
  sequence?: string;
399
406
  lines?: string[];
@@ -101,6 +101,12 @@ export declare function hasTerminalMultiplexerSession(multiplexer: TerminalMulti
101
101
  * order. Nested sessions all appear; TERM is not consulted.
102
102
  */
103
103
  export declare function terminalMultiplexerSessions(env?: NodeJS.ProcessEnv): TerminalMultiplexerModule<TerminalMultiplexer>[];
104
+ /**
105
+ * Whether OSC 11 background replies are trusted on this platform. Any active
106
+ * session with `backgroundQueryBrokenOn` matching the platform makes the entire
107
+ * path untrusted; TERM-only classification does not identify an active session.
108
+ */
109
+ export declare function isTerminalBackgroundQueryTrusted(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
104
110
  /**
105
111
  * The multiplexer TERM names, whether or not its session markers survived.
106
112
  * This is classification's TERM fallback; session markers are not consulted.
@@ -9,7 +9,7 @@ export type SymbolMap = Record<SymbolKey, string>;
9
9
  * resolves through `Theme.cmd` to either a dedicated `cmd.*` symbol or an
10
10
  * existing `icon.*` symbol shared with the rest of the UI.
11
11
  */
12
- export type SlashCommandIconName = "action" | "prompt" | "extension" | "settings" | "gear" | "shield" | "wave" | "compass" | "inbox" | "swap" | "expand" | "computer" | "eye" | "todo" | "stats" | "news" | "keyboard" | "export" | "clipboard" | "share" | "broadcast" | "globe" | "copy" | "plus" | "restart" | "eraser" | "trash" | "compress" | "vibrate" | "handoff" | "history" | "question" | "rocket" | "stethoscope" | "redo" | "bug" | "memory" | "pencil" | "folderMove" | "folderPlus" | "folderMinus" | "hammer" | "power" | "cart" | "model" | "plan" | "prewalk" | "goal" | "pause" | "loop" | "session" | "jobs" | "gauge" | "context" | "agents" | "branch" | "tree" | "signIn" | "signOut" | "advisor" | "host" | "package" | "fast" | "voice" | "tools" | "rule" | "skill" | "mcp" | "pin";
12
+ export type SlashCommandIconName = "action" | "prompt" | "extension" | "settings" | "gear" | "shield" | "wave" | "compass" | "inbox" | "swap" | "expand" | "computer" | "eye" | "todo" | "stats" | "news" | "keyboard" | "export" | "clipboard" | "share" | "broadcast" | "globe" | "copy" | "plus" | "restart" | "eraser" | "trash" | "compress" | "vibrate" | "handoff" | "history" | "question" | "rocket" | "stethoscope" | "redo" | "bug" | "memory" | "pencil" | "folderMove" | "folderPlus" | "folderMinus" | "hammer" | "power" | "cart" | "model" | "plan" | "prewalk" | "goal" | "pause" | "loop" | "session" | "jobs" | "gauge" | "context" | "ghost" | "agents" | "branch" | "tree" | "signIn" | "signOut" | "advisor" | "host" | "package" | "fast" | "voice" | "tools" | "rule" | "skill" | "mcp" | "pin";
13
13
  export declare const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap>;
14
14
  export type SpinnerType = "status" | "activity";
15
15
  export declare const SPINNER_FRAMES: Record<SymbolPreset, Record<SpinnerType, string[]>>;
@@ -7,6 +7,17 @@ export { isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema.js";
7
7
  export { getAvailableSymbolPresets, isValidSymbolPreset, type SpinnerType, type SymbolKey, type SymbolPreset, } from "./symbols.js";
8
8
  export { Theme } from "./theme-class.js";
9
9
  export { createHighlightStream, getEditorTheme, getMarkdownTheme, getMarkdownThemeWithLinkTargets, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters.js";
10
+ /**
11
+ * Subscribe to native fallback appearance changes independently of theme changes.
12
+ * Consumers must re-check the effective background: COLORFGBG can override it.
13
+ */
14
+ export declare function onTerminalBackgroundFallbackChange(callback: () => void): () => void;
15
+ /**
16
+ * Detect appearance from trusted OSC 11 reports, then COLORFGBG, then native
17
+ * macOS appearance only on known-broken multiplexer background-query paths.
18
+ * Defaults to dark when none of those signals is available.
19
+ */
20
+ export declare function detectTerminalBackground(): "dark" | "light";
10
21
  /** Active theme; imports remain live across initialization, previews, and theme switches. */
11
22
  export declare var theme: Theme;
12
23
  type ThemeBinding = (value: Theme) => void;
@@ -1,7 +1,7 @@
1
1
  import type { Component } from "../tui.js";
2
2
  import type { Theme } from "../theme/theme.js";
3
3
  import type { OutputMeta } from "./output-meta.js";
4
- import type { NativeToolView, RenderResultOptions } from "./renderer.js";
4
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions } from "./renderer.js";
5
5
  /** Display metadata returned by ast-edit. */
6
6
  export interface AstEditToolDetails {
7
7
  totalReplacements: number;
@@ -47,7 +47,7 @@ export declare const astEditToolRenderer: {
47
47
  }>;
48
48
  details?: AstEditToolDetails;
49
49
  isError?: boolean;
50
- }, options: RenderResultOptions, uiTheme: Theme, args?: AstEditRenderArgs): Component;
50
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: AstEditRenderArgs): Component;
51
51
  describeCall(args: AstEditRenderArgs): NativeToolView;
52
52
  describeResult(result: {
53
53
  content: Array<{
@@ -1,7 +1,7 @@
1
1
  import type { Component } from "../tui.js";
2
2
  import type { Theme } from "../theme/theme.js";
3
3
  import type { OutputMeta } from "./output-meta.js";
4
- import type { NativeToolView, RenderResultOptions } from "./renderer.js";
4
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions } from "./renderer.js";
5
5
  /** Display metadata returned by ast-grep. */
6
6
  export interface AstGrepToolDetails {
7
7
  matchCount: number;
@@ -46,7 +46,7 @@ export declare const astGrepToolRenderer: {
46
46
  }>;
47
47
  details?: AstGrepToolDetails;
48
48
  isError?: boolean;
49
- }, options: RenderResultOptions, uiTheme: Theme, args?: AstGrepRenderArgs): Component;
49
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: AstGrepRenderArgs): Component;
50
50
  describeCall(args: AstGrepRenderArgs): NativeToolView;
51
51
  describeResult(result: {
52
52
  content: Array<{
@@ -133,6 +133,8 @@ export interface EditRenderContext {
133
133
  renderDiff?: (diffText: string, options?: {
134
134
  filePath?: string;
135
135
  }) => string;
136
+ /** Unsanitized result metadata, used only for filesystem link targets. */
137
+ rawDetails?: unknown;
136
138
  }
137
139
  /** Render replacement text until a computed diff preview is available. */
138
140
  export declare function renderStreamingFallback(editMode: EditMode, args: unknown, theme: Theme): string;
@@ -1,7 +1,7 @@
1
1
  import type { Theme } from "../theme/theme.js";
2
2
  import type { Component } from "../tui.js";
3
3
  import type { OutputMeta } from "./output-meta.js";
4
- import type { NativeToolView, RenderResultOptions } from "./renderer.js";
4
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions } from "./renderer.js";
5
5
  /** A verified line range with its yes-probability and a one-line preview. */
6
6
  export interface FindRange {
7
7
  start: number;
@@ -22,12 +22,21 @@ export interface FindHit {
22
22
  linesSeen: number;
23
23
  truncated: boolean;
24
24
  }
25
+ /**
26
+ * How much of a search the judge actually covered. `unavailable`: every judge
27
+ * request failed, so an empty result is not evidence of absence. `partial`:
28
+ * some requests failed. `complete`: none failed (or none were needed).
29
+ */
30
+ export type FindCoverage = "complete" | "partial" | "unavailable";
25
31
  /** Search accounting reported alongside the hits. */
26
32
  export interface FindStats {
27
33
  /** Eligible files under the root. */
28
34
  listed: number;
29
35
  requests: number;
30
- errors: number;
36
+ /** Judge requests that failed outright; decides {@link FindCoverage}. */
37
+ failed: number;
38
+ /** Questions a successful response left without a usable answer. */
39
+ unanswered: number;
31
40
  /** Entries judged by name. */
32
41
  judged: number;
33
42
  /** Files whose content was read and sent. */
@@ -51,6 +60,7 @@ export interface FindToolDetails {
51
60
  /** Verified-passage probability at or above which a file is a hit. */
52
61
  threshold: number;
53
62
  hits: FindHit[];
63
+ coverage: FindCoverage;
54
64
  stats: FindStats;
55
65
  elapsedMs: number;
56
66
  /** Session cwd; hit paths are relative to it. */
@@ -74,7 +84,7 @@ export declare const findToolRenderer: {
74
84
  }>;
75
85
  details?: FindToolDetails;
76
86
  isError?: boolean;
77
- }, options: RenderResultOptions, uiTheme: Theme, args?: FindRenderArgs): Component;
87
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: FindRenderArgs): Component;
78
88
  describeCall(args: FindRenderArgs): NativeToolView;
79
89
  /**
80
90
  * Inline (§7.3 find): `Find “query” 2 hits · 8 files`, then per hit its
@@ -88,7 +98,7 @@ export declare const findToolRenderer: {
88
98
  }>;
89
99
  details?: FindToolDetails;
90
100
  isError?: boolean;
91
- }, options: RenderResultOptions, args?: FindRenderArgs): NativeToolView;
101
+ }, options: RenderResultContextOptions, args?: FindRenderArgs): NativeToolView;
92
102
  mergeCallAndResult: true;
93
103
  animatedPendingPreview: true;
94
104
  animatedPartialResult: true;
@@ -1,5 +1,5 @@
1
1
  import type { Component } from "../tui.js";
2
- import type { NativeToolView, RenderResultOptions } from "./renderer.js";
2
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions } from "./renderer.js";
3
3
  import { type Theme } from "../theme/theme.js";
4
4
  import type { OutputMeta } from "./output-meta.js";
5
5
  import type { TruncationResult } from "./streaming-output.js";
@@ -42,7 +42,7 @@ export declare const globToolRenderer: {
42
42
  }>;
43
43
  details?: GlobToolDetails;
44
44
  isError?: boolean;
45
- }, options: RenderResultOptions, uiTheme: Theme, args?: GlobRenderArgs): Component;
45
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: GlobRenderArgs): Component;
46
46
  describeCall(args: GlobRenderArgs): NativeToolView;
47
47
  /**
48
48
  * Inline (§7.3 glob): `Glob “pattern” 5 files`, then the files as rows
@@ -55,7 +55,7 @@ export declare const globToolRenderer: {
55
55
  }>;
56
56
  details?: GlobToolDetails;
57
57
  isError?: boolean;
58
- }, _options: RenderResultOptions, args?: GlobRenderArgs): NativeToolView;
58
+ }, options: RenderResultContextOptions, args?: GlobRenderArgs): NativeToolView;
59
59
  mergeCallAndResult: true;
60
60
  };
61
61
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { Component } from "../tui.js";
2
- import type { NativeToolView, RenderResultOptions } from "./renderer.js";
2
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions } from "./renderer.js";
3
3
  import { type Theme } from "../theme/theme.js";
4
4
  import type { OutputMeta } from "./output-meta.js";
5
5
  import type { TruncationResult } from "./streaming-output.js";
@@ -58,7 +58,7 @@ export declare const grepToolRenderer: {
58
58
  }>;
59
59
  details?: GrepToolDetails;
60
60
  isError?: boolean;
61
- }, options: RenderResultOptions, uiTheme: Theme, args?: GrepRenderArgs): Component;
61
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: GrepRenderArgs): Component;
62
62
  describeCall(args: GrepRenderArgs): NativeToolView;
63
63
  /**
64
64
  * Inline (§7.3 grep): `Grep “pattern” 5 matches · 2 files in src`, then
@@ -1,6 +1,6 @@
1
1
  import type { SummaryResult } from "@oh-my-pi/pi-natives";
2
2
  import type { Component } from "../tui.js";
3
- import type { NativeToolView, RenderResultOptions, ToolActivityContext, ToolActivitySummary } from "./renderer.js";
3
+ import type { RenderResultContextOptions, NativeToolView, RenderResultOptions, ToolActivityContext, ToolActivitySummary } from "./renderer.js";
4
4
  import type { NativeNode } from "../native/node.js";
5
5
  import type { Theme } from "../theme/theme.js";
6
6
  import type { OutputMeta } from "./output-meta.js";
@@ -40,6 +40,11 @@ export interface ReadToolDetails {
40
40
  fileSize?: number;
41
41
  /** Full source line count when the read reached EOF and the count is exact. */
42
42
  totalLines?: number;
43
+ /**
44
+ * Whether the text's rows carry line-number prefixes (`N:` hashline, `N|` line numbers). Set where a
45
+ * file read is formatted; a raw read sets `false`. Snapcompact reads it to tell a `12:` prefix from content.
46
+ */
47
+ rowPrefixes?: boolean;
43
48
  /** Raw text + start line for user-visible TUI rendering, set when content is text-like.
44
49
  * Mirrors the same lines the model receives but without hashline/line-number prefixes,
45
50
  * so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
@@ -55,7 +60,10 @@ export interface ReadToolDetails {
55
60
  };
56
61
  /** Number of unresolved git conflicts surfaced by this read (TUI uses for inline `⚠ N` badge). */
57
62
  conflictCount?: number;
58
- /** Paths recovered from a delimited read argument; used only by the TUI to render one call as multiple read rows. */
63
+ /**
64
+ * Paths recovered from a delimited read argument. The TUI renders one call as multiple read rows; snapcompact
65
+ * keeps such a composite result as text.
66
+ */
59
67
  displayReadTargets?: string[];
60
68
  /**
61
69
  * Resolved filesystem link target for each {@link displayReadTargets} entry, aligned by index; `null` when a
@@ -113,7 +121,7 @@ export declare function pendingFileLinkPath(inputPath: string): string;
113
121
  * target when the structured `resolvedPath` isn't set (the common plain-file and
114
122
  * image reads only record the path in `meta.source`). URL/internal sources are
115
123
  * not fs paths, so only `type: "path"` qualifies. */
116
- export declare function readSourceFsPath(details: ReadToolDetails | undefined): string | undefined;
124
+ export declare function readSourceFsPath(details: unknown): string | undefined;
117
125
  /**
118
126
  * Whether a read result renders as a Markdown preview rather than numbered source:
119
127
  * the read tool tagged it `text/markdown` (`read.renderMarkdown`, or a protocol
@@ -140,7 +148,7 @@ export declare const readToolRenderer: {
140
148
  }>;
141
149
  details?: ReadToolDetails;
142
150
  isError?: boolean;
143
- }, options: RenderResultOptions, uiTheme: Theme, args?: ReadRenderArgs): Component;
151
+ }, options: RenderResultContextOptions, uiTheme: Theme, args?: ReadRenderArgs): Component;
144
152
  describeCall(args: ReadRenderArgs): NativeToolView;
145
153
  /**
146
154
  * Inline (§7.3 read): the head names the file and range; the content
@@ -154,7 +162,7 @@ export declare const readToolRenderer: {
154
162
  }>;
155
163
  details?: ReadToolDetails;
156
164
  isError?: boolean;
157
- }, options: RenderResultOptions, args?: ReadRenderArgs): NativeToolView;
165
+ }, options: RenderResultContextOptions, args?: ReadRenderArgs): NativeToolView;
158
166
  mergeCallAndResult: true;
159
167
  };
160
168
  /** Inclusive line range describing one elided span in a structural summary. */