@oh-my-pi/pi-tui 18.11.0 → 18.11.1
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 +26 -37
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/components/image.d.ts +2 -0
- package/dist/types/components/markdown.d.ts +37 -0
- package/dist/types/native/blobs.d.ts +11 -11
- package/dist/types/overlays/job-tree.d.ts +50 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -11
- package/dist/types/prompt/image-references.d.ts +22 -0
- package/dist/types/render/hyperlink.d.ts +2 -0
- package/dist/types/render/utils.d.ts +1 -1
- package/dist/types/status-line/context-usage.d.ts +3 -0
- package/dist/types/status-line/startup.d.ts +6 -6
- package/dist/types/stdin-buffer.d.ts +7 -0
- package/dist/types/terminal.d.ts +7 -3
- package/dist/types/tools/eval.d.ts +5 -0
- package/dist/types/tools/grouped-file-output.d.ts +2 -0
- package/dist/types/tools/read.d.ts +5 -4
- package/dist/types/tui.d.ts +1 -0
- package/package.json +9 -9
- package/src/autocomplete.ts +14 -19
- package/src/bracketed-paste.ts +9 -2
- package/src/chat/assistant-message.ts +34 -9
- package/src/chat/bash-execution.ts +7 -2
- package/src/chat/image-loading.ts +4 -6
- package/src/chat/read-tool-group.ts +1 -0
- package/src/chat/tool-execution.ts +11 -4
- package/src/chat/user-message.ts +22 -15
- package/src/components/editor.ts +12 -12
- package/src/components/image.ts +23 -6
- package/src/components/markdown.ts +375 -6
- package/src/native/blobs.ts +22 -6
- package/src/native/reconcile.ts +19 -15
- package/src/overlays/job-tree.ts +177 -0
- package/src/overlays/jobs-panel.ts +107 -49
- package/src/prompt/composer.ts +22 -12
- package/src/prompt/image-references.ts +79 -2
- package/src/render/code-cell.ts +8 -1
- package/src/render/hyperlink.ts +5 -0
- package/src/render/utils.ts +1 -1
- package/src/render/width-aware-text.ts +6 -5
- package/src/status-line/component.ts +4 -0
- package/src/status-line/context-usage.ts +22 -16
- package/src/status-line/startup.ts +6 -6
- package/src/stdin-buffer.ts +11 -1
- package/src/terminal.ts +39 -18
- package/src/tools/ast-grep.ts +2 -2
- package/src/tools/edit.ts +16 -1
- package/src/tools/eval.ts +11 -4
- package/src/tools/grouped-file-output.ts +6 -1
- package/src/tools/read.ts +6 -5
- package/src/tools/write.ts +1 -1
- package/src/tui.ts +33 -23
- package/src/utils.ts +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.11.1] - 2026-10-15
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a tree layout to the background jobs sheet: subagents' jobs nest under the task job that runs them, busiest branches first, and Cancel appears only on the viewing agent's own jobs ([#15854](https://github.com/can1357/oh-my-pi/pull/15854) by [@H4vC](https://github.com/H4vC))
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed transcript images not opening their file: image placeholders and native image nodes now link to the source file or a stored copy ([#15872](https://github.com/can1357/oh-my-pi/pull/15872) by [@H4vC](https://github.com/H4vC))
|
|
14
|
+
- A collapsing live block no longer leaves its slack as a band of blank rows between the retired history and the live rows: the slack falls below the transcript, so a streamed reply stays contiguous and the input keeps the bottom row ([#15499](https://github.com/can1357/oh-my-pi/pull/15499) by [@iacore](https://github.com/iacore))
|
|
15
|
+
- Fixed width-aware text on native (TSP) terminals truncating content two columns early ([#15806](https://github.com/can1357/oh-my-pi/pull/15806) by [@yuzu-octopus](https://github.com/yuzu-octopus))
|
|
16
|
+
- Fixed Tab/right-arrow completion silently dismissing a selected `@"quoted path"` file suggestion when the path contains spaces ([#15698](https://github.com/can1357/oh-my-pi/pull/15698) by [@drawde-nadroj](https://github.com/drawde-nadroj))
|
|
17
|
+
- Bracketed pastes are now capped by their UTF-8 size, so a large multibyte paste no longer buffers past the byte limit ([#15803](https://github.com/can1357/oh-my-pi/pull/15803) by [@yuzu-octopus](https://github.com/yuzu-octopus))
|
|
18
|
+
- Fixed OSC 66 text-sized spans bypassing truncation when their UTF-16 length was short enough to fit the fast-path guard ([#15802](https://github.com/can1357/oh-my-pi/pull/15802) by [@yuzu-octopus](https://github.com/yuzu-octopus))
|
|
19
|
+
- Updated the `ast_grep` result view's truncated-match note to suggest `skip` pagination instead of the schema's nonexistent `increase limit` flag ([#15532](https://github.com/can1357/oh-my-pi/issues/15532), [#15543](https://github.com/can1357/oh-my-pi/pull/15543) by [@peda-cos](https://github.com/peda-cos)).
|
|
20
|
+
- Fixed a streaming Edit preview with long wrapped lines growing past its row cap, because its tail was budgeted without the diff gutter its continuation rows hang under ([#15679](https://github.com/can1357/oh-my-pi/pull/15679))
|
|
21
|
+
- Fixed malformed base64 padding being accepted for chat image attachments before native image decoding ([#15765](https://github.com/can1357/oh-my-pi/pull/15765) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy)).
|
|
22
|
+
- Fixed Shift+Enter submitting and Ctrl+Enter inserting a newline in Windows Terminal 1.25–1.27 after closing any fullscreen view such as `/settings`; the terminal dropped omp's keyboard mode on leaving the alternate screen ([microsoft/terminal#20763](https://github.com/microsoft/terminal/issues/20763)), so omp now re-applies it ([#15868](https://github.com/can1357/oh-my-pi/pull/15868) by [@H4vC](https://github.com/H4vC)).
|
|
23
|
+
- Long lines in native (Tern) `read`, `write`, and code-cell source now wrap instead of scrolling each block sideways ([#15823](https://github.com/can1357/oh-my-pi/issues/15823)).
|
|
24
|
+
- Fixed long text pastes taking seconds to land in terminals that deliver a paste one key at a time, such as JetBrains' terminal on Windows; omp no longer writes to the terminal for every pasted key ([#15757](https://github.com/can1357/oh-my-pi/issues/15757))
|
|
25
|
+
- Fixed Shift+Enter submitting the prompt and Ctrl+Enter inserting a newline in WSL under Windows Terminal 1.24 and earlier, conhost, and other ConPTY hosts without kitty keyboard support; Shift+Enter now inserts a newline and Ctrl+Enter sends a follow-up, as on native Windows.
|
|
26
|
+
- Fixed streamed Markdown tables jumping around as each row arrives: the terminal UI fills the row being written in under the existing column widths, and native (TSP) terminals receive each row once its line is complete ([#15856](https://github.com/can1357/oh-my-pi/pull/15856) by [@H4vC](https://github.com/H4vC)).
|
|
27
|
+
- Fixed streamed links and inline math showing raw `[text](url` or LaTeX and then rewrapping the paragraph when they close, in the terminal UI and native (TSP) terminals: they now render in their closed form while they stream ([#15864](https://github.com/can1357/oh-my-pi/pull/15864) by [@H4vC](https://github.com/H4vC)).
|
|
28
|
+
- Paint-only overlays no longer swallow typing intended for the focused input beneath them ([#15793](https://github.com/can1357/oh-my-pi/issues/15793)).
|
|
29
|
+
|
|
5
30
|
## [18.11.0] - 2026-10-14
|
|
6
31
|
|
|
7
32
|
### Added
|
|
@@ -2643,40 +2668,4 @@
|
|
|
2643
2668
|
- Added `cursorOverride` and `cursorOverrideWidth` properties to customize the end-of-text cursor glyph with ANSI-styled strings
|
|
2644
2669
|
- Added `getUseTerminalCursor()` method to query the terminal cursor mode setting
|
|
2645
2670
|
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
### Added
|
|
2649
|
-
|
|
2650
|
-
- Added `hint` property to autocomplete items to display dim ghost text after cursor when item is selected
|
|
2651
|
-
- Added `getInlineHint()` method to `SlashCommand` interface for providing inline hint text based on argument state
|
|
2652
|
-
- Added `getInlineHint()` method to `AutocompleteProvider` interface for displaying dim ghost text after cursor
|
|
2653
|
-
- Added `hintStyle` theme option to customize styling of inline hint/ghost text in editor
|
|
2654
|
-
|
|
2655
|
-
### Changed
|
|
2656
|
-
|
|
2657
|
-
- Updated editor to render inline hint text as dim ghost text after cursor when autocomplete suggestions are active or provider supplies hints
|
|
2658
|
-
|
|
2659
|
-
## [11.8.0] - 2026-02-10
|
|
2660
|
-
|
|
2661
|
-
### Added
|
|
2662
|
-
|
|
2663
|
-
- Added Alt+Y keybinding to cycle through kill ring entries (yank-pop)
|
|
2664
|
-
- Added undo support to Input component with Ctrl+Z keybinding
|
|
2665
|
-
- Added kill ring support to Input component for Emacs-style kill/yank operations
|
|
2666
|
-
- Added yank (Ctrl+Y) and yank-pop (Alt+Y) support to Input component
|
|
2667
|
-
|
|
2668
|
-
### Changed
|
|
2669
|
-
|
|
2670
|
-
- Changed Editor kill ring implementation to use dedicated KillRing class for better state management
|
|
2671
|
-
- Changed Editor undo stack to use generic UndoStack class with automatic state cloning
|
|
2672
|
-
- Changed kill/yank behavior to properly accumulate consecutive kill operations
|
|
2673
|
-
- Changed Input component deletion methods to record killed text in kill ring
|
|
2674
|
-
- Changed undo coalescing in Input component to group consecutive word typing into single undo units
|
|
2675
|
-
|
|
2676
|
-
## [11.4.1] - 2026-02-06
|
|
2677
|
-
|
|
2678
|
-
### Fixed
|
|
2679
|
-
|
|
2680
|
-
- Fixed terminal scrolling when displaying overlays after rendering large content, preventing hundreds of blank lines from being output
|
|
2681
|
-
|
|
2682
|
-
Older entries are archived in [packages/tui/CHANGELOG.md@a66558815743](https://github.com/can1357/oh-my-pi/blob/a66558815743a61164ceebceb2bb528989fb09b3/packages/tui/CHANGELOG.md).
|
|
2671
|
+
Older entries are archived in [packages/tui/CHANGELOG.md@090e359aa972](https://github.com/can1357/oh-my-pi/blob/090e359aa97267de88bd365054685c8234843cd7/packages/tui/CHANGELOG.md).
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function findLeadingSlashCommandStart(text: string): number | null;
|
|
8
8
|
export declare function findTrailingSlashCommandStart(text: string): number | null;
|
|
9
|
+
/** Extract the live whitespace-delimited or double-quoted `@` file token ending at the cursor. */
|
|
10
|
+
export declare function extractAtFilePrefix(text: string): string | null;
|
|
9
11
|
/**
|
|
10
12
|
* Whether an autocomplete value represents a directory: trailing slash or
|
|
11
13
|
* backslash, optionally followed by a closing quote for quoted paths.
|
|
@@ -8,6 +8,8 @@ export interface ImageOptions {
|
|
|
8
8
|
maxWidthCells?: number;
|
|
9
9
|
maxHeightCells?: number;
|
|
10
10
|
filename?: string;
|
|
11
|
+
/** File the image was read from; its link target. Images without one link to a blob copy of their bytes. */
|
|
12
|
+
sourcePath?: string;
|
|
11
13
|
/** Shared budget that caps how many inline images render as live graphics. */
|
|
12
14
|
budget?: ImageBudget;
|
|
13
15
|
/**
|
|
@@ -10,6 +10,43 @@ import type { Component } from "../tui.js";
|
|
|
10
10
|
* byte-identical to rendering `text`.
|
|
11
11
|
*/
|
|
12
12
|
export declare function streamingMarkdownSource(text: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Streaming `md` source for a native terminal, minus a still-growing table row:
|
|
15
|
+
* the unterminated last line when a GFM table (a run of pipe lines holding a
|
|
16
|
+
* header and its delimiter row, outer pipes optional) sits right above it,
|
|
17
|
+
* outside any open code fence. GFM continues a table on any non-blank line, so
|
|
18
|
+
* that line is a row whatever it holds so far. The terminal sizes columns by
|
|
19
|
+
* every row it gets, so a half-streamed row (an unclosed link shows its whole
|
|
20
|
+
* URL) would rewrap the table each frame, and TSP cannot ask it to skip one.
|
|
21
|
+
* The result keeps the newline before the held line, so it is a prefix of
|
|
22
|
+
* `text` and of the result for every append to it: streaming stays
|
|
23
|
+
* append-only. Pass untrimmed source: trimming first drops the newline that
|
|
24
|
+
* marks the last row finished, so that row would be held too.
|
|
25
|
+
*/
|
|
26
|
+
export declare function holdPartialTableRow(text: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* `text`, one inline run whose end is still streaming, with a trailing open
|
|
29
|
+
* link or math span closed the way the finished text most likely closes it,
|
|
30
|
+
* or `undefined` when nothing at its end is open. A link gains its missing
|
|
31
|
+
* `)` (one per open parenthesis in its destination), so it renders as the
|
|
32
|
+
* link it becomes instead of literal brackets and URL. A math body is cut back
|
|
33
|
+
* to its last valid form (a half-typed command, dangling `^`/`_`, or the first
|
|
34
|
+
* `$` of a `$$` closer dropped, open braces closed) and closed, so the
|
|
35
|
+
* rendered math grows in place instead of raw LaTeX turning into symbols at
|
|
36
|
+
* the closer. A `$` body needs a LaTeX character, so a price (`$5 per`) is
|
|
37
|
+
* never speculated. Only prose is: escaped openers and anything inside a code
|
|
38
|
+
* span, closed or still being written, stay literal.
|
|
39
|
+
*/
|
|
40
|
+
export declare function speculativeInlineClose(text: string): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Streaming `md` source for a native terminal with the open link or math span
|
|
43
|
+
* at its end speculatively closed ({@link speculativeInlineClose}), as the TUI
|
|
44
|
+
* renders it. Only the last block is read (a span cannot cross a blank line),
|
|
45
|
+
* and source inside an open code fence is left as it is. Each frame rewrites
|
|
46
|
+
* only the guessed closers at the end, which the reconciler sends as a small
|
|
47
|
+
* `splice`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function speculativeStreamingSource(text: string): string;
|
|
13
50
|
/** @internal exported for tests — the grown-line block-kind gate. */
|
|
14
51
|
export declare function fastLineStartHazard(grownLine: string): boolean;
|
|
15
52
|
/**
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content-addressed binary blobs (images) for `image` nodes.
|
|
3
|
-
*
|
|
4
|
-
* `describe()` registers the bytes and puts the returned id in `image.p.blob`;
|
|
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.
|
|
9
|
-
*/
|
|
10
1
|
import type { TspProps } from "@oh-my-pi/pi-wire";
|
|
11
2
|
import type { NativeChild, NativeNode } from "./node.js";
|
|
12
3
|
export interface NativeBlob {
|
|
@@ -26,10 +17,19 @@ export declare function nativeImageNode(bytes: Uint8Array, mime: string, p?: Omi
|
|
|
26
17
|
* so callers cache the node per payload ({@link NativeImageCache}).
|
|
27
18
|
*/
|
|
28
19
|
export declare function base64ImageNode(data: string, mimeType: string, p?: Omit<TspProps<"image">, "blob" | "builtin" | "w" | "h">, key?: string): NativeNode;
|
|
29
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Props that make an `image` node open the file at `filePath` (as a `file://` URL, the TSP
|
|
22
|
+
* open-target form): a click zooms the image in the terminal, and the file opens from its
|
|
23
|
+
* context menu. Empty without a file.
|
|
24
|
+
*/
|
|
25
|
+
export declare function openableImageProps(filePath: string | undefined): Pick<TspProps<"image">, "href" | "actions">;
|
|
26
|
+
/**
|
|
27
|
+
* `image` nodes for base64 payloads, one per slot key, rebuilt only when the slot's payload
|
|
28
|
+
* or the link policy changes. Each opens its file (`sourcePath`, else a blob copy).
|
|
29
|
+
*/
|
|
30
30
|
export declare class NativeImageCache {
|
|
31
31
|
#private;
|
|
32
|
-
get(key: string, data: string, mimeType: string): NativeNode;
|
|
32
|
+
get(key: string, data: string, mimeType: string, sourcePath?: string): NativeNode;
|
|
33
33
|
}
|
|
34
34
|
/** The disk file a tool result's image was read from (`read` details `mediaPath`), when it has one. */
|
|
35
35
|
export declare function readMediaPath(details: unknown): string | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Theme } from "../theme/theme.js";
|
|
2
|
+
/** What the tree reads off a job. */
|
|
3
|
+
export interface JobTreeJob {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly status: "running" | "completed" | "failed" | "cancelled";
|
|
6
|
+
readonly startTime: number;
|
|
7
|
+
/** Registry id of the agent that owns the job; absent or the root id: a top-level job. */
|
|
8
|
+
readonly ownerId?: string;
|
|
9
|
+
/** Registry id of the subagent a task job runs: the job heads that agent's subtree. */
|
|
10
|
+
readonly agentId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** A subagent in the tree, by registry id: an owner of listed jobs, or an ancestor linking one to the root. */
|
|
13
|
+
export interface JobTreeAgent {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly parentId?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Where a row sits: its depth and the rails drawn left of it. */
|
|
18
|
+
export interface JobTreePlace {
|
|
19
|
+
/** 0 for the top level. */
|
|
20
|
+
readonly depth: number;
|
|
21
|
+
/** Per enclosing level below the top, whether that level's rail continues past this row. */
|
|
22
|
+
readonly rails: readonly boolean[];
|
|
23
|
+
/** Whether this row is the last of its siblings. */
|
|
24
|
+
readonly last: boolean;
|
|
25
|
+
/** Whether rows nest under this one. */
|
|
26
|
+
readonly parent: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** One tree row: a job (a task job also heads its subagent), or a header for a subagent with no listed task job. */
|
|
29
|
+
export type JobTreeRow<J extends JobTreeJob> = (JobTreePlace & {
|
|
30
|
+
readonly kind: "job";
|
|
31
|
+
readonly job: J;
|
|
32
|
+
readonly agent?: JobTreeAgent;
|
|
33
|
+
}) | (JobTreePlace & {
|
|
34
|
+
readonly kind: "agent";
|
|
35
|
+
readonly agent: JobTreeAgent;
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* `jobs` as tree rows in display order. `rootId` is the viewing agent: its
|
|
39
|
+
* jobs, ownerless jobs and jobs of owners outside `agents` sit at the top
|
|
40
|
+
* level, as do agents whose parent is the root or unknown.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildJobTree<J extends JobTreeJob>(jobs: readonly J[], agents: readonly JobTreeAgent[], rootId: string | undefined): JobTreeRow<J>[];
|
|
43
|
+
/** The connector drawn before a row's head: nothing at the top level, else rails then a branch. */
|
|
44
|
+
export declare function jobTreeGuide(row: JobTreePlace, theme: Theme): string;
|
|
45
|
+
/**
|
|
46
|
+
* The prefix of lines under a row's head (its label, a wrapped command): the
|
|
47
|
+
* rails that continue past it, then the rail down to its children or blank,
|
|
48
|
+
* two columns wide so a top-level leaf's lines indent by two.
|
|
49
|
+
*/
|
|
50
|
+
export declare function jobTreeIndent(row: JobTreePlace, theme: Theme): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Component } from "../tui.js";
|
|
2
2
|
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
|
|
3
|
+
import { type JobTreeAgent } from "./job-tree.js";
|
|
3
4
|
/** One async job as the session snapshots it. */
|
|
4
5
|
export interface JobsPanelJob {
|
|
5
6
|
readonly id: string;
|
|
@@ -8,13 +9,19 @@ export interface JobsPanelJob {
|
|
|
8
9
|
readonly label: string;
|
|
9
10
|
readonly startTime: number;
|
|
10
11
|
readonly endTime?: number;
|
|
12
|
+
/** Registry id of the agent that owns the job. */
|
|
13
|
+
readonly ownerId?: string;
|
|
11
14
|
/** Registry id of the subagent a task job runs. */
|
|
12
15
|
readonly agentId?: string;
|
|
13
16
|
}
|
|
14
|
-
/** Running and recently settled jobs. */
|
|
17
|
+
/** Running and recently settled jobs of an agent and its subagents. */
|
|
15
18
|
export interface JobsPanelSnapshot {
|
|
16
19
|
readonly running: readonly JobsPanelJob[];
|
|
17
20
|
readonly recent: readonly JobsPanelJob[];
|
|
21
|
+
/** The agent the sheet is for: its jobs sit at the top level, and only they can be cancelled. */
|
|
22
|
+
readonly rootId?: string;
|
|
23
|
+
/** Subagents owning listed jobs, with the ancestors linking them to the root. */
|
|
24
|
+
readonly agents?: readonly JobTreeAgent[];
|
|
18
25
|
}
|
|
19
26
|
/** What the sheet shows about the selected job beyond its row. */
|
|
20
27
|
export interface JobsPanelDetail {
|
|
@@ -35,20 +42,26 @@ export interface JobsSheetSource {
|
|
|
35
42
|
load(): JobsPanelSnapshot;
|
|
36
43
|
/** The selected job's detail; runs once per frame, undefined once the job is gone. */
|
|
37
44
|
inspect(id: string): JobsPanelDetail | undefined;
|
|
38
|
-
/**
|
|
39
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Cancel a running job. Omitted by a read-only sheet (a focused subagent
|
|
47
|
+
* view, which only watches that agent): it lists the jobs and offers no way
|
|
48
|
+
* to stop them.
|
|
49
|
+
*/
|
|
50
|
+
cancel?(id: string): void;
|
|
40
51
|
/** Dismiss the sheet. */
|
|
41
52
|
close(): void;
|
|
42
53
|
}
|
|
43
54
|
/**
|
|
44
55
|
* The jobs pill's sheet: a centred `lg` glass sheet titled Background jobs.
|
|
45
|
-
* A master–detail split (it stacks in a narrow pane): the selectable job
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
56
|
+
* A master–detail split (it stacks in a narrow pane): the selectable job tree
|
|
57
|
+
* beside the selected job's detail — a status pill with live elapsed and
|
|
58
|
+
* Cancel job, its command or task, facts (agent, cwd, live pids, exit code,
|
|
59
|
+
* artifact) and a tail-following output pane. With no jobs it shows an empty
|
|
60
|
+
* state. It re-reads the session every frame, so the caller re-renders it on
|
|
61
|
+
* a timer while it is open. ↑/↓ select, X cancels the selected running job,
|
|
62
|
+
* Esc or Close dismisses; nothing enters the transcript. A source without
|
|
63
|
+
* `cancel` renders read-only, and a subagent's job never offers Cancel: only
|
|
64
|
+
* the root's own jobs can be stopped here.
|
|
52
65
|
*/
|
|
53
66
|
export declare class JobsSheet implements Component {
|
|
54
67
|
#private;
|
|
@@ -60,7 +73,7 @@ export declare class JobsSheet implements Component {
|
|
|
60
73
|
};
|
|
61
74
|
constructor(source: JobsSheetSource);
|
|
62
75
|
describe(_cx: DescribeContext): NativeNode;
|
|
63
|
-
/** Rows for a terminal without the native surface:
|
|
76
|
+
/** Rows for a terminal without the native surface: the job tree, then the selected job's facts and output tail. */
|
|
64
77
|
render(width: number): readonly string[];
|
|
65
78
|
invalidate(): void;
|
|
66
79
|
handleInput(data: string): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { TspSpan } from "@oh-my-pi/pi-wire";
|
|
2
3
|
/** Materialized image destination returned by the host blob writer. */
|
|
3
4
|
interface ImageBlobResult {
|
|
4
5
|
displayPath: string;
|
|
@@ -23,4 +24,25 @@ type ImageBlobWriterSync = (data: Buffer, options?: {
|
|
|
23
24
|
export declare function imageReferenceHyperlink(label: string, index: number, imageLinks: readonly (string | undefined)[] | undefined, renderLabel: (text: string) => string): string;
|
|
24
25
|
export declare function materializeImageReferenceLinks(images: readonly ImageContent[] | undefined, putBlob: ImageBlobWriter): Promise<(string | undefined)[] | undefined>;
|
|
25
26
|
export declare function materializeImageReferenceLinksSync(images: readonly ImageContent[] | undefined, putBlob: ImageBlobWriterSync): (string | undefined)[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Register the host's blob writer that backs links for images without a file of their
|
|
29
|
+
* own. Returns a disposer that unregisters it unless another writer replaced it since.
|
|
30
|
+
*/
|
|
31
|
+
export declare function setImageBlobWriter(next: ImageBlobWriterSync): () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Whether transcript images carry links under the current `tui.hyperlinks` policy: a
|
|
34
|
+
* native surface honours only an explicit `off`, text mode the resolved OSC 8 support.
|
|
35
|
+
* Renderers caching a linked image key their cache on it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function imageLinksEnabled(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The file a transcript image opens: `sourcePath` when it was read from one, else a
|
|
40
|
+
* blob copy of its bytes written through the host. Undefined when links are disabled
|
|
41
|
+
* or no file exists, so a disabled policy never writes blobs.
|
|
42
|
+
*/
|
|
43
|
+
export declare function transcriptImageLink(image: Pick<ImageContent, "data" | "mimeType">, sourcePath?: string): string | undefined;
|
|
44
|
+
/** `text` (an image's placeholder) as an OSC 8 link to the image's file; plain when hyperlinks are off or no file exists. */
|
|
45
|
+
export declare function linkedImageText(text: string, image: Pick<ImageContent, "data" | "mimeType">, sourcePath?: string): string;
|
|
46
|
+
/** Native counterpart of {@link linkedImageText}: a span linking to the image's file when one exists. */
|
|
47
|
+
export declare function linkedImageSpan(text: string, image: Pick<ImageContent, "data" | "mimeType">, sourcePath: string | undefined, s?: string): TspSpan;
|
|
26
48
|
export {};
|
|
@@ -70,6 +70,8 @@ export declare function urlHyperlinkAlways(url: string, displayText: string): st
|
|
|
70
70
|
* Non-HTTP(S) or unparsable URLs yield a plain span.
|
|
71
71
|
*/
|
|
72
72
|
export declare function urlLinkSpan(target: string, displayText: string, s?: string): TspSpan;
|
|
73
|
+
/** Whether native link spans and nodes may carry an `href`: everything but an explicit `off` policy. */
|
|
74
|
+
export declare function isNativeLinkEnabled(): boolean;
|
|
73
75
|
/**
|
|
74
76
|
* A native link span to a filesystem path as a plain `file://` URI (the
|
|
75
77
|
* terminal opens it through the system; the location stays in the visible
|
|
@@ -32,7 +32,7 @@ export interface RenderCache {
|
|
|
32
32
|
lines: string[];
|
|
33
33
|
}
|
|
34
34
|
/** Build indentation and continuing branches for ancestor levels. */
|
|
35
|
-
export declare function buildTreePrefix(ancestors: boolean[], theme: Theme): string;
|
|
35
|
+
export declare function buildTreePrefix(ancestors: readonly boolean[], theme: Theme): string;
|
|
36
36
|
/** Return the branch glyph for a final or continuing tree item. */
|
|
37
37
|
export declare function getTreeBranch(isLast: boolean, theme: Theme): string;
|
|
38
38
|
/** Return the continuation prefix for subsequent lines of a tree item. */
|
|
@@ -98,6 +98,7 @@ export interface CompactionBoundaries {
|
|
|
98
98
|
export declare function computeCompactionBoundaries(compactionSettings: CompactionSettings, contextWindow: number, speculationLeadTokens?: number): CompactionBoundaries | null;
|
|
99
99
|
/** Stable inputs used to cache non-message token estimates. */
|
|
100
100
|
export interface NonMessageTokenSource {
|
|
101
|
+
readonly model?: Model;
|
|
101
102
|
readonly systemPrompt?: readonly string[];
|
|
102
103
|
readonly agent?: {
|
|
103
104
|
readonly state?: {
|
|
@@ -137,6 +138,8 @@ export declare function invalidateToolSchemaMetadata(tools: ToolSchemaSource): v
|
|
|
137
138
|
* {@link invalidateToolSchemaMetadata} when those inputs change.
|
|
138
139
|
*/
|
|
139
140
|
export declare function estimateToolSchemaTokens(tools: ToolSchemaSource, tokenizer: Tokenizer, sourceRevision?: number): number;
|
|
141
|
+
/** Count schemas carried on every request, excluding server-side deferred catalogs. */
|
|
142
|
+
export declare function estimateContextToolSchemaTokens(tools: ToolSchemaSource, tokenizer: Tokenizer, model: Model | undefined, sourceRevision?: number): number;
|
|
140
143
|
export declare function computeNonMessageTokens(session: NonMessageTokenSource, tokenizer: Tokenizer, sourceRevision?: number): number;
|
|
141
144
|
/**
|
|
142
145
|
* Shared helper for the four non-message token totals used by
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Status line for the speculative first frame, before any session exists.
|
|
3
3
|
*
|
|
4
|
-
* InteractiveMode persists {@link StatusLineStartupData} (settings plus the
|
|
5
|
-
*
|
|
6
|
-
* real {@link StatusLineComponent} as a fresh, unnamed session at
|
|
7
|
-
* terminal width. Path and git branch are read live; context usage is
|
|
8
|
-
* unknown so the gauge shows the window without a percent.
|
|
4
|
+
* InteractiveMode persists {@link StatusLineStartupData} (settings plus the
|
|
5
|
+
* resolved default role model and status inputs); the next launch renders it
|
|
6
|
+
* through the real {@link StatusLineComponent} as a fresh, unnamed session at
|
|
7
|
+
* the live terminal width. Path and git branch are read live; context usage is
|
|
8
|
+
* reported unknown so the gauge shows the window without a percent.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts ignore
|
|
@@ -22,7 +22,7 @@ import type { StatusLineSettings } from "./types.js";
|
|
|
22
22
|
export interface StatusLineStartupData {
|
|
23
23
|
readonly settings: StatusLineSettings;
|
|
24
24
|
readonly gitEnabled: boolean;
|
|
25
|
-
/** Model
|
|
25
|
+
/** Model resolved for the next fresh session's default role. */
|
|
26
26
|
readonly model?: Model;
|
|
27
27
|
readonly thinkingLevel?: ThinkingLevel;
|
|
28
28
|
readonly autoThinking: boolean;
|
|
@@ -61,6 +61,13 @@ export type StdinBufferEventMap = {
|
|
|
61
61
|
export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
|
|
62
62
|
#private;
|
|
63
63
|
constructor(options?: StdinBufferOptions);
|
|
64
|
+
/**
|
|
65
|
+
* Whether a bracketed paste has started and its end marker has not arrived.
|
|
66
|
+
* Terminal skips its per-read DEC 2004 reassertion meanwhile: the host is
|
|
67
|
+
* evidently in bracketed-paste mode, and a paste delivered one key record
|
|
68
|
+
* per read would otherwise cost one terminal write per pasted key (#15757).
|
|
69
|
+
*/
|
|
70
|
+
get pasting(): boolean;
|
|
64
71
|
process(data: string | Buffer): void;
|
|
65
72
|
/**
|
|
66
73
|
* Gate unbracketed raw-paste classification on event-loop responsiveness.
|
package/dist/types/terminal.d.ts
CHANGED
|
@@ -181,6 +181,7 @@ export interface Terminal {
|
|
|
181
181
|
get kittyEnableSequence(): string | null;
|
|
182
182
|
readonly keyboardEnhancementEnterSequence?: string | null;
|
|
183
183
|
readonly keyboardEnhancementExitSequence?: string | null;
|
|
184
|
+
readonly keyboardEnhancementRestoreSequence?: string | null;
|
|
184
185
|
moveBy(lines: number): void;
|
|
185
186
|
hideCursor(force?: boolean): void;
|
|
186
187
|
showCursor(force?: boolean): void;
|
|
@@ -283,9 +284,11 @@ export interface ProcessTerminalOptions {
|
|
|
283
284
|
conpty?: boolean;
|
|
284
285
|
/**
|
|
285
286
|
* Whether stdin is a native Windows console handle (win32, not WSL), whose
|
|
286
|
-
* console host
|
|
287
|
-
*
|
|
288
|
-
*
|
|
287
|
+
* console host is certain to answer `CSI ? 9001 h` with win32-input-mode key
|
|
288
|
+
* records. When false (WSL), the keyboard fallback requests modifyOtherKeys
|
|
289
|
+
* alongside win32-input-mode, since a WSL pty may not be relayed by a console
|
|
290
|
+
* host. Defaults to `process.platform === "win32"`; only consulted when
|
|
291
|
+
* `conpty` is also true.
|
|
289
292
|
*/
|
|
290
293
|
nativeWindowsConsole?: boolean;
|
|
291
294
|
}
|
|
@@ -298,6 +301,7 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
298
301
|
get kittyProtocolActive(): boolean;
|
|
299
302
|
get kittyEnableSequence(): string | null;
|
|
300
303
|
get keyboardEnhancementEnterSequence(): string | null;
|
|
304
|
+
get keyboardEnhancementRestoreSequence(): string | null;
|
|
301
305
|
get keyboardEnhancementExitSequence(): string | null;
|
|
302
306
|
get appearance(): TerminalAppearance | undefined;
|
|
303
307
|
onAppearanceChange(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): void;
|
|
@@ -20,6 +20,11 @@ export interface EvalCellResult {
|
|
|
20
20
|
code: string;
|
|
21
21
|
language?: EvalLanguage;
|
|
22
22
|
output: string;
|
|
23
|
+
/**
|
|
24
|
+
* `output` without its `display[N]:` JSON sections, which renderers show
|
|
25
|
+
* from `jsonOutputs` instead. Absent when the cell displayed no JSON.
|
|
26
|
+
*/
|
|
27
|
+
textOutput?: string;
|
|
23
28
|
status: "pending" | "running" | "complete" | "error";
|
|
24
29
|
durationMs?: number;
|
|
25
30
|
exitCode?: number;
|
|
@@ -111,9 +111,11 @@ export interface NumberedLine {
|
|
|
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
113
|
* (`n: null`) sits. No path header: the row or head above names the file.
|
|
114
|
+
* `wrap` soft-wraps long lines instead of scrolling the block sideways.
|
|
114
115
|
*/
|
|
115
116
|
export declare function numberedCode(lines: readonly NumberedLine[], opts?: {
|
|
116
117
|
lang?: string;
|
|
118
|
+
wrap?: boolean;
|
|
117
119
|
}): NativeNode[];
|
|
118
120
|
/**
|
|
119
121
|
* Describe grouped search output (grep / ast-grep / ast-edit display content)
|
|
@@ -122,10 +122,11 @@ export declare function readSourceFsPath(details: ReadToolDetails | undefined):
|
|
|
122
122
|
*/
|
|
123
123
|
export declare function readResultIsMarkdown(details: ReadToolDetails | undefined, args: ReadRenderArgs | undefined): boolean;
|
|
124
124
|
/**
|
|
125
|
-
* Read content as numbered `code` (no path header: the head names
|
|
126
|
-
*
|
|
127
|
-
* `null` for an elided
|
|
128
|
-
* every line, else count
|
|
125
|
+
* Read content as numbered, wrapped `code` (no path header: the head names
|
|
126
|
+
* the file), matching the ANSI preview's hanging wrap. Numbers come from
|
|
127
|
+
* `lineNumbers` (`displayContent`'s per-line numbers, `null` for an elided
|
|
128
|
+
* block; runs split with `…` between) when they cover every line, else count
|
|
129
|
+
* up from `start`.
|
|
129
130
|
*/
|
|
130
131
|
export declare function readContentCode(content: string, start: number, numbers: readonly (number | null)[] | undefined, lang: string | undefined): NativeNode[];
|
|
131
132
|
/** Render file, image, and URL reads in the transcript. */
|
package/dist/types/tui.d.ts
CHANGED
|
@@ -348,6 +348,7 @@ export declare class TUI extends Container {
|
|
|
348
348
|
overlayStack: {
|
|
349
349
|
component: Component;
|
|
350
350
|
options?: OverlayOptions;
|
|
351
|
+
/** Focus restored after closing; tracks live focus changes beneath an input-less key holder. */
|
|
351
352
|
preFocus: Component | null;
|
|
352
353
|
hidden: boolean;
|
|
353
354
|
/**
|
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.11.
|
|
4
|
+
"version": "18.11.1",
|
|
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.11.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "18.11.
|
|
46
|
-
"@oh-my-pi/pi-ai": "18.11.
|
|
47
|
-
"@oh-my-pi/pi-catalog": "18.11.
|
|
48
|
-
"@oh-my-pi/pi-natives": "18.11.
|
|
49
|
-
"@oh-my-pi/pi-utils": "18.11.
|
|
50
|
-
"@oh-my-pi/pi-wire": "18.11.
|
|
51
|
-
"@oh-my-pi/snapcompact": "18.11.
|
|
44
|
+
"@oh-my-pi/omptype": "18.11.1",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "18.11.1",
|
|
46
|
+
"@oh-my-pi/pi-ai": "18.11.1",
|
|
47
|
+
"@oh-my-pi/pi-catalog": "18.11.1",
|
|
48
|
+
"@oh-my-pi/pi-natives": "18.11.1",
|
|
49
|
+
"@oh-my-pi/pi-utils": "18.11.1",
|
|
50
|
+
"@oh-my-pi/pi-wire": "18.11.1",
|
|
51
|
+
"@oh-my-pi/snapcompact": "18.11.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"kitty-vt-wasm": "^0.2.0"
|
package/src/autocomplete.ts
CHANGED
|
@@ -106,6 +106,18 @@ function extractQuotedPrefix(text: string): string | null {
|
|
|
106
106
|
return text.slice(quoteStart);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/** Extract the live whitespace-delimited or double-quoted `@` file token ending at the cursor. */
|
|
110
|
+
export function extractAtFilePrefix(text: string): string | null {
|
|
111
|
+
const quotedPrefix = extractQuotedPrefix(text);
|
|
112
|
+
if (quotedPrefix?.startsWith('@"')) {
|
|
113
|
+
return quotedPrefix;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const lastDelimiterIndex = findLastDelimiter(text);
|
|
117
|
+
const tokenStart = lastDelimiterIndex === -1 ? 0 : lastDelimiterIndex + 1;
|
|
118
|
+
return text[tokenStart] === "@" ? text.slice(tokenStart) : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
function parsePathPrefix(prefix: string): { rawPrefix: string; isAtPrefix: boolean; isQuotedPrefix: boolean } {
|
|
110
122
|
if (prefix.startsWith('@"')) {
|
|
111
123
|
return { rawPrefix: prefix.slice(2), isAtPrefix: true, isQuotedPrefix: true };
|
|
@@ -719,7 +731,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
719
731
|
}
|
|
720
732
|
|
|
721
733
|
// Check for @ file reference (fuzzy search) - must be after a delimiter or at start
|
|
722
|
-
const atPrefix =
|
|
734
|
+
const atPrefix = extractAtFilePrefix(textBeforeCursor);
|
|
723
735
|
if (atPrefix) {
|
|
724
736
|
const { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
725
737
|
// Recursive fuzzy walks rooted outside the project (e.g. `@../`,
|
|
@@ -862,7 +874,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
862
874
|
|
|
863
875
|
// Check if we're completing a file attachment (prefix starts with "@")
|
|
864
876
|
if (prefix.startsWith("@")) {
|
|
865
|
-
const liveAtPrefix =
|
|
877
|
+
const liveAtPrefix = extractAtFilePrefix(textBeforeCursor);
|
|
866
878
|
if (liveAtPrefix) {
|
|
867
879
|
beforePrefix = currentLine.slice(0, cursorCol - liveAtPrefix.length);
|
|
868
880
|
}
|
|
@@ -896,23 +908,6 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
896
908
|
};
|
|
897
909
|
}
|
|
898
910
|
|
|
899
|
-
// Extract @ prefix for fuzzy file suggestions
|
|
900
|
-
#extractAtPrefix(text: string): string | null {
|
|
901
|
-
const quotedPrefix = extractQuotedPrefix(text);
|
|
902
|
-
if (quotedPrefix?.startsWith('@"')) {
|
|
903
|
-
return quotedPrefix;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
const lastDelimiterIndex = findLastDelimiter(text);
|
|
907
|
-
const tokenStart = lastDelimiterIndex === -1 ? 0 : lastDelimiterIndex + 1;
|
|
908
|
-
|
|
909
|
-
if (text[tokenStart] === "@") {
|
|
910
|
-
return text.slice(tokenStart);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
return null;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
911
|
// Extract a path-like prefix from the text before cursor
|
|
917
912
|
#extractPathPrefix(text: string, forceExtract: boolean = false): string | null {
|
|
918
913
|
const quotedPrefix = extractQuotedPrefix(text);
|
package/src/bracketed-paste.ts
CHANGED
|
@@ -68,6 +68,7 @@ const DEFAULT_BYTE_LIMIT = 64 * 1024 * 1024;
|
|
|
68
68
|
*/
|
|
69
69
|
export class BracketedPasteHandler {
|
|
70
70
|
#buffer = "";
|
|
71
|
+
#bufferBytes = 0;
|
|
71
72
|
#active = false;
|
|
72
73
|
readonly #byteLimit: number;
|
|
73
74
|
|
|
@@ -88,7 +89,10 @@ export class BracketedPasteHandler {
|
|
|
88
89
|
const start = data.indexOf(PASTE_START);
|
|
89
90
|
const end = data.indexOf(PASTE_END);
|
|
90
91
|
if (start !== -1 && (end === -1 || start < end)) {
|
|
91
|
-
if (!this.#active)
|
|
92
|
+
if (!this.#active) {
|
|
93
|
+
this.#buffer = "";
|
|
94
|
+
this.#bufferBytes = 0;
|
|
95
|
+
}
|
|
92
96
|
this.#active = true;
|
|
93
97
|
// Only strip nested starts before this paste's first end. Later starts
|
|
94
98
|
// belong to the next paste in `remaining`.
|
|
@@ -100,6 +104,7 @@ export class BracketedPasteHandler {
|
|
|
100
104
|
|
|
101
105
|
if (!this.#active) return { handled: false };
|
|
102
106
|
|
|
107
|
+
this.#bufferBytes += Buffer.byteLength(data, "utf8");
|
|
103
108
|
this.#buffer += data;
|
|
104
109
|
|
|
105
110
|
const endIndex = this.#buffer.indexOf(PASTE_END);
|
|
@@ -108,6 +113,7 @@ export class BracketedPasteHandler {
|
|
|
108
113
|
const remaining = this.#buffer.substring(endIndex + PASTE_END.length);
|
|
109
114
|
|
|
110
115
|
this.#buffer = "";
|
|
116
|
+
this.#bufferBytes = 0;
|
|
111
117
|
this.#active = false;
|
|
112
118
|
|
|
113
119
|
return { handled: true, pasteContent, remaining };
|
|
@@ -118,9 +124,10 @@ export class BracketedPasteHandler {
|
|
|
118
124
|
// neither lost nor held forever, and reset paste mode so subsequent
|
|
119
125
|
// input recovers. See `StdinBuffer#abortPaste` for the sibling recovery
|
|
120
126
|
// semantics inside `StdinBuffer`.
|
|
121
|
-
if (this.#
|
|
127
|
+
if (this.#bufferBytes > this.#byteLimit) {
|
|
122
128
|
const pasteContent = this.#buffer;
|
|
123
129
|
this.#buffer = "";
|
|
130
|
+
this.#bufferBytes = 0;
|
|
124
131
|
this.#active = false;
|
|
125
132
|
return { handled: true, pasteContent, remaining: "" };
|
|
126
133
|
}
|