@oh-my-pi/pi-tui 18.4.11 → 18.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/types/components/editor.d.ts +1 -1
  3. package/dist/types/components/input.d.ts +1 -1
  4. package/dist/types/native/encode.d.ts +2 -1
  5. package/dist/types/native/node.d.ts +22 -4
  6. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  7. package/dist/types/overlays/report-panel.d.ts +68 -0
  8. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  9. package/dist/types/prompt/composer-cache.d.ts +0 -16
  10. package/dist/types/prompt/composer.d.ts +17 -7
  11. package/dist/types/prompt/welcome.d.ts +17 -42
  12. package/dist/types/status-line/context-usage.d.ts +7 -5
  13. package/dist/types/thinking.d.ts +1 -1
  14. package/dist/types/tools/task.d.ts +5 -1
  15. package/package.json +9 -9
  16. package/src/apps/git/sidebar.ts +2 -2
  17. package/src/components/editor.ts +2 -1
  18. package/src/components/input.ts +2 -1
  19. package/src/desktop-notify.ts +2 -1
  20. package/src/native/backend.ts +5 -1
  21. package/src/native/encode.ts +4 -2
  22. package/src/native/node.ts +21 -4
  23. package/src/native/reconcile.ts +12 -4
  24. package/src/overlays/jobs-panel.ts +6 -53
  25. package/src/overlays/plan-review-overlay.ts +15 -1
  26. package/src/overlays/report-panel.ts +191 -0
  27. package/src/overlays/thinking-selector.ts +11 -11
  28. package/src/prompt/composer-cache.ts +11 -92
  29. package/src/prompt/composer.ts +29 -27
  30. package/src/prompt/custom-editor.ts +8 -2
  31. package/src/prompt/tips.txt +4 -0
  32. package/src/prompt/welcome.ts +101 -372
  33. package/src/render/render-utils.ts +5 -1
  34. package/src/status-line/context-usage.ts +21 -48
  35. package/src/thinking.ts +1 -1
  36. package/src/tools/streaming-output.ts +5 -0
  37. package/src/tools/task.ts +6 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,43 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.5.0] - 2026-10-03
6
+
7
+ ### Breaking Changes
8
+
9
+ - `WelcomeComponent` no longer takes a model or provider: its constructor is `(version)`, and `setModel()`, `setRecentSessions()`, `setLspServers()`, `handleNativeEvent()`, `RecentSession`, `LspServerInfo`, `WELCOME_SESSION_SLOTS` and `WELCOME_LSP_SLOTS` are gone; `ComposerWelcomeUpdate` drops `modelName`/`providerName`/`recentSessions`/`lspServers`, and `ComposerCache` drops `writeWelcome()`, `writeRecentSessions()`, `writeLspServers()`, `ComposerWelcomeCache` and the `welcome`/`recentSessions`/`lspServers` fields of `ComposerStartupCache`.
10
+ - `renderWelcomeTip()` returns its lines without an indent, `Tip:` and the body wrapped together, for the caller to place.
11
+
12
+ ### Added
13
+
14
+ - Added `ReportPanel`, a read-only command report: a `/btw`-style titled box with an Esc hint in text mode (above the editor, or as a full-screen page whose body scrolls on the arrow/page/Home/End keys and the wheel), and natively a `/usage`-style sheet whose body the terminal scrolls once it is long, with a Close button ([#14136](https://github.com/can1357/oh-my-pi/pull/14136) by [@H4vC](https://github.com/H4vC)).
15
+ - Added `contextUsageHead()`, the `/context` title naming the model and its window ([#14136](https://github.com/can1357/oh-my-pi/pull/14136) by [@H4vC](https://github.com/H4vC)).
16
+ - Added `Composer.rowsBelow()`, the rows the chrome under a below-transcript root took in the last frame ([#14136](https://github.com/can1357/oh-my-pi/pull/14136) by [@H4vC](https://github.com/H4vC)).
17
+ - Added `Composer.pinInputToBottom()`, which keeps the input on the bottom row after chrome above the editor closes when rows it displaced went to scrollback ([#14136](https://github.com/can1357/oh-my-pi/pull/14136) by [@H4vC](https://github.com/H4vC)).
18
+ - Added a repeatable native `reveal` (`{ at, n }`): a described node scrolls into view again whenever its `n` changes, without being re-added.
19
+
20
+ ### Changed
21
+
22
+ - `ContextUsageView` is now a bare report body without its own title, rules or card; `setBreakdown()` was removed ([#14136](https://github.com/can1357/oh-my-pi/pull/14136) by [@H4vC](https://github.com/H4vC)).
23
+ - The terminal welcome banner is the gradient logo beside the `omp` wordmark with the version under it and the tip, centered in the terminal; the logo stands alone when the lockup does not fit, and the tip drops below 50 columns. It no longer greets with "Welcome back!", natively either.
24
+
25
+ ### Fixed
26
+
27
+ - Fixed tool previews on Windows showing a working directory on another drive as a raw absolute path instead of its home-shortened `~/…` form.
28
+ - Fixed pasted drive-less `file:///…` URLs (forwarded from a macOS pasteboard or remote session) staying undecoded on Windows instead of loading as image paths.
29
+ - Fixed an output artifact whose file cannot be opened (e.g. a directory in the way) being reported on Windows as a write failure and retried later, instead of a terminal open failure.
30
+ - Fixed plan review's Contents in Tern: clicking an entry, or moving through them with the arrow keys, now scrolls the plan to that section.
31
+
32
+ ### Removed
33
+
34
+ - Removed `JobsPanel`, the `/jobs` transcript block; `/jobs` now opens `JobsSheet` natively ([#14138](https://github.com/can1357/oh-my-pi/pull/14138) by [@H4vC](https://github.com/H4vC)).
35
+
36
+ ## [18.4.12] - 2026-10-02
37
+
38
+ ### Changed
39
+
40
+ - Changed Ctrl+Z in Tern's native composer to undo the last edit (TSP `undo` event) instead of suspending
41
+
5
42
  ## [18.4.11] - 2026-10-02
6
43
 
7
44
  ### Added
@@ -296,7 +296,7 @@ export declare class Editor implements Component, Focusable {
296
296
  submit(): void;
297
297
  /** Insert text at the current cursor position */
298
298
  insertText(text: string): void;
299
- /** Terminal-side selection edits on the `editor` node (see {@link applyHostEdit}). */
299
+ /** Terminal-side selection edits and undo on the `editor` node (see {@link applyHostEdit}). */
300
300
  handleNativeEvent(event: NativeUiEvent): void;
301
301
  /**
302
302
  * Apply an edit the terminal made over its own selection (TSP `edit`):
@@ -39,7 +39,7 @@ export declare class Input implements Component, Focusable {
39
39
  /** Apply terminal paste semantics to text from non-bracketed paste transports
40
40
  * (e.g. kitty's OSC 5522 enhanced clipboard read). Mirrors `Editor.pasteText`. */
41
41
  pasteText(text: string): void;
42
- /** Terminal-side selection edits on the `input` node (see {@link applyHostEdit}). */
42
+ /** Terminal-side selection edits and undo on the `input` node (see {@link applyHostEdit}). */
43
43
  handleNativeEvent(event: NativeUiEvent): void;
44
44
  /**
45
45
  * Apply an edit the terminal made over its own selection (TSP `edit`):
@@ -27,7 +27,8 @@ export declare function encodeTspJson(verb: TspVerb, value: unknown, params?: Ts
27
27
  /**
28
28
  * The `hello` query; callers follow it with a DA1 sentinel. `features: ["edit"]`
29
29
  * tells the terminal that omp applies its `edit` events (TSP §8.5), so it may keep a
30
- * native selection in omp's editors; without it, every key stays omp's.
30
+ * native selection in omp's editors; without it, every key stays omp's. `"undo"`
31
+ * says omp applies `undo` events, so the terminal may turn ⌃Z in a field into one.
31
32
  */
32
33
  export declare function encodeTspHelloQuery(version?: string): string;
33
34
  /** One decoded APC message: verb, parameters and raw body. */
@@ -28,10 +28,12 @@ export type NativeNode = {
28
28
  */
29
29
  readonly key?: string;
30
30
  /**
31
- * Scroll the node into view, placed like the `reveal` op, when it is
32
- * added. Key a node by what it points at to reveal it again on a move.
31
+ * Scroll the node into view, placed like the `reveal` op. A placement
32
+ * reveals it when it is added: key a node by what it points at to
33
+ * reveal it again on a move. A {@link NativeReveal} reveals it whenever
34
+ * its `n` differs from the previous description of the same node.
33
35
  */
34
- readonly reveal?: "start" | "end" | "nearest";
36
+ readonly reveal?: NativeRevealAt | NativeReveal;
35
37
  /**
36
38
  * Keyboard scrolling forwarded to the terminal (PgUp/PgDn/End reach the
37
39
  * program): moves the scroller at or above the node by `by` whenever
@@ -42,6 +44,17 @@ export type NativeNode = {
42
44
  readonly scroll?: NativeScroll;
43
45
  };
44
46
  }[TspKind];
47
+ /** Where a revealed node lands in its scroller. */
48
+ export type NativeRevealAt = "start" | "end" | "nearest";
49
+ /**
50
+ * A repeatable {@link NativeNode.reveal}: bump `n` to bring an existing node
51
+ * into view again (a Contents entry jumping to its section). A freshly added
52
+ * node is not revealed.
53
+ */
54
+ export interface NativeReveal {
55
+ readonly at: NativeRevealAt;
56
+ readonly n: number;
57
+ }
45
58
  /** A {@link NativeNode.scroll} request. */
46
59
  export interface NativeScroll {
47
60
  readonly by: TspScrollBy;
@@ -117,7 +130,12 @@ export type NativeUiEvent = {
117
130
  | ({
118
131
  readonly type: "edit";
119
132
  readonly key: string;
120
- } & NativeTextEdit);
133
+ } & NativeTextEdit)
134
+ /** Undo the last change to an `editor`/`input` node's text (the terminal's ⌃Z); a no-op with no history. */
135
+ | {
136
+ readonly type: "undo";
137
+ readonly key: string;
138
+ };
121
139
  /**
122
140
  * A primitive edit the terminal made over its own text selection (cut,
123
141
  * typing, Backspace/Delete or paste over it, or collapsing it). Offsets are
@@ -1,4 +1,4 @@
1
- import { type Component, Container } from "../tui.js";
1
+ import type { Component } from "../tui.js";
2
2
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
3
3
  /** One async job as the session snapshots it. */
4
4
  export interface JobsPanelJob {
@@ -40,12 +40,6 @@ export interface JobsSheetSource {
40
40
  /** Dismiss the sheet. */
41
41
  close(): void;
42
42
  }
43
- /** The `/jobs` block: `ansi` renders in the terminal, the snapshot describes the native frame. */
44
- export declare class JobsPanel extends Container {
45
- #private;
46
- constructor(snapshot: JobsPanelSnapshot, nowMs: number, ansi: readonly Component[]);
47
- describe(cx: DescribeContext): NativeNode;
48
- }
49
43
  /**
50
44
  * The jobs pill's sheet: a centred `lg` glass sheet titled Background jobs.
51
45
  * A selectable list of every job (running first) sits over the selected
@@ -0,0 +1,68 @@
1
+ import type { TspText } from "@oh-my-pi/pi-wire";
2
+ import { OverlayPanel } from "../chrome/overlay-box.js";
3
+ import type { NativeNode, NativeUiEvent } from "../native/node.js";
4
+ import type { Component } from "../tui.js";
5
+ /** Options of a {@link ReportPanel}. */
6
+ export interface ReportPanelOptions {
7
+ /** Plain title: the text-mode border title, and the native sheet head unless {@link head} is given. */
8
+ readonly title: string;
9
+ /** Styled native sheet head. */
10
+ readonly head?: TspText;
11
+ /** The read-only report; it draws no frame or title of its own. */
12
+ readonly body: Component;
13
+ /**
14
+ * Display form of the key that dismisses the report while it sits above the
15
+ * editor (the editor's interrupt key, which routes to it). Once the panel
16
+ * holds focus ({@link ReportPanel.holdFocus}) the hint shows the key its own
17
+ * input closes on instead.
18
+ */
19
+ readonly closeKey: string;
20
+ /** The focused panel (native sheet, text-mode full-screen page) closes on Esc / its Close button: this runs. */
21
+ readonly onClose: () => void;
22
+ /**
23
+ * Most rows the text-mode panel may take, borders included (the screen rows
24
+ * left above the editor); a taller report scrolls inside it. `undefined`
25
+ * leaves it uncapped.
26
+ */
27
+ readonly maxRows?: () => number | undefined;
28
+ }
29
+ /**
30
+ * A read-only command report (`/changelog`, `/context`, `/tools`, …).
31
+ *
32
+ * Text mode draws a titled box with the body and an Esc hint: above the editor
33
+ * like the `/btw` panel while it fits there (the editor keeps focus and its
34
+ * Esc takes it away), or as a full-screen page on the alternate screen when it
35
+ * is taller, whose body scrolls on the arrow/page/Home/End keys and the wheel.
36
+ * Natively it is a sheet like `/usage`: a centred `lg` glass sheet titled by
37
+ * the report whose body the terminal scrolls (wheel, and the same keys
38
+ * forwarded as scroll requests), then a Close button; Esc closes it.
39
+ */
40
+ export declare class ReportPanel extends OverlayPanel {
41
+ #private;
42
+ readonly nativeOverlay: NonNullable<Component["nativeOverlay"]>;
43
+ constructor(options: ReportPanelOptions);
44
+ /**
45
+ * The panel is shown with focus (native sheet, text-mode full-screen page):
46
+ * its own {@link handleInput} closes it, so the hint names that key.
47
+ */
48
+ holdFocus(): void;
49
+ /**
50
+ * Text mode: cap the box to {@link ReportPanelOptions.maxRows}, scrolling a
51
+ * taller body. The cap is a hard limit: when a resize leaves fewer rows than
52
+ * the box's chrome needs, its bottom (the hint and closing border) is cut
53
+ * rather than spilling over the editor.
54
+ */
55
+ render(width: number): readonly string[];
56
+ /** Text mode: rows the whole box takes at `width` when nothing caps it. */
57
+ heightAt(width: number): number;
58
+ /** The sheet body (the overlay is the frame, and its body the scroller): the report, then Close (Esc). */
59
+ describe(): NativeNode;
60
+ invalidate(): void;
61
+ /**
62
+ * Focused (the native sheet, or the text-mode full-screen page): Esc closes;
63
+ * the arrow, page, Home and End keys and the wheel scroll the body.
64
+ */
65
+ handleInput(data: string): void;
66
+ /** Close runs what Esc runs. */
67
+ handleNativeEvent(event: NativeUiEvent): void;
68
+ }
@@ -1,4 +1,4 @@
1
- import type { Effort } from "@oh-my-pi/pi-ai";
1
+ import { type ConfiguredThinkingLevel } from "../thinking.js";
2
2
  import { SelectList, type SgrMouseEvent } from "../index.js";
3
3
  import { OverlayPanel } from "../chrome/overlay-box.js";
4
4
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
@@ -7,7 +7,7 @@ import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.
7
7
  */
8
8
  export declare class ThinkingSelectorComponent extends OverlayPanel {
9
9
  #private;
10
- constructor(currentLevel: Effort, availableLevels: Effort[], onSelect: (level: Effort) => void, onCancel: () => void);
10
+ constructor(currentLevel: ConfiguredThinkingLevel | undefined, availableLevels: ConfiguredThinkingLevel[], onSelect: (level: ConfiguredThinkingLevel) => void, onCancel: () => void);
11
11
  describe(cx: DescribeContext): NativeNode | null;
12
12
  /** Picker pointer events drive the list exactly as its keys do. */
13
13
  handleNativeEvent(event: NativeUiEvent): void;
@@ -1,4 +1,3 @@
1
- import type { LspServerInfo, RecentSession } from "./welcome.js";
2
1
  import type { ComposerPreferences, ComposerStatusCache } from "./composer.js";
3
2
  import type { SymbolPreset } from "../theme/theme.js";
4
3
  /** Theme inputs cached from the last resolved settings load for stable prepaint colors. */
@@ -8,19 +7,10 @@ export interface ComposerThemePreferences {
8
7
  readonly darkTheme?: string;
9
8
  readonly lightTheme?: string;
10
9
  }
11
- /** Last authoritative model labels shown in the welcome component. */
12
- export interface ComposerWelcomeCache {
13
- readonly modelName: string;
14
- readonly providerName: string;
15
- }
16
10
  /** Speculative composer state read before the settings/session graph is available. */
17
11
  export interface ComposerStartupCache {
18
12
  readonly preferences?: ComposerPreferences;
19
13
  readonly theme?: ComposerThemePreferences;
20
- readonly welcome?: ComposerWelcomeCache;
21
- readonly recentSessions: RecentSession[];
22
- /** `null` when the last run had LSP disabled. */
23
- readonly lspServers: LspServerInfo[] | null;
24
14
  readonly status?: ComposerStatusCache;
25
15
  }
26
16
  /**
@@ -43,12 +33,6 @@ export declare class ComposerCache {
43
33
  read(cwd: string): ComposerStartupCache;
44
34
  /** Resolved theme and composer settings for the next prepaint. */
45
35
  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
36
  /** Status-bar inputs for the next prepaint's startup status line. */
53
37
  writeStatus(cwd: string, status: ComposerStatusCache): void;
54
38
  close(): void;
@@ -6,7 +6,7 @@ import { type Component, type ResizeScrollbackMode, type TerminalFramePlan, type
6
6
  import type { NativeSurface, NativeSurfaceProvider } from "../native/node.js";
7
7
  import { CustomEditor } from "./custom-editor.js";
8
8
  import type { WordCompletionMethod } from "./word-completion.js";
9
- import { type LspServerInfo, type RecentSession, WelcomeComponent } from "./welcome.js";
9
+ import { WelcomeComponent } from "./welcome.js";
10
10
  /** Live settings that affect the composer before and after session adoption. */
11
11
  export interface ComposerPreferences {
12
12
  readonly quiet: boolean;
@@ -25,11 +25,6 @@ export declare const COMPOSER_DEFAULTS: ComposerPreferences;
25
25
  /** Welcome data that can be supplied initially or patched as startup resolves it. */
26
26
  export interface ComposerWelcomeUpdate {
27
27
  readonly version?: string;
28
- readonly modelName?: string;
29
- readonly providerName?: string;
30
- readonly recentSessions?: readonly RecentSession[];
31
- /** Detected project servers; `null` means LSP is disabled and hides the welcome section. */
32
- readonly lspServers?: readonly LspServerInfo[] | null;
33
28
  }
34
29
  /**
35
30
  * Status-bar inputs persisted by the last session. The first frame renders
@@ -122,6 +117,21 @@ export declare class Composer implements TerminalFrameProvider, NativeSurfacePro
122
117
  #private;
123
118
  readonly ui: TUI;
124
119
  constructor(options?: ComposerOptions);
120
+ /**
121
+ * Rows the below-transcript chrome under `root` (editor, status line, …)
122
+ * took in the last frame, so a root that grows upward can cap itself to
123
+ * the screen rows left above them; `undefined` before `root` was laid out.
124
+ */
125
+ rowsBelow(root: Component): number | undefined;
126
+ /**
127
+ * Keep the input on the bottom row while the live rows cannot fill the
128
+ * screen, as after an inline decision panel closes. A tall block that just
129
+ * left the chrome above the editor (a command report) may have scrolled
130
+ * rows into native history that cannot be pulled back; without the pin the
131
+ * editor would jump up to where the shorter frame now ends. The pin lifts
132
+ * once live rows fill the screen again.
133
+ */
134
+ pinInputToBottom(): void;
125
135
  /** Compose the bounded mutable viewport and the next ordered history append. */
126
136
  renderFrame(viewport: ViewportSize): TerminalFramePlan;
127
137
  /**
@@ -164,7 +174,7 @@ export declare class Composer implements TerminalFrameProvider, NativeSurfacePro
164
174
  enableInput(): void;
165
175
  /** Apply settings changes without replacing the editor or welcome component. */
166
176
  setPreferences(update: Partial<ComposerPreferences>): void;
167
- /** Patch welcome data in place as model, session, and project discovery complete. */
177
+ /** Patch welcome data in place as version, session, and project discovery complete. */
168
178
  updateWelcome(update: ComposerWelcomeUpdate): void;
169
179
  /** Replace optional header content around the stable welcome scene. */
170
180
  setHeaderExtras(before: readonly Component[], after: readonly Component[]): void;
@@ -1,58 +1,36 @@
1
- import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
1
+ import type { DescribeContext, NativeNode } from "../native/node.js";
2
2
  import type { Component } from "../tui.js";
3
- /**
4
- * Fixed number of session rows in the welcome box so its height stays stable
5
- * across recent-session updates.
6
- */
7
- export declare const WELCOME_SESSION_SLOTS = 4;
8
- /**
9
- * Fixed number of LSP-server rows, for the same reason. Overflow is sliced so
10
- * the box height is constant regardless of how many servers a project has.
11
- */
12
- export declare const WELCOME_LSP_SLOTS = 4;
13
3
  /** Pick a tip from `tips`, biased toward "[NEW]" tips by {@link NEW_TIP_WEIGHT};
14
4
  * `r` is a uniform sample in [0, 1). Returns "" when `tips` is empty.
15
5
  * Exported for tests. */
16
6
  export declare function pickWeightedTip(tips: readonly string[], r: number): string;
17
- export declare function renderWelcomeTip(tip: string, boxWidth: number, phase?: number): string[];
18
- export interface RecentSession {
19
- name: string;
20
- timeAgo: string;
21
- /** Session file; a native click on the row resumes it. */
22
- path?: string;
23
- }
24
- export interface LspServerInfo {
25
- name: string;
26
- status: "ready" | "error" | "connecting" | "available";
27
- fileTypes: string[];
28
- }
29
7
  /**
30
- * Premium welcome screen with block-based OMP logo and two-column layout.
8
+ * The welcome tip as lines of at most `width` columns: `Tip:` and the body
9
+ * wrapped together, with no indent, so the banner can center each line.
10
+ * `[]` when `width` leaves no room for a useful line.
11
+ */
12
+ export declare function renderWelcomeTip(tip: string, width: number, phase?: number): string[];
13
+ /**
14
+ * The session's welcome banner. In a terminal: the gradient logo beside the
15
+ * `omp` wordmark with the version under it (the logo alone when the lockup does
16
+ * not fit) and the tip of the session (dropped below {@link TIP_MIN_COLUMNS}
17
+ * columns). Natively: a card with the same logo, wordmark, version and tip
18
+ * ({@link WelcomeComponent.describe}).
31
19
  */
32
20
  export declare class WelcomeComponent implements Component {
33
21
  #private;
34
22
  private version;
35
- private modelName;
36
- private providerName;
37
- private recentSessions;
38
- /** Detected project servers; `null` means LSP is disabled and hides the section. */
39
- private lspServers;
40
- constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[],
41
- /** Detected project servers; `null` means LSP is disabled and hides the section. */
42
- lspServers?: LspServerInfo[] | null);
23
+ constructor(version: string);
43
24
  get tip(): string | undefined;
44
25
  invalidate(): void;
45
26
  /**
46
- * A `card` (`omp.welcome`) titled with the app version. The brand column
47
- * (`omp.welcome.brand`: greeting and the terminal's builtin `omp` mark, which
48
- * it animates) sits beside the info column (`omp.welcome.info`: prompt-sigil
49
- * keycaps, LSP servers, recent sessions); the tip of the session closes the card. Roles
50
- * carry the look (gradient logo, type scale, column hairline); a "[NEW]" tip
27
+ * A `card` (`omp.welcome`) mirroring the terminal banner: the lockup
28
+ * (`omp.welcome.lockup`: the terminal's builtin `omp` mark, which it animates,
29
+ * beside the wordmark with the version under it) and the tip of the session.
30
+ * Roles carry the look (gradient logo, type scale); a "[NEW]" tip
51
31
  * carries a terminal-clocked shimmering tag.
52
32
  */
53
33
  describe(_cx: DescribeContext): NativeNode;
54
- /** A click on a recent session resumes it. */
55
- handleNativeEvent(event: NativeUiEvent): void;
56
34
  /** The intro keeps the welcome block mutable; settling lets it retire to history. */
57
35
  isTranscriptBlockFinalized(): boolean;
58
36
  /**
@@ -71,9 +49,6 @@ export declare class WelcomeComponent implements Component {
71
49
  stopIntro(): void;
72
50
  /** Update the version embedded in the welcome border title. */
73
51
  setVersion(version: string): void;
74
- setModel(modelName: string, providerName: string): void;
75
- setRecentSessions(sessions: RecentSession[]): void;
76
- setLspServers(servers: LspServerInfo[] | null): void;
77
52
  render(termWidth: number): readonly string[];
78
53
  }
79
54
  /** Block-grid brand mark shared by the welcome and setup surfaces. */
@@ -3,6 +3,7 @@ import type { CompactionSettings } from "@oh-my-pi/pi-agent-core/compaction";
3
3
  import type { Tool as AiTool, Model } from "@oh-my-pi/pi-ai";
4
4
  import type { Theme } from "../theme/index.js";
5
5
  import { Container } from "../tui.js";
6
+ import type { TspText } from "@oh-my-pi/pi-wire";
6
7
  import type { DescribeContext, NativeNode } from "../native/node.js";
7
8
  interface ContextSkill {
8
9
  readonly name: string;
@@ -154,6 +155,8 @@ export declare function computeNonMessageBreakdown(session: NonMessageTokenSourc
154
155
  * session and model.
155
156
  */
156
157
  export declare function computeContextBreakdown(session: ContextUsageSession, options: ContextUsageOptions): ContextBreakdown;
158
+ /** The `/context` title: the model and its window once a model is selected. */
159
+ export declare function contextUsageHead(breakdown: ContextBreakdown): TspText;
157
160
  /**
158
161
  * Native context-usage panel for terminals without `meter`: the cell grid as
159
162
  * unwrapped styled rows beside the legend. The terminal places the legend
@@ -161,15 +164,14 @@ export declare function computeContextBreakdown(session: ContextUsageSession, op
161
164
  */
162
165
  export declare function describeContextUsage(breakdown: ContextBreakdown): NativeNode;
163
166
  /**
164
- * The `/context` transcript block: ANSI renders the titled cell grid between
165
- * rules; natively it is one frame, drawn with `meter`s where the terminal
166
- * has them and as the glyph grid otherwise.
167
+ * The `/context` report body, without a frame or title of its own (the panel
168
+ * showing it is titled {@link contextUsageHead}): ANSI renders the cell grid
169
+ * beside the legend; natively it is drawn with `meter`s where the terminal has
170
+ * them and as the glyph grid otherwise.
167
171
  */
168
172
  export declare class ContextUsageView extends Container {
169
173
  #private;
170
174
  constructor(breakdown: ContextBreakdown, theme: Theme);
171
- /** Replace the shown breakdown in place, refreshing both the ANSI and native views. */
172
- setBreakdown(breakdown: ContextBreakdown): void;
173
175
  describe(cx: DescribeContext): NativeNode;
174
176
  }
175
177
  /**
@@ -4,7 +4,7 @@ import { ThinkingLevel } from "@oh-my-pi/pi-agent-core/thinking";
4
4
  import { Effort } from "@oh-my-pi/pi-catalog/effort";
5
5
  import type { Model } from "@oh-my-pi/pi-catalog/types";
6
6
  /** Thinking selectors accepted by CLI inputs, in display order. */
7
- export declare const CLI_THINKING_LEVELS: readonly string[];
7
+ export declare const CLI_THINKING_LEVELS: readonly ConfiguredThinkingLevel[];
8
8
  /**
9
9
  * Metadata used to render thinking selector values in the coding-agent UI.
10
10
  */
@@ -108,6 +108,8 @@ export interface TaskItem {
108
108
  solutionSpace?: string;
109
109
  /** Per-spawn thinking effort: lowest/middle/highest level the resolved model supports. Overrides the agent's default selector (e.g. `auto`). */
110
110
  effort?: "lo" | "med" | "hi";
111
+ /** Per-spawn model selector or ordered selector array; overrides agent and settings preferences. */
112
+ model?: string | string[];
111
113
  /** Caller-provided output schema; its presence overrides the selected agent's schema. */
112
114
  outputSchema?: unknown;
113
115
  /** Validation behavior for a caller-provided or inherited output schema. */
@@ -134,6 +136,8 @@ export interface TaskParams {
134
136
  solutionSpace?: string;
135
137
  /** Per-spawn thinking effort (flat form): lowest/middle/highest level the resolved model supports. */
136
138
  effort?: "lo" | "med" | "hi";
139
+ /** Per-spawn model selector or ordered selector array; overrides agent and settings preferences. */
140
+ model?: string | string[];
137
141
  /** Caller-provided output schema; its presence overrides the selected agent's schema. */
138
142
  outputSchema?: unknown;
139
143
  /** Validation behavior for a caller-provided or inherited output schema. */
@@ -255,7 +259,7 @@ export interface AgentProgress {
255
259
  resolvedModelRoute?: string;
256
260
  /** True when a live advisor was attached to this run's session, not merely enabled in settings. */
257
261
  advisor?: boolean;
258
- /** The agent's latest self-estimate of task completion (0–100), from the periodic `task.completionProbeMs` side request. */
262
+ /** The agent's latest self-estimate of task completion (0–100), from the periodic `task.completionProbe` side request. */
259
263
  completionPercent?: number;
260
264
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
261
265
  extractedToolData?: Record<string, unknown[]>;
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.11",
4
+ "version": "18.5.0",
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.11",
45
- "@oh-my-pi/pi-agent-core": "18.4.11",
46
- "@oh-my-pi/pi-ai": "18.4.11",
47
- "@oh-my-pi/pi-catalog": "18.4.11",
48
- "@oh-my-pi/pi-natives": "18.4.11",
49
- "@oh-my-pi/pi-utils": "18.4.11",
50
- "@oh-my-pi/pi-wire": "18.4.11",
51
- "@oh-my-pi/snapcompact": "18.4.11"
44
+ "@oh-my-pi/omptype": "18.5.0",
45
+ "@oh-my-pi/pi-agent-core": "18.5.0",
46
+ "@oh-my-pi/pi-ai": "18.5.0",
47
+ "@oh-my-pi/pi-catalog": "18.5.0",
48
+ "@oh-my-pi/pi-natives": "18.5.0",
49
+ "@oh-my-pi/pi-utils": "18.5.0",
50
+ "@oh-my-pi/pi-wire": "18.5.0",
51
+ "@oh-my-pi/snapcompact": "18.5.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "kitty-vt-wasm": "^0.2.0"
@@ -1007,7 +1007,7 @@ export class Sidebar {
1007
1007
  * text field being edited. False when not the sidebar's event.
1008
1008
  */
1009
1009
  handleNativeEvent(event: NativeUiEvent): boolean {
1010
- if (event.type === "edit") {
1010
+ if (event.type === "edit" || event.type === "undo") {
1011
1011
  const editing = this.editing ? this.selected?.kind : undefined;
1012
1012
  const field =
1013
1013
  editing === "summary"
@@ -1017,7 +1017,7 @@ export class Sidebar {
1017
1017
  : editing === "description"
1018
1018
  ? this.description
1019
1019
  : undefined;
1020
- field?.applyHostEdit(event);
1020
+ field?.handleNativeEvent(event);
1021
1021
  return field !== undefined;
1022
1022
  }
1023
1023
  if (event.type === "select" || event.type === "activate") {
@@ -2862,9 +2862,10 @@ export class Editor implements Component, Focusable {
2862
2862
  this.#insertTextAtCursor(text);
2863
2863
  }
2864
2864
 
2865
- /** Terminal-side selection edits on the `editor` node (see {@link applyHostEdit}). */
2865
+ /** Terminal-side selection edits and undo on the `editor` node (see {@link applyHostEdit}). */
2866
2866
  handleNativeEvent(event: NativeUiEvent): void {
2867
2867
  if (event.type === "edit") this.applyHostEdit(event);
2868
+ else if (event.type === "undo") this.#applyUndo();
2868
2869
  }
2869
2870
 
2870
2871
  /**
@@ -284,9 +284,10 @@ export class Input implements Component, Focusable {
284
284
  this.#handlePaste(text);
285
285
  }
286
286
 
287
- /** Terminal-side selection edits on the `input` node (see {@link applyHostEdit}). */
287
+ /** Terminal-side selection edits and undo on the `input` node (see {@link applyHostEdit}). */
288
288
  handleNativeEvent(event: NativeUiEvent): void {
289
289
  if (event.type === "edit") this.applyHostEdit(event);
290
+ else if (event.type === "undo") this.#undo();
290
291
  }
291
292
 
292
293
  /**
@@ -50,7 +50,8 @@ export function hasLinuxDesktopSession(
50
50
  if (platform !== "linux") return false;
51
51
  if (env.DBUS_SESSION_BUS_ADDRESS) return true;
52
52
  const runtimeDir = env.XDG_RUNTIME_DIR;
53
- return Boolean(runtimeDir && fileExists(path.join(runtimeDir, "bus")));
53
+ // Only reached for Linux, so join with POSIX separators regardless of the host.
54
+ return Boolean(runtimeDir && fileExists(path.posix.join(runtimeDir, "bus")));
54
55
  }
55
56
 
56
57
  /**
@@ -671,6 +671,7 @@ export class NativeBackend {
671
671
  case "action":
672
672
  case "change":
673
673
  case "edit":
674
+ case "undo":
674
675
  this.#routeUiEvent(event);
675
676
  return;
676
677
  case "focus": {
@@ -687,7 +688,7 @@ export class NativeBackend {
687
688
  }
688
689
 
689
690
  #routeUiEvent(
690
- event: Extract<TspEvent, { ev: "toggle" | "select" | "activate" | "action" | "change" | "edit" }>,
691
+ event: Extract<TspEvent, { ev: "toggle" | "select" | "activate" | "action" | "change" | "edit" | "undo" }>,
691
692
  ): void {
692
693
  const reconciler = this.#surfaceFor(event.sf)?.reconciler;
693
694
  const target = reconciler?.target(event.id);
@@ -721,6 +722,9 @@ export class NativeBackend {
721
722
  ui = { type: "edit", key: target.keypath, from, to, text, cursor, len };
722
723
  break;
723
724
  }
725
+ case "undo":
726
+ ui = { type: "undo", key: target.keypath };
727
+ break;
724
728
  }
725
729
  target.component.handleNativeEvent(ui);
726
730
  this.#host.requestRender();
@@ -109,10 +109,11 @@ export function encodeTspJson(verb: TspVerb, value: unknown, params?: TspParams,
109
109
  /**
110
110
  * The `hello` query; callers follow it with a DA1 sentinel. `features: ["edit"]`
111
111
  * tells the terminal that omp applies its `edit` events (TSP §8.5), so it may keep a
112
- * native selection in omp's editors; without it, every key stays omp's.
112
+ * native selection in omp's editors; without it, every key stays omp's. `"undo"`
113
+ * says omp applies `undo` events, so the terminal may turn ⌃Z in a field into one.
113
114
  */
114
115
  export function encodeTspHelloQuery(version?: string): string {
115
- return encodeTspJson("q", { q: "hello", v: [TSP_VERSION], app: "omp", features: ["edit"], ver: version });
116
+ return encodeTspJson("q", { q: "hello", v: [TSP_VERSION], app: "omp", features: ["edit", "undo"], ver: version });
116
117
  }
117
118
 
118
119
  /** One decoded APC message: verb, parameters and raw body. */
@@ -172,6 +173,7 @@ const EVENT_REQUIRED: Readonly<Record<string, Readonly<Record<string, "string" |
172
173
  // `value` varies by control (boolean, number, string, string[], null): the handler checks it.
173
174
  change: { id: "string", item: "string" },
174
175
  edit: { id: "string", from: "number", to: "number", text: "string", cursor: "number", len: "number" },
176
+ undo: { id: "string" },
175
177
  focus: { id: "string" },
176
178
  error: { msg: "string" },
177
179
  gone: { ids: "array" },