@xynogen/pix-pretty 1.14.0 → 1.16.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/package.json +2 -2
- package/src/icon-catalog.test.ts +6 -0
- package/src/icon-catalog.ts +1 -0
- package/src/types.ts +3 -1
- package/src/utils.test.ts +26 -0
- package/src/utils.ts +49 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-pretty",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Enhanced tool output rendering with syntax highlighting, file icons, tree views, diff rendering, and FFF search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@xynogen/pix-data": "^0.4.3",
|
|
63
|
-
"@xynogen/pix-runtime": "^0.
|
|
63
|
+
"@xynogen/pix-runtime": "^0.6.0",
|
|
64
64
|
"chalk": "^4.1.2",
|
|
65
65
|
"cli-highlight": "^2.1.11",
|
|
66
66
|
"@ff-labs/fff-node": "^0.5.2",
|
package/src/icon-catalog.test.ts
CHANGED
|
@@ -31,6 +31,12 @@ describe("icon-catalog", () => {
|
|
|
31
31
|
expect(getIconMode()).toBe("nerd"); // unchanged
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it("provides AFK keyboard fallbacks for every icon mode", () => {
|
|
35
|
+
expect(iconFor("afk", "nerd")).toBe("\u{F0310}");
|
|
36
|
+
expect(iconFor("afk", "unicode")).toBe("\u2328\uFE0E");
|
|
37
|
+
expect(iconFor("afk", "ascii")).toBe("kbd");
|
|
38
|
+
});
|
|
39
|
+
|
|
34
40
|
it("every catalog key has a non-empty glyph in every mode", () => {
|
|
35
41
|
for (const mode of ICON_MODES) {
|
|
36
42
|
for (const key of ICON_KEYS) {
|
package/src/icon-catalog.ts
CHANGED
|
@@ -44,6 +44,7 @@ const CATALOG = {
|
|
|
44
44
|
mcp: { nerd: "\u{F048D}", unicode: `\u25D0${VS}`, ascii: "MCP" },
|
|
45
45
|
cwd: { nerd: "\u{F024B}", unicode: `\u2302${VS}`, ascii: "~" },
|
|
46
46
|
folder: { nerd: "\u{F024B}", unicode: `\u2302${VS}`, ascii: "/" },
|
|
47
|
+
afk: { nerd: "\u{F0310}", unicode: `\u2328${VS}`, ascii: "kbd" },
|
|
47
48
|
|
|
48
49
|
// ── footer indicators (git status, score) ─────────────────────────────
|
|
49
50
|
"git.unstaged": { nerd: "\u2717", unicode: "\u2717", ascii: "x" },
|
package/src/types.ts
CHANGED
|
@@ -42,9 +42,11 @@ export type ToolContent = TextContent | ImageContent;
|
|
|
42
42
|
|
|
43
43
|
export type ToolResultLike<TDetails = unknown> = AgentToolResult<TDetails | undefined>;
|
|
44
44
|
|
|
45
|
-
type TextComponentLike = {
|
|
45
|
+
export type TextComponentLike = {
|
|
46
46
|
setText(value: string): void;
|
|
47
47
|
getText?: () => string;
|
|
48
|
+
render?: (width: number) => string[];
|
|
49
|
+
invalidate?: () => void;
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export type TextComponentCtor = new (text?: string, x?: number, y?: number) => TextComponentLike;
|
package/src/utils.test.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { MAX_PREVIEW_LINES } from "./config.js";
|
|
|
4
4
|
import type { FgTheme } from "./types.js";
|
|
5
5
|
import {
|
|
6
6
|
dotJoin,
|
|
7
|
+
fillToolBackground,
|
|
7
8
|
formatCollapsedToolRow,
|
|
8
9
|
formatJson,
|
|
9
10
|
hideCollapsedToolCall,
|
|
@@ -13,8 +14,33 @@ import {
|
|
|
13
14
|
renderDimPreview,
|
|
14
15
|
ruleFrame,
|
|
15
16
|
setResultDetails,
|
|
17
|
+
viewportText,
|
|
16
18
|
} from "./utils.js";
|
|
17
19
|
|
|
20
|
+
class MockTextComponent {
|
|
21
|
+
private text = "";
|
|
22
|
+
|
|
23
|
+
setText(value: string): void {
|
|
24
|
+
this.text = value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
render(): string[] {
|
|
28
|
+
return this.text.split("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
invalidate(): void {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("viewportText", () => {
|
|
35
|
+
it("trims a pre-filled row to Pi's narrower fullscreen viewport", () => {
|
|
36
|
+
const text = viewportText(MockTextComponent);
|
|
37
|
+
text.setText(fillToolBackground("tool", "", 10));
|
|
38
|
+
|
|
39
|
+
expect(plain(text.render(9)[0]!)).toBe("tool".padEnd(9));
|
|
40
|
+
expect(plain(text.render(10)[0]!)).toBe("tool".padEnd(10));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
18
44
|
// Strip ANSI escapes so assertions test content, not color codes.
|
|
19
45
|
const ANSI = /\x1b\[[0-9;]*m/g;
|
|
20
46
|
function plain(text: string): string {
|
package/src/utils.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
import { MAX_PREVIEW_LINES } from "./config.js";
|
|
15
15
|
import type {
|
|
16
16
|
FgTheme,
|
|
17
|
+
TextComponentCtor,
|
|
18
|
+
TextComponentLike,
|
|
17
19
|
ToolContent,
|
|
18
20
|
ToolImageContent,
|
|
19
21
|
ToolResultLike,
|
|
@@ -48,6 +50,53 @@ export function fillToolBackground(text: string, bg = BG_BASE, width?: number):
|
|
|
48
50
|
.join("\n");
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
export function viewportTextConstructor(TextComponent: TextComponentCtor): TextComponentCtor {
|
|
54
|
+
return function ViewportTextComponent(text = "") {
|
|
55
|
+
const component = viewportText(TextComponent);
|
|
56
|
+
component.setText(text);
|
|
57
|
+
return component;
|
|
58
|
+
} as unknown as TextComponentCtor;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function viewportText(
|
|
62
|
+
TextComponent: TextComponentCtor,
|
|
63
|
+
): TextComponentLike & ViewportComponent {
|
|
64
|
+
return new ViewportText(new TextComponent("", 0, 0));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type ViewportComponent = {
|
|
68
|
+
render(width: number): string[];
|
|
69
|
+
invalidate(): void;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
class ViewportText implements TextComponentLike, ViewportComponent {
|
|
73
|
+
private text = "";
|
|
74
|
+
|
|
75
|
+
constructor(private readonly component: TextComponentLike) {}
|
|
76
|
+
|
|
77
|
+
setText(value: string): void {
|
|
78
|
+
this.text = value;
|
|
79
|
+
this.component.setText(value);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getText(): string {
|
|
83
|
+
return this.text;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
render(width: number): string[] {
|
|
87
|
+
const fitted = this.text
|
|
88
|
+
.split("\n")
|
|
89
|
+
.map((line) => truncateToWidth(line, width, ""))
|
|
90
|
+
.join("\n");
|
|
91
|
+
this.component.setText(fitted);
|
|
92
|
+
return this.component.render?.(width) ?? fitted.split("\n");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
invalidate(): void {
|
|
96
|
+
this.component.invalidate?.();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
51
100
|
export function pluralize(count: number, noun: string, plural?: string): string {
|
|
52
101
|
return `${count} ${count === 1 ? noun : (plural ?? `${noun}s`)}`;
|
|
53
102
|
}
|