@oh-my-pi/pi-tui 18.4.8 → 18.4.9

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 (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/types/apps/debug/log-viewer.d.ts +17 -0
  3. package/dist/types/apps/debug/raw-sse.d.ts +14 -0
  4. package/dist/types/native/backend.d.ts +8 -0
  5. package/dist/types/native/encode.d.ts +5 -1
  6. package/dist/types/native/node.d.ts +14 -1
  7. package/dist/types/native/reconcile.d.ts +5 -0
  8. package/dist/types/overlays/extensions/extension-dashboard.d.ts +31 -0
  9. package/dist/types/overlays/extensions/extension-list.d.ts +37 -0
  10. package/dist/types/overlays/extensions/inspector-panel.d.ts +8 -0
  11. package/dist/types/overlays/jobs-panel.d.ts +35 -9
  12. package/dist/types/prompt/word-completion.d.ts +12 -0
  13. package/dist/types/terminal.d.ts +7 -0
  14. package/dist/types/tools/bash-interactive.d.ts +15 -0
  15. package/dist/types/tools/output-meta.d.ts +8 -2
  16. package/dist/types/tools/streaming-output.d.ts +15 -10
  17. package/dist/types/tui.d.ts +5 -0
  18. package/dist/types/windows-input-mode.d.ts +26 -0
  19. package/package.json +9 -9
  20. package/src/apps/debug/log-viewer.ts +236 -2
  21. package/src/apps/debug/protocol-probe.ts +10 -1
  22. package/src/apps/debug/raw-sse.ts +210 -28
  23. package/src/native/apply.ts +2 -1
  24. package/src/native/backend.ts +22 -0
  25. package/src/native/encode.ts +7 -2
  26. package/src/native/node.ts +15 -1
  27. package/src/native/reconcile.ts +28 -1
  28. package/src/overlays/extensions/extension-dashboard.ts +275 -23
  29. package/src/overlays/extensions/extension-list.ts +274 -14
  30. package/src/overlays/extensions/inspector-panel.ts +368 -0
  31. package/src/overlays/hook-selector.ts +1 -1
  32. package/src/overlays/jobs-panel.ts +222 -49
  33. package/src/prompt/composer-cache.ts +26 -3
  34. package/src/prompt/word-completion.ts +31 -13
  35. package/src/terminal.ts +37 -1
  36. package/src/tools/bash-interactive.ts +85 -16
  37. package/src/tools/output-meta.ts +13 -5
  38. package/src/tools/streaming-output.ts +48 -23
  39. package/src/tui.ts +52 -14
  40. package/src/windows-input-mode.ts +206 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.4.9] - 2026-10-01
6
+
7
+ ### Added
8
+
9
+ - Exported `wordCompletionQuery()` so hosts outside the editor can apply the same prose eligibility rules used by ghost-text word completion.
10
+ - Added a full-featured Background jobs view with selectable jobs, live status and elapsed time, working directory, process IDs, exit code, command, tailing output, and cancellation for running jobs.
11
+
12
+ ### Changed
13
+
14
+ - `OutputSink` now limits artifact files to 16 MiB by default while preserving the beginning and end of oversized output and marking the omitted bytes. Set `artifactMaxBytes: 0` to keep artifacts unbounded; `dump()` reports omitted bytes and full-output references identify sampled artifacts.
15
+
16
+ ### Fixed
17
+
18
+ - Reduced unnecessary composer startup-cache writes and ensured the cache database is released when it closes on Windows.
19
+ - Fixed Shift+Enter and Ctrl+Enter prompt behavior in Windows Terminal 1.24 and earlier; Shift+Enter now inserts a newline and Ctrl+Enter sends a follow-up, matching other platforms.
20
+ - Improved the Tern native terminal experience across background jobs, settings, debugging, logs, extension management, interactive shell, and provider streams: views remain usable and navigable, preserve output and selection behavior, support keyboard scrolling, and keep key actions accessible.
21
+ - Fixed multi-line labels in Background jobs and multi-line titles in native-terminal prompts so their formatting remains readable.
22
+
5
23
  ## [18.4.8] - 2026-10-01
6
24
 
7
25
  ### Fixed
@@ -1,4 +1,5 @@
1
1
  import type { Component } from "../../tui.js";
2
+ import type { DescribeContext, NativeNode, NativeUiEvent } from "../../native/node.js";
2
3
  /** Host capabilities for copying and fetching earlier log entries. */
3
4
  export interface LogViewerDeps {
4
5
  copyToClipboard(text: string): void;
@@ -76,6 +77,22 @@ export declare class DebugLogViewerComponent implements Component {
76
77
  handleInput(keyData: string): void;
77
78
  invalidate(): void;
78
79
  dispose(): void;
80
+ /** A `picker` sheet floats over the transcript instead of taking the screen. */
81
+ nativeSheet(cx: DescribeContext): boolean;
82
+ /**
83
+ * The logs as a picker sheet: the filter as its query, one row per entry
84
+ * (message, fields, time, pid, level dot) with the session boundary as a
85
+ * group head, the range selection checked, the entry under the cursor
86
+ * pretty-printed below, and the keys' actions in the bar. Without the
87
+ * `picker` kind it falls back to rows.
88
+ */
89
+ describe(cx: DescribeContext): NativeNode | null;
90
+ /**
91
+ * Pointer actions run the keys' code: a row click moves the cursor there,
92
+ * a second click copies it (or loads older entries from that row), and the
93
+ * bar's buttons mirror their keys.
94
+ */
95
+ handleNativeEvent(event: NativeUiEvent): void;
79
96
  render(width: number): readonly string[];
80
97
  }
81
98
  export {};
@@ -1,4 +1,5 @@
1
1
  import type { Component } from "../../tui.js";
2
+ import type { NativeNode, NativeScreen, NativeUiEvent } from "../../native/node.js";
2
3
  import { type RawSseDebugBuffer } from "./raw-sse-buffer.js";
3
4
  /** @internal Exported for tests. */
4
5
  export declare function expandPrettyDataLines(raw: readonly string[]): string[];
@@ -23,4 +24,17 @@ export declare class RawSseViewerComponent implements Component {
23
24
  handleInput(keyData: string): void;
24
25
  invalidate(): void;
25
26
  render(width: number): readonly string[];
27
+ /** The action bar's buttons run the same code as their keys. */
28
+ handleNativeEvent(event: NativeUiEvent): void;
29
+ /**
30
+ * The stream as a page of its own: the capture counters over the wire
31
+ * text, one `ansi` block that fills the page, scrolls itself and follows
32
+ * its tail until the user scrolls away (Tern-local, so no follow state
33
+ * here; the scroll keys reach it as `scroll` requests). The copy status
34
+ * and the buttons are this component's own node, docked under the page
35
+ * so they never scroll away.
36
+ */
37
+ describeScreen(): NativeScreen;
38
+ /** The docked bar under the stream page: the copy status and the buttons. */
39
+ describe(): NativeNode;
26
40
  }
@@ -19,6 +19,14 @@ export interface NativeHost {
19
19
  overlays(): readonly NativeOverlay[];
20
20
  /** Component receiving keyboard input. */
21
21
  focused(): Component | null;
22
+ /**
23
+ * The user clicked into a node described by `owners[0]` (then the
24
+ * components containing it, innermost first): move keyboard focus there.
25
+ * `field` is the outermost owner that takes keys and whose focus target is
26
+ * the clicked `editor`/`input`, if any; `sheet` tells the overlays that
27
+ * don't hold the keys while the user works beside them.
28
+ */
29
+ focusFromPointer(owners: readonly Component[], field: Component | null, sheet: (overlay: Component) => boolean): void;
22
30
  requestRender(): void;
23
31
  /** The terminal switched appearance. */
24
32
  appearanceChanged(dark: boolean): void;
@@ -24,7 +24,11 @@ export declare function splitUtf8(body: string, limit: number): string[];
24
24
  export declare function encodeTspMessage(verb: TspVerb, body: string, params?: TspParams, limit?: number): string;
25
25
  /** Encode a JSON message body. */
26
26
  export declare function encodeTspJson(verb: TspVerb, value: unknown, params?: TspParams, limit?: number): string;
27
- /** The `hello` query; callers follow it with a DA1 sentinel. */
27
+ /**
28
+ * The `hello` query; callers follow it with a DA1 sentinel. `features: ["edit"]`
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.
31
+ */
28
32
  export declare function encodeTspHelloQuery(version?: string): string;
29
33
  /** One decoded APC message: verb, parameters and raw body. */
30
34
  export interface TspRawMessage {
@@ -13,7 +13,7 @@
13
13
  * See `packages/wire/src/tsp.ts` for the wire vocabulary and
14
14
  * `crates/tern/SURFACE_PROTOCOL.md` (Stencil repository) for the spec.
15
15
  */
16
- import type { TspEvent, TspKind, TspProps, TspSpan } from "@oh-my-pi/pi-wire";
16
+ import type { TspEvent, TspKind, TspProps, TspScrollBy, TspSpan } from "@oh-my-pi/pi-wire";
17
17
  import type { Component } from "../tui.js";
18
18
  /** A described node: a wire node minus its id, with components allowed as children. */
19
19
  export type NativeNode = {
@@ -32,8 +32,21 @@ export type NativeNode = {
32
32
  * added. Key a node by what it points at to reveal it again on a move.
33
33
  */
34
34
  readonly reveal?: "start" | "end" | "nearest";
35
+ /**
36
+ * Keyboard scrolling forwarded to the terminal (PgUp/PgDn/End reach the
37
+ * program): moves the scroller at or above the node by `by` whenever
38
+ * `n` differs from the previous description of the same node. Bump `n`
39
+ * per key press; presses between two frames repeat the latest `by` once
40
+ * each (`start`/`end` once). A freshly added node never scrolls.
41
+ */
42
+ readonly scroll?: NativeScroll;
35
43
  };
36
44
  }[TspKind];
45
+ /** A {@link NativeNode.scroll} request. */
46
+ export interface NativeScroll {
47
+ readonly by: TspScrollBy;
48
+ readonly n: number;
49
+ }
37
50
  /** A child slot: a described node, or a component that describes itself. */
38
51
  export type NativeChild = NativeNode | Component;
39
52
  /** What a component may consult while describing itself. Never a clock: motion is terminal-clocked. */
@@ -32,6 +32,11 @@ export declare class Reconciler {
32
32
  detachLive(): void;
33
33
  /** The component and keypath that described wire node `id`. */
34
34
  target(id: string): NativeTarget | null;
35
+ /**
36
+ * The component that described wire node `id`, then each component whose
37
+ * node contains it, innermost first (empty for an unknown id).
38
+ */
39
+ owners(id: string): Component[];
35
40
  /** The key a list item node was described with (its key, else its child index). */
36
41
  itemKey(id: string): string;
37
42
  /** Wire id of the first `editor`/`input` `component` describes (for `focus`), or null. */
@@ -12,8 +12,15 @@
12
12
  * - Space/Enter or click: toggle selected item (or provider master switch)
13
13
  * - Wheel over the inspector, or PageUp/PageDown when the inspector overflows: scroll the detail pane
14
14
  * - Esc: clear search (if active) then close
15
+ *
16
+ * Natively (Tern) it is a `picker` sheet: provider scopes, the inventory as
17
+ * rows grouped by kind, the inspector as the side preview, and Toggle /
18
+ * Expand / Close actions; without `picker` it describes a page with the same
19
+ * parts. Pointer events run the key paths above.
15
20
  */
21
+ import type { TspPickerScope } from "@oh-my-pi/pi-wire";
16
22
  import type { Component } from "../../tui.js";
23
+ import type { DescribeContext, NativeNode, NativeUiEvent } from "../../native/node.js";
17
24
  import { type Tab } from "../../components/tab-bar.js";
18
25
  import { type ToolRuntimeSource } from "./inspector-panel.js";
19
26
  import type { ExtensionInspectorSource } from "./inspector-model.js";
@@ -45,6 +52,13 @@ export interface ExtensionDashboardOptions {
45
52
  * re-enabled from the list. The "all" tab is never muted or marked.
46
53
  */
47
54
  export declare function buildTabBarTabs(tabs: ProviderTab[]): Tab[];
55
+ /**
56
+ * Picker scopes from the provider tabs: "all" first, then each provider with
57
+ * its mark and count. Empty enabled providers are disabled (muted in the
58
+ * {@link TabBar}); switched-off providers keep a muted dot and stay
59
+ * selectable so their master switch can be turned back on.
60
+ */
61
+ export declare function buildPickerScopes(tabs: ProviderTab[]): TspPickerScope[];
48
62
  export declare class ExtensionDashboard implements Component {
49
63
  #private;
50
64
  onClose?: () => void;
@@ -59,5 +73,22 @@ export declare class ExtensionDashboard implements Component {
59
73
  render(width: number): readonly string[];
60
74
  invalidate(): void;
61
75
  handleInput(data: string): void;
76
+ /** The dashboard is a `picker` sheet (scopes = providers, preview = inspector) wherever Tern draws pickers. */
77
+ nativeSheet(cx: DescribeContext): boolean;
78
+ /**
79
+ * The picker sheet, or without `picker` a page: head, provider tabs,
80
+ * search, the inventory list beside the inspector, and an action bar.
81
+ * Rebuilt when state moves or the live tool set changes (MCP health
82
+ * changes arrive as render requests, which bump the version).
83
+ */
84
+ describe(cx: DescribeContext): NativeNode;
85
+ /**
86
+ * Pointer input, each on the path of the key it stands for: a row click
87
+ * selects (click on the selected row or double click toggles, like
88
+ * Space/Enter), a scope switches provider (←/→), `toggle`/`expand` run
89
+ * Space and the expand key, `clear` empties the search (first Esc) and
90
+ * `close` closes. Provider tabs in the page route to the {@link TabBar}.
91
+ */
92
+ handleNativeEvent(event: NativeUiEvent): void;
62
93
  dispose(): void;
63
94
  }
@@ -5,7 +5,9 @@
5
5
  * that toggles the entire provider. All items below are dimmed when the
6
6
  * master switch is off.
7
7
  */
8
+ import type { TspPickerGroup, TspPickerItem, TspProps } from "@oh-my-pi/pi-wire";
8
9
  import type { Component } from "../../tui.js";
10
+ import type { NativeNode } from "../../native/node.js";
9
11
  import { type ToolRuntimeSource } from "./inspector-model.js";
10
12
  import { type MCPRuntimeSource } from "./mcp-runtime.js";
11
13
  import { type Extension, type ExtensionKind, type ExtensionProvider } from "./types.js";
@@ -19,6 +21,22 @@ export interface ExtensionListCallbacks {
19
21
  mcpSource?: MCPRuntimeSource;
20
22
  toolSource?: ToolRuntimeSource;
21
23
  }
24
+ /** The inventory as `picker` data: rows, display order with kind groups, the selection and search hits. */
25
+ export interface ExtensionListPickerView {
26
+ readonly items: TspPickerItem[];
27
+ /** Item ids in display order; kind headers become group entries. */
28
+ readonly order: (string | TspPickerGroup)[];
29
+ /** Selected item id; null when nothing or a kind header is selected. */
30
+ readonly selected: string | null;
31
+ readonly hits: Record<string, [number, number][]>;
32
+ /** Whether kind headers group the rows (the unfiltered ALL view). */
33
+ readonly grouped: boolean;
34
+ }
35
+ /** The selected row's switch: its state, and why it cannot flip when it can't. */
36
+ export interface ExtensionListSwitch {
37
+ readonly on: boolean;
38
+ readonly blocked?: string;
39
+ }
22
40
  export declare class ExtensionList implements Component {
23
41
  #private;
24
42
  constructor(extensions: Extension[], callbacks?: ExtensionListCallbacks, maxVisible?: number);
@@ -29,6 +47,8 @@ export declare class ExtensionList implements Component {
29
47
  setMcpSource(source: MCPRuntimeSource | undefined): void;
30
48
  setToolSource(source: ToolRuntimeSource | undefined): void;
31
49
  getSearchQuery(): string;
50
+ /** The search field's text and caret for a native `picker` head. */
51
+ nativeQuery(): Pick<TspProps<"picker">, "query" | "cursor">;
32
52
  resetSelection(): void;
33
53
  getSelectedExtension(): Extension | null;
34
54
  /** Get the currently selected kind header (for preview purposes) */
@@ -38,6 +58,23 @@ export declare class ExtensionList implements Component {
38
58
  clearSearch(): void;
39
59
  invalidate(): void;
40
60
  render(width: number): readonly string[];
61
+ /** The visible rows as `picker` data, joined with live MCP health and tool runtime like {@link render}. */
62
+ pickerView(): ExtensionListPickerView;
63
+ /**
64
+ * The visible rows as a native `list` (kind headers included, so the
65
+ * selection matches the keyboard's), for terminals without `picker`.
66
+ */
67
+ describeList(key: string): NativeNode;
68
+ /**
69
+ * A pointer pick of the row with native id `id`: select it, then toggle it
70
+ * when `activate` (the click-on-selected / double-click path). Returns
71
+ * whether the id named a visible row.
72
+ */
73
+ pickNative(id: string, activate: boolean): boolean;
74
+ /** The selected row's switch, or undefined on a kind header or an empty list. */
75
+ selectedSwitch(): ExtensionListSwitch | undefined;
76
+ /** Toggle the selected item, or flip the provider master switch when on it (Space/Enter). */
77
+ activateSelected(): void;
41
78
  /** Highlight the row under the pointer (null clears). */
42
79
  setHoverIndex(index: number | null): void;
43
80
  /**
@@ -1,4 +1,5 @@
1
1
  import type { Component } from "../../tui.js";
2
+ import type { NativeNode } from "../../native/node.js";
2
3
  import { type ExtensionInspectorSource, type ToolRuntimeSource } from "./inspector-model.js";
3
4
  import { type MCPRuntimeSource } from "./mcp-runtime.js";
4
5
  import { type Extension } from "./types.js";
@@ -14,4 +15,11 @@ export declare class InspectorPanel implements Component {
14
15
  toggleExpanded(): boolean;
15
16
  invalidate(): void;
16
17
  render(width: number): readonly string[];
18
+ /**
19
+ * The selected extension as native preview nodes, in the same order as
20
+ * {@link render}: identity, runtime/enablement and origin facts,
21
+ * description, guidance, kind sections, content preview, config. The
22
+ * collapsed budgets follow {@link toggleExpanded} like the ANSI view.
23
+ */
24
+ describePreview(): NativeNode[];
17
25
  }
@@ -16,6 +16,30 @@ export interface JobsPanelSnapshot {
16
16
  readonly running: readonly JobsPanelJob[];
17
17
  readonly recent: readonly JobsPanelJob[];
18
18
  }
19
+ /** What the sheet shows about the selected job beyond its row. */
20
+ export interface JobsPanelDetail {
21
+ /** Full command line of a job that runs a process (rows show the cut label). */
22
+ readonly command?: string;
23
+ readonly cwd?: string;
24
+ /** Live pids the job's command spawned. */
25
+ readonly pids: readonly number[];
26
+ readonly exitCode?: number;
27
+ /** Output tail while running; the final text once settled. */
28
+ readonly output?: string;
29
+ /** Artifact holding the full output when `output` is cut. */
30
+ readonly artifactId?: string;
31
+ }
32
+ /** The session behind a {@link JobsSheet}. */
33
+ export interface JobsSheetSource {
34
+ /** Current jobs; runs once per frame. */
35
+ load(): JobsPanelSnapshot;
36
+ /** The selected job's detail; runs once per frame, undefined once the job is gone. */
37
+ inspect(id: string): JobsPanelDetail | undefined;
38
+ /** Cancel a running job. */
39
+ cancel(id: string): void;
40
+ /** Dismiss the sheet. */
41
+ close(): void;
42
+ }
19
43
  /** The `/jobs` block: `ansi` renders in the terminal, the snapshot describes the native frame. */
20
44
  export declare class JobsPanel extends Container {
21
45
  #private;
@@ -23,26 +47,28 @@ export declare class JobsPanel extends Container {
23
47
  describe(cx: DescribeContext): NativeNode;
24
48
  }
25
49
  /**
26
- * The jobs pill's sheet: a centred `md` glass sheet titled Background jobs
27
- * that re-reads the session's jobs on every frame, so rows start, settle and
28
- * move to Recent while it is open. Esc or Close dismisses it; nothing enters
29
- * the transcript.
50
+ * The jobs pill's sheet: a centred `lg` glass sheet titled Background jobs.
51
+ * A selectable list of every job (running first) sits over the selected
52
+ * job's detail: status and live elapsed (a task job's `agent` node), cwd,
53
+ * live pids, exit code, the full command and a tail-following output pane.
54
+ * It re-reads the session every frame, so the caller re-renders it on a
55
+ * timer while it is open. ↑/↓ select, X cancels the selected running job,
56
+ * Esc or Close dismisses; nothing enters the transcript.
30
57
  */
31
58
  export declare class JobsSheet implements Component {
32
59
  #private;
33
60
  readonly nativeOverlay: {
34
61
  readonly role: "omp.overlay.jobs";
35
- readonly size: "md";
62
+ readonly size: "lg";
36
63
  readonly anchor: "center";
37
64
  readonly head: "Background jobs";
38
65
  };
39
- /** `load` returns the current jobs; it runs once per frame. */
40
- constructor(load: () => JobsPanelSnapshot, onClose: () => void);
66
+ constructor(source: JobsSheetSource);
41
67
  describe(cx: DescribeContext): NativeNode;
42
- /** Rows for a terminal without the native surface: status, label, type and age per job. */
68
+ /** Rows for a terminal without the native surface: one per job, then the selected job's facts and output tail. */
43
69
  render(width: number): readonly string[];
44
70
  invalidate(): void;
45
71
  handleInput(data: string): void;
46
- /** Close runs what Esc runs. */
72
+ /** Clicks select a row; Close and Cancel job run what Esc and X run. */
47
73
  handleNativeEvent(event: NativeUiEvent): void;
48
74
  }
@@ -1,4 +1,5 @@
1
1
  import type { EditorTextAssistProvider } from "../components/editor.js";
2
+ import { ProseSource } from "./prose-gate.js";
2
3
  /** Values of the `spelling.autocomplete` setting. */
3
4
  export declare const WORD_COMPLETION_METHODS: readonly ["off", "auto", "ngram", "smollm", "apple"];
4
5
  /** Configured word-completion engine; `off` disables ghost text. */
@@ -21,6 +22,17 @@ export type WordPredictionBackendResolver = (method: WordCompletionEngine) => Wo
21
22
  * before the host finished starting. Pass `undefined` to detach.
22
23
  */
23
24
  export declare function setWordPredictionHost(resolver: WordPredictionBackendResolver | undefined): void;
25
+ /** The prose word ending at the cursor and the editor text before it: what an engine is asked to complete. */
26
+ export interface WordCompletionQuery {
27
+ before: string;
28
+ prefix: string;
29
+ }
30
+ /**
31
+ * The query for the prose word ending at the cursor, or `undefined` where no
32
+ * ghost text applies (mid-word, no word, code, paths, commands). `prose`
33
+ * memoizes the whole-buffer mask; pass one instance across keystrokes.
34
+ */
35
+ export declare function wordCompletionQuery(lines: readonly string[], cursorLine: number, cursorCol: number, prose?: ProseSource): WordCompletionQuery | undefined;
24
36
  /**
25
37
  * Ghost-text word completion for the prose word ending at the cursor, served
26
38
  * by an injected asynchronous {@link WordPredictionBackend}.
@@ -276,6 +276,13 @@ export interface ProcessTerminalOptions {
276
276
  * `WSL_INTEROP`) — the suite must behave identically on WSL and on CI.
277
277
  */
278
278
  conpty?: boolean;
279
+ /**
280
+ * Whether stdin is a native Windows console handle (win32, not WSL), whose
281
+ * console host answers `CSI ? 9001 h` with win32-input-mode key records.
282
+ * Defaults to `process.platform === "win32"`; only consulted when `conpty`
283
+ * is also true.
284
+ */
285
+ nativeWindowsConsole?: boolean;
279
286
  }
280
287
  /**
281
288
  * Real terminal using process.stdin/stdout
@@ -1,5 +1,6 @@
1
1
  import type * as XtermModule from "@oh-my-pi/pi-utils/vterm";
2
2
  import type { Component } from "../tui.js";
3
+ import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
3
4
  import type { Theme } from "../theme/theme.js";
4
5
  /** Resize-only backend exposed by an interactive PTY controller. */
5
6
  export interface BashInteractiveTerminalBackend {
@@ -8,6 +9,12 @@ export interface BashInteractiveTerminalBackend {
8
9
  /** Interactive terminal overlay driven by an external PTY controller. */
9
10
  export declare class BashInteractiveOverlayComponent implements Component {
10
11
  #private;
12
+ /** Native sheet: a full-pane glass overlay titled Console. */
13
+ readonly nativeOverlay: {
14
+ readonly role: "omp.overlay.console";
15
+ readonly size: "full";
16
+ readonly head: "Console";
17
+ };
11
18
  constructor(command: string, uiTheme: Theme, getTerminalRows: () => number, terminalCtor: typeof XtermModule.Terminal, backend: BashInteractiveTerminalBackend);
12
19
  /** Connects normalized keyboard input and overlay lifecycle events to the controller. */
13
20
  setHandlers(onInput: (data: string) => void, onDismiss: () => void, onDispose: () => void): void;
@@ -22,6 +29,14 @@ export declare class BashInteractiveOverlayComponent implements Component {
22
29
  timedOut: boolean;
23
30
  }): void;
24
31
  handleInput(data: string): void;
32
+ /** The Force kill button runs the Esc path. */
33
+ handleNativeEvent(event: NativeUiEvent): void;
34
+ /**
35
+ * Native view: the command with its state badge, the PTY screen as an
36
+ * `ansi` node (SGR kept, fixed to the PTY grid), and a footer with the
37
+ * Force kill action while running.
38
+ */
39
+ describe(cx: DescribeContext): NativeNode;
25
40
  render(width: number): readonly string[];
26
41
  invalidate(): void;
27
42
  dispose(): void;
@@ -31,6 +31,8 @@ export interface TruncationMeta {
31
31
  elidedLines?: number;
32
32
  /** Artifact ID if full output was saved */
33
33
  artifactId?: string;
34
+ /** Bytes the artifact cap dropped from the saved file's middle; the artifact is then a head/tail sample. */
35
+ artifactElidedBytes?: number;
34
36
  /** Next offset for pagination (head truncation only) */
35
37
  nextOffset?: number;
36
38
  /**
@@ -87,6 +89,7 @@ export interface LimitsMeta {
87
89
  maxColumn: number;
88
90
  unit?: "bytes" | "chars";
89
91
  artifactId?: string;
92
+ artifactElidedBytes?: number;
90
93
  };
91
94
  }
92
95
  /**
@@ -111,8 +114,11 @@ export interface OutputMeta {
111
114
  * Messages that don't match the expected format are appended ungrouped at the end.
112
115
  */
113
116
  export declare function formatGroupedDiagnosticMessages(messages: string[]): string;
114
- /** Format a recoverable output artifact link. */
115
- export declare function formatFullOutputReference(artifactId: string): string;
117
+ /**
118
+ * Format a recoverable output artifact link. An artifact the size cap cut
119
+ * (`artifactElidedBytes > 0`) is labeled as the head/tail sample it holds.
120
+ */
121
+ export declare function formatFullOutputReference(artifactId: string, artifactElidedBytes?: number): string;
116
122
  /** Strip the last literal notice or a matching final line; optionally preserve surrounding whitespace. */
117
123
  export declare function stripTrailingNotice(text: string, notice: string | ((line: string) => boolean), trimResult?: boolean): string;
118
124
  /** Remove the trailing bash raw-output artifact footer while preserving its artifact id. */
@@ -6,12 +6,11 @@ export declare const DEFAULT_MAX_BYTES: number;
6
6
  /** Default UTF-8 byte budget per match line. */
7
7
  export declare const DEFAULT_MAX_COLUMN = 512;
8
8
  /**
9
- * Default artifact-on-disk cap for {@link OutputSink}.
10
- *
11
- * `0` means unbounded: by default, `artifact://<id>` references preserve the
12
- * complete raw stream instead of a capped head/tail sample.
9
+ * Default artifact-on-disk cap for {@link OutputSink}: 16 MiB, split into a
10
+ * {@link ARTIFACT_DEFAULT_HEAD_BYTES} head and a rolling tail. `0` means
11
+ * unbounded (the complete raw stream is preserved).
13
12
  */
14
- export declare const ARTIFACT_DEFAULT_MAX_BYTES = 0;
13
+ export declare const ARTIFACT_DEFAULT_MAX_BYTES: number;
15
14
  /** Default head budget; the remainder becomes the rolling tail window. */
16
15
  export declare const ARTIFACT_DEFAULT_HEAD_BYTES: number;
17
16
  /** First failed artifact I/O operation; safe to persist without exposing filesystem errors. */
@@ -36,6 +35,11 @@ export interface OutputSummary {
36
35
  columnMax?: number;
37
36
  /** Artifact ID for internal URL access (artifact://<id>) when truncated */
38
37
  artifactId?: string;
38
+ /**
39
+ * Bytes the artifact cap dropped from the middle of the saved file. When
40
+ * set, `artifact://<id>` holds a head/tail sample, not the full output.
41
+ */
42
+ artifactElidedBytes?: number;
39
43
  /** Full raw output was not completely saved; artifactId is unavailable. */
40
44
  artifactError?: OutputArtifactError;
41
45
  }
@@ -67,17 +71,18 @@ export interface OutputSinkOptions {
67
71
  /** Minimum ms between onChunk calls. 0 = every chunk (default). */
68
72
  chunkThrottleMs?: number;
69
73
  /**
70
- * Optional cap on bytes written to the artifact-on-disk file. When the cap
71
- * is hit, the head window is preserved verbatim and subsequent output feeds
72
- * a rolling tail window; on close, the sink writes a single
74
+ * Cap on bytes written to the artifact-on-disk file. When the cap is hit,
75
+ * the head window is preserved verbatim and subsequent output feeds a
76
+ * rolling tail window; on close, the sink writes a single
73
77
  * `[ARTIFACT TRUNCATED: …]` notice between them. Default
74
- * {@link ARTIFACT_DEFAULT_MAX_BYTES} (unbounded).
78
+ * {@link ARTIFACT_DEFAULT_MAX_BYTES} (16 MiB); `0` = unbounded.
75
79
  */
76
80
  artifactMaxBytes?: number;
77
81
  /**
78
82
  * Bytes reserved for the head window of the capped artifact file. The
79
83
  * tail window receives `artifactMaxBytes - artifactHeadBytes`. Default
80
- * {@link ARTIFACT_DEFAULT_HEAD_BYTES}; clamped to `[0, artifactMaxBytes]`.
84
+ * {@link ARTIFACT_DEFAULT_HEAD_BYTES}, but at most half of
85
+ * `artifactMaxBytes`; clamped to `[0, artifactMaxBytes]`.
81
86
  */
82
87
  artifactHeadBytes?: number;
83
88
  }
@@ -330,6 +330,11 @@ export declare class TUI extends Container {
330
330
  options?: OverlayOptions;
331
331
  preFocus: Component | null;
332
332
  hidden: boolean;
333
+ /**
334
+ * A sheet the user clicked away from (see {@link TUI.#focusFromPointer}):
335
+ * it stays up but no longer holds the keys, until focus moves back into it.
336
+ */
337
+ released: boolean;
333
338
  }[];
334
339
  constructor(terminal: Terminal, showHardwareCursor?: boolean, options?: TUIOptions);
335
340
  /**
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Decode Windows console win32-input-mode key records into the key sequences
3
+ * the rest of the TUI already understands.
4
+ *
5
+ * Hosts without the kitty keyboard protocol (Windows Terminal before 1.25)
6
+ * deliver Shift+Enter as a bare `\r` (indistinguishable from Enter) and
7
+ * Ctrl+Enter as `\n`. Enabling `CSI ? 9001 h` makes the console host serving
8
+ * this process report every key as `CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _`, which
9
+ * carries the full modifier state. Paste text, mouse reports and terminal
10
+ * replies keep arriving as plain VT, so only key records need translating.
11
+ *
12
+ * Unmodified and legacy-expressible keys translate to the bytes a legacy
13
+ * terminal sends; chords legacy encoding loses (modified Enter/Tab/Escape,
14
+ * Ctrl+Shift+letter, …) translate to kitty CSI-u, which key matching already
15
+ * parses.
16
+ */
17
+ /** Stateful decoder: joins UTF-16 surrogate halves that arrive as separate records. */
18
+ export declare class Win32InputModeDecoder {
19
+ #private;
20
+ /**
21
+ * Translate one stdin sequence. Returns `undefined` when `data` is not a
22
+ * win32-input-mode record (pass it through unchanged), otherwise the key
23
+ * events it produces — empty for key releases and bare modifier presses.
24
+ */
25
+ decode(data: string): string[] | undefined;
26
+ }
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.8",
4
+ "version": "18.4.9",
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.8",
45
- "@oh-my-pi/pi-agent-core": "18.4.8",
46
- "@oh-my-pi/pi-ai": "18.4.8",
47
- "@oh-my-pi/pi-catalog": "18.4.8",
48
- "@oh-my-pi/pi-natives": "18.4.8",
49
- "@oh-my-pi/pi-utils": "18.4.8",
50
- "@oh-my-pi/pi-wire": "18.4.8",
51
- "@oh-my-pi/snapcompact": "18.4.8"
44
+ "@oh-my-pi/omptype": "18.4.9",
45
+ "@oh-my-pi/pi-agent-core": "18.4.9",
46
+ "@oh-my-pi/pi-ai": "18.4.9",
47
+ "@oh-my-pi/pi-catalog": "18.4.9",
48
+ "@oh-my-pi/pi-natives": "18.4.9",
49
+ "@oh-my-pi/pi-utils": "18.4.9",
50
+ "@oh-my-pi/pi-wire": "18.4.9",
51
+ "@oh-my-pi/snapcompact": "18.4.9"
52
52
  },
53
53
  "devDependencies": {
54
54
  "kitty-vt-wasm": "^0.2.0"