@oh-my-pi/pi-tui 18.6.3 → 18.8.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 (116) hide show
  1. package/CHANGELOG.md +46 -109
  2. package/dist/types/app-keybindings.d.ts +1 -1
  3. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  4. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  5. package/dist/types/autocomplete.d.ts +5 -2
  6. package/dist/types/charts/chart-plan.d.ts +92 -0
  7. package/dist/types/charts/chart-svg.d.ts +30 -0
  8. package/dist/types/charts/table-data.d.ts +107 -0
  9. package/dist/types/chat/assistant-message.d.ts +4 -0
  10. package/dist/types/chat/chat-transcript-builder.d.ts +2 -0
  11. package/dist/types/chat/fence-figure.d.ts +42 -0
  12. package/dist/types/chat/figure-markdown.d.ts +42 -0
  13. package/dist/types/chat/svg-figure.d.ts +33 -0
  14. package/dist/types/chat/svg-source.d.ts +49 -0
  15. package/dist/types/chat/table-chart.d.ts +88 -0
  16. package/dist/types/chat/tool-execution.d.ts +2 -0
  17. package/dist/types/components/editor.d.ts +9 -4
  18. package/dist/types/components/image.d.ts +9 -0
  19. package/dist/types/components/markdown.d.ts +16 -2
  20. package/dist/types/fuzzy.d.ts +29 -1
  21. package/dist/types/keybindings.d.ts +1 -1
  22. package/dist/types/native/backend.d.ts +2 -1
  23. package/dist/types/native/blobs.d.ts +4 -2
  24. package/dist/types/native/encode.d.ts +3 -4
  25. package/dist/types/overlays/agent-transcript-viewer.d.ts +8 -1
  26. package/dist/types/overlays/agents-hub.d.ts +9 -0
  27. package/dist/types/overlays/annotation-overlay.d.ts +5 -2
  28. package/dist/types/overlays/history-search.d.ts +10 -0
  29. package/dist/types/overlays/model-browser.d.ts +31 -3
  30. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  31. package/dist/types/prompt/custom-editor.d.ts +7 -4
  32. package/dist/types/prompt/interactive-context-helpers.d.ts +4 -1
  33. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  34. package/dist/types/render/composite.d.ts +2 -0
  35. package/dist/types/tools/glob.d.ts +4 -0
  36. package/dist/types/tools/renderer.d.ts +22 -0
  37. package/dist/types/tools/streaming-output.d.ts +11 -1
  38. package/dist/types/tools/write.d.ts +8 -1
  39. package/dist/types/utils.d.ts +6 -0
  40. package/package.json +9 -9
  41. package/src/apps/debug/log-viewer.ts +188 -72
  42. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  43. package/src/apps/debug/raw-sse.ts +39 -5
  44. package/src/apps/git/diff-pane.ts +26 -5
  45. package/src/autocomplete.ts +67 -43
  46. package/src/charts/chart-plan.ts +367 -0
  47. package/src/charts/chart-svg.ts +693 -0
  48. package/src/charts/table-data.ts +436 -0
  49. package/src/chat/assistant-message.ts +169 -39
  50. package/src/chat/chat-transcript-builder.ts +4 -0
  51. package/src/chat/fence-figure.ts +80 -0
  52. package/src/chat/figure-markdown.ts +174 -0
  53. package/src/chat/svg-figure.ts +362 -0
  54. package/src/chat/svg-source.ts +188 -0
  55. package/src/chat/table-chart.ts +301 -0
  56. package/src/chat/tool-execution.ts +53 -4
  57. package/src/chrome/transcript-container.ts +7 -1
  58. package/src/components/editor.ts +68 -33
  59. package/src/components/image.ts +27 -1
  60. package/src/components/markdown.ts +80 -304
  61. package/src/fuzzy.ts +115 -30
  62. package/src/keybindings.ts +1 -1
  63. package/src/native/backend.ts +170 -15
  64. package/src/native/blobs.ts +4 -2
  65. package/src/native/encode.ts +58 -31
  66. package/src/overlays/agent-hub-projection.ts +70 -2
  67. package/src/overlays/agent-hub.ts +3 -2
  68. package/src/overlays/agent-transcript-viewer.ts +66 -13
  69. package/src/overlays/agents-hub.ts +28 -8
  70. package/src/overlays/annotation-overlay.ts +179 -80
  71. package/src/overlays/ask-dialog.ts +1 -1
  72. package/src/overlays/copy-selector.ts +30 -8
  73. package/src/overlays/extensions/state-manager.ts +52 -12
  74. package/src/overlays/history-search.ts +100 -7
  75. package/src/overlays/jobs-panel.ts +30 -2
  76. package/src/overlays/model-browser.ts +269 -90
  77. package/src/overlays/model-hub.ts +34 -7
  78. package/src/overlays/oauth-selector.ts +41 -26
  79. package/src/overlays/plan-review-overlay.ts +183 -68
  80. package/src/overlays/rewind-selector.ts +86 -17
  81. package/src/overlays/session-selector.ts +24 -4
  82. package/src/overlays/settings-selector.ts +21 -6
  83. package/src/overlays/tree-selector.ts +20 -4
  84. package/src/overlays/usage-dashboard.ts +12 -15
  85. package/src/prompt/attachment-chips.ts +2 -1
  86. package/src/prompt/composer-attachments.ts +6 -4
  87. package/src/prompt/composer.ts +2 -14
  88. package/src/prompt/custom-editor.ts +48 -19
  89. package/src/prompt/interactive-context-helpers.ts +30 -18
  90. package/src/prompt/macos-spelling.ts +7 -0
  91. package/src/prompt/magic-keywords.ts +11 -2
  92. package/src/prompt/model-mention-autocomplete.ts +14 -14
  93. package/src/prompt/word-completion.ts +38 -5
  94. package/src/render/composite.ts +62 -0
  95. package/src/setup/scenes/splash.ts +109 -45
  96. package/src/status-line/component.ts +220 -64
  97. package/src/status-line/segments.ts +8 -3
  98. package/src/terminal.ts +3 -2
  99. package/src/tools/ast-edit.ts +59 -35
  100. package/src/tools/ast-grep.ts +35 -16
  101. package/src/tools/default-renderer.ts +35 -19
  102. package/src/tools/eval-format/index.ts +28 -2
  103. package/src/tools/eval-format/javascript.ts +7 -5
  104. package/src/tools/eval.ts +164 -78
  105. package/src/tools/glob.ts +23 -6
  106. package/src/tools/grep.ts +81 -48
  107. package/src/tools/lsp.ts +81 -59
  108. package/src/tools/mcp.ts +98 -68
  109. package/src/tools/renderer.ts +27 -0
  110. package/src/tools/streaming-output.ts +120 -48
  111. package/src/tools/task.ts +111 -54
  112. package/src/tools/todo.ts +115 -29
  113. package/src/tools/wait.ts +34 -24
  114. package/src/tools/write.ts +77 -39
  115. package/src/tui.ts +118 -105
  116. package/src/utils.ts +19 -0
@@ -0,0 +1,88 @@
1
+ import type { Tokens } from "@oh-my-pi/pi-utils/marked";
2
+ import { type ChartPlan } from "../charts/chart-plan.js";
3
+ import { type TableAnalysis } from "../charts/table-data.js";
4
+ import type { ImageBudget } from "../components/image.js";
5
+ import type { NativeNode } from "../native/node.js";
6
+ import type { Component } from "../tui.js";
7
+ /**
8
+ * `smart`: a model picks the kind and columns of multi-series tables;
9
+ * `always`: the local best guess charts every table it reads as numeric;
10
+ * `off`: tables stay tables.
11
+ */
12
+ export type TableChartMode = "smart" | "always" | "off";
13
+ /** A multi-series table the host's planner picks a chart for. */
14
+ export interface TableChartRequest {
15
+ /** The table's Markdown source. */
16
+ readonly markdown: string;
17
+ readonly table: TableAnalysis;
18
+ /** The local best guess; the planner may keep it. */
19
+ readonly guess: ChartPlan;
20
+ }
21
+ /**
22
+ * Picks the chart for a multi-series table under `smart`: a plan, or `null`
23
+ * for no chart. A rejection falls back to the local guess.
24
+ */
25
+ export type TableChartPlanner = (request: TableChartRequest) => Promise<ChartPlan | null>;
26
+ /** A drawn chart, ready to resolve against the theme. */
27
+ export interface TableChart {
28
+ /** SVG colored with figure tokens (`var(--accent)`, …). */
29
+ readonly svg: string;
30
+ readonly alt: string;
31
+ /** Size in SVG user units. */
32
+ readonly width: number;
33
+ readonly height: number;
34
+ }
35
+ /** The chart under a table: drawn, none, or a smart pick on its way (the promise settles once the cache holds it). */
36
+ export type ChartLookup = TableChart | null | Promise<void>;
37
+ /** One run of a Markdown block: prose (ending at a charted table), or the chart drawn after that table. */
38
+ export type ChartSegment = {
39
+ readonly kind: "markdown";
40
+ readonly text: string;
41
+ } | {
42
+ readonly kind: "chart";
43
+ readonly table: Tokens.Table;
44
+ };
45
+ /** Set how tables become charts (the host's `tui.autoGraph`) and its `smart` planner; the host rebuilds the transcript after. */
46
+ export declare function setTableCharts(mode: TableChartMode, planner?: TableChartPlanner): void;
47
+ /** How assistant tables currently become charts. */
48
+ export declare function tableChartMode(): TableChartMode;
49
+ /** Whether `markdown` may hold a table {@link splitTableCharts} would chart. */
50
+ export declare function hasChartTable(markdown: string): boolean;
51
+ /**
52
+ * Split `markdown` after each top-level table that has (or may get) a chart.
53
+ * A table still streaming at the block's tail is left whole: rows may follow.
54
+ */
55
+ export declare function splitTableCharts(markdown: string, streaming: boolean): ChartSegment[];
56
+ /**
57
+ * The chart for `table` under the current mode, read once per table source.
58
+ * Under `smart` the first lookup of a multi-series table asks the planner.
59
+ */
60
+ export declare function lookupTableChart(table: Tokens.Table): ChartLookup;
61
+ /**
62
+ * A native `image` node drawing `chart` from its SVG — no raster — resolved
63
+ * against the active theme; rebuilt when the theme changes.
64
+ */
65
+ export declare function describeTableChart(chart: TableChart, key: string): NativeNode;
66
+ export interface TableChartFigureOptions {
67
+ /** Shared inline-image budget. */
68
+ budget?: ImageBudget;
69
+ /** The chart's rows changed outside a text update: its pick or raster landed. */
70
+ onChange: () => void;
71
+ }
72
+ /**
73
+ * The chart under one table, as a terminal image a blank row below it. Looks
74
+ * the chart up on first render — a block rebuilt mid-stream never asks for a
75
+ * pick it drops — and renders nothing until it is drawn, or when the table
76
+ * gets no chart.
77
+ */
78
+ export declare class TableChartFigure implements Component {
79
+ #private;
80
+ constructor(options: TableChartFigureOptions);
81
+ /** Point the figure at `table`. */
82
+ update(table: Tokens.Table): void;
83
+ /** Whether the chart is still being looked up, picked or drawn. */
84
+ get pending(): boolean;
85
+ render(width: number): readonly string[];
86
+ invalidate(): void;
87
+ dispose(): void;
88
+ }
@@ -90,6 +90,8 @@ export declare class ToolExecutionComponent extends Container {
90
90
  * component-scoped so the TUI reuses every other root subtree (issue #4377).
91
91
  */
92
92
  tickSpinner(frame: number): void;
93
+ /** Whether the result's figure still waits for its raster; the transcript holds retirement meanwhile. */
94
+ isTranscriptBlockPending(): boolean;
93
95
  /**
94
96
  * Whether this block is ready to retire as immutable history. Partial
95
97
  * results, including detached background tasks, remain active and mutable
@@ -1,5 +1,5 @@
1
1
  import { type AutocompleteProvider } from "../autocomplete.js";
2
- import type { TspEditorDecoration, TspTone } from "@oh-my-pi/pi-wire";
2
+ import type { TspEditorDecoration, TspText, TspTone } from "@oh-my-pi/pi-wire";
3
3
  import { type DescribeContext, type NativeChild, type NativeNode, type NativeTextEdit, type NativeUiEvent } from "../native/node.js";
4
4
  import type { SymbolTheme } from "../symbols.js";
5
5
  import { type Component, type Focusable } from "../tui.js";
@@ -106,8 +106,11 @@ export declare class Editor implements Component, Focusable {
106
106
  * dialog's text field; only the prompt composer claims `omp.editor`).
107
107
  */
108
108
  describeLayout: ((input: NativeNode, cx: DescribeContext) => NativeEditorLayout) | undefined;
109
- /** TSP placeholder for the empty buffer; natively it replaces the rotating ANSI {@link placeholder} hints. */
110
- describePlaceholder: (() => string) | undefined;
109
+ /**
110
+ * TSP placeholder for the empty buffer; natively it replaces the rotating ANSI {@link placeholder} hints.
111
+ * Return a stable value (the same spans array) while it is unchanged: the editor node is reused by identity.
112
+ */
113
+ describePlaceholder: (() => TspText) | undefined;
111
114
  /** Called with the selected text when Visual mode yanks, so hosts can reach the system
112
115
  * clipboard — `packages/tui` deliberately has no clipboard dependency of its own. */
113
116
  onYank?: (text: string) => void;
@@ -250,6 +253,8 @@ export declare class Editor implements Component, Focusable {
250
253
  get atoms(): ReadonlyMap<string, string>;
251
254
  /** Monotonic buffer-content revision for render caches. Cursor-only movement does not advance it. */
252
255
  get textRevision(): number;
256
+ /** Monotonic atom-table revision for render caches: advances whenever {@link atoms} is mutated or replaced. */
257
+ get atomsRevision(): number;
253
258
  /** Whether the buffer text equals `value`, without `getText()`'s full join —
254
259
  * O(1) for the hot per-keystroke probes against short single-line values. */
255
260
  textEquals(value: string): boolean;
@@ -285,7 +290,7 @@ export declare class Editor implements Component, Focusable {
285
290
  * Mirrors the transient state the key dispatch tears down before this action so the two
286
291
  * cannot diverge: a pending character jump is cancelled by any other key, and an open
287
292
  * spelling-assist popup is dismissed by anything that is not one of its accept keys (its
288
- * debounced refresh skips assist mode, so a surviving list would hang around forever).
293
+ * refresh skips assist mode, so a surviving list would hang around forever).
289
294
  * While Vim owns the buffer (Normal or Visual) the operation is Vim's `x` — deleting the
290
295
  * selection and returning to Normal in Visual mode, the grapheme under the cursor
291
296
  * otherwise. Only Insert mode and Vim-off editors delete straight through. */
@@ -29,6 +29,8 @@ export interface ImageOptions {
29
29
  * synchronously instead of committing its reserved blank rows.
30
30
  */
31
31
  export declare function renderForScrollback<T>(render: () => T): T;
32
+ /** Widest an {@link Image} rendered at `width` columns draws; callers sizing a raster for it (`SvgFigure`) match it. */
33
+ export declare function imageMaxColumns(width: number): number;
32
34
  /** Default count of inline images kept as live graphics before older ones fall back to text. */
33
35
  export declare const DEFAULT_MAX_INLINE_IMAGES = 8;
34
36
  /**
@@ -73,6 +75,13 @@ export declare class ImageBudget {
73
75
  * gets a fresh id every call.
74
76
  */
75
77
  acquireId(key?: string): number;
78
+ /**
79
+ * Retire the graphic held under `key` as soon as no frame shows it, instead
80
+ * of when the residency sweep reaches it. For an owner that replaced the
81
+ * image for good — a streaming SVG figure's earlier raster — so superseded
82
+ * revisions never push older images (scrollback included) out of the store.
83
+ */
84
+ release(key: string): void;
76
85
  /**
77
86
  * Start an alternate-buffer lifecycle. Call once per `?1049h`, before the
78
87
  * first pass of the fullscreen overlay or resize borrow that owns the buffer.
@@ -1,10 +1,24 @@
1
+ import { type TokensList } from "@oh-my-pi/pi-utils/marked";
1
2
  import type { DescribeContext, NativeNode } from "../native/node.js";
2
3
  import type { SymbolTheme } from "../symbols.js";
3
4
  import type { Component } from "../tui.js";
4
5
  /** @internal exported for tests — the grown-line block-kind gate. */
5
6
  export declare function fastLineStartHazard(grownLine: string): boolean;
6
- /** @internal exported for tests — the windowed lexer's first-probe window size. */
7
- export declare const LEX_WINDOW_BYTES: number;
7
+ /**
8
+ * Lex a whole document with the renderer's rules (math, custom rules,
9
+ * strikethrough). Callers that split a document along its blocks (table
10
+ * charts) lex through this so they see the blocks Markdown renders.
11
+ */
12
+ export declare function lexDocument(text: string): TokensList;
13
+ /**
14
+ * `source` as a fenced code block in `lang`: the fence is one backtick longer
15
+ * than any backtick run in the source (at least three), so the block always
16
+ * closes where intended. `open` leaves it unclosed, the source verbatim, for
17
+ * a body that is still arriving.
18
+ */
19
+ export declare function fencedCode(lang: string, source: string, options?: {
20
+ open?: boolean;
21
+ }): string;
8
22
  /** A hyperlink as the renderer sees it: inline `[text](href)`, `<autolink>`, bare GFM URL, or reference link. */
9
23
  export interface MarkdownLink {
10
24
  /** Flattened visible label with whitespace collapsed to one row; falls back to `href` when empty. */
@@ -16,6 +16,20 @@ export interface FuzzyFilterResult<T> {
16
16
  score: number;
17
17
  }
18
18
  export declare function fuzzyMatch(query: string, text: string): FuzzyMatch;
19
+ /**
20
+ * A query normalized and tokenized once for matching against many texts.
21
+ *
22
+ * `fuzzyMatch(query, text)` re-normalizes `query` (and rebuilds its
23
+ * alphanumeric-swap variants) on every call; filters that test one query
24
+ * against every row should build one `FuzzyQuery` per keystroke instead.
25
+ * `query.match(text)` returns exactly what `fuzzyMatch(query, text)` returns.
26
+ */
27
+ export declare class FuzzyQuery {
28
+ #private;
29
+ constructor(query: string);
30
+ /** Same result as `fuzzyMatch(query, text)`; `text` may be a prepared {@link FuzzyText}. */
31
+ match(text: string | FuzzyText): FuzzyMatch;
32
+ }
19
33
  /**
20
34
  * A text prepared once for repeated fuzzy matching.
21
35
  *
@@ -30,7 +44,21 @@ export declare class FuzzyText {
30
44
  #private;
31
45
  constructor(text: string);
32
46
  /** Match `query` (space-separated tokens; all must match) against the prepared text. */
33
- match(query: string): FuzzyMatch;
47
+ match(query: string | FuzzyQuery): FuzzyMatch;
48
+ }
49
+ /**
50
+ * Items prepared once for repeated {@link fuzzyRank}-equivalent ranking.
51
+ *
52
+ * `fuzzyRank` resolves each candidate's search index through the bounded module
53
+ * cache, which holds at most {@link INDEX_CACHE_MAX} entries; larger candidate
54
+ * lists (the full model catalog) rebuild the overflow on every query. Hold one
55
+ * corpus per stable candidate list and call {@link rank} per keystroke instead.
56
+ */
57
+ export declare class FuzzyCorpus<T> {
58
+ #private;
59
+ constructor(items: readonly T[], getText: (item: T) => string);
60
+ /** Same results as `fuzzyRank(items, query, getText)` for the prepared items. */
61
+ rank(query: string): FuzzyFilterResult<T>[];
34
62
  }
35
63
  /**
36
64
  * Filter and sort items by fuzzy match quality (best matches first).
@@ -107,7 +107,7 @@ export declare const TUI_KEYBINDINGS: {
107
107
  readonly description: "Delete word backward";
108
108
  };
109
109
  readonly "tui.editor.deleteWordForward": {
110
- readonly defaultKeys: ["alt+delete", "alt+d", "super+alt+delete", "super+alt+d"];
110
+ readonly defaultKeys: ["alt+delete", "alt+d", "ctrl+delete", "super+alt+delete", "super+alt+d"];
111
111
  readonly description: "Delete word forward";
112
112
  };
113
113
  readonly "tui.editor.deleteToLineStart": {
@@ -49,7 +49,8 @@ export interface NativeBackendOptions {
49
49
  * The reply a v1 terminal is assumed to give before its real `hello` arrives
50
50
  * (the `TERM_PROGRAM=tern` optimistic start): the whole vocabulary, the
51
51
  * default APC limit and credits, the terminal's width, the appearance omp
52
- * already detected, and full motion.
52
+ * already detected, full motion, and `blobs`, so a resumed session's images
53
+ * are asked about rather than resent before the reply comes.
53
54
  */
54
55
  export declare function assumedTspHello(terminal: Terminal): TspHello;
55
56
  export declare class NativeBackend {
@@ -2,8 +2,10 @@
2
2
  * Content-addressed binary blobs (images) for `image` nodes.
3
3
  *
4
4
  * `describe()` registers the bytes and puts the returned id in `image.p.blob`;
5
- * the native backend uploads each referenced blob once per surface with verb
6
- * `b` before the frame that first references it.
5
+ * the native backend delivers each referenced blob once per terminal
6
+ * connection (asking the terminal first which it holds, or through its blob
7
+ * cache), else uploads it with verb `b` before the frame that first
8
+ * references it.
7
9
  */
8
10
  import type { TspProps } from "@oh-my-pi/pi-wire";
9
11
  import type { NativeNode } from "./node.js";
@@ -15,11 +15,10 @@ export type TspIncoming = {
15
15
  };
16
16
  /** Message parameters (`k=v`, ASCII, never containing `;`). */
17
17
  export type TspParams = Readonly<Record<string, string | number>>;
18
- /** Split `body` into pieces of at most `limit` UTF-8 bytes, never inside a code point. */
19
- export declare function splitUtf8(body: string, limit: number): string[];
20
18
  /**
21
- * Encode one logical message, chunked when `body` exceeds `limit` UTF-8 bytes.
22
- * Returns the complete byte string to write (all chunks, in order).
19
+ * Encode one logical message, chunked when `body` exceeds `limit` UTF-8 bytes:
20
+ * the first chunk carries `params`, every chunk `c=<id>` and all but the last
21
+ * `m=1`. Returns the complete byte string to write (all chunks, in order).
23
22
  */
24
23
  export declare function encodeTspMessage(verb: TspVerb, body: string, params?: TspParams, limit?: number): string;
25
24
  /** Encode a JSON message body. */
@@ -31,7 +31,14 @@ export type AgentTranscriptEntry = SessionMessageEntryLike | {
31
31
  };
32
32
  /** Local filesystem and session parsing capabilities supplied by the host. */
33
33
  export interface AgentTranscriptSource {
34
- fs: Pick<typeof fs, "openSync" | "closeSync" | "readSync" | "readFileSync" | "statSync">;
34
+ /**
35
+ * Sync primitives drive the first paint and the incremental tail. When the
36
+ * host also supplies `promises.readFile` (e.g. passes `node:fs` itself), full
37
+ * reloads after rotation/rewrite read the file off the UI thread.
38
+ */
39
+ fs: Pick<typeof fs, "openSync" | "closeSync" | "readSync" | "readFileSync" | "statSync"> & {
40
+ promises?: Pick<typeof fs.promises, "readFile">;
41
+ };
35
42
  parseEntries(text: string): AgentTranscriptEntry[];
36
43
  }
37
44
  export interface AgentTranscriptViewerDeps {
@@ -60,6 +60,15 @@ export interface AgentsHubDeps {
60
60
  export interface AgentsHubCallbacks {
61
61
  onCancel: () => void;
62
62
  }
63
+ /**
64
+ * Parse model output into a {@link GeneratedAgentSpec}. Accepts a bare JSON object
65
+ * (tried first) or one wrapped in a code fence. Returns the spec with trimmed fields.
66
+ * Throws if the output is not valid JSON or not an object, if `identifier`, `whenToUse`
67
+ * or `systemPrompt` is missing or not a string, if the identifier is not lowercase
68
+ * kebab-case with 2+ words, if `whenToUse` does not start with "Use this agent when",
69
+ * or if `systemPrompt` is empty.
70
+ */
71
+ export declare function parseGeneratedAgentSpec(raw: string): GeneratedAgentSpec;
63
72
  /**
64
73
  * The fullscreen agents hub component. Hosted via
65
74
  * `ui.showOverlay(..., { fullscreen: true })`; the host must call
@@ -1,4 +1,4 @@
1
- import { type Component, type TUI } from "../index.js";
1
+ import { type Focusable, type TUI } from "../index.js";
2
2
  import { type KeybindingsManager } from "../app-keybindings.js";
3
3
  import { type Theme } from "../theme/theme.js";
4
4
  import type { CodeReviewAnnotation, CodeReviewOverlayResult, ReviewDiffFile, TextReviewAnnotation, TextReviewOverlayResult, TextReviewSource } from "./annotation-types.js";
@@ -18,10 +18,13 @@ export interface TextReviewOverlayCallbacks extends AnnotationOverlayCallbacks {
18
18
  onComplete(result: TextReviewOverlayResult | undefined): void;
19
19
  }
20
20
  /** A fullscreen, annotated diff picker that only relies on public OMP APIs. */
21
- export declare class AnnotationOverlay implements Component {
21
+ export declare class AnnotationOverlay implements Focusable {
22
22
  #private;
23
+ focused: boolean;
23
24
  constructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, files: readonly ReviewDiffFile[], mode: string, callbacks: CodeReviewOverlayCallbacks);
24
25
  constructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, source: TextReviewSource, callbacks: TextReviewOverlayCallbacks);
26
+ /** Keep the nested editor's cursor mode aligned with the overlay focus target. */
27
+ setUseTerminalCursor(useTerminalCursor: boolean): void;
25
28
  invalidate(): void;
26
29
  dispose(): void;
27
30
  getAnnotations(): CodeReviewAnnotation[];
@@ -15,6 +15,13 @@ import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.
15
15
  export declare class HistorySearchComponent extends OverlayPanel {
16
16
  #private;
17
17
  constructor(historyStorage: HistorySource, onSelect: (prompt: string) => void, onCancel: () => void);
18
+ /**
19
+ * Repaint hook for debounced result updates. Once set, query edits search
20
+ * storage after a {@link SEARCH_DEBOUNCE_MS} quiet period instead of on
21
+ * every key; without it every edit searches synchronously.
22
+ */
23
+ setOnRequestRender(callback: () => void): void;
24
+ dispose(): void;
18
25
  handleInput(keyData: string): void;
19
26
  /**
20
27
  * With the `picker` kind: a `size:"md"` sheet of past prompts (first line
@@ -29,6 +36,9 @@ export declare class HistorySearchComponent extends OverlayPanel {
29
36
  * Picker: a row click highlights, a second click or `Insert` inserts it
30
37
  * (Enter), `Close` cancels (Esc), `Clear search` empties the query.
31
38
  * List: picking a result does what highlighting it and pressing Enter does.
39
+ * A row picked while a debounced search is pending belongs to the previous
40
+ * query: results are refreshed first and the pick only lands if that entry
41
+ * is still among them.
32
42
  */
33
43
  handleNativeEvent(event: NativeUiEvent): void;
34
44
  }
@@ -49,6 +49,12 @@ export interface ModelBrowserSource extends ModelRoleLookup {
49
49
  readonly knownRoleIds: readonly string[];
50
50
  readonly mruOrder: readonly string[];
51
51
  readonly modelPerf: ReadonlyMap<string, ModelBrowserPerf>;
52
+ /**
53
+ * Service tier the host would send for `model`, when it runs one. Rows with a
54
+ * measured tier aggregate (`selector@tier`) then display that tier's numbers
55
+ * instead of the standard aggregate.
56
+ */
57
+ serviceTierFor?(model: Model): string | undefined;
52
58
  getRoleInfo(role: string): ModelBrowserRoleInfo;
53
59
  defaultRoleChain(role: string): string[];
54
60
  resolveRoleValue(value: string | undefined, models: Model[], roleLookup?: ModelRoleLookup): ResolvedModelRoleValue;
@@ -134,12 +140,28 @@ export interface SearchAffinity {
134
140
  * policy, not evidence of user preference.
135
141
  */
136
142
  export declare function buildSearchAffinity(providerOrder: ReadonlyArray<string>, roles: RoleAssignments, mruOrder: ReadonlyArray<string>): SearchAffinity;
137
- /** Rank picker and mention candidates by text relevance, user affinity, and MRU/version order. */
138
- export declare function rankModelItems(query: string, items: ReadonlyArray<ModelBrowserItem>, options: {
143
+ /** Inputs to {@link rankModelItems} / {@link ModelItemRanker}. */
144
+ export interface ModelRankOptions {
139
145
  roles: RoleAssignments;
140
146
  mruOrder: ReadonlyArray<string>;
141
147
  affinity: SearchAffinity;
142
- }): ModelBrowserItem[];
148
+ }
149
+ /**
150
+ * {@link rankModelItems} prepared for one candidate list: the fuzzy index, the
151
+ * MRU/version order, and the affinity ranks depend only on the items and options,
152
+ * so per-keystroke callers (composer mentions, `/switch` arguments, the picker)
153
+ * hold one ranker and pay only the per-query fuzzy scan.
154
+ */
155
+ export declare class ModelItemRanker {
156
+ #private;
157
+ readonly items: ReadonlyArray<ModelBrowserItem>;
158
+ readonly options: ModelRankOptions;
159
+ constructor(items: ReadonlyArray<ModelBrowserItem>, options: ModelRankOptions);
160
+ /** Rank by text relevance, user affinity, and MRU/version order. */
161
+ rank(query: string): ModelBrowserItem[];
162
+ }
163
+ /** Rank picker and mention candidates by text relevance, user affinity, and MRU/version order. */
164
+ export declare function rankModelItems(query: string, items: ReadonlyArray<ModelBrowserItem>, options: ModelRankOptions): ModelBrowserItem[];
143
165
  /**
144
166
  * A slim role chip: `● default ◉` — solid dot for configured assignments,
145
167
  * hollow for auto-selected fallbacks, thinking glyph attached when set.
@@ -240,6 +262,12 @@ export declare class ModelBrowser implements Component {
240
262
  setQuery(query: string): void;
241
263
  getSelected(): ModelBrowserItem | undefined;
242
264
  get visibleCount(): number;
265
+ /**
266
+ * Base items matching the live query, best first, without the separator
267
+ * row; undefined while the query is blank. Reuses the ranking the list
268
+ * already ran, so hosts can derive match counts without ranking again.
269
+ */
270
+ get queryMatches(): readonly ModelBrowserItem[] | undefined;
243
271
  /** Move selection to `selector`; false when it is not in the current view. */
244
272
  selectSelector(selector: string): boolean;
245
273
  /** True when `item`'s context window is smaller than the live session token count (grayed row; hosts compact before switching). */
@@ -13,8 +13,9 @@
13
13
  * Focus regions (`toc`/`body`/`actions`) cycle with Tab/Shift+Tab; arrows move
14
14
  * within the focused region and step left into the sidebar. The default focus is
15
15
  * `actions`, so the muscle memory of the old single-target overlay carries over:
16
- * ↑/↓ select options, Enter confirms, ←/→ drives the slider when there is no
17
- * sidebar, g/G + PgUp/PgDn scroll, and the external-editor key opens the plan.
16
+ * ↑/↓ select options, Enter confirms, ←/→ drives the slider, g/G + PgUp/PgDn
17
+ * scroll, and the external-editor key opens the plan. Under Tern the options
18
+ * are a horizontal bar, so ←/→ select options and Shift+←/→ drive the slider.
18
19
  */
19
20
  import { type Component } from "../index.js";
20
21
  import type { HookSelectorSlider } from "./hook-selector.js";
@@ -7,7 +7,7 @@ import type { AppKeybinding } from "../app-keybindings.js";
7
7
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
8
8
  import type { ComposerFactsSource } from "../status-line/types.js";
9
9
  import { type MacOSSpellingFeatures } from "./macos-spelling.js";
10
- import type { TspEditorDecoration } from "@oh-my-pi/pi-wire";
10
+ import type { TspEditorDecoration, TspText } from "@oh-my-pi/pi-wire";
11
11
  import { type WordCompletionMethod } from "./word-completion.js";
12
12
  /** Live composer state the TSP layout shows; the interactive host wires {@link CustomEditor.composerState}. */
13
13
  export interface ComposerNativeState {
@@ -26,6 +26,8 @@ export interface ComposerNativeState {
26
26
  readonly running: boolean;
27
27
  /** The viewed subagent's lineage, outermost first and the viewed agent last; undefined on the main session. */
28
28
  readonly viewing?: readonly string[];
29
+ /** The session's title, quoted in italics as the TSP placeholder; undefined until it has one. */
30
+ readonly title?: string;
29
31
  }
30
32
  /** Independently switchable prose-assistance features of the composer. */
31
33
  export interface SpellingFeatures extends MacOSSpellingFeatures {
@@ -191,7 +193,8 @@ export declare class CustomEditor extends Editor {
191
193
  static readonly SHIMMER_PERIOD_MS = 1800;
192
194
  /** Decorate magic keywords, attachments, and the queue-composer header/list markers.
193
195
  * Queue shorthand reserves its first logical line as a dim `Queueing` label; sequential
194
- * item markers use the accent color so separate follow-ups remain visible while composing. */
196
+ * item markers use the accent color so separate follow-ups remain visible while composing.
197
+ * Called once per layout segment, so whole-buffer inputs are computed once per text revision. */
195
198
  decorateText: (text: string, context: EditorTextDecorationContext) => string;
196
199
  /**
197
200
  * TSP decorations: magic keywords shimmer (terminal-clocked) in the accent
@@ -219,8 +222,8 @@ export declare class CustomEditor extends Editor {
219
222
  setShimmerRepaintHandler(handler: (() => void) | undefined): void;
220
223
  /** Editing is available during bootstrap; atomic sends wait until submission is wired and enabled. */
221
224
  protected get nativeSendable(): boolean;
222
- /** Viewing a subagent, the draft goes to it: the placeholder names it. */
223
- describePlaceholder: () => string;
225
+ /** Viewing a subagent, the draft goes to it: the placeholder names it; else the session's title, quoted in italics. */
226
+ describePlaceholder: () => TspText;
224
227
  /** A shell-mode draft highlights as its language. */
225
228
  describeLanguage: () => string | undefined;
226
229
  /** Host-owned live state for the TSP composer (shell mode, effort chip, send/stop). */
@@ -17,13 +17,16 @@ export interface AssistantMessageHost {
17
17
  readonly proseOnlyThinking: boolean;
18
18
  readonly expandThinkingBlocks: boolean;
19
19
  readonly assistantImagesVisible: boolean;
20
+ /** Whether the viewed session's tables get charts: the main session's do, a focused subagent's do not. */
21
+ readonly tableChartsVisible: boolean;
20
22
  readonly hideToolActivity: boolean;
21
23
  readonly toolOutputExpanded: boolean;
22
24
  readonly ui: {
23
25
  requestRender(): void;
24
26
  readonly imageBudget: ImageBudget;
25
27
  };
26
- resolveAssistantMessageLinks(texts: readonly string[]): Promise<ReadonlyMap<string, string>>;
28
+ /** Resolve already-extracted Markdown link destinations to local targets. */
29
+ resolveAssistantMessageLinkHrefs(hrefs: readonly string[]): Promise<ReadonlyMap<string, string>>;
27
30
  }
28
31
  /**
29
32
  * Resolve and cache the current session's model-authored prose links. Existing
@@ -19,7 +19,7 @@ export declare function applyModelMentionCompletion(lines: string[], cursorLine:
19
19
  };
20
20
  /**
21
21
  * Create a session-scoped model candidate lookup using picker ordering. The
22
- * scope and search affinity are reused across queries until their inputs change.
22
+ * scope and its prepared ranker are reused across queries until their inputs change.
23
23
  */
24
24
  export declare function createModelMentionSource(host: {
25
25
  source: ModelBrowserSource;
@@ -0,0 +1,2 @@
1
+ /** Splice overlay content into a base line at a specific column. Single-pass optimized. */
2
+ export declare function compositeLineAt(baseLine: string, overlayLine: string, startCol: number, overlayWidth: number, totalWidth: number): string;
@@ -12,6 +12,10 @@ export interface GlobToolDetails {
12
12
  fileCount?: number;
13
13
  files?: string[];
14
14
  truncated?: boolean;
15
+ /** The scan hit the tool deadline, so the listed files are an incomplete
16
+ * set rather than a complete answer. Distinct from `truncated`, which also
17
+ * covers result-limit and output truncation. */
18
+ timedOut?: boolean;
15
19
  error?: string;
16
20
  /** Working directory at search time. Used by the renderer to resolve relative
17
21
  * file paths to absolute paths for OSC 8 hyperlinks. */
@@ -39,6 +39,18 @@ export interface ToolRenderResult<TDetails = unknown> {
39
39
  details?: TDetails;
40
40
  isError?: boolean;
41
41
  }
42
+ /**
43
+ * A fence a tool call draws under its card, as assistant Markdown draws that
44
+ * fence. Any fence language may be named (`svg`, `mermaid`, `obj`); the
45
+ * transcript draws only those it draws as figures in assistant text.
46
+ */
47
+ export interface ToolFigure {
48
+ readonly lang: string;
49
+ /** The body so far. */
50
+ readonly source: string;
51
+ /** No more of the body arrives. */
52
+ readonly closed: boolean;
53
+ }
42
54
  /**
43
55
  * Per-renderer opt-in for a full viewport replay when the first result
44
56
  * replaces a painted pending-call render. A predicate receives the painted
@@ -103,6 +115,8 @@ export interface NativeToolView {
103
115
  readonly preview?: TspPreview | {
104
116
  readonly tail: number;
105
117
  } | "none";
118
+ /** Starts expanded whatever the transcript's expand state (the todo checklist); the user can still fold it. */
119
+ readonly open?: boolean;
106
120
  /** Render frameless (`frame:"inline"`): a head line plus a disclosed body. */
107
121
  readonly inline?: boolean;
108
122
  /** Head actions offered on hover (`copy`, `retry`). */
@@ -125,6 +139,14 @@ export interface ToolRenderer<TArgs = unknown, TDetails = unknown> {
125
139
  /** Semantic result view for TSP terminals; merged with the call view when {@link mergeCallAndResult}. */
126
140
  describeResult?(result: ToolRenderResult<TDetails>, options: RenderResultContextOptions, args?: TArgs): NativeToolView | undefined;
127
141
  mergeCallAndResult?: boolean;
142
+ /**
143
+ * The fence a call draws under its card (a `.svg` being written, as its
144
+ * image): growing while the args stream, closed once they are final; none
145
+ * after an error. `result` is undefined until the call has one. Rendered
146
+ * TUI only, and only fences the terminal draws as figures; native views put
147
+ * their drawing in their describe hooks.
148
+ */
149
+ figure?(args: TArgs, result: ToolRenderResult<TDetails> | undefined, options: RenderResultOptions): ToolFigure | undefined;
128
150
  /** Describes current activity without coupling a renderer to terminal layout. */
129
151
  activitySummary?(args: TArgs, context: ToolActivityContext): ToolActivitySummary;
130
152
  /** Render without background box, inline in the response flow */
@@ -236,7 +236,8 @@ export declare class OutputSink {
236
236
  constructor(options?: OutputSinkOptions);
237
237
  /**
238
238
  * Push a chunk of output. The buffer management and onChunk callback run
239
- * synchronously. File sink writes are deferred and serialized internally.
239
+ * synchronously; onChunk fires after the chunk is stored, so {@link preview}
240
+ * already includes it. File sink writes are deferred and serialized internally.
240
241
  *
241
242
  * `inline` substitutes a bounded representation for the in-memory buffer and
242
243
  * live preview while the complete chunk is mirrored to the artifact.
@@ -260,6 +261,15 @@ export declare class OutputSink {
260
261
  * branch in `dump()` against stale totals.
261
262
  */
262
263
  replace(text: string): void;
264
+ /**
265
+ * Current inline view without finalizing: the body {@link dump} would
266
+ * return right now (head + elision marker + rolling tail), minus any notice
267
+ * and minus the newline `dump()` appends after output that ends in a bare
268
+ * carriage return. Each call trims the rolling tail to its budget (a copy
269
+ * of the tail window once it overflowed), so call it from a throttled
270
+ * onChunk (`chunkThrottleMs`), not once per raw chunk.
271
+ */
272
+ preview(): string;
263
273
  dump(notice?: string): Promise<OutputSummary>;
264
274
  /**
265
275
  * Release the artifact spill descriptor on an exit path that skips
@@ -6,7 +6,7 @@ import { type ProcWriteDetails } from "./proc-render.js";
6
6
  import { type CfgWriteDetails } from "./cfg-render.js";
7
7
  import type { FileDiagnosticsResult } from "./lsp.js";
8
8
  import type { OutputMeta } from "./output-meta.js";
9
- import type { NativeToolView, RenderResultOptions, ToolActivityContext, ToolActivitySummary } from "./renderer.js";
9
+ import type { NativeToolView, RenderResultOptions, ToolActivityContext, ToolActivitySummary, ToolFigure } from "./renderer.js";
10
10
  import { type XdevRenderDispatch, type XdevMountedRenderer } from "./xdev.js";
11
11
  /** Details returned by the write tool for transcript rendering. */
12
12
  export interface WriteToolDetails {
@@ -37,6 +37,12 @@ interface WriteResult {
37
37
  details?: WriteToolDetails;
38
38
  isError?: boolean;
39
39
  }
40
+ /**
41
+ * The fence a file write draws as while its content streams and after: its
42
+ * extension names a figure language and it has content, closed once the args
43
+ * are final. None for URL-card writes, or after an error.
44
+ */
45
+ declare function writeFigure(args: WriteRenderArgs | undefined, result: WriteResult | undefined, options: RenderResultOptions): ToolFigure | undefined;
40
46
  declare const writeStreamingPreviewStateKey: unique symbol;
41
47
  /**
42
48
  * Per-component state for incrementally rendering a streamed write.
@@ -84,6 +90,7 @@ export declare const writeToolRenderer: {
84
90
  describeResult(result: WriteResult, options: RenderResultOptions & {
85
91
  renderContext?: WriteRenderContext;
86
92
  }, args?: WriteRenderArgs): NativeToolView | undefined;
93
+ figure: typeof writeFigure;
87
94
  mergeCallAndResult: true;
88
95
  forceFirstResultViewportRepaint: (args: unknown, options: RenderResultOptions) => boolean;
89
96
  };
@@ -26,6 +26,12 @@ export interface TextSizingOptions {
26
26
  */
27
27
  export declare function encodeTextSized(text: string, options?: TextSizingOptions): string;
28
28
  export declare function sliceWithWidth(line: string, startCol: number, length: number, strict?: boolean | null): SliceResult;
29
+ /**
30
+ * Hard-wrap one literal terminal row without trimming spaces or splitting graphemes.
31
+ * An oversized grapheme is emitted intact even when it exceeds the requested width.
32
+ * Zero-cell rows remain one empty row, matching restored-terminal reflow.
33
+ */
34
+ export declare function wrapLiteralLine(line: string, width: number): string[];
29
35
  export declare function truncateToWidth(text: string, maxWidth: number, ellipsisKind?: Ellipsis | null | "", pad?: boolean | null): string;
30
36
  export declare function wrapTextWithAnsi(text: string, width: number): string[];
31
37
  export declare function extractSegments(line: string, beforeEnd: number, afterStart: number, afterLen: number, strictAfter: boolean): ExtractSegmentsResult;