@oh-my-pi/pi-tui 18.11.2 → 18.12.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.
- package/CHANGELOG.md +37 -100
- package/README.md +36 -0
- package/dist/types/components/markdown.d.ts +4 -3
- package/dist/types/overlays/approval-dialog.d.ts +25 -15
- package/dist/types/overlays/model-picker.d.ts +2 -0
- package/dist/types/overlays/session-selector.d.ts +2 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
- package/dist/types/render/hanging-wrap.d.ts +2 -0
- package/dist/types/render/render-utils.d.ts +9 -1
- package/dist/types/status-line/types.d.ts +3 -0
- package/dist/types/terminal-capabilities.d.ts +8 -1
- package/dist/types/terminal-multiplexer.d.ts +6 -0
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme.d.ts +11 -0
- package/dist/types/tools/ast-edit.d.ts +2 -2
- package/dist/types/tools/ast-grep.d.ts +2 -2
- package/dist/types/tools/edit.d.ts +2 -0
- package/dist/types/tools/find.d.ts +14 -4
- package/dist/types/tools/glob.d.ts +3 -3
- package/dist/types/tools/grep.d.ts +2 -2
- package/dist/types/tools/read.d.ts +13 -5
- package/dist/types/tools/task.d.ts +8 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/package.json +9 -9
- package/src/chat/assistant-message.ts +3 -1
- package/src/chat/read-tool-group.ts +17 -9
- package/src/chat/tool-execution.ts +23 -6
- package/src/components/image.ts +31 -14
- package/src/components/markdown.ts +74 -5
- package/src/overlays/approval-dialog.ts +104 -36
- package/src/overlays/model-picker.ts +58 -21
- package/src/overlays/session-selector.ts +14 -9
- package/src/prompt/internal-url-autocomplete.ts +1 -0
- package/src/render/hanging-wrap.ts +28 -1
- package/src/render/render-utils.ts +16 -2
- package/src/status-line/component.ts +1 -0
- package/src/status-line/segments.ts +16 -5
- package/src/status-line/types.ts +1 -0
- package/src/terminal-capabilities.ts +35 -21
- package/src/terminal-multiplexer.ts +14 -0
- package/src/terminal.ts +11 -2
- package/src/theme/symbols.ts +1 -0
- package/src/theme/theme-class.ts +1 -0
- package/src/theme/theme.ts +46 -14
- package/src/tmux.ts +7 -1
- package/src/tools/ast-edit.ts +28 -4
- package/src/tools/ast-grep.ts +24 -4
- package/src/tools/debug.ts +2 -1
- package/src/tools/default-renderer.ts +3 -2
- package/src/tools/edit.ts +77 -16
- package/src/tools/find.ts +65 -15
- package/src/tools/glob.ts +51 -17
- package/src/tools/grep.ts +94 -11
- package/src/tools/lsp.ts +23 -18
- package/src/tools/mcp.ts +2 -2
- package/src/tools/read.ts +52 -22
- package/src/tools/task.ts +17 -7
- package/src/tools/web-search.ts +2 -1
- package/src/tools/write.ts +17 -5
- package/src/tools/yield.ts +2 -2
- package/src/tui.ts +36 -0
package/src/tools/write.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
2
3
|
import type { HighlightStream } from "@oh-my-pi/pi-natives";
|
|
3
4
|
import type { Component } from "../tui";
|
|
4
5
|
import { fencedCode } from "../components/markdown";
|
|
@@ -434,6 +435,8 @@ function renderContentPreview(
|
|
|
434
435
|
/** Render context for the write tool: resolves an `xd://`-mounted tool so its live renderer drives device dispatch previews. */
|
|
435
436
|
export interface WriteRenderContext {
|
|
436
437
|
resolveXdevMounted?: (name: string) => XdevMountedRenderer | undefined;
|
|
438
|
+
/** Original result metadata used only for filesystem link targets. */
|
|
439
|
+
rawDetails?: unknown;
|
|
437
440
|
}
|
|
438
441
|
|
|
439
442
|
/** Process id and operation named by the text after `proc://`. */
|
|
@@ -550,6 +553,15 @@ function describeWriteUrl(
|
|
|
550
553
|
}
|
|
551
554
|
}
|
|
552
555
|
|
|
556
|
+
function writeResolvedPath(rawDetails: unknown, fallback: string | undefined): string | undefined {
|
|
557
|
+
return rawDetails !== null &&
|
|
558
|
+
typeof rawDetails === "object" &&
|
|
559
|
+
"resolvedPath" in rawDetails &&
|
|
560
|
+
typeof rawDetails.resolvedPath === "string"
|
|
561
|
+
? rawDetails.resolvedPath
|
|
562
|
+
: fallback;
|
|
563
|
+
}
|
|
564
|
+
|
|
553
565
|
/** Native write head shared by the call and result: `Write · path · 22 lines`, executable/diagnostics chips. */
|
|
554
566
|
function writeToolHead(
|
|
555
567
|
rawPath: string,
|
|
@@ -563,7 +575,7 @@ function writeToolHead(
|
|
|
563
575
|
if (diagnostics) badges.push(diagnostics);
|
|
564
576
|
return {
|
|
565
577
|
title: "Write",
|
|
566
|
-
target: rawPath ? displayPath(rawPath) : undefined,
|
|
578
|
+
target: rawPath ? sanitizeText(displayPath(rawPath)) : undefined,
|
|
567
579
|
targetKind: "path",
|
|
568
580
|
href: fileHref(details?.resolvedPath),
|
|
569
581
|
meta: lineCount > 0 ? [`${lineCount} line${lineCount === 1 ? "" : "s"}`] : undefined,
|
|
@@ -621,7 +633,7 @@ export const writeToolRenderer = {
|
|
|
621
633
|
if (xdev.name === REPORT_ISSUE_DEVICE_NAME) return renderReportIssueDeviceCall(args.content, uiTheme);
|
|
622
634
|
return renderXdevCall(xdev.name, args.content, options, uiTheme, options.renderContext?.resolveXdevMounted);
|
|
623
635
|
}
|
|
624
|
-
const filePath = shortenPath(rawPath);
|
|
636
|
+
const filePath = sanitizeText(shortenPath(rawPath));
|
|
625
637
|
const lang = rawPath ? (getLanguageFromPath(rawPath) ?? "text") : "text";
|
|
626
638
|
const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
627
639
|
const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
|
|
@@ -705,14 +717,14 @@ export const writeToolRenderer = {
|
|
|
705
717
|
}
|
|
706
718
|
const rawPath =
|
|
707
719
|
typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
|
|
708
|
-
const filePath = shortenPath(rawPath);
|
|
720
|
+
const filePath = sanitizeText(shortenPath(rawPath));
|
|
709
721
|
const fileContent = normalizeDisplayText(args?.content);
|
|
710
722
|
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
711
723
|
const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
712
724
|
// The header shows the cwd-relative path but links to the absolute path the
|
|
713
725
|
// write resolved to (args.path may be relative, which would yield a broken
|
|
714
726
|
// `file://` URI). Falls back to plain text when the result lacks a path.
|
|
715
|
-
const linkTarget = result.details?.resolvedPath;
|
|
727
|
+
const linkTarget = writeResolvedPath(options.renderContext?.rawDetails, result.details?.resolvedPath);
|
|
716
728
|
const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
|
|
717
729
|
const pathDisplay = filePath && linkTarget ? fileHyperlink(linkTarget, styledPath) : styledPath;
|
|
718
730
|
|
|
@@ -835,7 +847,7 @@ export const writeToolRenderer = {
|
|
|
835
847
|
const diagnostics = isPartial ? undefined : result.details?.diagnostics;
|
|
836
848
|
const tool = writeToolHead(rawPath, fileContent, {
|
|
837
849
|
madeExecutable: !isPartial && result.details?.madeExecutable === true,
|
|
838
|
-
resolvedPath: result.details?.resolvedPath,
|
|
850
|
+
resolvedPath: writeResolvedPath(options.renderContext?.rawDetails, result.details?.resolvedPath),
|
|
839
851
|
diagnostics,
|
|
840
852
|
});
|
|
841
853
|
if (result.isError) return { tool, tone: "error", body: [errorText(resultText(result))] };
|
package/src/tools/yield.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { compact, md } from "../native/describe";
|
|
|
9
9
|
import { OwnerMemo } from "../native/memo";
|
|
10
10
|
import type { NativeChild } from "../native/node";
|
|
11
11
|
import { formatOutputPaneLines, styleToolOutputLine } from "../render/output-pane";
|
|
12
|
-
import { formatExpandHint, sanitizeDisplayLines
|
|
12
|
+
import { clipCollapsed, formatExpandHint, sanitizeDisplayLines } from "../render/render-utils";
|
|
13
13
|
import type { StatusLineOptions } from "../render/status-line";
|
|
14
14
|
import { plainToolCard, type ToolCardPhase } from "../render/tool-card";
|
|
15
15
|
import type { Theme } from "../theme/theme";
|
|
@@ -134,7 +134,7 @@ function dataLines(data: unknown, expanded: boolean, width: number, theme: Theme
|
|
|
134
134
|
lines: sanitizeText(data).trimEnd().split("\n"),
|
|
135
135
|
expanded,
|
|
136
136
|
collapsedMaxLines: TEXT_ROWS_COLLAPSED,
|
|
137
|
-
styleLine: line =>
|
|
137
|
+
styleLine: line => clipCollapsed(styleToolOutputLine(line, theme), width, expanded),
|
|
138
138
|
},
|
|
139
139
|
theme,
|
|
140
140
|
).lines,
|
package/src/tui.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
encodeKittyDeleteImage,
|
|
37
37
|
encodeKittyDeletePlacement,
|
|
38
38
|
encodeKittyPlacementLine,
|
|
39
|
+
getTerminalBackgroundColor,
|
|
39
40
|
ImageProtocol,
|
|
40
41
|
isImageProtocolForced,
|
|
41
42
|
isInsideTerminalMultiplexer,
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
synchronizedOutputUserOverride,
|
|
47
48
|
TERMINAL,
|
|
48
49
|
} from "./terminal-capabilities";
|
|
50
|
+
import { onTerminalBackgroundFallbackChange } from "./theme/theme";
|
|
49
51
|
import { compositeLineAt } from "./render/composite";
|
|
50
52
|
import { tmuxMultiplexer } from "./multiplexers/tmux";
|
|
51
53
|
import {
|
|
@@ -1007,6 +1009,7 @@ export class TUI extends Container {
|
|
|
1007
1009
|
#forceViewportRepaintOnNextRender = false;
|
|
1008
1010
|
#hasEverRendered = false;
|
|
1009
1011
|
#stopped = false;
|
|
1012
|
+
#appearanceReportUnsubscribe: (() => void) | undefined;
|
|
1010
1013
|
#cancelPostmortemRestore?: () => void;
|
|
1011
1014
|
/** True between a `deferInput` start() and enableInput(). */
|
|
1012
1015
|
#inputDeferred = false;
|
|
@@ -1478,6 +1481,34 @@ export class TUI extends Container {
|
|
|
1478
1481
|
|
|
1479
1482
|
start(options?: TUIStartOptions): void {
|
|
1480
1483
|
this.#stopped = false;
|
|
1484
|
+
this.#appearanceReportUnsubscribe?.();
|
|
1485
|
+
let backgroundRgb: number | undefined;
|
|
1486
|
+
let subscribed = true;
|
|
1487
|
+
const repaintForBackgroundChange = () => {
|
|
1488
|
+
if (!subscribed || this.#stopped) return;
|
|
1489
|
+
const nextBackgroundRgb = getTerminalBackgroundColor();
|
|
1490
|
+
// Track RGB before the SIXEL guard: the startup OSC 11 reply usually
|
|
1491
|
+
// arrives before DA1 enables SIXEL, so revisiting the same color once
|
|
1492
|
+
// SIXEL is live must not replay identical rows.
|
|
1493
|
+
if (nextBackgroundRgb === backgroundRgb) return;
|
|
1494
|
+
backgroundRgb = nextBackgroundRgb;
|
|
1495
|
+
if (TERMINAL.imageProtocol !== ImageProtocol.Sixel) return;
|
|
1496
|
+
// Track RGB even during optimistic native ownership: fallback paints
|
|
1497
|
+
// with the latest color, so a later return to the startup RGB must repaint.
|
|
1498
|
+
// Never write rows while the native surface still owns rendering.
|
|
1499
|
+
if (this.#nativeLive) return;
|
|
1500
|
+
this.invalidate();
|
|
1501
|
+
// Match theme changes: replay owned history on direct terminals, but
|
|
1502
|
+
// leave multiplexer scrollback intact and repaint only the viewport.
|
|
1503
|
+
this.requestRender(true, { clearScrollback: !isInsideTerminalMultiplexer() });
|
|
1504
|
+
};
|
|
1505
|
+
const unsubscribe = this.terminal.onAppearanceReport?.(repaintForBackgroundChange);
|
|
1506
|
+
const unsubscribeFallback = onTerminalBackgroundFallbackChange(repaintForBackgroundChange);
|
|
1507
|
+
this.#appearanceReportUnsubscribe = () => {
|
|
1508
|
+
subscribed = false;
|
|
1509
|
+
unsubscribe?.();
|
|
1510
|
+
unsubscribeFallback();
|
|
1511
|
+
};
|
|
1481
1512
|
this.#debugPaint = undefined;
|
|
1482
1513
|
this.#debugServer?.stop();
|
|
1483
1514
|
this.#debugServer = undefined;
|
|
@@ -1605,6 +1636,9 @@ export class TUI extends Container {
|
|
|
1605
1636
|
() => this.stop(),
|
|
1606
1637
|
{ deferInput: this.#inputDeferred, isLoopStalled: () => this.#watchdog.isStalled() },
|
|
1607
1638
|
);
|
|
1639
|
+
// ProcessTerminal.start() resets the previous owner's RGB. Seed only
|
|
1640
|
+
// after that reset so an identical OSC 11 reply still repaints fallback rows.
|
|
1641
|
+
backgroundRgb = TERMINAL.imageProtocol === ImageProtocol.Sixel ? getTerminalBackgroundColor() : undefined;
|
|
1608
1642
|
if (this.#stopped) return;
|
|
1609
1643
|
this.#cancelPostmortemRestore?.();
|
|
1610
1644
|
this.#cancelPostmortemRestore = postmortem.register("tui-restore", () => this.stop());
|
|
@@ -2580,6 +2614,8 @@ export class TUI extends Container {
|
|
|
2580
2614
|
}
|
|
2581
2615
|
|
|
2582
2616
|
stop(): void {
|
|
2617
|
+
this.#appearanceReportUnsubscribe?.();
|
|
2618
|
+
this.#appearanceReportUnsubscribe = undefined;
|
|
2583
2619
|
this.#cancelPostmortemRestore?.();
|
|
2584
2620
|
this.#cancelPostmortemRestore = undefined;
|
|
2585
2621
|
this.#debugServer?.stop();
|