@oh-my-pi/pi-tui 18.12.0 → 18.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -76
- package/dist/types/chat/advisor-message.d.ts +4 -1
- package/dist/types/chat/messages.d.ts +3 -1
- package/dist/types/chrome/diff.d.ts +2 -3
- package/dist/types/chrome/status-notice.d.ts +2 -0
- package/dist/types/prompt/composer.d.ts +2 -0
- package/dist/types/tmux.d.ts +9 -0
- package/dist/types/tools/eval.d.ts +51 -7
- package/dist/types/tools/grouped-file-output.d.ts +3 -3
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/web-search-types.d.ts +1 -0
- package/package.json +9 -9
- package/src/charts/table-data.ts +17 -6
- package/src/chat/advisor-message.ts +77 -24
- package/src/chat/messages.ts +4 -1
- package/src/chat/read-tool-group.ts +2 -2
- package/src/chrome/diff.ts +4 -9
- package/src/chrome/status-notice.ts +5 -0
- package/src/components/markdown.ts +21 -1
- package/src/prompt/composer-cache.ts +3 -0
- package/src/prompt/composer.ts +8 -2
- package/src/status-line/component.ts +56 -1
- package/src/stdin-buffer.ts +289 -61
- package/src/terminal-capabilities.ts +7 -1
- package/src/terminal.ts +30 -17
- package/src/tmux.ts +30 -0
- package/src/tools/ast-edit.ts +2 -3
- package/src/tools/ast-grep.ts +1 -2
- package/src/tools/edit.ts +3 -3
- package/src/tools/eval.ts +132 -132
- package/src/tools/find.ts +6 -3
- package/src/tools/grep.ts +0 -2
- package/src/tools/grouped-file-output.ts +6 -5
- package/src/tools/read.ts +3 -3
- package/src/tools/web-search-types.ts +1 -0
- package/src/tools/write.ts +5 -3
- package/src/tui.ts +20 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.12.2] - 2026-10-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `ComposerPreferences.completeScrollback` (default `false`): rows a growing HUD or dialog pushes off screen go straight to scrollback instead of being held back until it shrinks ([#15692](https://github.com/can1357/oh-my-pi/pull/15692) by [@Ilm-Alan](https://github.com/Ilm-Alan))
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed table charts picking the wrong label and measure columns when numbers group thousands with spaces (`1 988`, `17 083`); they now read like `1,988` ([#16175](https://github.com/can1357/oh-my-pi/issues/16175))
|
|
14
|
+
- Fixed terminal capability replies relayed through win32-input-mode appearing as typed input in WSL terminals ([#16137](https://github.com/can1357/oh-my-pi/issues/16137))
|
|
15
|
+
- Fixed the status-bar activity spinner and turn timer freezing until the next keypress when the working-row loader is stopped mid-turn; an open turn window now repaints the status line on its own timer ([#16109](https://github.com/can1357/oh-my-pi/pull/16109) by [@tommymorgan](https://github.com/tommymorgan)).
|
|
16
|
+
- Fixed pasting sometimes inserting a block of base64 text instead of the pasted text in terminals that deliver pastes through the Kitty clipboard protocol (OSC 5522), most often with dictation apps that paste for you; a slow paste now arrives whole, and keys typed while a paste never finishes are given back instead of lost ([#16170](https://github.com/can1357/oh-my-pi/pull/16170) by [@H4vC](https://github.com/H4vC))
|
|
17
|
+
- Fixed a streaming table bouncing everything below it up and down while its last cell was still being written ([#16116](https://github.com/can1357/oh-my-pi/pull/16116) by [@galtamam-cmyk](https://github.com/galtamam-cmyk))
|
|
18
|
+
- Fixed inline images in iTerm2 being sized for the wrong cell shape, which left charts smeared or offset ([#16118](https://github.com/can1357/oh-my-pi/pull/16118) by [@galtamam-cmyk](https://github.com/galtamam-cmyk))
|
|
19
|
+
|
|
20
|
+
## [18.12.1] - 2026-10-17
|
|
21
|
+
|
|
22
|
+
### Breaking Changes
|
|
23
|
+
|
|
24
|
+
- `numberedCode`, `readContentCode` and `nativeDiff` take the file's path (`path`) instead of a language, and `describeGroupedOutput` drops its `lang` option: Tern picks the grammar of file contents from their path
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Advisor notes in Tern read as a callout tinted by their worst severity: its icon and a tally (`Concern`, `1 blocker · 2 concerns`) head the card with the advisor's name at the end, and several notes each lead with their own severity icon
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Fixed images rendering blank inside tmux on Kitty-capable terminals when tmux's `allow-passthrough` is off (the default); omp now falls back to SIXEL or the text placeholder instead of sending Kitty graphics tmux drops
|
|
33
|
+
- Fixed Tern drawing tool cards' file contents (write, read, edit, grep, find, ast-grep, ast-edit) plain or in the wrong language when omp did not know the file's extension (`.ptx`) or mapped it to a neighbor (`.cu` as C++); omp now sends the file's path and Tern picks the grammar
|
|
34
|
+
|
|
5
35
|
## [18.12.0] - 2026-10-17
|
|
6
36
|
|
|
7
37
|
### Breaking Changes
|
|
@@ -2447,79 +2477,4 @@
|
|
|
2447
2477
|
|
|
2448
2478
|
- Increased the minimum required Bun version for the TUI package from >=1.3.7 to >=1.3.14
|
|
2449
2479
|
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
### Added
|
|
2453
|
-
|
|
2454
|
-
- Added `Terminal.setProgress(active)` to emit OSC 9;4 progress sequences with a ~1s keepalive interval so Ghostty does not clear the indicator during long-running work (ports pi-mono `a900d251` + `76bc605a`)
|
|
2455
|
-
- Added optional `argumentHint?: string` to `SlashCommand`; rendered before the description in the autocomplete dropdown (ports pi-mono `aa25726e`)
|
|
2456
|
-
- Added `VirtualTerminal.waitForRender()` test helper for the throttled render pipeline (ports pi-mono `41377ee8`)
|
|
2457
|
-
|
|
2458
|
-
### Changed
|
|
2459
|
-
|
|
2460
|
-
- `ProcessTerminal` `columns`/`rows` getters consult `Bun.env.COLUMNS` / `Bun.env.LINES` before falling back to 80×24, so piped/non-TTY runs honour environment-provided dimensions (ports pi-mono `32f7fc6a`)
|
|
2461
|
-
- `requestRender()` non-force calls are coalesced to a ~16ms frame budget; `requestRender(true)` still flushes immediately via `process.nextTick` (ports pi-mono `6f5f37f8`)
|
|
2462
|
-
- `KNOWN_TERMINALS.base` / `KNOWN_TERMINALS.trueColor` default `hyperlinks: false`; tmux and screen (`TMUX` env or `TERM` starts with `tmux`/`screen`) force `hyperlinks: false` even when the outer terminal would advertise OSC 8 (adapts pi-mono `30a8a41f`)
|
|
2463
|
-
- `SlashCommand.getArgumentCompletions()` may return a `Promise`; results are now awaited and non-array returns are ignored (ports pi-mono `a1e10789`)
|
|
2464
|
-
- Fuzzy `@` autocomplete now follows symlinked directories via `ScanOptions.follow_links` plumbed through the native walker (ports pi-mono `780d5367`)
|
|
2465
|
-
- Plain `@<query>` (no slash) fuzzy matches by basename only, so `@plan` no longer surfaces every file whose ancestor directories contain `plan` (ports pi-mono `968430f6`)
|
|
2466
|
-
- Changed slash-command autocomplete list rendering to combine command hint and description in a single displayed suggestion text
|
|
2467
|
-
- Changed render scheduling to throttle `requestRender` calls to roughly 60fps by batching updates
|
|
2468
|
-
- Changed terminal input handling to process complete cell-size responses without buffering partial input
|
|
2469
|
-
- Changed `KeyId` to accept super-modifier combinations and improve typed key-id validation
|
|
2470
|
-
|
|
2471
|
-
### Fixed
|
|
2472
|
-
|
|
2473
|
-
- Fixed editor corruption on Thai Sara Am (U+0E33) and Lao AM (U+0EB3) vowels by normalizing to their compatibility decompositions on the terminal-write path while keeping editor content logically unchanged (ports pi-mono `bc668826` + `338ce3a3` + `20ca45d5`)
|
|
2474
|
-
- Fixed cell-size detection (`CSI 6;h;w t` response) to consume only exact replies, so a bare `Escape` keystroke is no longer swallowed while waiting for terminal image metadata (ports pi-mono `49c0d860`)
|
|
2475
|
-
- Fixed Kitty CSI-u printable input duplicating on layouts (e.g. Italian) where the terminal also emits the raw character: the immediately-following matching codepoint is now suppressed (ports pi-mono `bdb416cb`)
|
|
2476
|
-
- Fixed bracketed-paste CSI-u `Ctrl+<letter>` re-encoding (tmux popup with `extended-keys-format=csi-u`) leaking literal `[<code>;5u` into the editor; control bytes are decoded back to their literal byte before per-char filtering (ports pi-mono `d06db09a`)
|
|
2477
|
-
- Fixed xterm `modifyOtherKeys` shifted printable input so uppercase letters inserted via `CSI 27;mod;codepoint~` reach the editor correctly (ports pi-mono `6b55d685`)
|
|
2478
|
-
- Fixed `super`-modified Kitty shortcuts (`super+k`, `ctrl+super+enter`, …) to parse and match via the new `KITTY_MOD_SUPER` mask (ports pi-mono `ddb8454c` + `5ed46003`)
|
|
2479
|
-
- Fixed `ctrl+alt+<letter>` in tmux falling through to CSI-u / `modifyOtherKeys` when the legacy `ESC<ctrl-char>` form does not match (ports pi-mono `6cf5098f`)
|
|
2480
|
-
- Fixed Markdown strikethrough requiring strict `~~text~~` delimiters with non-whitespace boundaries; single tildes no longer render strikethrough (ports pi-mono `db5274b4`)
|
|
2481
|
-
- Allowed `SlashCommand.getArgumentCompletions` to return asynchronous results by accepting Promise-based completions
|
|
2482
|
-
- Added `argumentHint` support to slash command definitions and displayed it in command suggestion descriptions
|
|
2483
|
-
- Added support for xterm `modifyOtherKeys` printable key sequences by decoding `CSI 27;mod;key~` into text input
|
|
2484
|
-
- Normalized line output during rendering to correct Thai/Lao AM glyph composition for displayed text
|
|
2485
|
-
- Fixed duplicated Kitty key input emissions by dropping the matching unmodified follow-up sequence after a Kitty CSI-u printable-key event
|
|
2486
|
-
|
|
2487
|
-
## [14.9.5] - 2026-05-12
|
|
2488
|
-
|
|
2489
|
-
### Fixed
|
|
2490
|
-
|
|
2491
|
-
- Fixed rapidly blinking cursor artifact during task execution by consolidating cursor control sequences into the synchronized output buffer ([#992](https://github.com/can1357/oh-my-pi/issues/992))
|
|
2492
|
-
|
|
2493
|
-
## [14.5.7] - 2026-04-29
|
|
2494
|
-
|
|
2495
|
-
### Fixed
|
|
2496
|
-
|
|
2497
|
-
- Fixed editor Ctrl+Enter handling to recognize NumLock and keypad Enter variants.
|
|
2498
|
-
|
|
2499
|
-
## [14.3.0] - 2026-04-25
|
|
2500
|
-
|
|
2501
|
-
### Fixed
|
|
2502
|
-
|
|
2503
|
-
- Fixed shared Markdown Mermaid fenced-block rendering to resolve diagrams from fenced source text instead of external prerender state
|
|
2504
|
-
|
|
2505
|
-
## [14.1.1] - 2026-04-14
|
|
2506
|
-
|
|
2507
|
-
### Breaking Changes
|
|
2508
|
-
|
|
2509
|
-
- Removed the `searchDb` constructor argument from `CombinedAutocompleteProvider`, requiring callers to use the built-in search behavior
|
|
2510
|
-
|
|
2511
|
-
### Changed
|
|
2512
|
-
|
|
2513
|
-
- Changed truncation debug logging to run only when `debugRedraw` is enabled
|
|
2514
|
-
|
|
2515
|
-
## [14.0.5] - 2026-04-11
|
|
2516
|
-
|
|
2517
|
-
### Changed
|
|
2518
|
-
|
|
2519
|
-
- Updated hash computation to use `Bun.hash()` instead of `Bun.hash.xxHash64()`, which may return `number` in addition to `bigint`
|
|
2520
|
-
- Simplified cache key computation in Box component by removing intermediate hash updates and consolidating hash operations
|
|
2521
|
-
- Wrapped native text utility functions (`sliceWithWidth`, `truncateToWidth`, `wrapTextWithAnsi`, `extractSegments`) to automatically pass the current default tab width, simplifying the API for consumers
|
|
2522
|
-
- Added `getIndentationNoescape` wrapper that uses `process.cwd()` as the project root for relative file paths
|
|
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
|
|
2524
|
-
|
|
2525
|
-
Older entries are archived in [packages/tui/CHANGELOG.md@691b3d448f3d](https://github.com/can1357/oh-my-pi/blob/691b3d448f3da1f99010854dea4a562395fd190d/packages/tui/CHANGELOG.md).
|
|
2480
|
+
Older entries are archived in [packages/tui/CHANGELOG.md@04d4ab14b45f](https://github.com/can1357/oh-my-pi/blob/04d4ab14b45f8dbd4055b675519f8815a1eb1841/packages/tui/CHANGELOG.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Component } from "../tui.js";
|
|
2
2
|
import type { AdvisorMessageDetails } from "./messages.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { Theme } from "../theme/index.js";
|
|
4
4
|
import { type NativeUiEvent } from "../native/node.js";
|
|
5
5
|
/** The advisor card: shared by the controlled expansion getter and native toggles. */
|
|
6
6
|
export interface AdvisorMessageCard extends Component {
|
|
@@ -14,6 +14,9 @@ export interface AdvisorMessageCard extends Component {
|
|
|
14
14
|
* output (whose `thinkingText` color equals `toolOutput` in most themes):
|
|
15
15
|
* a bold `customMessageLabel` header tag (skill-card convention), a heavy
|
|
16
16
|
* rail tinted per-note severity, and the note body on the default text color.
|
|
17
|
+
* Natively it is a callout tinted by its worst note: that severity's icon and
|
|
18
|
+
* the tally head it, the advisor's name ends the head, and with several notes
|
|
19
|
+
* each leads with its own severity icon.
|
|
17
20
|
*
|
|
18
21
|
* `getExpanded` owns expansion; a native toggle overrides it for this card
|
|
19
22
|
* until the next transcript-wide change.
|
|
@@ -173,7 +173,9 @@ export declare function isUserInvokedSkillPrompt(message: CustomMessage): boolea
|
|
|
173
173
|
* reminders, and auto-continues are not turn starts.
|
|
174
174
|
*/
|
|
175
175
|
export declare function isUserTurnInitiator(message: CustomMessage): boolean;
|
|
176
|
-
|
|
176
|
+
/** Advisor note severities, weakest first; ranks severity thresholds such as `advisor.continueSeverity`. */
|
|
177
|
+
export declare const ADVISOR_SEVERITIES: readonly ["nit", "concern", "blocker"];
|
|
178
|
+
export type AdvisorSeverity = (typeof ADVISOR_SEVERITIES)[number];
|
|
177
179
|
/** One queued advice note. */
|
|
178
180
|
export interface AdvisorNote {
|
|
179
181
|
note: string;
|
|
@@ -7,10 +7,9 @@ import type { NativeNode } from "../native/node.js";
|
|
|
7
7
|
* becomes one hunk; the terminal draws gutters, word emphasis and highlighting.
|
|
8
8
|
* Removed rows carry old line numbers, added rows new ones and context rows
|
|
9
9
|
* old ones, so the other side is derived from the running line delta.
|
|
10
|
+
* Tern picks the grammar from `path`.
|
|
10
11
|
*/
|
|
11
|
-
export declare function nativeDiff(diffText: string,
|
|
12
|
-
filePath?: string;
|
|
13
|
-
} & TspProps<"diff">): NativeNode;
|
|
12
|
+
export declare function nativeDiff(diffText: string, props?: TspProps<"diff">): NativeNode;
|
|
14
13
|
export interface RenderDiffOptions {
|
|
15
14
|
/** File path used to resolve indentation (.editorconfig + defaults) */
|
|
16
15
|
filePath?: string;
|
|
@@ -21,6 +21,8 @@ export interface StatusNoticeOptions {
|
|
|
21
21
|
export declare class StatusNotice extends Container {
|
|
22
22
|
#private;
|
|
23
23
|
constructor(message: string, options?: StatusNoticeOptions);
|
|
24
|
+
/** The text the notice currently shows. */
|
|
25
|
+
get message(): string;
|
|
24
26
|
setMessage(message: string, options?: StatusNoticeOptions): void;
|
|
25
27
|
describe(): NativeNode;
|
|
26
28
|
}
|
|
@@ -14,6 +14,8 @@ export interface ComposerPreferences {
|
|
|
14
14
|
readonly showHardwareCursor: boolean;
|
|
15
15
|
readonly maxInlineImages: number;
|
|
16
16
|
readonly resizeScrollback: ResizeScrollbackMode;
|
|
17
|
+
/** Send rows a growing HUD or dialog pushes off screen straight to scrollback instead of holding them back until it shrinks. */
|
|
18
|
+
readonly completeScrollback: boolean;
|
|
17
19
|
readonly imeSafeCursor: boolean;
|
|
18
20
|
readonly autocompleteMaxVisible: number;
|
|
19
21
|
readonly spellingTypoDetection: boolean;
|
package/dist/types/tmux.d.ts
CHANGED
|
@@ -45,3 +45,12 @@ export declare function resolveTmuxClientTerminalNames(env?: NodeJS.ProcessEnv):
|
|
|
45
45
|
* for features.
|
|
46
46
|
*/
|
|
47
47
|
export declare function tmuxClientRendersSixel(env?: NodeJS.ProcessEnv): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether tmux forwards this pane's `\x1bPtmux;…\x1b\\` passthrough envelopes
|
|
50
|
+
* to the attached client. tmux 3.3+ defaults `allow-passthrough` to `off` and
|
|
51
|
+
* then silently drops every wrapped sequence, so Kitty graphics sent through
|
|
52
|
+
* the envelope never reach a Kitty-capable outer terminal. Resolves the
|
|
53
|
+
* effective pane value (pane → window → global). Queried once per pane;
|
|
54
|
+
* `false` outside tmux or when the query fails.
|
|
55
|
+
*/
|
|
56
|
+
export declare function tmuxPaneAllowsPassthrough(env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -13,30 +13,60 @@ export interface EvalStatusEvent {
|
|
|
13
13
|
resolvedThinkingLevel?: ConfiguredThinkingLevel;
|
|
14
14
|
[key: string]: unknown;
|
|
15
15
|
}
|
|
16
|
+
/** One column of a table view. */
|
|
17
|
+
export interface EvalTableColumn {
|
|
18
|
+
head: string;
|
|
19
|
+
/** Numeric values: renderers right-align the column. */
|
|
20
|
+
numeric?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A pretty rendering of something a cell printed, shown to the user in place
|
|
24
|
+
* of its text; the model only ever sees the text.
|
|
25
|
+
* - `table`: a data frame or `console.table`; leading index columns are plain
|
|
26
|
+
* columns, and a truncated frame's elided rows/columns are `…` cells.
|
|
27
|
+
* - `json`: a structured value (dict/list, object/array) as a collapsible tree.
|
|
28
|
+
* - `markdown`: an object's Markdown representation.
|
|
29
|
+
*/
|
|
30
|
+
export type EvalView = {
|
|
31
|
+
kind: "table";
|
|
32
|
+
columns: EvalTableColumn[];
|
|
33
|
+
rows: string[][];
|
|
34
|
+
} | {
|
|
35
|
+
kind: "json";
|
|
36
|
+
value: unknown;
|
|
37
|
+
} | {
|
|
38
|
+
kind: "markdown";
|
|
39
|
+
text: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* A view and the exact printed text it stands for. Renderers locate `text` in
|
|
43
|
+
* the cell's `output` (entries in print order) and show the view there; an
|
|
44
|
+
* entry whose text is no longer in the output (truncated) is skipped.
|
|
45
|
+
*/
|
|
46
|
+
export interface EvalViewEntry {
|
|
47
|
+
text: string;
|
|
48
|
+
view: EvalView;
|
|
49
|
+
}
|
|
16
50
|
/** Per-cell execution result for transcript rendering. */
|
|
17
51
|
export interface EvalCellResult {
|
|
18
52
|
index: number;
|
|
19
53
|
title?: string;
|
|
20
54
|
code: string;
|
|
21
55
|
language?: EvalLanguage;
|
|
56
|
+
/** What the model saw: the cell's printed text plus image notes. */
|
|
22
57
|
output: string;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
* from `jsonOutputs` instead. Absent when the cell displayed no JSON.
|
|
26
|
-
*/
|
|
27
|
-
textOutput?: string;
|
|
58
|
+
/** Pretty views of spans of `output`, in print order. */
|
|
59
|
+
views?: EvalViewEntry[];
|
|
28
60
|
status: "pending" | "running" | "complete" | "error";
|
|
29
61
|
durationMs?: number;
|
|
30
62
|
exitCode?: number;
|
|
31
63
|
statusEvents?: EvalStatusEvent[];
|
|
32
64
|
/** Discrete events dropped from the front of `statusEvents` (see {@link recordStatusEvent}). */
|
|
33
65
|
statusEventsElided?: number;
|
|
34
|
-
hasMarkdown?: boolean;
|
|
35
66
|
}
|
|
36
67
|
/** Tool result detail object surfaced to the UI/transcript. */
|
|
37
68
|
export interface EvalToolDetails {
|
|
38
69
|
cells?: EvalCellResult[];
|
|
39
|
-
jsonOutputs?: unknown[];
|
|
40
70
|
images?: ImageContent[];
|
|
41
71
|
statusEvents?: EvalStatusEvent[];
|
|
42
72
|
/** Discrete events dropped from the front of `statusEvents` (see {@link recordStatusEvent}). */
|
|
@@ -105,6 +135,20 @@ export declare function upsertStatusEvent(events: EvalStatusEvent[], event: Eval
|
|
|
105
135
|
* events behind an "… N earlier" row.
|
|
106
136
|
*/
|
|
107
137
|
export declare function recordStatusEvent(log: StatusEventLog, event: EvalStatusEvent): void;
|
|
138
|
+
/** One ordered piece of a cell's output: printed text, or a view shown in place of the text it matched. */
|
|
139
|
+
export type EvalOutputPart = {
|
|
140
|
+
kind: "text";
|
|
141
|
+
text: string;
|
|
142
|
+
} | {
|
|
143
|
+
kind: "view";
|
|
144
|
+
view: EvalView;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Split `output` around its views, in order: each view's `text` is searched
|
|
148
|
+
* from the end of the previous match, unmatched entries are skipped, and
|
|
149
|
+
* blank text between views is dropped (newlines bordering a view trimmed).
|
|
150
|
+
*/
|
|
151
|
+
export declare function splitEvalOutput(output: string, views: readonly EvalViewEntry[] | undefined): EvalOutputPart[];
|
|
108
152
|
/** Render eval code cells, structured display output, and progress events. */
|
|
109
153
|
export declare const evalToolRenderer: {
|
|
110
154
|
animatedPendingPreview: true;
|
|
@@ -110,11 +110,12 @@ export interface NumberedLine {
|
|
|
110
110
|
* Numbered `code` for possibly non-contiguous source lines: one block per run
|
|
111
111
|
* of consecutive line numbers (`start` = the run's first line, match lines
|
|
112
112
|
* `mark`ed), with a `…` row wherever the numbers jump or an elision marker
|
|
113
|
-
* (`n: null`) sits.
|
|
113
|
+
* (`n: null`) sits. `path` is the file the lines come from: Tern picks their
|
|
114
|
+
* grammar from it and draws no header (the row or head above names the file).
|
|
114
115
|
* `wrap` soft-wraps long lines instead of scrolling the block sideways.
|
|
115
116
|
*/
|
|
116
117
|
export declare function numberedCode(lines: readonly NumberedLine[], opts?: {
|
|
117
|
-
|
|
118
|
+
path?: string;
|
|
118
119
|
wrap?: boolean;
|
|
119
120
|
}): NativeNode[];
|
|
120
121
|
/**
|
|
@@ -128,7 +129,6 @@ export declare function numberedCode(lines: readonly NumberedLine[], opts?: {
|
|
|
128
129
|
* same file row as a directory search's.
|
|
129
130
|
*/
|
|
130
131
|
export declare function describeGroupedOutput(lines: readonly string[], options?: {
|
|
131
|
-
lang?: (path: string) => string | undefined;
|
|
132
132
|
maxFiles?: number;
|
|
133
133
|
file?: string;
|
|
134
134
|
}): NativeNode[];
|
|
@@ -136,7 +136,7 @@ export declare function readResultIsMarkdown(details: ReadToolDetails | undefine
|
|
|
136
136
|
* block; runs split with `…` between) when they cover every line, else count
|
|
137
137
|
* up from `start`.
|
|
138
138
|
*/
|
|
139
|
-
export declare function readContentCode(content: string, start: number, numbers: readonly (number | null)[] | undefined,
|
|
139
|
+
export declare function readContentCode(content: string, start: number, numbers: readonly (number | null)[] | undefined, path: string | undefined): NativeNode[];
|
|
140
140
|
/** Render file, image, and URL reads in the transcript. */
|
|
141
141
|
export declare const readToolRenderer: {
|
|
142
142
|
activitySummary(args: unknown, _context: ToolActivityContext): ToolActivitySummary;
|
|
@@ -25,6 +25,7 @@ export declare const SEARCH_PROVIDER_LABELS: {
|
|
|
25
25
|
readonly tavily: "Tavily";
|
|
26
26
|
readonly firecrawl: "Firecrawl";
|
|
27
27
|
readonly brave: "Brave";
|
|
28
|
+
readonly deepseek: "DeepSeek";
|
|
28
29
|
readonly kimi: "Kimi";
|
|
29
30
|
readonly synthetic: "Synthetic";
|
|
30
31
|
readonly ollama: "Ollama";
|
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.12.
|
|
4
|
+
"version": "18.12.2",
|
|
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.12.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "18.12.
|
|
46
|
-
"@oh-my-pi/pi-ai": "18.12.
|
|
47
|
-
"@oh-my-pi/pi-catalog": "18.12.
|
|
48
|
-
"@oh-my-pi/pi-natives": "18.12.
|
|
49
|
-
"@oh-my-pi/pi-utils": "18.12.
|
|
50
|
-
"@oh-my-pi/pi-wire": "18.12.
|
|
51
|
-
"@oh-my-pi/snapcompact": "18.12.
|
|
44
|
+
"@oh-my-pi/omptype": "18.12.2",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "18.12.2",
|
|
46
|
+
"@oh-my-pi/pi-ai": "18.12.2",
|
|
47
|
+
"@oh-my-pi/pi-catalog": "18.12.2",
|
|
48
|
+
"@oh-my-pi/pi-natives": "18.12.2",
|
|
49
|
+
"@oh-my-pi/pi-utils": "18.12.2",
|
|
50
|
+
"@oh-my-pi/pi-wire": "18.12.2",
|
|
51
|
+
"@oh-my-pi/snapcompact": "18.12.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"kitty-vt-wasm": "^0.2.0"
|
package/src/charts/table-data.ts
CHANGED
|
@@ -148,7 +148,17 @@ export interface TableAnalysis {
|
|
|
148
148
|
readonly composition?: Composition;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
/**
|
|
152
|
+
* A whole number as written: bare, or grouped in thousands by commas (`1,988`)
|
|
153
|
+
* or by spaces as many locales write it (`1 988`, also U+00A0 and U+202F).
|
|
154
|
+
* Space groups take exactly three digits, so `12 5` and `128 2560` stay apart.
|
|
155
|
+
*/
|
|
156
|
+
const INTEGER = String.raw`\d{1,3}(?:,\d{3})+|\d{1,3}(?:[ \u00A0\u202F]\d{3})+(?!\d)|\d+`;
|
|
157
|
+
const NUMBER = String.raw`(?:${INTEGER})(?:\.\d+)?(?:[eE][+-]?\d+)?|\.\d+`;
|
|
158
|
+
/** A cell holding one whole number and nothing else (`38`, `1,980`). */
|
|
159
|
+
const WHOLE = new RegExp(String.raw`^(?:${INTEGER})$`);
|
|
160
|
+
/** The first number of a figure, its whole part and its decimals apart. */
|
|
161
|
+
const WRITTEN = new RegExp(String.raw`(${INTEGER})(?:\.(\d+))?`);
|
|
152
162
|
const CELL = new RegExp(
|
|
153
163
|
String.raw`^(?<approx>~|≈|<=|>=|<|>|≤|≥)?\s*(?<sign>[+\-−±])?\s*(?<cur>[$€£¥])?\s*(?<num>${NUMBER})(?<plus>\+(?=\s*[A-Za-zµμ%]))?\s*(?<unit>%|[A-Za-zµμ×]+(?:\/[A-Za-z]+)?|\/s)?(?<rest>.*)$`,
|
|
154
164
|
);
|
|
@@ -517,8 +527,9 @@ export function parseCell(markdown: string): Cell {
|
|
|
517
527
|
return { ...cell, fit: "prose" };
|
|
518
528
|
}
|
|
519
529
|
|
|
530
|
+
/** The value of a number {@link NUMBER} matched, its comma or space group separators removed. */
|
|
520
531
|
function toNumber(written: string): number {
|
|
521
|
-
return Number(written.
|
|
532
|
+
return Number(written.replace(/[,\s]/g, ""));
|
|
522
533
|
}
|
|
523
534
|
|
|
524
535
|
/** A number of a list or sum: its value as written, its unit (known or a noun) and how it was marked. */
|
|
@@ -632,7 +643,7 @@ function listCell(text: string, base: CellBase): NumberCell | undefined {
|
|
|
632
643
|
// A spaced pair of bare numbers that is no score (`25.0 / 27.8`, `12.6 / 9.4`: the second is no whole, or
|
|
633
644
|
// the first exceeds it) lists two values of the column's quantity; `27 / 30` stays a score.
|
|
634
645
|
const [first, second] = items;
|
|
635
|
-
const whole =
|
|
646
|
+
const whole = WHOLE.test(text.slice(text.indexOf("/") + 1, end).trim());
|
|
636
647
|
const score = whole && first!.value >= 0 && first!.value <= second!.value;
|
|
637
648
|
const pair = items.length === 2 && !marked.some(Boolean) && separators[0] !== "/" && !score;
|
|
638
649
|
if (!pair && (!marked.at(-1) || !(marked.every(Boolean) || carried))) return undefined;
|
|
@@ -876,9 +887,9 @@ const WHOLE_HEADER = /\b(?:total|all|sum|overall)\b/i;
|
|
|
876
887
|
*/
|
|
877
888
|
export function writtenSlack(cell: NumberCell): number {
|
|
878
889
|
if (cell.denominator !== undefined && cell.dim === "percent") return 0;
|
|
879
|
-
const match =
|
|
890
|
+
const match = WRITTEN.exec(cell.figure);
|
|
880
891
|
if (!match) return 0;
|
|
881
|
-
const written =
|
|
892
|
+
const written = toNumber(`${match[1]!}${match[2] ? `.${match[2]}` : ""}`);
|
|
882
893
|
const factor = written ? Math.abs(cell.value) / written : 1;
|
|
883
894
|
const decimals = match[2]?.length ?? 0;
|
|
884
895
|
if (cell.dim === "count" && decimals === 0 && factor === 1 && !cell.approx) return 0;
|
|
@@ -1097,7 +1108,7 @@ const MIN_LISTS = 2;
|
|
|
1097
1108
|
/** A spaced slash between list items. */
|
|
1098
1109
|
const LIST_SLASH = /\s+\/\s+/;
|
|
1099
1110
|
/** A list item that is one number, as written (`12`, `~1,400`, `3.5 ms`, `40%`). */
|
|
1100
|
-
const LISTED_NUMBER =
|
|
1111
|
+
const LISTED_NUMBER = new RegExp(String.raw`^[~≈]?[$€£¥]?(?:${INTEGER})(?:\.\d+)?\s*(?:%|[A-Za-zµμ]+)?$`);
|
|
1101
1112
|
|
|
1102
1113
|
/**
|
|
1103
1114
|
* Rows folding several rows into one (`response anthropic / chat / responses /
|
|
@@ -4,9 +4,9 @@ import { visibleWidth } from "../utils";
|
|
|
4
4
|
import type { AdvisorMessageDetails, AdvisorNote, AdvisorSeverity } from "./messages";
|
|
5
5
|
import { formatBadge, replaceTabs, type ToolUIColor, wrapTextWithAnsi } from "../render/render-utils";
|
|
6
6
|
import { Ellipsis, truncateToWidth } from "../render";
|
|
7
|
-
import {
|
|
8
|
-
import type { TspSpan } from "@oh-my-pi/pi-wire";
|
|
9
|
-
import { card, span, text } from "../native/describe";
|
|
7
|
+
import type { Theme } from "../theme";
|
|
8
|
+
import type { TspSpan, TspTone } from "@oh-my-pi/pi-wire";
|
|
9
|
+
import { card, node, span, text } from "../native/describe";
|
|
10
10
|
import { type NativeNode, type NativeUiEvent, rootToggleExpanded } from "../native/node";
|
|
11
11
|
import { plainText } from "../native/spans";
|
|
12
12
|
import { Memo } from "../native/memo";
|
|
@@ -111,13 +111,16 @@ class AdvisorNotes implements Component {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/** The advisor a note came from; undefined for the implicit single ("default") advisor, which goes unlabeled. */
|
|
115
|
+
function sourceOf(entry: AdvisorNote): string | undefined {
|
|
116
|
+
return entry.advisor && entry.advisor !== "default" ? entry.advisor : undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
114
119
|
/** Wrapped, truncated rail rows for a single note; shared by both branches. */
|
|
115
120
|
function renderAdvisorNote(entry: AdvisorNote, width: number, uiTheme: Theme): string[] {
|
|
116
121
|
const badge = entry.severity ? `${formatBadge(entry.severity, severityColor(entry.severity), uiTheme)} ` : "";
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const who =
|
|
120
|
-
entry.advisor && entry.advisor !== "default" ? `${uiTheme.fg("dim", `[${replaceTabs(entry.advisor)}]`)} ` : "";
|
|
122
|
+
const source = sourceOf(entry);
|
|
123
|
+
const who = source ? `${uiTheme.fg("dim", `[${replaceTabs(source)}]`)} ` : "";
|
|
121
124
|
const age = entry.turnsAgo !== undefined ? `${uiTheme.fg("dim", `T-${entry.turnsAgo}`)} ` : "";
|
|
122
125
|
const railGlyph = uiTheme.symbol("advisor.rail");
|
|
123
126
|
const rail = uiTheme.fg(severityColor(entry.severity), railGlyph);
|
|
@@ -145,16 +148,47 @@ function renderAdvisorNote(entry: AdvisorNote, width: number, uiTheme: Theme): s
|
|
|
145
148
|
);
|
|
146
149
|
}
|
|
147
150
|
|
|
148
|
-
/**
|
|
149
|
-
|
|
151
|
+
/** A note's rank in the native callout: its severity, `note` when it has none. */
|
|
152
|
+
type AdvisorRank = AdvisorSeverity | "note";
|
|
153
|
+
|
|
154
|
+
/** Ranks worst first: the callout wears the worst rank among its notes. */
|
|
155
|
+
const RANKS: readonly AdvisorRank[] = ["blocker", "concern", "nit", "note"];
|
|
156
|
+
|
|
157
|
+
/** Each rank's native look, GitHub-alert style: card/icon tone, Tern icon name, label and its span style. */
|
|
158
|
+
const RANK_LOOK: Record<AdvisorRank, { tone: TspTone; icon: string; label: string; style: string }> = {
|
|
159
|
+
blocker: { tone: "error", icon: "octagon-alert", label: "Blocker", style: "error strong" },
|
|
160
|
+
concern: { tone: "warning", icon: "warn", label: "Concern", style: "warning strong" },
|
|
161
|
+
nit: { tone: "neutral", icon: "lightbulb", label: "Nit", style: "strong" },
|
|
162
|
+
note: { tone: "neutral", icon: "info", label: "Note", style: "strong" },
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
function rankOf(entry: AdvisorNote): AdvisorRank {
|
|
166
|
+
return entry.severity ?? "note";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The callout's title: one note's rank (`Concern`), else a tally worst first (`1 blocker · 2 concerns`). */
|
|
170
|
+
function tallySpans(notes: readonly AdvisorNote[]): TspSpan[] {
|
|
171
|
+
if (notes.length === 1) {
|
|
172
|
+
const { label, style } = RANK_LOOK[rankOf(notes[0]!)];
|
|
173
|
+
return [span(label, style)];
|
|
174
|
+
}
|
|
150
175
|
const spans: TspSpan[] = [];
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
for (const rank of RANKS) {
|
|
177
|
+
const count = notes.filter(entry => rankOf(entry) === rank).length;
|
|
178
|
+
if (count === 0) continue;
|
|
179
|
+
if (spans.length > 0) spans.push(span(" · ", "dim"));
|
|
180
|
+
spans.push(span(`${count} ${rank}${count === 1 ? "" : "s"}`, RANK_LOOK[rank].style));
|
|
181
|
+
}
|
|
155
182
|
return spans;
|
|
156
183
|
}
|
|
157
184
|
|
|
185
|
+
/** Native spans for one note: its advisor when the callout mixes several, then the text. */
|
|
186
|
+
function noteSpans(entry: AdvisorNote, attribute: boolean): TspSpan[] {
|
|
187
|
+
const who = attribute ? sourceOf(entry) : undefined;
|
|
188
|
+
const body = span(plainText(entry.note));
|
|
189
|
+
return who ? [span(`${who} · `, "dim"), body] : [body];
|
|
190
|
+
}
|
|
191
|
+
|
|
158
192
|
/** The advisor card: shared by the controlled expansion getter and native toggles. */
|
|
159
193
|
export interface AdvisorMessageCard extends Component {
|
|
160
194
|
/** Transcript-wide expansion (`Ctrl+O`); clears a toggle made in the terminal. */
|
|
@@ -168,6 +202,9 @@ export interface AdvisorMessageCard extends Component {
|
|
|
168
202
|
* output (whose `thinkingText` color equals `toolOutput` in most themes):
|
|
169
203
|
* a bold `customMessageLabel` header tag (skill-card convention), a heavy
|
|
170
204
|
* rail tinted per-note severity, and the note body on the default text color.
|
|
205
|
+
* Natively it is a callout tinted by its worst note: that severity's icon and
|
|
206
|
+
* the tally head it, the advisor's name ends the head, and with several notes
|
|
207
|
+
* each leads with its own severity icon.
|
|
171
208
|
*
|
|
172
209
|
* `getExpanded` owns expansion; a native toggle overrides it for this card
|
|
173
210
|
* until the next transcript-wide change.
|
|
@@ -193,24 +230,40 @@ export function createAdvisorMessageCard(
|
|
|
193
230
|
};
|
|
194
231
|
const nativeMemo = new Memo();
|
|
195
232
|
const describeCard = (isExpanded: boolean): NativeNode => {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
const look = RANK_LOOK[RANKS.find(rank => notes.some(entry => rankOf(entry) === rank)) ?? "note"];
|
|
234
|
+
const sources = new Set(notes.map(sourceOf));
|
|
235
|
+
const [sole] = sources;
|
|
236
|
+
const head = node(
|
|
237
|
+
"row",
|
|
238
|
+
{ gap: "sm", align: "center", role: "omp.advisor.head" },
|
|
239
|
+
[
|
|
240
|
+
node("icon", { name: look.icon, tone: look.tone }),
|
|
241
|
+
text(tallySpans(notes), { grow: 1 }),
|
|
242
|
+
text([span(sources.size === 1 && sole ? sole : "Advisor", "dim")], { role: "omp.advisor.source" }),
|
|
243
|
+
],
|
|
244
|
+
"head",
|
|
203
245
|
);
|
|
246
|
+
const attribute = sources.size > 1;
|
|
247
|
+
const body = notes.map((entry, index) => {
|
|
248
|
+
const spans = noteSpans(entry, attribute);
|
|
249
|
+
if (notes.length === 1) return text(spans, { wrap: "word", role: "omp.advisor.note", key: `n${index}` });
|
|
250
|
+
const { tone, icon, label } = RANK_LOOK[rankOf(entry)];
|
|
251
|
+
return node(
|
|
252
|
+
"row",
|
|
253
|
+
{ gap: "sm", align: "start", role: "omp.advisor.note", tone },
|
|
254
|
+
[node("icon", { name: icon, tone, aria: label }), text(spans, { wrap: "word", grow: 1, shrink: 1 })],
|
|
255
|
+
`n${index}`,
|
|
256
|
+
);
|
|
257
|
+
});
|
|
204
258
|
return card(
|
|
205
259
|
{
|
|
206
260
|
role: "omp.advisor",
|
|
207
|
-
tone:
|
|
208
|
-
head,
|
|
261
|
+
tone: look.tone,
|
|
209
262
|
collapsible: notes.length > COLLAPSED_NOTES,
|
|
210
263
|
collapsed: notes.length > COLLAPSED_NOTES ? !isExpanded : undefined,
|
|
211
264
|
preview: notes.length > COLLAPSED_NOTES ? "auto" : undefined,
|
|
212
265
|
},
|
|
213
|
-
body,
|
|
266
|
+
[head, ...body],
|
|
214
267
|
);
|
|
215
268
|
};
|
|
216
269
|
|
|
@@ -231,7 +284,7 @@ export function createAdvisorMessageCard(
|
|
|
231
284
|
},
|
|
232
285
|
describe(): NativeNode {
|
|
233
286
|
const isExpanded = expanded();
|
|
234
|
-
return nativeMemo.get([isExpanded
|
|
287
|
+
return nativeMemo.get([isExpanded], () => describeCard(isExpanded));
|
|
235
288
|
},
|
|
236
289
|
setExpanded(value: boolean): void {
|
|
237
290
|
lastGlobal = value;
|
package/src/chat/messages.ts
CHANGED
|
@@ -286,7 +286,10 @@ export function isUserTurnInitiator(message: CustomMessage): boolean {
|
|
|
286
286
|
);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
/** Advisor note severities, weakest first; ranks severity thresholds such as `advisor.continueSeverity`. */
|
|
290
|
+
export const ADVISOR_SEVERITIES = ["nit", "concern", "blocker"] as const;
|
|
291
|
+
|
|
292
|
+
export type AdvisorSeverity = (typeof ADVISOR_SEVERITIES)[number];
|
|
290
293
|
|
|
291
294
|
/** One queued advice note. */
|
|
292
295
|
export interface AdvisorNote {
|
|
@@ -761,7 +761,7 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
761
761
|
lines.slice(0, shown).join("\n"),
|
|
762
762
|
entry.codeStartLine ?? firstSelectorLine(split.sel) ?? 1,
|
|
763
763
|
entry.codeLineNumbers?.slice(0, shown),
|
|
764
|
-
|
|
764
|
+
split.path,
|
|
765
765
|
);
|
|
766
766
|
const hidden = lines.length - shown;
|
|
767
767
|
if (hidden > 0) {
|
|
@@ -836,7 +836,7 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
836
836
|
previewsMarkdown(entry)
|
|
837
837
|
? md(entry.contentText ?? "")
|
|
838
838
|
: code(entry.contentText ?? "", {
|
|
839
|
-
|
|
839
|
+
path: split.path,
|
|
840
840
|
start: entry.codeStartLine,
|
|
841
841
|
numbers: true,
|
|
842
842
|
wrap: true,
|