@oh-my-pi/pi-tui 18.4.0 → 18.4.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 +28 -0
- package/dist/types/chat/image-loading.d.ts +24 -0
- package/dist/types/chat/transcript-entry.d.ts +9 -0
- package/dist/types/chrome/transcript-container.d.ts +9 -2
- package/dist/types/overlays/agents-hub.d.ts +7 -4
- package/dist/types/overlays/rewind-selector.d.ts +3 -3
- package/dist/types/overlays/usage-dashboard.d.ts +9 -2
- package/dist/types/prompt/composer-cache.d.ts +34 -16
- package/dist/types/prompt/composer.d.ts +15 -18
- package/dist/types/prompt/welcome.d.ts +5 -2
- package/dist/types/status-line/component.d.ts +13 -4
- package/dist/types/status-line/metrics.d.ts +0 -1
- package/dist/types/status-line/startup.d.ts +38 -0
- package/dist/types/status-line/types.d.ts +0 -2
- package/dist/types/terminal-capabilities.d.ts +10 -6
- package/dist/types/tools/output-meta.d.ts +2 -1
- package/dist/types/tools/vibe.d.ts +4 -0
- package/package.json +9 -9
- package/src/chat/assistant-message.ts +27 -21
- package/src/chat/image-loading.ts +81 -0
- package/src/chat/tool-execution.ts +29 -13
- package/src/chat/transcript-entry.ts +16 -0
- package/src/chrome/transcript-container.ts +208 -74
- package/src/components/image.ts +10 -2
- package/src/overlays/agents-hub.ts +7 -18
- package/src/overlays/copy-selector.ts +3 -31
- package/src/overlays/model-hub.ts +49 -16
- package/src/overlays/rewind-selector.ts +51 -12
- package/src/overlays/usage-dashboard.ts +47 -6
- package/src/prompt/composer-cache.ts +217 -196
- package/src/prompt/composer.ts +40 -70
- package/src/prompt/welcome.ts +31 -28
- package/src/status-line/component.ts +73 -50
- package/src/status-line/metrics.ts +3 -21
- package/src/status-line/segments.ts +34 -50
- package/src/status-line/startup.ts +181 -0
- package/src/status-line/types.ts +0 -2
- package/src/terminal-capabilities.ts +12 -6
- package/src/terminal.ts +38 -9
- package/src/tools/output-meta.ts +11 -3
- package/src/tools/vibe.ts +15 -2
- package/src/tui.ts +66 -42
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.4.1] - 2026-09-28
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Replaced `AgentsHubDeps.setDisabledAgents`/`setOverrides` with `setAgentDisabled(name, { disabled })` and `setAgentOverride(property, name, value)`, so each hub edit persists only the agent it changes; `PropertyKind` is exported ([#13308](https://github.com/can1357/oh-my-pi/pull/13308) by [@Vortex727](https://github.com/Vortex727))
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- LSP servers section is omitted from the welcome screen when LSP is disabled
|
|
14
|
+
- Usage dashboards and provider cards can show connected accounts with unavailable usage separately from reported quotas, without treating missing reports as unused or unlimited ([#13476](https://github.com/can1357/oh-my-pi/pull/13476) by [@aktanazat](https://github.com/aktanazat)).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Fixed sessions exiting as if the terminal hung up when it only stopped reading for a few seconds during a very large repaint (a long transcript's replay behind a busy tmux or container attach) ([#13576](https://github.com/can1357/oh-my-pi/pull/13576) by [@sjawhar](https://github.com/sjawhar))
|
|
19
|
+
- Fixed fullscreen overlays rewriting the whole terminal on every change; each frame now repaints only the rows that changed ([#13568](https://github.com/can1357/oh-my-pi/pull/13568) by [@sjawhar](https://github.com/sjawhar))
|
|
20
|
+
- Fixed the armed `/loop` status reading `Loop waiting`, which hid that the next prompt becomes the repeated loop body; it now reads `Loop: next prompt repeats` ([#13435](https://github.com/can1357/oh-my-pi/pull/13435) by [@Dante-dan](https://github.com/Dante-dan)).
|
|
21
|
+
- Reduced memory held by long sessions: committed transcript blocks no longer keep their rendered-row caches ([#13242](https://github.com/can1357/oh-my-pi/pull/13242) by [@iliaal](https://github.com/iliaal)).
|
|
22
|
+
- Fixed the results-limit notice advising `Use limit=` values the tool would clamp straight back; at the hard cap it now reports the reached count alone ([#13263](https://github.com/can1357/oh-my-pi/issues/13263))
|
|
23
|
+
- Fixed the rewind filter finding nothing for query words in scripts without spaces (e.g. Chinese): non-Latin words now match as substrings while Latin words keep whole-word matching ([#13361](https://github.com/can1357/oh-my-pi/pull/13361) by [@jchanghong023](https://github.com/jchanghong023))
|
|
24
|
+
- The Esc-Esc rewind selector opens on the recent tail of long sessions instead of replaying the whole branch; press `a` to load earlier turns ([#12819](https://github.com/can1357/oh-my-pi/pull/12819) by [@lemonleks](https://github.com/lemonleks)).
|
|
25
|
+
- Fixed OSC 8 hyperlinks (file paths, URLs, GitHub refs) rendering as plain text in Herdr panes. Herdr reports `TERM=xterm-256color` without `TERM_PROGRAM`, so its panes were treated as an unknown terminal, although Herdr renders OSC 8 itself and opens links on Ctrl+click. Screen or tmux nested inside a pane still follow their own rules, and `PI_NO_HYPERLINKS=1` still opts out ([#13289](https://github.com/can1357/oh-my-pi/pull/13289) by [@andrebrait](https://github.com/andrebrait)).
|
|
26
|
+
- Fixed native stderr output painting over the TUI on Linux ([#13176](https://github.com/can1357/oh-my-pi/issues/13176)).
|
|
27
|
+
- Fixed heavy lag while output streams with inline images on SIXEL terminals such as Orca: each visible image was re-encoded on every render pass ([#12998](https://github.com/can1357/oh-my-pi/pull/12998) by [@wimjan123](https://github.com/wimjan123))
|
|
28
|
+
- Fixed Kitty-protocol terminals re-encoding every non-PNG image on redelivery and transcript rebuilds ([#13118](https://github.com/can1357/oh-my-pi/issues/13118)).
|
|
29
|
+
- Fixed the Windows Terminal taskbar progress animation restarting every second while the agent works ([#12940](https://github.com/can1357/oh-my-pi/pull/12940)).
|
|
30
|
+
- Fixed resuming a long session freezing the TUI for several seconds; first paint is now spread across frames ([#12933](https://github.com/can1357/oh-my-pi/issues/12933)).
|
|
31
|
+
- The model hub no longer opens a thinking strip or shows the `t` hint for models that cannot reason ([#13112](https://github.com/can1357/oh-my-pi/pull/13112)).
|
|
32
|
+
|
|
5
33
|
## [18.4.0] - 2026-09-28
|
|
6
34
|
|
|
7
35
|
### Fixed
|
|
@@ -43,4 +43,28 @@ export declare class InvalidImageDataError extends Error {
|
|
|
43
43
|
export declare function imageDecodeFailureReason(image: ImageContent): Promise<string | null>;
|
|
44
44
|
/** Converts an image to PNG, rejecting when the runtime cannot decode or encode it. */
|
|
45
45
|
export declare function convertImageToPng(image: ImageContent): Promise<ImageContent>;
|
|
46
|
+
/**
|
|
47
|
+
* Content-addressed identity of an image payload, stable across components and
|
|
48
|
+
* transcript rebuilds. Callers key their own per-image state by this instead of
|
|
49
|
+
* positional ids like `${toolCallId}:${index}`, which go stale when the images
|
|
50
|
+
* behind a position are replaced.
|
|
51
|
+
*/
|
|
52
|
+
export declare function imagePayloadKey(image: ImageContent): string;
|
|
53
|
+
/**
|
|
54
|
+
* The PNG conversion of `image` when one is still resident, else `undefined`.
|
|
55
|
+
* Synchronous so renderers can use an already-converted image on the spot
|
|
56
|
+
* instead of scheduling another async re-render.
|
|
57
|
+
*/
|
|
58
|
+
export declare function cachedPngConversion(image: ImageContent): ImageContent | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Converts `image` to PNG at most once per distinct payload: concurrent callers
|
|
61
|
+
* share one in-flight conversion and later callers hit {@link pngCache}.
|
|
62
|
+
* Kitty-graphics renderers use this because `convertImageToPng` is a full
|
|
63
|
+
* decode plus re-encode, and the same image is delivered repeatedly (read-result
|
|
64
|
+
* replay) and rebuilt from scratch on resume, rewind, and `/tree` navigation.
|
|
65
|
+
*
|
|
66
|
+
* Rejections are not cached — a payload that failed to decode is retried by the
|
|
67
|
+
* next caller, matching {@link convertImageToPng}'s contract.
|
|
68
|
+
*/
|
|
69
|
+
export declare function convertImageToPngShared(image: ImageContent): Promise<ImageContent>;
|
|
46
70
|
export declare function ensureSupportedImageInput(image: ImageContent): Promise<ImageContent | null>;
|
|
@@ -31,6 +31,15 @@ export declare function transcriptEntryMessage(entry: TranscriptEntryLike): Agen
|
|
|
31
31
|
export declare function isUserRequestEntry(entry: TranscriptEntryLike | {
|
|
32
32
|
type: string;
|
|
33
33
|
}): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Recent transcript tail starting at a user-request boundary, so tool calls
|
|
36
|
+
* and results stay together. ChatTranscriptBuilder drops a tool result whose
|
|
37
|
+
* initiating call was sliced away, so a tail of orphaned results can leave
|
|
38
|
+
* the picker without any target.
|
|
39
|
+
*
|
|
40
|
+
* A whole user turn may exceed `limit`; paginate rendering if one turn grows too large.
|
|
41
|
+
*/
|
|
42
|
+
export declare function recentTranscriptEntries(entries: TranscriptEntryLike[], limit?: number): TranscriptEntryLike[];
|
|
34
43
|
/** Editable user request text, preserving skill invocation syntax. */
|
|
35
44
|
export declare function userTurnDraft(entry: TranscriptEntryLike): string | undefined;
|
|
36
45
|
/** Extract text blocks verbatim, optionally separating block boundaries. */
|
|
@@ -96,8 +96,15 @@ export declare class TranscriptContainer extends Container {
|
|
|
96
96
|
* quit is pure write volume. An already offered replay batch stays valid.
|
|
97
97
|
*/
|
|
98
98
|
cancelReplay(): void;
|
|
99
|
-
/**
|
|
100
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Total rows the live, un-emitted tail occupies at `width`.
|
|
101
|
+
*
|
|
102
|
+
* `limit` stops the walk once the total passes it: measuring a resumed
|
|
103
|
+
* session's whole ledger costs one full render per block, and callers only
|
|
104
|
+
* compare the height against a viewport budget. Past `limit` the result is
|
|
105
|
+
* a lower bound, guaranteed only to be greater than `limit`.
|
|
106
|
+
*/
|
|
107
|
+
liveRowCount(width: number, limit?: number): number;
|
|
101
108
|
/** Block spans of the last `renderViewport` output, in output coordinates. Empty when the tail is empty. */
|
|
102
109
|
getLastViewportSpans(): readonly TranscriptViewportSpan[];
|
|
103
110
|
/** Render the live tail, constrained to the supplied transcript height. */
|
|
@@ -32,7 +32,7 @@ export interface HubAgent {
|
|
|
32
32
|
advisorOverride?: string;
|
|
33
33
|
}
|
|
34
34
|
/** The per-agent knob a strip or the model browser is editing. */
|
|
35
|
-
type PropertyKind = "model" | "prewalk" | "advisor";
|
|
35
|
+
export type PropertyKind = "model" | "prewalk" | "advisor";
|
|
36
36
|
export interface GeneratedAgentSpec {
|
|
37
37
|
identifier: string;
|
|
38
38
|
whenToUse: string;
|
|
@@ -47,8 +47,12 @@ export interface AgentsHubDeps {
|
|
|
47
47
|
resolvePatterns: (patterns: string[]) => string | undefined;
|
|
48
48
|
effectivePrewalkPattern: (agent: HubAgent) => string | undefined;
|
|
49
49
|
effectiveAdvisorPattern: (agent: HubAgent) => string | undefined;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
/** Persist one agent's enabled state; other agents are untouched. */
|
|
51
|
+
setAgentDisabled: (name: string, options: {
|
|
52
|
+
disabled: boolean;
|
|
53
|
+
}) => void;
|
|
54
|
+
/** Persist one agent's override for `property`; `undefined` clears it. Other agents are untouched. */
|
|
55
|
+
setAgentOverride: (property: PropertyKind, name: string, value: string | undefined) => void;
|
|
52
56
|
generateAgent: (description: string, onText: (text: string) => void) => Promise<string>;
|
|
53
57
|
saveAgent: (scope: "project" | "user", spec: GeneratedAgentSpec) => Promise<string>;
|
|
54
58
|
}
|
|
@@ -69,4 +73,3 @@ export declare class AgentsHubComponent implements Component {
|
|
|
69
73
|
handleInput(data: string): void;
|
|
70
74
|
render(width: number): readonly string[];
|
|
71
75
|
}
|
|
72
|
-
export {};
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
* active column when a strip is open), Left/Right slide between branch
|
|
22
22
|
* variants at a fork and jump between user turns elsewhere, `f` opens a
|
|
23
23
|
* filter (typing narrows the current path to matching items; Esc leaves the
|
|
24
|
-
* filter with the selection kept), Enter rewinds to the outlined item,
|
|
25
|
-
* cancels.
|
|
24
|
+
* filter with the selection kept), Enter rewinds to the outlined item, A loads
|
|
25
|
+
* earlier turns without changing selection, Esc cancels.
|
|
26
26
|
*/
|
|
27
27
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
28
28
|
import { type Component, type TUI } from "../index.js";
|
|
29
29
|
import type { MessageRenderer } from "../chat/extension-types.js";
|
|
30
|
-
import type
|
|
30
|
+
import { type TranscriptEntryLike as TranscriptEntry } from "../chat/transcript-entry.js";
|
|
31
31
|
/** One alternate branch at a divergence: its root and message path root → most-recent leaf. */
|
|
32
32
|
export interface BranchVariantPath {
|
|
33
33
|
rootId: string;
|
|
@@ -20,12 +20,18 @@ export interface CardWindowRow {
|
|
|
20
20
|
/** Absolute one-sided amount (e.g. `$12.34 used`, `100 credits left`) for limits without a fraction. */
|
|
21
21
|
usedText?: string;
|
|
22
22
|
}
|
|
23
|
+
/** A connected account whose usage lookup produced no attributable report. */
|
|
24
|
+
export interface UnavailableUsageAccount {
|
|
25
|
+
provider: string;
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
23
28
|
/** Compact per-provider summary backing one card in the subscriptions grid. */
|
|
24
29
|
export interface ProviderCard {
|
|
25
30
|
provider: string;
|
|
26
31
|
name: string;
|
|
27
|
-
/** Number of accounts
|
|
32
|
+
/** Number of represented accounts, including unavailable usage lookups. */
|
|
28
33
|
accounts: number;
|
|
34
|
+
unavailableAccounts: string[];
|
|
29
35
|
/** Window rows sorted most-pressing first. */
|
|
30
36
|
windows: CardWindowRow[];
|
|
31
37
|
/** True when every account reports no limits (e.g. enterprise plans). */
|
|
@@ -48,7 +54,7 @@ export interface ProviderCard {
|
|
|
48
54
|
* most-used account's reset countdown. Cards sort most-pressing first so
|
|
49
55
|
* what's burning is on top-left; fully idle providers collapse into a tick.
|
|
50
56
|
*/
|
|
51
|
-
export declare function buildProviderCards(reports: UsageReport[], nowMs: number): ProviderCard[];
|
|
57
|
+
export declare function buildProviderCards(reports: UsageReport[], nowMs: number, unavailableAccounts?: readonly UnavailableUsageAccount[]): ProviderCard[];
|
|
52
58
|
/** GitHub-style week-per-column heatmap grid derived from daily activity. */
|
|
53
59
|
export interface HeatmapLayout {
|
|
54
60
|
/** Per week column: short month name when the column starts a new month. */
|
|
@@ -71,6 +77,7 @@ export declare function buildHeatmapLayout(points: DailyActivityPoint[], weeks:
|
|
|
71
77
|
/** Callbacks and data sources for {@link UsageDashboardComponent}. */
|
|
72
78
|
export interface UsageDashboardOptions {
|
|
73
79
|
reports: UsageReport[];
|
|
80
|
+
unavailableAccounts?: readonly UnavailableUsageAccount[];
|
|
74
81
|
/**
|
|
75
82
|
* Full classic `/usage` report for the expanded detail view; re-invoked per
|
|
76
83
|
* terminal width.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import "@oh-my-pi/pi-utils/env";
|
|
2
1
|
import type { LspServerInfo, RecentSession } from "./welcome.js";
|
|
3
|
-
import type { ComposerPreferences,
|
|
2
|
+
import type { ComposerPreferences, ComposerStatusCache } from "./composer.js";
|
|
4
3
|
import type { SymbolPreset } from "../theme/theme.js";
|
|
5
4
|
/** Theme inputs cached from the last resolved settings load for stable prepaint colors. */
|
|
6
5
|
export interface ComposerThemePreferences {
|
|
@@ -20,18 +19,37 @@ export interface ComposerStartupCache {
|
|
|
20
19
|
readonly theme?: ComposerThemePreferences;
|
|
21
20
|
readonly welcome?: ComposerWelcomeCache;
|
|
22
21
|
readonly recentSessions: RecentSession[];
|
|
23
|
-
|
|
24
|
-
readonly
|
|
22
|
+
/** `null` when the last run had LSP disabled. */
|
|
23
|
+
readonly lspServers: LspServerInfo[] | null;
|
|
24
|
+
readonly status?: ComposerStatusCache;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Process-wide store at {@link getComposerCacheDbPath}, opened on first use and
|
|
28
|
+
* closed at exit. `undefined` when it cannot be opened (logged once; startup
|
|
29
|
+
* paints without speculation) and under the test runner, so tests never read
|
|
30
|
+
* or clobber the user's cache; tests open {@link ComposerCache.open} explicitly.
|
|
31
|
+
*/
|
|
32
|
+
export declare function sharedComposerCache(): ComposerCache | undefined;
|
|
33
|
+
/** SQLite store of the composer state the next launch paints before its session exists. */
|
|
34
|
+
export declare class ComposerCache {
|
|
35
|
+
#private;
|
|
36
|
+
private constructor();
|
|
37
|
+
/**
|
|
38
|
+
* Open (creating if needed) the store at `dbPath`, quarantining a corrupt one once.
|
|
39
|
+
* @throws when the directory or database cannot be created.
|
|
40
|
+
*/
|
|
41
|
+
static open(dbPath?: string): ComposerCache;
|
|
42
|
+
/** Everything cached for `cwd`, with any-project rows as fallback for shared kinds. Never throws. */
|
|
43
|
+
read(cwd: string): ComposerStartupCache;
|
|
44
|
+
/** Resolved theme and composer settings for the next prepaint. */
|
|
45
|
+
writeUi(cwd: string, preferences: ComposerPreferences, theme: ComposerThemePreferences): void;
|
|
46
|
+
/** Authoritative model/provider labels for the next welcome prepaint. */
|
|
47
|
+
writeWelcome(cwd: string, welcome: ComposerWelcomeCache): void;
|
|
48
|
+
/** The latest recent-session rows (first four). */
|
|
49
|
+
writeRecentSessions(cwd: string, sessions: readonly RecentSession[]): void;
|
|
50
|
+
/** The latest detected project LSP rows; `null` records that LSP is disabled. */
|
|
51
|
+
writeLspServers(cwd: string, servers: readonly LspServerInfo[] | null): void;
|
|
52
|
+
/** Status-bar inputs for the next prepaint's startup status line. */
|
|
53
|
+
writeStatus(cwd: string, status: ComposerStatusCache): void;
|
|
54
|
+
close(): void;
|
|
25
55
|
}
|
|
26
|
-
/** Read all speculative composer caches synchronously before the first terminal paint. */
|
|
27
|
-
export declare function readComposerStartupCache(cwd: string): ComposerStartupCache;
|
|
28
|
-
/** Persist resolved theme and composer settings for the next prepaint. */
|
|
29
|
-
export declare function writeComposerUiCache(cwd: string, preferences: ComposerPreferences, theme: ComposerThemePreferences): Promise<void>;
|
|
30
|
-
/** Persist authoritative model/provider labels for the next welcome prepaint. */
|
|
31
|
-
export declare function writeComposerWelcomeCache(cwd: string, welcome: ComposerWelcomeCache): Promise<void>;
|
|
32
|
-
/** Persist placeholder-only status chrome for speculative first-frame rendering. */
|
|
33
|
-
export declare function writeComposerStatusCache(cwd: string, status: ComposerStatusSnapshot): Promise<void>;
|
|
34
|
-
/** Persist the latest recent-session rows as a compact JSONL speculation cache. */
|
|
35
|
-
export declare function writeComposerRecentSessionsCache(cwd: string, sessions: readonly RecentSession[]): Promise<void>;
|
|
36
|
-
/** Persist the latest detected project LSP rows for the next prepaint. */
|
|
37
|
-
export declare function writeComposerLspCache(cwd: string, servers: readonly LspServerInfo[]): Promise<void>;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { StatusLineComponent } from "../status-line/component.js";
|
|
2
|
+
import type { StatusLineSession } from "../status-line/host.js";
|
|
3
|
+
import { type StatusLineStartupData } from "../status-line/startup.js";
|
|
1
4
|
import { type Terminal } from "../terminal.js";
|
|
2
5
|
import { type Component, type ResizeScrollbackMode, type TerminalFramePlan, type TerminalFrameProvider, TUI, type TUIOptions, type ViewportSize } from "../tui.js";
|
|
3
6
|
import { CustomEditor } from "./custom-editor.js";
|
|
@@ -24,27 +27,21 @@ export interface ComposerWelcomeUpdate {
|
|
|
24
27
|
readonly modelName?: string;
|
|
25
28
|
readonly providerName?: string;
|
|
26
29
|
readonly recentSessions?: readonly RecentSession[];
|
|
27
|
-
|
|
30
|
+
/** Detected project servers; `null` means LSP is disabled and hides the welcome section. */
|
|
31
|
+
readonly lspServers?: readonly LspServerInfo[] | null;
|
|
28
32
|
}
|
|
29
33
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* Status-bar inputs persisted by the last session. The first frame renders
|
|
35
|
+
* them through a startup {@link StatusLineComponent} at the live width until
|
|
36
|
+
* the session-aware status line attaches.
|
|
33
37
|
*/
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
/** ANSI wrapper of the editor border at snapshot time (session accent or thinking color). */
|
|
38
|
+
export interface ComposerStatusCache {
|
|
39
|
+
/** ANSI wrapper of the editor border when persisted (session accent or thinking color). */
|
|
37
40
|
readonly borderColor?: {
|
|
38
41
|
readonly prefix: string;
|
|
39
42
|
readonly suffix: string;
|
|
40
43
|
};
|
|
41
|
-
|
|
42
|
-
readonly topBorder?: {
|
|
43
|
-
readonly content: string;
|
|
44
|
-
readonly width: number;
|
|
45
|
-
};
|
|
46
|
-
/** Standalone bottom-bar rows (`pi`/`claude` shapes), gap row included. */
|
|
47
|
-
readonly bottomLines: readonly string[];
|
|
44
|
+
readonly statusLine: StatusLineStartupData;
|
|
48
45
|
}
|
|
49
46
|
/** Optional dependencies and initial state for a standalone composer. */
|
|
50
47
|
export interface ComposerOptions {
|
|
@@ -53,7 +50,7 @@ export interface ComposerOptions {
|
|
|
53
50
|
readonly tuiOptions?: TUIOptions;
|
|
54
51
|
readonly preferences?: Partial<ComposerPreferences>;
|
|
55
52
|
readonly welcome?: ComposerWelcomeUpdate;
|
|
56
|
-
readonly status?:
|
|
53
|
+
readonly status?: ComposerStatusCache;
|
|
57
54
|
readonly exit?: (code: number) => void;
|
|
58
55
|
readonly now?: () => number;
|
|
59
56
|
}
|
|
@@ -155,11 +152,11 @@ export declare class Composer implements TerminalFrameProvider {
|
|
|
155
152
|
/** Update the canonical editor reference after InteractiveMode remounts a custom editor. */
|
|
156
153
|
setEditor(editor: CustomEditor): void;
|
|
157
154
|
/**
|
|
158
|
-
* Mount the session-aware status component into the slot below the editor
|
|
159
|
-
*
|
|
155
|
+
* Mount the session-aware status component into the slot below the editor,
|
|
156
|
+
* retiring the startup status line; the caller installs the real top-border
|
|
160
157
|
* provider through its composer-shape sync.
|
|
161
158
|
*/
|
|
162
|
-
setStatusComponent(component:
|
|
159
|
+
setStatusComponent<TSession extends StatusLineSession>(component: StatusLineComponent<TSession>): void;
|
|
163
160
|
/** Mount or replace session-aware root children while preserving the header and status hosts. */
|
|
164
161
|
setRuntimeChildren(children: readonly Component[], options?: RuntimeChildrenOptions): void;
|
|
165
162
|
/** Play or replay the welcome intro against the stable header render target. */
|
|
@@ -32,8 +32,11 @@ export declare class WelcomeComponent implements Component {
|
|
|
32
32
|
private modelName;
|
|
33
33
|
private providerName;
|
|
34
34
|
private recentSessions;
|
|
35
|
+
/** Detected project servers; `null` means LSP is disabled and hides the section. */
|
|
35
36
|
private lspServers;
|
|
36
|
-
constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[],
|
|
37
|
+
constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[],
|
|
38
|
+
/** Detected project servers; `null` means LSP is disabled and hides the section. */
|
|
39
|
+
lspServers?: LspServerInfo[] | null);
|
|
37
40
|
get tip(): string | undefined;
|
|
38
41
|
invalidate(): void;
|
|
39
42
|
/** The intro keeps the welcome block mutable; settling lets it retire to history. */
|
|
@@ -56,7 +59,7 @@ export declare class WelcomeComponent implements Component {
|
|
|
56
59
|
setVersion(version: string): void;
|
|
57
60
|
setModel(modelName: string, providerName: string): void;
|
|
58
61
|
setRecentSessions(sessions: RecentSession[]): void;
|
|
59
|
-
setLspServers(servers: LspServerInfo[]): void;
|
|
62
|
+
setLspServers(servers: LspServerInfo[] | null): void;
|
|
60
63
|
render(termWidth: number): readonly string[];
|
|
61
64
|
}
|
|
62
65
|
/** Block-grid brand mark shared by the welcome and setup surfaces. */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Component, type ComposerStyle } from "../index.js";
|
|
2
2
|
import type { StatusLineHost, StatusLineSession } from "./host.js";
|
|
3
|
+
import type { Editor } from "../components/editor.js";
|
|
3
4
|
import { type CodexResetFireworksEvent } from "../overlays/codex-reset-fireworks.js";
|
|
4
5
|
import { type SegmentContext } from "./segments.js";
|
|
5
6
|
import type { CollabStatus, EffectiveStatusLineSettings, StatusLineSettings } from "./types.js";
|
|
6
|
-
type StatusLineLayout = "box" | "band" | "plain-full" | "plain-left" | "plain-right";
|
|
7
7
|
export declare class StatusLineComponent<TSession extends StatusLineSession = StatusLineSession> implements Component {
|
|
8
8
|
#private;
|
|
9
9
|
private session;
|
|
@@ -114,6 +114,12 @@ export declare class StatusLineComponent<TSession extends StatusLineSession = St
|
|
|
114
114
|
* never retire the watcher or abort a live resolve.
|
|
115
115
|
*/
|
|
116
116
|
applyCwdChange(): void;
|
|
117
|
+
/**
|
|
118
|
+
* Seed working-tree status from the status line this one replaces (the
|
|
119
|
+
* startup bar), so the dirty counts stay on screen instead of blanking until
|
|
120
|
+
* this instance's first fetch lands. Normal TTL refreshes follow.
|
|
121
|
+
*/
|
|
122
|
+
adoptGitStatus(source: StatusLineComponent): void;
|
|
117
123
|
/**
|
|
118
124
|
* Startup redraws only arm a short-delayed task; timeout releases the render
|
|
119
125
|
* cadence while a late successful fetch can still refresh the cached segment.
|
|
@@ -133,8 +139,12 @@ export declare class StatusLineComponent<TSession extends StatusLineSession = St
|
|
|
133
139
|
usedTokens: number;
|
|
134
140
|
contextWindow: number;
|
|
135
141
|
};
|
|
136
|
-
/**
|
|
137
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Wire this bar into `editor` for a composer layout: the matching top-border
|
|
144
|
+
* provider, the autocomplete probe, and the standalone bottom-bar placement.
|
|
145
|
+
* Callers re-run it whenever the composer shape changes.
|
|
146
|
+
*/
|
|
147
|
+
attachToEditor(editor: Pick<Editor, "isAutocompleteActive" | "setTopBorderProvider" | "setTopBorder">, style: Pick<ComposerStyle, "statusAttachment" | "bottomBar" | "bottomBarGap">): void;
|
|
138
148
|
getTopBorder(width: number, previewTitle?: string): {
|
|
139
149
|
content: string;
|
|
140
150
|
width: number;
|
|
@@ -181,4 +191,3 @@ export declare class StatusLineComponent<TSession extends StatusLineSession = St
|
|
|
181
191
|
getPreviewLines(width: number, style?: Pick<ComposerStyle, "statusAttachment" | "bottomBar">): string[];
|
|
182
192
|
render(width: number): readonly string[];
|
|
183
193
|
}
|
|
184
|
-
export {};
|
|
@@ -5,7 +5,6 @@ export interface BillingSummaryOptions {
|
|
|
5
5
|
readonly usingSubscription: boolean;
|
|
6
6
|
readonly premiumRequests: number;
|
|
7
7
|
readonly fractionDigits: number;
|
|
8
|
-
readonly startupPlaceholder?: boolean;
|
|
9
8
|
readonly pricingPeriod?: "peak" | "off-peak";
|
|
10
9
|
readonly advisor?: {
|
|
11
10
|
readonly cost: number;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status line for the speculative first frame, before any session exists.
|
|
3
|
+
*
|
|
4
|
+
* InteractiveMode persists {@link StatusLineStartupData} (settings plus the last
|
|
5
|
+
* session's model and thinking state); the next launch renders it through the
|
|
6
|
+
* real {@link StatusLineComponent} as a fresh, unnamed session at the live
|
|
7
|
+
* terminal width. Path and git branch are read live; context usage is reported
|
|
8
|
+
* unknown so the gauge shows the window without a percent.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts ignore
|
|
12
|
+
* const statusLine = createStartupStatusLine(cached.statusLine);
|
|
13
|
+
* statusLine.attachToEditor(editor, getComposerStyle(shape));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
17
|
+
import type { Model } from "@oh-my-pi/pi-catalog/types";
|
|
18
|
+
import { StatusLineComponent } from "./component.js";
|
|
19
|
+
import type { CompactionBoundaries } from "./context-usage.js";
|
|
20
|
+
import type { StatusLineSettings } from "./types.js";
|
|
21
|
+
/** Session-independent inputs that reproduce a fresh session's status bar on the next launch. */
|
|
22
|
+
export interface StatusLineStartupData {
|
|
23
|
+
readonly settings: StatusLineSettings;
|
|
24
|
+
readonly gitEnabled: boolean;
|
|
25
|
+
/** Model of the last session; the next fresh session usually starts on it too. */
|
|
26
|
+
readonly model?: Model;
|
|
27
|
+
readonly thinkingLevel?: ThinkingLevel;
|
|
28
|
+
readonly autoThinking: boolean;
|
|
29
|
+
readonly fastMode: boolean;
|
|
30
|
+
/** Whether `model` bills through a subscription (drives the cost segment's prefix). */
|
|
31
|
+
readonly usingSubscription: boolean;
|
|
32
|
+
readonly autoCompactEnabled: boolean;
|
|
33
|
+
readonly compactionBoundaries: CompactionBoundaries | null;
|
|
34
|
+
}
|
|
35
|
+
/** Build the prepaint status line; the caller disposes it once the session-bound bar mounts. */
|
|
36
|
+
export declare function createStartupStatusLine(data: StatusLineStartupData): StatusLineComponent;
|
|
37
|
+
/** Validate persisted {@link StatusLineStartupData}; `undefined` for anything malformed. */
|
|
38
|
+
export declare function readStatusLineStartupData(value: unknown): StatusLineStartupData | undefined;
|
|
@@ -82,8 +82,6 @@ export interface SegmentContext {
|
|
|
82
82
|
sessionAccent?: boolean;
|
|
83
83
|
/** Stand-in session title for previews; `session_name` renders it when the session is unnamed. */
|
|
84
84
|
previewTitle?: string;
|
|
85
|
-
/** Replace dynamic values with ellipses while preserving each segment's icon, color, and static text. */
|
|
86
|
-
startupPlaceholder?: boolean;
|
|
87
85
|
activeRepo: ActiveRepoContext | null;
|
|
88
86
|
width: number;
|
|
89
87
|
options: StatusLineSegmentOptions;
|
|
@@ -154,11 +154,15 @@ export declare function hyperlinksUserOverride(env?: NodeJS.ProcessEnv): boolean
|
|
|
154
154
|
* Policy (highest precedence first):
|
|
155
155
|
* 1. Explicit user override (`PI_NO_HYPERLINKS=1` off, `PI_FORCE_HYPERLINKS=1`
|
|
156
156
|
* on). Opt-out wins ties.
|
|
157
|
-
* 2.
|
|
157
|
+
* 2. Herdr pane with no nested screen/tmux: on. Herdr hides the outer
|
|
158
|
+
* terminal (`TERM=xterm-256color`, no `TERM_PROGRAM`), but it renders
|
|
159
|
+
* OSC 8 in its own grid and opens links itself on Ctrl+click, so the
|
|
160
|
+
* outer terminal's support does not matter.
|
|
161
|
+
* 3. Static terminal capability — terminals whose {@link TerminalInfo} marks
|
|
158
162
|
* `hyperlinks: false` (e.g. `base`) stay off unless the user forced on.
|
|
159
|
-
*
|
|
163
|
+
* 4. GNU screen's explicit session marker (`STY`) always off, even if tmux is
|
|
160
164
|
* also present: a screen layer anywhere in the path cannot forward OSC 8.
|
|
161
|
-
*
|
|
165
|
+
* 5. tmux session (`TMUX` set): enabled when tmux self-reports >= 3.4 via
|
|
162
166
|
* `TERM_PROGRAM_VERSION` (tmux 3.4 stores OSC 8 as a cell attribute and
|
|
163
167
|
* forwards it to outer terminals whose `terminal-features` include
|
|
164
168
|
* `hyperlinks`). Older or unknown versions stay off; on outer terminals
|
|
@@ -166,11 +170,11 @@ export declare function hyperlinksUserOverride(env?: NodeJS.ProcessEnv): boolean
|
|
|
166
170
|
* identical to today. Checked before the screen-family TERM heuristic
|
|
167
171
|
* because tmux's historical `default-terminal` is `screen-256color`, so
|
|
168
172
|
* `TERM=screen*` inside a tmux session must NOT short-circuit to off.
|
|
169
|
-
*
|
|
173
|
+
* 6. screen-family TERM without `TMUX` always off: screen never gained OSC 8
|
|
170
174
|
* support.
|
|
171
|
-
*
|
|
175
|
+
* 7. tmux-family TERM without `TMUX` env — unusual (e.g. inspection scripts);
|
|
172
176
|
* no version available, so off.
|
|
173
|
-
*
|
|
177
|
+
* 8. Otherwise honor the static terminal capability.
|
|
174
178
|
*/
|
|
175
179
|
export declare function shouldEnableHyperlinksByDefault(env?: NodeJS.ProcessEnv, terminalId?: TerminalId): boolean;
|
|
176
180
|
/**
|
|
@@ -73,9 +73,10 @@ export interface LimitsMeta {
|
|
|
73
73
|
reached: number;
|
|
74
74
|
suggestion: number;
|
|
75
75
|
};
|
|
76
|
+
/** `suggestion` is omitted when the tool is already at its hard cap, so no larger usable limit exists to advise. */
|
|
76
77
|
resultLimit?: {
|
|
77
78
|
reached: number;
|
|
78
|
-
suggestion
|
|
79
|
+
suggestion?: number;
|
|
79
80
|
};
|
|
80
81
|
headLimit?: {
|
|
81
82
|
reached: number;
|
|
@@ -8,6 +8,8 @@ export interface VibeToolDetails {
|
|
|
8
8
|
op: VibeOp;
|
|
9
9
|
/** Live TV-wall snapshot of the owner's worker sessions at (or during) the call. */
|
|
10
10
|
screens: VibeScreenSnapshot[];
|
|
11
|
+
/** Sessions the director killed, left off `screens`; shown as a count. */
|
|
12
|
+
hiddenKilled?: string[];
|
|
11
13
|
spawned?: {
|
|
12
14
|
id: string;
|
|
13
15
|
cli: VibeCli;
|
|
@@ -38,6 +40,8 @@ export interface VibeScreenSnapshot {
|
|
|
38
40
|
id: string;
|
|
39
41
|
cli: VibeCli;
|
|
40
42
|
state: VibeSessionState;
|
|
43
|
+
/** Terminated by the director (`vibe_kill`) or mode teardown; unset for workers that died on their own. */
|
|
44
|
+
killed?: boolean;
|
|
41
45
|
model?: string;
|
|
42
46
|
turns: number;
|
|
43
47
|
queued: number;
|
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.4.
|
|
4
|
+
"version": "18.4.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.4.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "18.4.
|
|
46
|
-
"@oh-my-pi/pi-ai": "18.4.
|
|
47
|
-
"@oh-my-pi/pi-catalog": "18.4.
|
|
48
|
-
"@oh-my-pi/pi-natives": "18.4.
|
|
49
|
-
"@oh-my-pi/pi-utils": "18.4.
|
|
50
|
-
"@oh-my-pi/pi-wire": "18.4.
|
|
51
|
-
"@oh-my-pi/snapcompact": "18.4.
|
|
44
|
+
"@oh-my-pi/omptype": "18.4.1",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "18.4.1",
|
|
46
|
+
"@oh-my-pi/pi-ai": "18.4.1",
|
|
47
|
+
"@oh-my-pi/pi-catalog": "18.4.1",
|
|
48
|
+
"@oh-my-pi/pi-natives": "18.4.1",
|
|
49
|
+
"@oh-my-pi/pi-utils": "18.4.1",
|
|
50
|
+
"@oh-my-pi/pi-wire": "18.4.1",
|
|
51
|
+
"@oh-my-pi/snapcompact": "18.4.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"kitty-vt-wasm": "^0.2.0"
|
|
@@ -12,7 +12,7 @@ import type { AssistantThinkingRenderer } from "./extension-types";
|
|
|
12
12
|
import { ensureThemeSync, getMarkdownTheme, theme } from "../theme";
|
|
13
13
|
import { EMPTY_LINK_TARGETS, resolveImageOptions } from "../render/render-utils";
|
|
14
14
|
import { WidthAwareText } from "../render";
|
|
15
|
-
import {
|
|
15
|
+
import { cachedPngConversion, convertImageToPngShared, imagePayloadKey } from "./image-loading";
|
|
16
16
|
import { canonicalizeMessage, formatThinkingForDisplay, hasDisplayableThinking } from "./thinking-display";
|
|
17
17
|
import { resolveAssistantErrorPresentation } from "./transcript-render-helpers";
|
|
18
18
|
import { type CacheInvalidation, CacheInvalidationMarkerComponent } from "./cache-invalidation-marker";
|
|
@@ -186,10 +186,20 @@ export class AssistantMessageComponent extends Container {
|
|
|
186
186
|
#lastMessage?: AssistantMessage;
|
|
187
187
|
#emergencyText?: Markdown;
|
|
188
188
|
#toolImagesByCallId = new Map<string, ImageContent[]>();
|
|
189
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Payload keys ({@link imagePayloadKey}) whose Kitty PNG conversion this
|
|
191
|
+
* component already awaits, so a re-delivered image neither re-encodes nor
|
|
192
|
+
* schedules a second {@link updateContent} cascade. The conversions
|
|
193
|
+
* themselves are shared process-wide by {@link convertImageToPngShared}.
|
|
194
|
+
*/
|
|
195
|
+
#kittyConversionsAwaited = new Set<string>();
|
|
196
|
+
/**
|
|
197
|
+
* Conversions this component displays, held so a later re-render (theme
|
|
198
|
+
* invalidation) still finds them after the bounded shared cache evicts them.
|
|
199
|
+
*/
|
|
200
|
+
#kittyConverted = new Map<string, ImageContent>();
|
|
190
201
|
#showImages = true;
|
|
191
202
|
#showToolResultImages = true;
|
|
192
|
-
#kittyConversionsInFlight = new Set<string>();
|
|
193
203
|
#transcriptBlockFinalized: boolean;
|
|
194
204
|
/** See {@link setMidStreamPublication}; the wire's `stream-revision` axis decides it. */
|
|
195
205
|
#midStreamPublication = true;
|
|
@@ -832,16 +842,6 @@ export class AssistantMessageComponent extends Container {
|
|
|
832
842
|
setToolResultImages(toolCallId: string, images: ImageContent[]): void {
|
|
833
843
|
if (!toolCallId) return;
|
|
834
844
|
const validImages = images.filter(img => img.type === "image" && img.data && img.mimeType);
|
|
835
|
-
for (const key of Array.from(this.#convertedKittyImages.keys())) {
|
|
836
|
-
if (key.startsWith(`${toolCallId}:`)) {
|
|
837
|
-
this.#convertedKittyImages.delete(key);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
for (const key of Array.from(this.#kittyConversionsInFlight)) {
|
|
841
|
-
if (key.startsWith(`${toolCallId}:`)) {
|
|
842
|
-
this.#kittyConversionsInFlight.delete(key);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
845
|
if (validImages.length === 0) {
|
|
846
846
|
this.#toolImagesByCallId.delete(toolCallId);
|
|
847
847
|
} else {
|
|
@@ -855,21 +855,27 @@ export class AssistantMessageComponent extends Container {
|
|
|
855
855
|
|
|
856
856
|
#convertImagesForKitty(entries: Array<{ image: ImageContent; key: string }>): void {
|
|
857
857
|
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
858
|
-
for (const { image
|
|
858
|
+
for (const { image } of entries) {
|
|
859
859
|
if (image.mimeType === "image/png") continue;
|
|
860
|
-
|
|
861
|
-
this.#
|
|
862
|
-
|
|
860
|
+
const key = imagePayloadKey(image);
|
|
861
|
+
if (this.#kittyConverted.has(key)) continue;
|
|
862
|
+
const cached = cachedPngConversion(image);
|
|
863
|
+
if (cached) {
|
|
864
|
+
this.#kittyConverted.set(key, cached);
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (this.#kittyConversionsAwaited.has(key)) continue;
|
|
868
|
+
this.#kittyConversionsAwaited.add(key);
|
|
869
|
+
convertImageToPngShared(image)
|
|
863
870
|
.then(converted => {
|
|
864
|
-
this.#
|
|
865
|
-
this.#convertedKittyImages.set(key, converted);
|
|
871
|
+
this.#kittyConverted.set(key, converted);
|
|
866
872
|
if (this.#lastMessage) {
|
|
867
873
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
868
874
|
}
|
|
869
875
|
this.#onImageUpdate?.();
|
|
870
876
|
})
|
|
871
877
|
.catch(() => {
|
|
872
|
-
this.#
|
|
878
|
+
this.#kittyConversionsAwaited.delete(key);
|
|
873
879
|
});
|
|
874
880
|
}
|
|
875
881
|
}
|
|
@@ -882,7 +888,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
882
888
|
for (const { image, key } of entries) {
|
|
883
889
|
const displayImage =
|
|
884
890
|
TERMINAL.imageProtocol === ImageProtocol.Kitty && image.mimeType !== "image/png"
|
|
885
|
-
? this.#
|
|
891
|
+
? this.#kittyConverted.get(imagePayloadKey(image))
|
|
886
892
|
: image;
|
|
887
893
|
if (TERMINAL.imageProtocol && displayImage) {
|
|
888
894
|
this.#contentContainer.addChild(
|