@xynogen/pix-pretty 1.7.18 → 1.7.19
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/package.json +1 -1
- package/src/utils.test.ts +26 -3
- package/src/utils.ts +24 -17
package/package.json
CHANGED
package/src/utils.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { MAX_PREVIEW_LINES } from "./config.js";
|
|
4
4
|
import type { FgTheme } from "./types.js";
|
|
5
|
-
import { pluralize, renderDimPreview } from "./utils.js";
|
|
5
|
+
import { pluralize, renderDimPreview, setResultDetails } from "./utils.js";
|
|
6
6
|
|
|
7
7
|
// Strip ANSI escapes so assertions test content, not color codes.
|
|
8
8
|
const ANSI = /\x1b\[[0-9;]*m/g;
|
|
@@ -29,6 +29,27 @@ describe("pluralize", () => {
|
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
describe("setResultDetails", () => {
|
|
33
|
+
it("preserves upstream metadata while adding renderer details", () => {
|
|
34
|
+
const result = {
|
|
35
|
+
content: [{ type: "text" as const, text: "output" }],
|
|
36
|
+
details: {
|
|
37
|
+
truncation: { truncated: true, totalLines: 500 },
|
|
38
|
+
fullOutputPath: "/tmp/full.log",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
setResultDetails(result, { _type: "bashResult", exitCode: 0 });
|
|
43
|
+
|
|
44
|
+
expect(result.details as Record<string, unknown>).toEqual({
|
|
45
|
+
truncation: { truncated: true, totalLines: 500 },
|
|
46
|
+
fullOutputPath: "/tmp/full.log",
|
|
47
|
+
_type: "bashResult",
|
|
48
|
+
exitCode: 0,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
32
53
|
describe("renderDimPreview", () => {
|
|
33
54
|
it("renders 'done' for empty input", () => {
|
|
34
55
|
expect(plain(renderDimPreview("", theme))).toContain("done");
|
|
@@ -79,7 +100,9 @@ describe("renderDimPreview", () => {
|
|
|
79
100
|
expect(plain(raw)).toContain("foo bar foo");
|
|
80
101
|
});
|
|
81
102
|
|
|
82
|
-
it("
|
|
83
|
-
|
|
103
|
+
it("treats regex metacharacters as literal highlight text", () => {
|
|
104
|
+
const raw = renderDimPreview("call(foo)", theme, { highlight: "(" });
|
|
105
|
+
expect(plain(raw)).toContain("call(foo)");
|
|
106
|
+
expect(raw).toContain("\x1b[");
|
|
84
107
|
});
|
|
85
108
|
});
|
package/src/utils.ts
CHANGED
|
@@ -59,21 +59,23 @@ export type DimPreviewOptions = {
|
|
|
59
59
|
highlight?: string;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
function dimLineWithHighlight(line: string, theme: FgTheme, pattern?: string): string {
|
|
63
|
+
if (!pattern) return theme.fg("dim", line);
|
|
64
|
+
const foldedLine = line.toLocaleLowerCase();
|
|
65
|
+
const foldedPattern = pattern.toLocaleLowerCase();
|
|
66
|
+
if (!foldedPattern) return theme.fg("dim", line);
|
|
67
|
+
|
|
68
|
+
const parts: string[] = [];
|
|
69
|
+
let start = 0;
|
|
70
|
+
for (;;) {
|
|
71
|
+
const match = foldedLine.indexOf(foldedPattern, start);
|
|
72
|
+
if (match < 0) break;
|
|
73
|
+
if (match > start) parts.push(theme.fg("dim", line.slice(start, match)));
|
|
74
|
+
parts.push(`${FG_GREEN}${BOLD}${line.slice(match, match + pattern.length)}${RST}`);
|
|
75
|
+
start = match + pattern.length;
|
|
67
76
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
function dimLineWithHighlight(line: string, theme: FgTheme, re: RegExp | null): string {
|
|
71
|
-
if (!re) return theme.fg("dim", line);
|
|
72
|
-
// split with capture group: odd indexes are matches
|
|
73
|
-
return line
|
|
74
|
-
.split(re)
|
|
75
|
-
.map((part, i) => (i % 2 ? `${FG_GREEN}${BOLD}${part}${RST}` : theme.fg("dim", part)))
|
|
76
|
-
.join("");
|
|
77
|
+
if (start < line.length) parts.push(theme.fg("dim", line.slice(start)));
|
|
78
|
+
return parts.length > 0 ? parts.join("") : theme.fg("dim", line);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
export function renderDimPreview(
|
|
@@ -82,12 +84,12 @@ export function renderDimPreview(
|
|
|
82
84
|
opts: DimPreviewOptions = {},
|
|
83
85
|
): string {
|
|
84
86
|
const maxLines = opts.maxLines ?? MAX_PREVIEW_LINES;
|
|
85
|
-
const
|
|
87
|
+
const highlight = opts.highlight;
|
|
86
88
|
const output = normalizeLineEndings(text).trim() || "done";
|
|
87
89
|
const lines = output.split("\n");
|
|
88
90
|
const preview = lines
|
|
89
91
|
.slice(0, maxLines)
|
|
90
|
-
.map((line) => ` ${dimLineWithHighlight(line, theme,
|
|
92
|
+
.map((line) => ` ${dimLineWithHighlight(line, theme, highlight)}`);
|
|
91
93
|
if (opts.header) preview.unshift(` ${theme.fg("dim", opts.header)}`);
|
|
92
94
|
if (lines.length > maxLines) {
|
|
93
95
|
const more = pluralize(lines.length - maxLines, "more line");
|
|
@@ -208,8 +210,13 @@ export function getTextContent(result: ToolResultLike): string {
|
|
|
208
210
|
);
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
/** Add renderer metadata without discarding execution metadata from the upstream tool. */
|
|
211
214
|
export function setResultDetails<T>(result: ToolResultLike, details: T): void {
|
|
212
|
-
|
|
215
|
+
const upstream =
|
|
216
|
+
result.details && typeof result.details === "object"
|
|
217
|
+
? (result.details as Record<string, unknown>)
|
|
218
|
+
: undefined;
|
|
219
|
+
result.details = upstream ? { ...upstream, ...details } : details;
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
export function makeTextResult<TDetails>(
|