@oh-my-pi/pi-tui 18.6.1 → 18.6.3
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 +24 -0
- package/README.md +6 -2
- package/dist/types/app-keybindings.d.ts +6 -1
- package/dist/types/chat/assistant-message.d.ts +6 -1
- package/dist/types/chat/chat-transcript-builder.d.ts +1 -0
- package/dist/types/chat/user-message.d.ts +2 -2
- package/dist/types/chrome/status-notice.d.ts +15 -3
- package/dist/types/components/editor.d.ts +4 -0
- package/dist/types/components/image.d.ts +14 -0
- package/dist/types/components/input.d.ts +1 -0
- package/dist/types/components/loader.d.ts +5 -6
- package/dist/types/key-hint-format.d.ts +8 -0
- package/dist/types/native/backend.d.ts +2 -2
- package/dist/types/native/node.d.ts +6 -0
- package/dist/types/native/overlay.d.ts +3 -3
- package/dist/types/overlays/agent-hub.d.ts +1 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +1 -0
- package/dist/types/overlays/btw-history-panel.d.ts +5 -1
- package/dist/types/overlays/copy-selector.d.ts +1 -0
- package/dist/types/overlays/rewind-selector.d.ts +1 -0
- package/dist/types/overlays/usage-dashboard.d.ts +4 -4
- package/dist/types/prompt/custom-editor.d.ts +10 -4
- package/dist/types/prompt/interactive-context-helpers.d.ts +1 -0
- package/dist/types/space-hold.d.ts +40 -25
- package/dist/types/terminal-capabilities.d.ts +13 -0
- package/dist/types/tui.d.ts +5 -0
- package/package.json +9 -9
- package/src/app-keybindings.ts +6 -1
- package/src/apps/git/diff-pane.ts +42 -16
- package/src/apps/git/git-tui.ts +9 -9
- package/src/apps/git/sidebar.ts +6 -1
- package/src/chat/assistant-message.ts +26 -4
- package/src/chat/chat-transcript-builder.ts +4 -0
- package/src/chat/tool-execution.ts +6 -1
- package/src/chat/user-message.ts +16 -5
- package/src/chrome/status-notice.ts +25 -7
- package/src/chrome/transcript-container.ts +6 -3
- package/src/components/editor.ts +14 -0
- package/src/components/image.ts +68 -1
- package/src/components/input.ts +14 -6
- package/src/components/loader.ts +10 -17
- package/src/hotkeys-markdown.ts +9 -2
- package/src/key-hint-format.ts +25 -10
- package/src/native/backend.ts +10 -3
- package/src/native/node.ts +6 -0
- package/src/native/overlay.ts +4 -4
- package/src/overlays/agent-hub.ts +4 -0
- package/src/overlays/agent-transcript-viewer.ts +2 -0
- package/src/overlays/btw-history-panel.ts +9 -2
- package/src/overlays/copy-selector.ts +2 -0
- package/src/overlays/extensions/extension-dashboard.ts +4 -4
- package/src/overlays/pause-screen.ts +2 -2
- package/src/overlays/rewind-selector.ts +2 -0
- package/src/overlays/usage-dashboard.ts +17 -8
- package/src/overlays/usage-row.ts +22 -8
- package/src/prompt/custom-editor.ts +188 -67
- package/src/prompt/interactive-context-helpers.ts +2 -0
- package/src/prompt/queued-messages.ts +6 -4
- package/src/space-hold.ts +156 -52
- package/src/terminal-capabilities.ts +42 -23
- package/src/tui.ts +36 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.6.3] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- `WorkingRowSpec` no longer takes `rate`: the native working row reads spinner, elapsed time, divider, then the intent, and the tok/s readout moved to the composer bar
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `ComposerNativeState.rate` shows a tok/s readout in the native composer bar after the effort chip, and `ComposerNativeState.thinkingInModel` draws the thinking level as the model chip's icon instead of a separate chip
|
|
14
|
+
- `formatTooltipKey` formats a key for a native tooltip: unicode keycap glyphs whatever the symbol preset, `esc` for Escape
|
|
15
|
+
- `ImageOptions.requestRender` repaints an image without an image budget once its SIXEL encode lands ([#14529](https://github.com/can1357/oh-my-pi/pull/14529) by [@H4vC](https://github.com/H4vC))
|
|
16
|
+
- `AssistantMessageComponent` can keep finished thinking sections expanded via `setExpandThinkingBlocks()`; sections folded by hand stay folded ([#14519](https://github.com/can1357/oh-my-pi/pull/14519) by [@H4vC](https://github.com/H4vC))
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `/hotkeys` shows the effective `app.stt.pushToTalk` binding, including `Disabled` when unbound ([#6592](https://github.com/can1357/oh-my-pi/pull/6592) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy)).
|
|
21
|
+
- SIXEL images are encoded off the main thread, so showing an image no longer stalls the terminal ([#14529](https://github.com/can1357/oh-my-pi/pull/14529) by [@H4vC](https://github.com/H4vC))
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fixed native tooltips (composer, working row, queue, pause screen, git and extension dashboards) naming keys with Nerd Font icons Tern's UI font lacks, or as raw key ids (`ctrl+g`); they use keycap glyphs (`⇧⇥`, `⌃G`)
|
|
26
|
+
- Fixed Tern's per-turn usage row showing a 24-hour time on a 12-hour terminal clock; the row and its tooltip now follow the terminal's clock, keeping the tooltip's ISO date ([#14565](https://github.com/can1357/oh-my-pi/pull/14565) by [@wolfiesch](https://github.com/wolfiesch))
|
|
27
|
+
- Fixed hold-Space push-to-talk stopping the instant recording began (showing "No speech detected.") when opening the microphone briefly froze the UI ([#14463](https://github.com/can1357/oh-my-pi/pull/14463) by [@pgkt04](https://github.com/pgkt04))
|
|
28
|
+
|
|
5
29
|
## [18.6.1] - 2026-10-04
|
|
6
30
|
|
|
7
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -455,7 +455,7 @@ const spacer = new Spacer(2); // 2 empty lines (default: 1)
|
|
|
455
455
|
|
|
456
456
|
### Image
|
|
457
457
|
|
|
458
|
-
Renders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, and Warp on macOS/Linux)
|
|
458
|
+
Renders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, and Warp on macOS/Linux), iTerm2 inline images or SIXEL. Falls back to a text placeholder on unsupported terminals.
|
|
459
459
|
|
|
460
460
|
```typescript
|
|
461
461
|
interface ImageTheme {
|
|
@@ -466,17 +466,21 @@ interface ImageOptions {
|
|
|
466
466
|
maxWidthCells?: number;
|
|
467
467
|
maxHeightCells?: number;
|
|
468
468
|
filename?: string;
|
|
469
|
+
budget?: ImageBudget; // usually tui.imageBudget
|
|
470
|
+
requestRender?: () => void; // repaint once a SIXEL encode lands; defaults to the budget's
|
|
469
471
|
}
|
|
470
472
|
|
|
471
473
|
const image = new Image(
|
|
472
474
|
base64Data, // base64-encoded image data
|
|
473
475
|
"image/png", // MIME type
|
|
474
476
|
theme, // ImageTheme
|
|
475
|
-
options, // optional ImageOptions
|
|
477
|
+
{ ...options, budget: tui.imageBudget }, // optional ImageOptions
|
|
476
478
|
);
|
|
477
479
|
tui.addChild(image);
|
|
478
480
|
```
|
|
479
481
|
|
|
482
|
+
SIXEL images encode off the JavaScript thread: the first render reserves the image's rows and the image appears on the repaint `budget` or `requestRender` triggers once the encode lands. Without either, it waits for an unrelated repaint.
|
|
483
|
+
|
|
480
484
|
Supported formats: PNG, JPEG, GIF, WebP. Dimensions are parsed from the image headers automatically.
|
|
481
485
|
|
|
482
486
|
## Autocomplete
|
|
@@ -40,6 +40,7 @@ interface AppKeybindings {
|
|
|
40
40
|
"app.tree.unfoldOrDown": true;
|
|
41
41
|
"app.plan.toggle": true;
|
|
42
42
|
"app.history.search": true;
|
|
43
|
+
"app.stt.pushToTalk": true;
|
|
43
44
|
"app.stt.toggle": true;
|
|
44
45
|
"app.live.toggle": true;
|
|
45
46
|
}
|
|
@@ -329,9 +330,13 @@ export declare const KEYBINDINGS: {
|
|
|
329
330
|
readonly defaultKeys: "ctrl+r";
|
|
330
331
|
readonly description: "Search history";
|
|
331
332
|
};
|
|
333
|
+
readonly "app.stt.pushToTalk": {
|
|
334
|
+
readonly defaultKeys: "space";
|
|
335
|
+
readonly description: "Hold to record speech-to-text";
|
|
336
|
+
};
|
|
332
337
|
readonly "app.stt.toggle": {
|
|
333
338
|
readonly defaultKeys: [];
|
|
334
|
-
readonly description: "Toggle speech-to-text
|
|
339
|
+
readonly description: "Toggle speech-to-text recording";
|
|
335
340
|
};
|
|
336
341
|
readonly "app.live.toggle": {
|
|
337
342
|
readonly defaultKeys: "ctrl+l";
|
|
@@ -38,7 +38,7 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
38
38
|
* for lack of a target is re-rendered with its reaction stripped.
|
|
39
39
|
*/
|
|
40
40
|
pickReactionTarget(transcript: readonly Component[]): void;
|
|
41
|
-
constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string
|
|
41
|
+
constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string>, expandThinkingBlocks?: boolean);
|
|
42
42
|
/**
|
|
43
43
|
* Show or clear the trailing cache-invalidation divider. Set at `message_end`
|
|
44
44
|
* (live) or during rebuild, once the turn's usage is known and compared
|
|
@@ -65,6 +65,11 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
65
65
|
*/
|
|
66
66
|
setMidStreamPublication(allowed: boolean): void;
|
|
67
67
|
setProseOnlyThinking(proseOnly: boolean): void;
|
|
68
|
+
/**
|
|
69
|
+
* Keep finished thinking sections expanded instead of folding them to "Thought for 12s".
|
|
70
|
+
* Sections the user folded or unfolded by hand keep that choice.
|
|
71
|
+
*/
|
|
72
|
+
setExpandThinkingBlocks(expand: boolean): void;
|
|
68
73
|
dispose(): void;
|
|
69
74
|
/**
|
|
70
75
|
* Toggle suppression of the inline `Error: …` line while the same error is
|
|
@@ -25,6 +25,7 @@ export interface ChatTranscriptBuilderDeps {
|
|
|
25
25
|
cwd: string;
|
|
26
26
|
hideThinkingBlock?: () => boolean;
|
|
27
27
|
proseOnlyThinking?: () => boolean;
|
|
28
|
+
expandThinkingBlocks?: () => boolean;
|
|
28
29
|
/** Session-scoped resolved destinations for model-authored Markdown links. */
|
|
29
30
|
linkTargets?: ReadonlyMap<string, string>;
|
|
30
31
|
requestRender: () => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Component, Container } from "../tui.js";
|
|
2
2
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { ReactionTarget } from "./reaction.js";
|
|
4
|
-
import { type NativeNode, type NativeUiEvent } from "../native/node.js";
|
|
4
|
+
import { type DescribeContext, type NativeNode, type NativeUiEvent } from "../native/node.js";
|
|
5
5
|
/** How a user bubble styles its prose and chips (see {@link userBubbleColor}). */
|
|
6
6
|
export interface UserBubbleOptions {
|
|
7
7
|
/** Materialized `file://` targets per attached image, indexed by chip number. */
|
|
@@ -42,7 +42,7 @@ export declare class UserMessageComponent extends Container implements ReactionT
|
|
|
42
42
|
* agent's reaction are chips at the bottom-right, so a reaction landing
|
|
43
43
|
* later updates the frame in place, even deep in scrollback.
|
|
44
44
|
*/
|
|
45
|
-
describe(): NativeNode;
|
|
45
|
+
describe(cx?: DescribeContext): NativeNode;
|
|
46
46
|
/** Hover toolbar clicks: omp's own copy and rewind commands. */
|
|
47
47
|
handleNativeEvent(event: NativeUiEvent): void;
|
|
48
48
|
render(width: number): readonly string[];
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import type { NativeNode } from "../native/node.js";
|
|
2
2
|
import { Container } from "../tui.js";
|
|
3
|
+
/** How a {@link StatusNotice} shows. */
|
|
4
|
+
export interface StatusNoticeOptions {
|
|
5
|
+
/** Styles the ANSI line; resolved at render so a theme change re-shapes it. */
|
|
6
|
+
styleFn?: (text: string) => string;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a native terminal toasts it (default). False keeps the notice to
|
|
9
|
+
* the ANSI transcript: background chatter such as MCP connection progress,
|
|
10
|
+
* which would otherwise re-toast on every server that connects or fails.
|
|
11
|
+
*/
|
|
12
|
+
toast?: boolean;
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* A transient status notice (`showStatus`: "Thinking blocks: hidden", "Copied
|
|
5
16
|
* to clipboard"). The ANSI render appends a blank row and the dim line to the
|
|
6
17
|
* transcript; a native terminal shows it as a toast instead, so notices never
|
|
7
|
-
* pile up in the history. Updating the text re-shows the toast
|
|
18
|
+
* pile up in the history. Updating the text re-shows the toast; a notice
|
|
19
|
+
* with `toast: false` describes nothing natively.
|
|
8
20
|
*/
|
|
9
21
|
export declare class StatusNotice extends Container {
|
|
10
22
|
#private;
|
|
11
|
-
constructor(message: string,
|
|
12
|
-
setMessage(message: string,
|
|
23
|
+
constructor(message: string, options?: StatusNoticeOptions);
|
|
24
|
+
setMessage(message: string, options?: StatusNoticeOptions): void;
|
|
13
25
|
describe(): NativeNode;
|
|
14
26
|
}
|
|
@@ -330,6 +330,10 @@ export declare class Editor implements Component, Focusable {
|
|
|
330
330
|
* {@link getExpandedText}). Hosts that intercept large pastes through {@link onLargePaste} use
|
|
331
331
|
* this to re-insert a (possibly transformed) paste without re-triggering the interception hook. */
|
|
332
332
|
insertPaste(content: string): void;
|
|
333
|
+
/** Whether a character-jump hotkey is waiting for its target character. */
|
|
334
|
+
get isJumpPending(): boolean;
|
|
335
|
+
/** Type literal text through the character-editing pipeline without keybinding dispatch. */
|
|
336
|
+
typeCharacter(text: string): void;
|
|
333
337
|
/**
|
|
334
338
|
* Whether an autocomplete popup is on screen. An `@` list narrowed to no match
|
|
335
339
|
* while its refresh is pending stays open internally but is hidden, so it does
|
|
@@ -16,7 +16,19 @@ export interface ImageOptions {
|
|
|
16
16
|
* repaint replaces the placement instead of stacking a duplicate.
|
|
17
17
|
*/
|
|
18
18
|
imageKey?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Schedules a repaint once this image's off-thread SIXEL encode lands.
|
|
21
|
+
* Defaults to the budget's repaint; a host without a budget must pass one,
|
|
22
|
+
* or the image's reserved rows stay blank until an unrelated repaint.
|
|
23
|
+
*/
|
|
24
|
+
requestRender?: () => void;
|
|
19
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Run `render` for rows bound for native scrollback. Those rows are never
|
|
28
|
+
* repainted, so an image whose off-thread SIXEL encode has not landed encodes
|
|
29
|
+
* synchronously instead of committing its reserved blank rows.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderForScrollback<T>(render: () => T): T;
|
|
20
32
|
/** Default count of inline images kept as live graphics before older ones fall back to text. */
|
|
21
33
|
export declare const DEFAULT_MAX_INLINE_IMAGES = 8;
|
|
22
34
|
/**
|
|
@@ -52,6 +64,8 @@ export declare class ImageBudget {
|
|
|
52
64
|
get cap(): number;
|
|
53
65
|
get enabled(): boolean;
|
|
54
66
|
setRequestRender(requestRender: () => void): void;
|
|
67
|
+
/** Ask for a repaint, e.g. once an image's off-thread encode settles. */
|
|
68
|
+
requestRender(): void;
|
|
55
69
|
setCap(cap: number): void;
|
|
56
70
|
/**
|
|
57
71
|
* Stable graphics id for a logical image. A non-empty `key` maps to the same
|
|
@@ -28,6 +28,7 @@ export declare class Input implements Component, Focusable {
|
|
|
28
28
|
setValue(value: string): void;
|
|
29
29
|
setUseTerminalCursor(useTerminalCursor: boolean): void;
|
|
30
30
|
getUseTerminalCursor(): boolean;
|
|
31
|
+
capturesInput(data: string): boolean;
|
|
31
32
|
/**
|
|
32
33
|
* Apply one key: the editor's text bindings (motion, deletion, kill ring,
|
|
33
34
|
* undo), pastes and printable text. Returns whether the key was the
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { KeyId } from "../keys.js";
|
|
1
2
|
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
|
|
2
3
|
import { type ShimmerPalette } from "../theme/shimmer.js";
|
|
3
4
|
import type { TUI } from "../tui.js";
|
|
@@ -23,16 +24,14 @@ export interface WorkingRowSpec {
|
|
|
23
24
|
readonly variant?: WorkingRowVariant;
|
|
24
25
|
/** Shimmer palette of the label (a session-accented intent). */
|
|
25
26
|
readonly palette?: ShimmerPalette;
|
|
26
|
-
/** Live tok/s, docked before the stop control. */
|
|
27
|
-
readonly rate?: number;
|
|
28
27
|
/** Key id that interrupts (`escape`); undefined hides the stop control (Esc would not cancel). */
|
|
29
|
-
readonly interruptKey?:
|
|
28
|
+
readonly interruptKey?: KeyId;
|
|
30
29
|
}
|
|
31
30
|
/**
|
|
32
31
|
* The dock's working row: `starburst` spinner (or a retry countdown ring),
|
|
33
|
-
* the
|
|
34
|
-
* control whose click sends `interrupt`. Compaction adds an
|
|
35
|
-
* `progress` before the stop control; a retry says "Cancel".
|
|
32
|
+
* the elapsed time, the `·` divider, the intent shimmering, a grow spacer,
|
|
33
|
+
* then the stop control whose click sends `interrupt`. Compaction adds an
|
|
34
|
+
* indeterminate `progress` before the stop control; a retry says "Cancel".
|
|
36
35
|
*/
|
|
37
36
|
export declare function describeWorkingRow(spec: WorkingRowSpec, cx: DescribeContext, now?: number): NativeNode;
|
|
38
37
|
type ColorFn = (str: string) => string;
|
|
@@ -23,6 +23,14 @@ export type KeyName = KeyId | ModifierName;
|
|
|
23
23
|
* is capitalized. Other keys are capitalized (`f5` → `F5`).
|
|
24
24
|
*/
|
|
25
25
|
export declare function formatKeyHint(key: KeyName): string;
|
|
26
|
+
/**
|
|
27
|
+
* Format one key for a native tooltip (a TSP `title`): unicode keycap glyphs
|
|
28
|
+
* whatever the theme's symbol preset (`shift+tab` → `⇧⇥`), which Tern's
|
|
29
|
+
* tooltip splits into keycaps, and `esc` as Tern's own keycaps read. The nerd
|
|
30
|
+
* preset's icons are missing from Tern's UI font, and its spaced chords would
|
|
31
|
+
* not parse as keys.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatTooltipKey(key: KeyName): string;
|
|
26
34
|
/** Format alternative keys as a slash-separated hint (`["f5", "alt+r"]` → `F5/⌥R`). */
|
|
27
35
|
export declare function formatKeyHints(keys: KeyName | readonly KeyName[]): string;
|
|
28
36
|
/** Format a double-tap gesture: `←←` with glyphs, `Left Left` with words. */
|
|
@@ -83,8 +83,8 @@ export declare class NativeBackend {
|
|
|
83
83
|
* The terminal's real `hello` reply after an optimistic start: adopt its
|
|
84
84
|
* APC limit, credits, cell size, kinds, appearance and motion preference.
|
|
85
85
|
* A width the terminal already reported in a `resize` event wins over the
|
|
86
|
-
* reply's. A different vocabulary
|
|
87
|
-
* component, so kinds the terminal lacks fall back.
|
|
86
|
+
* reply's. A different vocabulary, motion preference or clock re-describes
|
|
87
|
+
* every component, so kinds the terminal lacks fall back.
|
|
88
88
|
*/
|
|
89
89
|
confirm(hello: TspHello): void;
|
|
90
90
|
/** SIGWINCH: refresh the width used for `rows` fallback until the terminal reports one. */
|
|
@@ -71,6 +71,12 @@ export interface DescribeContext {
|
|
|
71
71
|
readonly reduceMotion: boolean;
|
|
72
72
|
/** The terminal's appearance is dark. */
|
|
73
73
|
readonly dark: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* The user's system reads a 12-hour clock (`false`: 24-hour); `undefined`
|
|
76
|
+
* when the terminal doesn't say. Pass it as `hour12` when formatting times:
|
|
77
|
+
* the process's own default locale may not know.
|
|
78
|
+
*/
|
|
79
|
+
readonly hour12?: boolean;
|
|
74
80
|
/** Whether the terminal renders `kind` natively (else describe something simpler or let it fall back). */
|
|
75
81
|
supports(kind: TspKind): boolean;
|
|
76
82
|
/**
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* component's root is a `card` whose `head` carries the title.
|
|
8
8
|
*/
|
|
9
9
|
import type { TspProps, TspSpan, TspText } from "@oh-my-pi/pi-wire";
|
|
10
|
-
import type
|
|
10
|
+
import { type KeyName } from "../key-hint-format.js";
|
|
11
11
|
import { type Keybinding } from "../keybindings.js";
|
|
12
12
|
import type { NativeChild, NativeNode } from "./node.js";
|
|
13
13
|
/** One footer hint: the keys that trigger it and what it does. */
|
|
@@ -44,10 +44,10 @@ export interface ActionButtonOptions {
|
|
|
44
44
|
/** Sibling identity key (defaults to the action id); `leafKey(event.key)` reports it back. */
|
|
45
45
|
readonly key?: string;
|
|
46
46
|
/** Key id of the keyboard path the button mirrors, drawn as a keycap inside it (`"r"`, `"ctrl+g"`). */
|
|
47
|
-
readonly keys?:
|
|
47
|
+
readonly keys?: KeyName;
|
|
48
48
|
/** `accent` = the view's hero action, `error` = destructive. */
|
|
49
49
|
readonly tone?: "accent" | "error";
|
|
50
|
-
/** Tooltip; defaults to the label plus the keycap (`"Refresh r"
|
|
50
|
+
/** Tooltip; defaults to the label plus the keycap (`"Refresh r"`, see {@link formatTooltipKey}). */
|
|
51
51
|
readonly title?: string;
|
|
52
52
|
/** Target of an `open` action (URL or `file://` path). */
|
|
53
53
|
readonly href?: string;
|
|
@@ -73,6 +73,7 @@ export interface AgentHubDeps<TRecord extends AgentRecordLike = AgentRecordLike>
|
|
|
73
73
|
/** Mirrors the main transcript's thinking-block visibility. */
|
|
74
74
|
hideThinkingBlock?: () => boolean;
|
|
75
75
|
proseOnlyThinking?: () => boolean;
|
|
76
|
+
expandThinkingBlocks?: () => boolean;
|
|
76
77
|
/** Keys toggling tool output expansion (app.tools.expand). */
|
|
77
78
|
expandKeys?: KeyId[];
|
|
78
79
|
/** Focus the main view on this agent's live session (ctx.focusAgentSession). When absent (collab guest, tests), Enter opens the in-hub chat view instead. */
|
|
@@ -54,6 +54,7 @@ export interface AgentTranscriptViewerDeps {
|
|
|
54
54
|
cwd: string;
|
|
55
55
|
hideThinkingBlock?: () => boolean;
|
|
56
56
|
proseOnlyThinking?: () => boolean;
|
|
57
|
+
expandThinkingBlocks?: () => boolean;
|
|
57
58
|
expandKeys: KeyId[];
|
|
58
59
|
/** Keys that toggle the whole hub closed (app.agents.hub + app.session.observe). */
|
|
59
60
|
hubKeys: KeyId[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Component, type Focusable, Input } from "../index.js";
|
|
2
2
|
import { type BtwHistoryRecord } from "./btw-history.js";
|
|
3
3
|
import type { SpaceHoldHandler } from "../space-hold.js";
|
|
4
|
+
import { type KeyId } from "../app-keybindings.js";
|
|
4
5
|
import type { NativeNode, NativeUiEvent } from "../native/node.js";
|
|
5
6
|
interface BtwHistoryPanelOptions {
|
|
6
7
|
records: readonly BtwHistoryRecord[];
|
|
@@ -9,7 +10,8 @@ interface BtwHistoryPanelOptions {
|
|
|
9
10
|
onCancel: (record: BtwHistoryRecord) => void;
|
|
10
11
|
canFollowUp?: (record: BtwHistoryRecord) => boolean;
|
|
11
12
|
onFollowUp?: (record: BtwHistoryRecord, question: string, signal: AbortSignal) => Promise<boolean>;
|
|
12
|
-
/**
|
|
13
|
+
/** Configured push-to-talk keys for a follow-up composer. */
|
|
14
|
+
spaceHoldKeys?: readonly KeyId[];
|
|
13
15
|
spaceHold?: (input: Input) => SpaceHoldHandler;
|
|
14
16
|
requestRender: () => void;
|
|
15
17
|
getHeight: () => number;
|
|
@@ -33,6 +35,8 @@ export declare class BtwHistoryPanel implements Component, Focusable {
|
|
|
33
35
|
openFollowUp(recordId: string): boolean;
|
|
34
36
|
/** Select a record with its answer pane focused, read from the top (a just-asked /btw). */
|
|
35
37
|
showRecord(recordId: string): void;
|
|
38
|
+
/** Only an active follow-up composer can claim its configured push-to-talk keys. */
|
|
39
|
+
capturesInput(data: string): boolean;
|
|
36
40
|
/** Enhanced clipboard pastes belong only to the active composer. */
|
|
37
41
|
pasteText(text: string): void;
|
|
38
42
|
handleInput(data: string): void;
|
|
@@ -31,6 +31,7 @@ export interface CopySelectorDeps {
|
|
|
31
31
|
cwd: string;
|
|
32
32
|
hideThinkingBlock?: () => boolean;
|
|
33
33
|
proseOnlyThinking?: () => boolean;
|
|
34
|
+
expandThinkingBlocks?: () => boolean;
|
|
34
35
|
linkTargets?: ReadonlyMap<string, string>;
|
|
35
36
|
requestRender: () => void;
|
|
36
37
|
/** Replaces the "Copy" header when the picker is reused for another purpose. */
|
|
@@ -48,6 +48,7 @@ export interface RewindSelectorDeps {
|
|
|
48
48
|
cwd: string;
|
|
49
49
|
hideThinkingBlock?: () => boolean;
|
|
50
50
|
proseOnlyThinking?: () => boolean;
|
|
51
|
+
expandThinkingBlocks?: () => boolean;
|
|
51
52
|
linkTargets?: ReadonlyMap<string, string>;
|
|
52
53
|
requestRender: () => void;
|
|
53
54
|
/** Sibling branch paths of `entryId`'s turn (excluding the turn itself). */
|
|
@@ -121,12 +121,12 @@ export declare class UsageDashboardComponent implements Component {
|
|
|
121
121
|
* The sheet body (the terminal's `lg` overlay is the frame): a head row
|
|
122
122
|
* (checked ago, Overview/Details tabs, Refresh), then either the provider
|
|
123
123
|
* grid of frames with window meters and the activity heatmap, or the
|
|
124
|
-
* per-provider detail tables
|
|
125
|
-
* `progress`/`table` on terminals without
|
|
126
|
-
* inputs change.
|
|
124
|
+
* per-provider detail tables, then Close like the other report sheets.
|
|
125
|
+
* `meter`/`chart` fall back to `progress`/`table` on terminals without
|
|
126
|
+
* them. Rebuilt only when its inputs change.
|
|
127
127
|
*/
|
|
128
128
|
describe(cx: DescribeContext): NativeNode;
|
|
129
|
-
/** Tab clicks switch views like Enter/Esc; the Refresh button runs `r
|
|
129
|
+
/** Tab clicks switch views like Enter/Esc; the Refresh button runs `r`; Close closes from either view. */
|
|
130
130
|
handleNativeEvent(event: NativeUiEvent): void;
|
|
131
131
|
handleInput(data: string): void;
|
|
132
132
|
}
|
|
@@ -18,6 +18,10 @@ export interface ComposerNativeState {
|
|
|
18
18
|
};
|
|
19
19
|
/** Thinking effort word (`high`, `off`, `auto`); undefined when the model has no thinking. */
|
|
20
20
|
readonly thinking?: string;
|
|
21
|
+
/** The thinking level draws as the model chip's icon instead of its own chip (where the terminal has `effort`). */
|
|
22
|
+
readonly thinkingInModel?: boolean;
|
|
23
|
+
/** Generation tok/s after the thinking level (live while running, else the last reading); undefined hides it. */
|
|
24
|
+
readonly rate?: number;
|
|
21
25
|
/** A turn is running: the send keycap becomes a Stop button. */
|
|
22
26
|
readonly running: boolean;
|
|
23
27
|
/** The viewed subagent's lineage, outermost first and the viewed agent last; undefined on the main session. */
|
|
@@ -229,7 +233,8 @@ export declare class CustomEditor extends Editor {
|
|
|
229
233
|
* The TSP composer: role `omp.editor[.bash|.python]` (tone `pending` while
|
|
230
234
|
* a turn runs) over the context hairline, the viewing header while a
|
|
231
235
|
* subagent is focused, the attachment chips, a `line` row of the
|
|
232
|
-
* shell-mode chip and the input, and the `bar`: model chip, effort chip
|
|
236
|
+
* shell-mode chip and the input, and the `bar`: model chip, effort chip
|
|
237
|
+
* (or the effort glyph as the model chip's icon), the generation rate,
|
|
233
238
|
* the other status facts, usage, then send (Stop while a turn runs).
|
|
234
239
|
* Clicks come back as `status.model`, `thinking.cycle`, `submit`,
|
|
235
240
|
* `interrupt` and `focus:<id>` actions.
|
|
@@ -271,9 +276,9 @@ export declare class CustomEditor extends Editor {
|
|
|
271
276
|
onLeftAtStart?: () => void;
|
|
272
277
|
/** Called when the viewing header asks to view agent `id` ({@link MAIN_AGENT_ID}: the main session). */
|
|
273
278
|
onFocusAgent?: (id: string) => void;
|
|
274
|
-
/**
|
|
275
|
-
* stays out of Vim's Normal/Visual modes (where the space bar is the `l` motion)
|
|
276
|
-
* open autocomplete menu. */
|
|
279
|
+
/** Configurable push-to-talk; set its `handler` to enable it. It is a text-composition gesture, so
|
|
280
|
+
* it stays out of Vim's Normal/Visual modes (where the space bar is the `l` motion), away from an
|
|
281
|
+
* open autocomplete menu, and out of a pending character jump (whose target may be the key). */
|
|
277
282
|
readonly spaceHold: SpaceHoldGesture;
|
|
278
283
|
setActionKeys(action: ConfigurableEditorAction, keys: KeyId[]): void;
|
|
279
284
|
/**
|
|
@@ -288,6 +293,7 @@ export declare class CustomEditor extends Editor {
|
|
|
288
293
|
* Clear all custom key handlers.
|
|
289
294
|
*/
|
|
290
295
|
clearCustomKeyHandlers(): void;
|
|
296
|
+
capturesInput(data: string): boolean;
|
|
291
297
|
handleInput(data: string): void;
|
|
292
298
|
/**
|
|
293
299
|
* Route a keystroke through the base text-editor pipeline only, skipping the
|
|
@@ -15,6 +15,7 @@ export interface AssistantMessageHost {
|
|
|
15
15
|
readonly viewSession: AssistantMessageSession;
|
|
16
16
|
readonly effectiveHideThinkingBlock: boolean;
|
|
17
17
|
readonly proseOnlyThinking: boolean;
|
|
18
|
+
readonly expandThinkingBlocks: boolean;
|
|
18
19
|
readonly assistantImagesVisible: boolean;
|
|
19
20
|
readonly hideToolActivity: boolean;
|
|
20
21
|
readonly toolOutputExpanded: boolean;
|
|
@@ -1,45 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { type KeyId } from "./keys.js";
|
|
2
|
+
/** Printable text a push-to-talk candidate key types (plain or shifted single characters and Space),
|
|
3
|
+
* or undefined for non-printable keys and chords, which the gesture reserves instead of typing. */
|
|
4
|
+
export declare function getSpaceHoldText(data: string, canonical: string | undefined): string | undefined;
|
|
5
|
+
/** Max gap (ms) between two configured-key presses for a later one to count as OS key auto-repeat
|
|
6
|
+
* rather than a deliberate press. OS auto-repeat is fast; a deliberate tap (even a fast one) is slower. */
|
|
3
7
|
export declare const SPACE_REPEAT_MAX_GAP_MS = 120;
|
|
4
|
-
/** Two consecutive inter-
|
|
8
|
+
/** Two consecutive inter-key gaps are "mechanical" (machine-driven auto-repeat) when both are
|
|
5
9
|
* within {@link SPACE_REPEAT_MAX_GAP_MS} and differ by no more than this — an absolute jitter floor
|
|
6
10
|
* or, for slower repeat rates, {@link SPACE_REPEAT_JITTER_RATIO} of the smaller gap. OS key-repeat
|
|
7
|
-
* is metronomic;
|
|
8
|
-
* steady. */
|
|
11
|
+
* is metronomic; human typing is irregular, so its deltas do not stay this steady. */
|
|
9
12
|
export declare const SPACE_REPEAT_JITTER_MS = 18;
|
|
10
13
|
export declare const SPACE_REPEAT_JITTER_RATIO = 0.35;
|
|
11
|
-
/** Consecutive mechanical (fast + steady) deltas that confirm
|
|
12
|
-
* recording. Needs a sustained metronomic cadence, so jittery
|
|
14
|
+
/** Consecutive mechanical (fast + steady) deltas that confirm a configured key is held and start
|
|
15
|
+
* recording. Needs a sustained metronomic cadence, so jittery typing and deliberate taps never
|
|
13
16
|
* reach it. */
|
|
14
17
|
export declare const SPACE_HOLD_MECHANICAL_RUN = 2;
|
|
15
|
-
/** Idle gap (ms) after the last repeated
|
|
16
|
-
*
|
|
18
|
+
/** Idle gap (ms) after the last repeated configured-key press that counts as release and ends
|
|
19
|
+
* push-to-talk. Must comfortably exceed the OS key-repeat interval. */
|
|
17
20
|
export declare const SPACE_HOLD_RELEASE_MS = 250;
|
|
18
|
-
/**
|
|
21
|
+
/** A release timer firing more than this (ms) past its deadline means the event loop stalled (a
|
|
22
|
+
* synchronous microphone open, GC, a heavy render) while the bar may still be held: the repeats
|
|
23
|
+
* emitted during the stall are queued but unread, so the stall must not read as a release. */
|
|
24
|
+
export declare const SPACE_HOLD_STALL_SLACK_MS = 100;
|
|
25
|
+
/** What a recognized push-to-talk hold drives — the start and stop callbacks. */
|
|
19
26
|
export interface SpaceHoldHandler {
|
|
20
|
-
/** Gate for the gesture. Returns false to
|
|
21
|
-
* disabling push-to-talk restores plain space behavior. */
|
|
27
|
+
/** Gate for the gesture. Returns false to let matching keys behave normally. */
|
|
22
28
|
enabled(): boolean;
|
|
23
|
-
/** A sustained hold was recognized
|
|
29
|
+
/** A sustained hold was recognized; any safely typed candidate text has already been deleted. */
|
|
24
30
|
onStart(): void;
|
|
25
|
-
/** The held
|
|
31
|
+
/** The held key was released (an idle gap with no further repeats, or any other key). */
|
|
26
32
|
onEnd(): void;
|
|
27
33
|
}
|
|
28
34
|
/** What the host must do with a keypress after {@link SpaceHoldGesture.process} has seen it:
|
|
29
|
-
* `"pass"` — handle it normally; `"type"` — insert
|
|
30
|
-
* turns into a hold); `"swallow"` — drop it, the gesture consumed it. */
|
|
35
|
+
* `"pass"` — handle it normally; `"type"` — insert its safely decoded printable text now (it is
|
|
36
|
+
* tracked back out if the run turns into a hold); `"swallow"` — drop it, the gesture consumed it. */
|
|
31
37
|
export type SpaceHoldStep = "pass" | "type" | "swallow";
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* taps are too slow, so neither escalates and both keep typing real spaces; the few spaces typed
|
|
37
|
-
* before a real hold is recognized are tracked back out of the host input. */
|
|
38
|
+
/** Push-to-talk key-hold state machine. A held key emits OS auto-repeat at a steady fast interval.
|
|
39
|
+
* We watch inter-key deltas and only recognize a hold once {@link SPACE_HOLD_MECHANICAL_RUN}
|
|
40
|
+
* consecutive deltas are mechanical (see {@link gapsAreMechanical}). Deliberate taps and jittery
|
|
41
|
+
* runs stay typed; only text typed during the current steady run is removed on recognition. */
|
|
38
42
|
export declare class SpaceHoldGesture {
|
|
39
43
|
#private;
|
|
40
|
-
/**
|
|
44
|
+
/** Configured alternative keys. Alternatives are tracked independently and never combine into one run. */
|
|
45
|
+
get keys(): readonly KeyId[];
|
|
46
|
+
set keys(keys: readonly KeyId[]);
|
|
47
|
+
/** Unset (the default) keeps configured keys behaving normally. */
|
|
41
48
|
handler: SpaceHoldHandler | undefined;
|
|
42
49
|
constructor(deleteTyped: (count: number) => void, allowed?: () => boolean);
|
|
43
|
-
/**
|
|
44
|
-
|
|
50
|
+
/** Pure focused-owner preflight on raw input: whether the key must reach the host before
|
|
51
|
+
* TUI-wide input listeners. Only non-printable bindings are claimed — they would otherwise
|
|
52
|
+
* trigger app shortcuts mid-hold; printable candidates keep the normal listener path so
|
|
53
|
+
* raw-input observers (e.g. extensions) still see typed text, as they did before remapping. */
|
|
54
|
+
shouldRoute(data: string): boolean;
|
|
55
|
+
/** Feed one canonical keypress and, when safely printable, its decoded text length. The host
|
|
56
|
+
* records the actual inserted length with {@link recordTyped} after applying normal typing. */
|
|
57
|
+
process(key: string | undefined, insertedCharacterLength?: number): SpaceHoldStep;
|
|
58
|
+
/** Account for text the host actually inserted for the preceding `"type"` step. */
|
|
59
|
+
recordTyped(insertedCharacterLength: number): void;
|
|
45
60
|
}
|
|
@@ -284,6 +284,13 @@ export interface ImageRenderOptions {
|
|
|
284
284
|
placementId?: number;
|
|
285
285
|
/** When true (Kitty + {@link imageId}), also return the one-time transmit sequence. */
|
|
286
286
|
includeTransmit?: boolean;
|
|
287
|
+
/**
|
|
288
|
+
* SIXEL sequence for the target pixel size renderImage computes, so the
|
|
289
|
+
* caller can encode off the JS thread: answer `undefined` while the encode
|
|
290
|
+
* is pending (the image's rows stay reserved) and `null` once it failed.
|
|
291
|
+
* Without a provider, renderImage encodes synchronously.
|
|
292
|
+
*/
|
|
293
|
+
sixel?: (widthPx: number, heightPx: number) => string | null | undefined;
|
|
287
294
|
}
|
|
288
295
|
export declare function getCellDimensions(): CellDimensions;
|
|
289
296
|
export declare function setCellDimensions(dims: CellDimensions): void;
|
|
@@ -381,6 +388,12 @@ export declare function getJpegDimensions(base64Data: string): ImageDimensions |
|
|
|
381
388
|
export declare function getGifDimensions(base64Data: string): ImageDimensions | null;
|
|
382
389
|
export declare function getWebpDimensions(base64Data: string): ImageDimensions | null;
|
|
383
390
|
export declare function getImageDimensions(base64Data: string, mimeType: string): ImageDimensions | null;
|
|
391
|
+
/**
|
|
392
|
+
* SIXEL sequence for `base64Data` at the given pixel size, encoded on the JS
|
|
393
|
+
* thread; `null` when the encode fails. For output that cannot wait for an
|
|
394
|
+
* off-thread encode.
|
|
395
|
+
*/
|
|
396
|
+
export declare function encodeSixelNow(base64Data: string, widthPx: number, heightPx: number): string | null;
|
|
384
397
|
export declare function renderImage(base64Data: string, imageDimensions: ImageDimensions, options?: ImageRenderOptions): {
|
|
385
398
|
sequence?: string;
|
|
386
399
|
lines?: string[];
|
package/dist/types/tui.d.ts
CHANGED
|
@@ -150,6 +150,11 @@ export interface Component {
|
|
|
150
150
|
describeScreen?(cx: DescribeContext): NativeScreen;
|
|
151
151
|
/** User actions on nodes this component described (toggle, select, activate, custom). */
|
|
152
152
|
handleNativeEvent?(event: NativeUiEvent): void;
|
|
153
|
+
/**
|
|
154
|
+
* Pure preflight for focused input that must precede TUI-wide input listeners
|
|
155
|
+
* and debug shortcuts. A true result routes the event to this component first.
|
|
156
|
+
*/
|
|
157
|
+
capturesInput?(data: string): boolean;
|
|
153
158
|
/**
|
|
154
159
|
* Optional handler for keyboard input when component has focus
|
|
155
160
|
*/
|
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.6.
|
|
4
|
+
"version": "18.6.3",
|
|
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.6.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "18.6.
|
|
46
|
-
"@oh-my-pi/pi-ai": "18.6.
|
|
47
|
-
"@oh-my-pi/pi-catalog": "18.6.
|
|
48
|
-
"@oh-my-pi/pi-natives": "18.6.
|
|
49
|
-
"@oh-my-pi/pi-utils": "18.6.
|
|
50
|
-
"@oh-my-pi/pi-wire": "18.6.
|
|
51
|
-
"@oh-my-pi/snapcompact": "18.6.
|
|
44
|
+
"@oh-my-pi/omptype": "18.6.3",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "18.6.3",
|
|
46
|
+
"@oh-my-pi/pi-ai": "18.6.3",
|
|
47
|
+
"@oh-my-pi/pi-catalog": "18.6.3",
|
|
48
|
+
"@oh-my-pi/pi-natives": "18.6.3",
|
|
49
|
+
"@oh-my-pi/pi-utils": "18.6.3",
|
|
50
|
+
"@oh-my-pi/pi-wire": "18.6.3",
|
|
51
|
+
"@oh-my-pi/snapcompact": "18.6.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"kitty-vt-wasm": "^0.2.0"
|