@oh-my-pi/pi-tui 16.3.5 → 16.3.7
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 +23 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/components/box.d.ts +1 -1
- package/dist/types/components/cancellable-loader.d.ts +1 -1
- package/dist/types/components/editor.d.ts +4 -4
- package/dist/types/components/image.d.ts +2 -2
- package/dist/types/components/input.d.ts +1 -1
- package/dist/types/components/loader.d.ts +2 -2
- package/dist/types/components/markdown.d.ts +12 -2
- package/dist/types/components/scroll-view.d.ts +2 -2
- package/dist/types/components/select-list.d.ts +3 -3
- package/dist/types/components/settings-list.d.ts +2 -2
- package/dist/types/components/spacer.d.ts +1 -1
- package/dist/types/components/tab-bar.d.ts +1 -1
- package/dist/types/components/text.d.ts +1 -1
- package/dist/types/components/truncated-text.d.ts +1 -1
- package/dist/types/desktop-notify.d.ts +1 -1
- package/dist/types/editor-component.d.ts +2 -2
- package/dist/types/index.d.ts +32 -32
- package/dist/types/keybindings.d.ts +1 -1
- package/dist/types/terminal-capabilities.d.ts +6 -0
- package/dist/types/terminal.d.ts +1 -1
- package/dist/types/tui.d.ts +37 -65
- package/package.json +3 -3
- package/src/autocomplete.ts +57 -45
- package/src/components/image.ts +18 -2
- package/src/components/markdown.ts +58 -5
- package/src/terminal-capabilities.ts +16 -0
- package/src/tui.ts +190 -318
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.3.7] - 2026-07-05
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed an issue where `@` file-reference tokens in slash-command arguments incorrectly triggered prompt-composer autocompletion when the command did not define argument completions.
|
|
10
|
+
- Fixed a memory leak caused by unbounded map growth in the image budget cache.
|
|
11
|
+
|
|
12
|
+
## [16.3.6] - 2026-07-04
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added `Markdown.getLastRenderSettledRows()`: the rendered frozen-token-prefix row count of the most recent streaming render, exposed (hard-monotone per text lineage) for native-scrollback commit gating. Frozen-prefix code blocks now syntax-highlight during streaming renders so settled rows stay byte-stable across finalize.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Rewrote the native-scrollback commit law around a visual-record model: whatever scrolls above the viewport enters history exactly once, in order — nothing painted ever vanishes. `NativeScrollbackLiveRegion` is now a single method (`getNativeScrollbackLiveRegionStart`) reporting an exactness boundary: rows below it commit as exact audited bytes; rows above it commit as frozen visual snapshots that are audit-exempt while their source stays live and strict-verified exactly once when the boundary passes them (a divergence recommits the final content below the frozen fragment — duplication, never loss). `getNativeScrollbackCommitSafeEnd`/`getNativeScrollbackSnapshotSafeEnd` and the heuristic promotion machinery they fed are removed.
|
|
21
|
+
- Simplified committed-prefix auditing to one hard-verified mark deriving three zones (verified/newly-final/frozen); a frame that shrinks below the committed row count re-bases at the first divergence against the recorded prefix so a collapsing live suffix never re-shows or re-commits already-recorded rows.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fixed live tool/eval preview boxes spraying duplicated stale copies into native scrollback mid-run: a still-live block's scrolled rows are now frozen visual snapshots that never re-anchor while it runs; at most one repair happens at finalize.
|
|
26
|
+
- Fixed streamed content vanishing above the viewport mid-turn: scrolled-off rows always reach native scrollback (as exact bytes for declared-final content, as visual snapshots otherwise) instead of being deferred invisibly.
|
|
27
|
+
|
|
5
28
|
## [16.3.5] - 2026-07-04
|
|
6
29
|
|
|
7
30
|
### Fixed
|
|
@@ -19,6 +19,8 @@ export interface SlashCommand {
|
|
|
19
19
|
aliases?: string[];
|
|
20
20
|
description?: string;
|
|
21
21
|
argumentHint?: string;
|
|
22
|
+
/** Whether the command consumes argument text after the command name. False means the full input stays normal prompt text once args are present. */
|
|
23
|
+
allowArgs?: boolean;
|
|
22
24
|
/** Dynamic display-only description for slash-command autocomplete. Must be synchronous and side-effect free. */
|
|
23
25
|
getAutocompleteDescription?: () => string | undefined;
|
|
24
26
|
getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type AutocompleteProvider } from "../autocomplete";
|
|
2
|
-
import type { SymbolTheme } from "../symbols";
|
|
3
|
-
import { type Component, type Focusable } from "../tui";
|
|
4
|
-
import { type SelectListTheme } from "./select-list";
|
|
1
|
+
import { type AutocompleteProvider } from "../autocomplete.js";
|
|
2
|
+
import type { SymbolTheme } from "../symbols.js";
|
|
3
|
+
import { type Component, type Focusable } from "../tui.js";
|
|
4
|
+
import { type SelectListTheme } from "./select-list.js";
|
|
5
5
|
export interface EditorTheme {
|
|
6
6
|
borderColor: (str: string) => string;
|
|
7
7
|
selectList: SelectListTheme;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type ImageDimensions } from "../terminal-capabilities";
|
|
2
|
-
import type { Component } from "../tui";
|
|
1
|
+
import { type ImageDimensions } from "../terminal-capabilities.js";
|
|
2
|
+
import type { Component } from "../tui.js";
|
|
3
3
|
export interface ImageTheme {
|
|
4
4
|
fallbackColor: (str: string) => string;
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SymbolTheme } from "../symbols";
|
|
2
|
-
import type { Component } from "../tui";
|
|
1
|
+
import type { SymbolTheme } from "../symbols.js";
|
|
2
|
+
import type { Component } from "../tui.js";
|
|
3
3
|
/** Drop all L2 cache entries. Call on theme change to prevent stale styled output. */
|
|
4
4
|
export declare function clearRenderCache(): void;
|
|
5
5
|
/**
|
|
@@ -55,6 +55,16 @@ export declare class Markdown implements Component {
|
|
|
55
55
|
invalidate(): void;
|
|
56
56
|
get transientRenderCache(): boolean;
|
|
57
57
|
set transientRenderCache(value: boolean);
|
|
58
|
+
/**
|
|
59
|
+
* Rows at the top of the most recent render() (top padding + rendered
|
|
60
|
+
* frozen-token prefix) whose bytes are settled: byte-stable at this
|
|
61
|
+
* width/theme for as long as the text keeps growing append-only. Hosts
|
|
62
|
+
* feed this to transcript commit gating (see the coding agent's
|
|
63
|
+
* `FinalizableBlock.getTranscriptBlockSettledRows`). 0 outside streaming
|
|
64
|
+
* (`transientRenderCache`) mode, after a text rewind (re-earned on the new
|
|
65
|
+
* lineage), and on cache-served non-streaming renders.
|
|
66
|
+
*/
|
|
67
|
+
getLastRenderSettledRows(): number;
|
|
58
68
|
render(width: number): readonly string[];
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Component } from "../tui";
|
|
2
|
-
import { Ellipsis } from "../utils";
|
|
1
|
+
import type { Component } from "../tui.js";
|
|
2
|
+
import { Ellipsis } from "../utils.js";
|
|
3
3
|
type ScrollbarMode = "auto" | "always" | "never";
|
|
4
4
|
export interface ScrollViewTheme {
|
|
5
5
|
track?: (text: string) => string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type MouseRoutable, type SgrMouseEvent } from "../mouse";
|
|
2
|
-
import type { SymbolTheme } from "../symbols";
|
|
3
|
-
import type { Component } from "../tui";
|
|
1
|
+
import { type MouseRoutable, type SgrMouseEvent } from "../mouse.js";
|
|
2
|
+
import type { SymbolTheme } from "../symbols.js";
|
|
3
|
+
import type { Component } from "../tui.js";
|
|
4
4
|
export interface SelectItem {
|
|
5
5
|
value: string;
|
|
6
6
|
label: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SgrMouseEvent } from "../mouse";
|
|
2
|
-
import type { Component } from "../tui";
|
|
1
|
+
import type { SgrMouseEvent } from "../mouse.js";
|
|
2
|
+
import type { Component } from "../tui.js";
|
|
3
3
|
export interface SettingItem {
|
|
4
4
|
/** Unique identifier for this setting */
|
|
5
5
|
id: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TerminalId, TerminalNotification } from "./terminal-capabilities";
|
|
1
|
+
import type { TerminalId, TerminalNotification } from "./terminal-capabilities.js";
|
|
2
2
|
/** Resolved notifier binary used to fan a notification out to D-Bus. */
|
|
3
3
|
export type DesktopNotifierKind = "notify-send" | "gdbus";
|
|
4
4
|
export interface DesktopNotifier {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
export * from "./autocomplete";
|
|
2
|
-
export * from "./components/box";
|
|
3
|
-
export * from "./components/cancellable-loader";
|
|
4
|
-
export * from "./components/editor";
|
|
5
|
-
export * from "./components/image";
|
|
6
|
-
export * from "./components/input";
|
|
7
|
-
export * from "./components/loader";
|
|
8
|
-
export * from "./components/markdown";
|
|
9
|
-
export * from "./components/scroll-view";
|
|
10
|
-
export * from "./components/select-list";
|
|
11
|
-
export * from "./components/settings-list";
|
|
12
|
-
export * from "./components/spacer";
|
|
13
|
-
export * from "./components/tab-bar";
|
|
14
|
-
export * from "./components/text";
|
|
15
|
-
export * from "./components/truncated-text";
|
|
16
|
-
export * from "./deccara";
|
|
17
|
-
export * from "./desktop-notify";
|
|
18
|
-
export type * from "./editor-component";
|
|
19
|
-
export * from "./fuzzy";
|
|
20
|
-
export * from "./keybindings";
|
|
21
|
-
export * from "./keys";
|
|
22
|
-
export * from "./kitty-graphics";
|
|
23
|
-
export * from "./latex-block";
|
|
24
|
-
export * from "./latex-to-unicode";
|
|
25
|
-
export * from "./mouse";
|
|
26
|
-
export * from "./stdin-buffer";
|
|
27
|
-
export type * from "./symbols";
|
|
28
|
-
export * from "./terminal";
|
|
29
|
-
export * from "./terminal-capabilities";
|
|
30
|
-
export * from "./ttyid";
|
|
31
|
-
export * from "./tui";
|
|
32
|
-
export * from "./utils";
|
|
1
|
+
export * from "./autocomplete.js";
|
|
2
|
+
export * from "./components/box.js";
|
|
3
|
+
export * from "./components/cancellable-loader.js";
|
|
4
|
+
export * from "./components/editor.js";
|
|
5
|
+
export * from "./components/image.js";
|
|
6
|
+
export * from "./components/input.js";
|
|
7
|
+
export * from "./components/loader.js";
|
|
8
|
+
export * from "./components/markdown.js";
|
|
9
|
+
export * from "./components/scroll-view.js";
|
|
10
|
+
export * from "./components/select-list.js";
|
|
11
|
+
export * from "./components/settings-list.js";
|
|
12
|
+
export * from "./components/spacer.js";
|
|
13
|
+
export * from "./components/tab-bar.js";
|
|
14
|
+
export * from "./components/text.js";
|
|
15
|
+
export * from "./components/truncated-text.js";
|
|
16
|
+
export * from "./deccara.js";
|
|
17
|
+
export * from "./desktop-notify.js";
|
|
18
|
+
export type * from "./editor-component.js";
|
|
19
|
+
export * from "./fuzzy.js";
|
|
20
|
+
export * from "./keybindings.js";
|
|
21
|
+
export * from "./keys.js";
|
|
22
|
+
export * from "./kitty-graphics.js";
|
|
23
|
+
export * from "./latex-block.js";
|
|
24
|
+
export * from "./latex-to-unicode.js";
|
|
25
|
+
export * from "./mouse.js";
|
|
26
|
+
export * from "./stdin-buffer.js";
|
|
27
|
+
export type * from "./symbols.js";
|
|
28
|
+
export * from "./terminal.js";
|
|
29
|
+
export * from "./terminal-capabilities.js";
|
|
30
|
+
export * from "./ttyid.js";
|
|
31
|
+
export * from "./tui.js";
|
|
32
|
+
export * from "./utils.js";
|
|
@@ -42,6 +42,12 @@ export declare class TerminalInfo {
|
|
|
42
42
|
export declare function isInsideTmux(env?: NodeJS.ProcessEnv): boolean;
|
|
43
43
|
/** Detect terminal multiplexers where scrollback clearing and height-change redraws are hostile. */
|
|
44
44
|
export declare function isInsideTerminalMultiplexer(env?: NodeJS.ProcessEnv): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the agent process is running inside a Zellij session. Read fresh on
|
|
47
|
+
* each call (like {@link isInsideTmux}) so a session attached/detached mid-run
|
|
48
|
+
* is observed and tests can toggle `Bun.env.ZELLIJ` per case.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isInsideZellij(env?: NodeJS.ProcessEnv): boolean;
|
|
45
51
|
/**
|
|
46
52
|
* Wrap a control-sequence payload in tmux's DCS passthrough envelope. Each
|
|
47
53
|
* ESC byte inside `payload` is doubled per tmux's escape rules. tmux strips
|
package/dist/types/terminal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type HangulCompatibilityJamoWidth } from "./utils";
|
|
1
|
+
import { type HangulCompatibilityJamoWidth } from "./utils.js";
|
|
2
2
|
export declare function resolveHangulCompatibilityJamoWidthFromTerminalIdentity(env?: NodeJS.ProcessEnv): HangulCompatibilityJamoWidth;
|
|
3
3
|
/**
|
|
4
4
|
* Split `data` into chunks whose encoded UTF-8 byte length is no greater than
|
package/dist/types/tui.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ImageBudget } from "./components/image";
|
|
2
|
-
import { type Terminal } from "./terminal";
|
|
3
|
-
import { visibleWidth } from "./utils";
|
|
1
|
+
import { ImageBudget } from "./components/image.js";
|
|
2
|
+
import { type Terminal } from "./terminal.js";
|
|
3
|
+
import { visibleWidth } from "./utils.js";
|
|
4
4
|
type InputListenerResult = {
|
|
5
5
|
consume?: boolean;
|
|
6
6
|
data?: string;
|
|
@@ -76,45 +76,23 @@ export interface OverlayFocusOwner {
|
|
|
76
76
|
ownsOverlayFocusTarget(component: Component): boolean;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Component seam for append-only native-scrollback commits. A component
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
79
|
+
* Component seam for append-only native-scrollback commits. A component whose
|
|
80
|
+
* rendered rows can still change reports, after each render, the local line
|
|
81
|
+
* index where that mutable suffix begins. Rows above the boundary are declared
|
|
82
|
+
* FINAL — byte-stable at the current width for the component's lifetime — and
|
|
83
|
+
* commit to native scrollback as exact, audited content. Rows at/after the
|
|
84
|
+
* boundary repaint in place inside the visible window; when they scroll above
|
|
85
|
+
* the window top they still commit — the tape records what was on screen —
|
|
86
|
+
* but as frozen visual snapshots that are permanently audit-exempt: later
|
|
87
|
+
* re-layout of their source never re-anchors or recommits them. A root that
|
|
88
|
+
* reports no seam commits everything that scrolls as final (shell semantics).
|
|
85
89
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* Rows in `[liveRegionStart, commitSafeEnd)` may commit even though they are
|
|
90
|
-
* technically live, because they will never change. Without it, a single live
|
|
91
|
-
* block that alone overflows the window would hold its scrolled-off head out
|
|
92
|
-
* of history until it finalizes. Volatile live blocks (tool previews that
|
|
93
|
-
* collapse) omit it. Defaults to `liveRegionStart` when absent; a root that
|
|
94
|
-
* reports no seam at all commits everything that scrolls (shell semantics).
|
|
95
|
-
* `getNativeScrollbackSnapshotSafeEnd` optionally reports a still deeper
|
|
96
|
-
* boundary: the line index up to which the live region is *durable* — its rows
|
|
97
|
-
* may still change bytes later (a streaming markdown table re-aligning its
|
|
98
|
-
* columns every row), but their CURRENT snapshot is permanent content, so
|
|
99
|
-
* dropping them when they scroll above the window is forbidden. Unlike
|
|
100
|
-
* `commitSafeEnd` (byte-stable: offered rows are asserted never to re-layout and
|
|
101
|
-
* stay under the committed-prefix audit), rows committed under the snapshot end
|
|
102
|
-
* are audit-EXEMPT once they pass the window top — the engine appends their
|
|
103
|
-
* scroll-off snapshot and never recommits them, so later layout drift becomes a
|
|
104
|
-
* frozen stale row in history (duplication never loss) instead of either a
|
|
105
|
-
* dropped row or an audit re-anchor spray. Provisional live blocks (collapsing
|
|
106
|
-
* tool/edit previews whose head is a throwaway tail window) omit it. Defaults to
|
|
107
|
-
* `commitSafeEnd ?? liveRegionStart` when absent.
|
|
108
|
-
*
|
|
109
|
-
* When several root children report a seam in the same frame, the topmost
|
|
110
|
-
* one (and its commit-safe / snapshot-safe extension) defines the boundary:
|
|
111
|
-
* commits are prefix-only, so everything below the first seam is already
|
|
112
|
-
* excluded.
|
|
90
|
+
* When several root children report a seam in the same frame, the topmost one
|
|
91
|
+
* defines the boundary: exactness is prefix-only, so everything below the
|
|
92
|
+
* first seam is already excluded.
|
|
113
93
|
*/
|
|
114
94
|
export interface NativeScrollbackLiveRegion {
|
|
115
95
|
getNativeScrollbackLiveRegionStart(): number | undefined;
|
|
116
|
-
getNativeScrollbackCommitSafeEnd?(): number | undefined;
|
|
117
|
-
getNativeScrollbackSnapshotSafeEnd?(): number | undefined;
|
|
118
96
|
}
|
|
119
97
|
export interface NativeScrollbackCommittedRows {
|
|
120
98
|
setNativeScrollbackCommittedRows(rows: number): void;
|
|
@@ -294,40 +272,34 @@ export declare function coalesceAdjacentSgr(line: string): string;
|
|
|
294
272
|
* re-anchor the commit index when it does not. Returns the resync row index,
|
|
295
273
|
* or -1 when no resync is needed.
|
|
296
274
|
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
275
|
+
* Zones (verifiedTo ≤ finalTo ≤ prefix.length):
|
|
276
|
+
* [0, verifiedTo) VERIFIED exact rows — sampled with tolerance.
|
|
277
|
+
* [verifiedTo, finalTo) NEWLY-FINAL rows — frozen visual snapshots whose
|
|
278
|
+
* source just became declared-final (the block finalized / a barrier
|
|
279
|
+
* cleared). Hard-scanned in FULL with no tolerance: any content change
|
|
280
|
+
* (a pending header settling, a preview replaced by its result, a tail
|
|
281
|
+
* shifting up after a barrier removal) re-anchors so the final content
|
|
282
|
+
* recommits below the frozen snapshot — duplication, never loss —
|
|
283
|
+
* instead of being committed nowhere and painted nowhere.
|
|
284
|
+
* [finalTo, prefix.length) FROZEN visual snapshots of still-live rows —
|
|
285
|
+
* exempt: their drift is expected (a collapsing preview, a ticking
|
|
286
|
+
* progress tree) and must never spray re-anchors mid-run.
|
|
305
287
|
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* between the two mutation classes — an in-place edit/restyle of a committed
|
|
315
|
-
* row disturbs only the touched rows (alignment below intact; the stale copy
|
|
316
|
-
* in history is the long-accepted artifact), while an insertion/deletion
|
|
317
|
-
* shifts EVERY row below it. So up to 8 non-blank rows within the last 24
|
|
318
|
-
* audited rows are compared SGR-stripped (theme changes stay quiet),
|
|
319
|
-
* tolerating a SINGLE non-hard mismatch (a legitimate one-row edit): aligned ⇒
|
|
320
|
-
* no resync; misaligned ⇒ resync at the first non-equivalent audited row. The
|
|
321
|
-
* tolerance keeps both an offscreen still-live barrier (a ticking spinner) and
|
|
322
|
-
* a no-seam in-place row edit from spraying duplicate snapshots every frame;
|
|
323
|
-
* the hard scan above is what forbids it from swallowing a finalized row.
|
|
288
|
+
* The verified zone's sampled check exploits the asymmetry between the two
|
|
289
|
+
* mutation classes: an in-place edit/restyle disturbs only the touched rows
|
|
290
|
+
* (alignment below stays intact; the stale copy in history is the accepted
|
|
291
|
+
* artifact), while an insertion/deletion shifts EVERY row below it. Up to 8
|
|
292
|
+
* non-blank rows within the last 24 verified rows are compared SGR-stripped
|
|
293
|
+
* (theme changes stay quiet), tolerating a SINGLE mismatch. The tolerance is
|
|
294
|
+
* load-bearing for roots that report NO seam: an animated row already in
|
|
295
|
+
* history would otherwise re-anchor on every glyph tick.
|
|
324
296
|
*
|
|
325
297
|
* Highly repetitive tails (identical filler rows) can mask a shift in the tail
|
|
326
298
|
* sample, in which case the skipped rows are content-identical to the committed
|
|
327
299
|
* ones — observationally harmless. Exported for the render-stress harness, whose
|
|
328
300
|
* shadow commit ledger must mirror the engine's law exactly.
|
|
329
301
|
*/
|
|
330
|
-
export declare function findCommittedPrefixResync(frame: readonly string[], prefix: readonly string[],
|
|
302
|
+
export declare function findCommittedPrefixResync(frame: readonly string[], prefix: readonly string[], verifiedTo?: number, finalTo?: number): number;
|
|
331
303
|
/**
|
|
332
304
|
* TUI - Main class for managing terminal UI with differential rendering
|
|
333
305
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-tui",
|
|
4
|
-
"version": "16.3.
|
|
4
|
+
"version": "16.3.7",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"fmt": "biome format --write ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@oh-my-pi/pi-natives": "16.3.
|
|
41
|
-
"@oh-my-pi/pi-utils": "16.3.
|
|
40
|
+
"@oh-my-pi/pi-natives": "16.3.7",
|
|
41
|
+
"@oh-my-pi/pi-utils": "16.3.7",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.5"
|
|
44
44
|
},
|
package/src/autocomplete.ts
CHANGED
|
@@ -182,6 +182,8 @@ export interface SlashCommand {
|
|
|
182
182
|
aliases?: string[];
|
|
183
183
|
description?: string;
|
|
184
184
|
argumentHint?: string;
|
|
185
|
+
/** Whether the command consumes argument text after the command name. False means the full input stays normal prompt text once args are present. */
|
|
186
|
+
allowArgs?: boolean;
|
|
185
187
|
/** Dynamic display-only description for slash-command autocomplete. Must be synchronous and side-effect free. */
|
|
186
188
|
getAutocompleteDescription?: () => string | undefined;
|
|
187
189
|
// Function to get argument completions for this command
|
|
@@ -387,44 +389,18 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
387
389
|
const currentLine = lines[cursorLine] || "";
|
|
388
390
|
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
389
391
|
|
|
390
|
-
// Check for @ file reference (fuzzy search) - must be after a delimiter or at start
|
|
391
|
-
const atPrefix = this.#extractAtPrefix(textBeforeCursor);
|
|
392
|
-
if (atPrefix) {
|
|
393
|
-
const { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
394
|
-
// Recursive fuzzy walks rooted outside the project (e.g. `@../`,
|
|
395
|
-
// `@~/`, `@/abs`) can be huge — a parent dir full of sibling
|
|
396
|
-
// projects blows past several seconds of latency. Outside cwd,
|
|
397
|
-
// fall back to plain prefix listing of the immediate directory
|
|
398
|
-
// (matches Claude Code's behavior). Inside cwd we keep the
|
|
399
|
-
// fuzzy-then-prefix flow.
|
|
400
|
-
if (rawPrefix.length > 0 && this.#isOutsideCwd(rawPrefix)) {
|
|
401
|
-
const items = await this.#getFileSuggestions(atPrefix);
|
|
402
|
-
if (items.length === 0) return null;
|
|
403
|
-
return { items, prefix: atPrefix };
|
|
404
|
-
}
|
|
405
|
-
const suggestions =
|
|
406
|
-
rawPrefix.length > 0
|
|
407
|
-
? await this.#getFuzzyFileSuggestions(rawPrefix, { isQuotedPrefix })
|
|
408
|
-
: await this.#getFileSuggestions("@");
|
|
409
|
-
if (suggestions.length === 0 && rawPrefix.length > 0) {
|
|
410
|
-
const fallback = await this.#getFileSuggestions(atPrefix);
|
|
411
|
-
if (fallback.length === 0) return null;
|
|
412
|
-
return { items: fallback, prefix: atPrefix };
|
|
413
|
-
}
|
|
414
|
-
if (suggestions.length === 0) return null;
|
|
415
|
-
|
|
416
|
-
return {
|
|
417
|
-
items: suggestions,
|
|
418
|
-
prefix: atPrefix,
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
392
|
const leadingSlashStart = findLeadingSlashCommandStart(textBeforeCursor);
|
|
423
393
|
const trailingSlashStart = findTrailingSlashCommandStart(textBeforeCursor);
|
|
424
394
|
const hasPromptTextBeforeTrailingSlash =
|
|
425
395
|
trailingSlashStart !== null &&
|
|
426
396
|
hasPromptTextBeforeSlash(lines, cursorLine, textBeforeCursor, trailingSlashStart);
|
|
427
|
-
const
|
|
397
|
+
const hasPromptTextBeforeLeadingSlash =
|
|
398
|
+
leadingSlashStart !== null && hasPromptTextBeforeSlash(lines, cursorLine, textBeforeCursor, leadingSlashStart);
|
|
399
|
+
const slashStart = hasPromptTextBeforeTrailingSlash
|
|
400
|
+
? trailingSlashStart
|
|
401
|
+
: hasPromptTextBeforeLeadingSlash
|
|
402
|
+
? null
|
|
403
|
+
: leadingSlashStart;
|
|
428
404
|
if (slashStart !== null) {
|
|
429
405
|
const commandText = textBeforeCursor.slice(slashStart);
|
|
430
406
|
const spaceIndex = commandText.indexOf(" ");
|
|
@@ -453,26 +429,62 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
453
429
|
// A mid-prompt slash token with no matching skill may still be an
|
|
454
430
|
// absolute path (`see /tmp`); fall through to file-path completion.
|
|
455
431
|
} else if (!isMidPromptSkillLookup) {
|
|
456
|
-
//
|
|
432
|
+
// Submitted slash commands own their argument text only when the
|
|
433
|
+
// matched command accepts args. No-arg slash-looking prompts such
|
|
434
|
+
// as `/settings @file` still fall through to prompt-composer
|
|
435
|
+
// completions because submit treats them as normal prompt text.
|
|
457
436
|
const commandName = commandText.slice(1, spaceIndex); // Command without "/"
|
|
458
437
|
const argumentText = commandText.slice(spaceIndex + 1); // Text after space
|
|
459
438
|
|
|
460
439
|
const command = this.#commands.find(cmd => commandMatchesNameOrAlias(cmd, commandName));
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
|
|
440
|
+
if (command && (!("allowArgs" in command) || command.allowArgs !== false)) {
|
|
441
|
+
if (!("getArgumentCompletions" in command) || !command.getArgumentCompletions) {
|
|
442
|
+
return null; // No argument completion for this command
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const argumentSuggestions = await command.getArgumentCompletions(argumentText);
|
|
446
|
+
if (!Array.isArray(argumentSuggestions) || argumentSuggestions.length === 0) {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
464
449
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
450
|
+
return {
|
|
451
|
+
items: argumentSuggestions,
|
|
452
|
+
prefix: argumentText,
|
|
453
|
+
};
|
|
468
454
|
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
469
457
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
458
|
+
// Check for @ file reference (fuzzy search) - must be after a delimiter or at start
|
|
459
|
+
const atPrefix = this.#extractAtPrefix(textBeforeCursor);
|
|
460
|
+
if (atPrefix) {
|
|
461
|
+
const { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
462
|
+
// Recursive fuzzy walks rooted outside the project (e.g. `@../`,
|
|
463
|
+
// `@~/`, `@/abs`) can be huge — a parent dir full of sibling
|
|
464
|
+
// projects blows past several seconds of latency. Outside cwd,
|
|
465
|
+
// fall back to plain prefix listing of the immediate directory
|
|
466
|
+
// (matches Claude Code's behavior). Inside cwd we keep the
|
|
467
|
+
// fuzzy-then-prefix flow.
|
|
468
|
+
if (rawPrefix.length > 0 && this.#isOutsideCwd(rawPrefix)) {
|
|
469
|
+
const items = await this.#getFileSuggestions(atPrefix);
|
|
470
|
+
if (items.length === 0) return null;
|
|
471
|
+
return { items, prefix: atPrefix };
|
|
474
472
|
}
|
|
475
|
-
|
|
473
|
+
const suggestions =
|
|
474
|
+
rawPrefix.length > 0
|
|
475
|
+
? await this.#getFuzzyFileSuggestions(rawPrefix, { isQuotedPrefix })
|
|
476
|
+
: await this.#getFileSuggestions("@");
|
|
477
|
+
if (suggestions.length === 0 && rawPrefix.length > 0) {
|
|
478
|
+
const fallback = await this.#getFileSuggestions(atPrefix);
|
|
479
|
+
if (fallback.length === 0) return null;
|
|
480
|
+
return { items: fallback, prefix: atPrefix };
|
|
481
|
+
}
|
|
482
|
+
if (suggestions.length === 0) return null;
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
items: suggestions,
|
|
486
|
+
prefix: atPrefix,
|
|
487
|
+
};
|
|
476
488
|
}
|
|
477
489
|
|
|
478
490
|
// Check for file paths - triggered by Tab or if we detect a path pattern
|