@xynogen/pix-pretty 1.7.14 → 1.7.17
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/README.md +12 -10
- package/package.json +1 -1
- package/src/ansi.ts +4 -12
- package/src/config.ts +5 -72
- package/src/confirm.ts +6 -24
- package/src/diff-render.ts +42 -174
- package/src/diff.ts +1 -2
- package/src/fff.ts +3 -9
- package/src/gate-overlay.test.ts +2 -6
- package/src/gate-overlay.ts +5 -26
- package/src/highlight.ts +3 -6
- package/src/icon-catalog.ts +11 -8
- package/src/icon-persist.test.ts +10 -2
- package/src/icon-persist.ts +19 -44
- package/src/index.ts +2 -16
- package/src/modal-frame.ts +2 -6
- package/src/progress.ts +1 -5
- package/src/renderers.ts +115 -18
- package/src/types-diff.d.ts +1 -5
- package/src/types.ts +3 -12
- package/src/utils.test.ts +3 -11
- package/src/utils.ts +6 -23
- package/src/commands/pretty.ts +0 -135
package/src/utils.ts
CHANGED
|
@@ -41,21 +41,14 @@ export function fillToolBackground(text: string, bg = BG_BASE): string {
|
|
|
41
41
|
.split("\n")
|
|
42
42
|
.map((line) => {
|
|
43
43
|
const normalized = preserveToolBackground(line, bg);
|
|
44
|
-
const fitted = preserveToolBackground(
|
|
45
|
-
truncateToWidth(normalized, width, ""),
|
|
46
|
-
bg,
|
|
47
|
-
);
|
|
44
|
+
const fitted = preserveToolBackground(truncateToWidth(normalized, width, ""), bg);
|
|
48
45
|
const padding = Math.max(0, width - visibleWidth(fitted));
|
|
49
46
|
return `${bg}${fitted}${" ".repeat(padding)}${RST}`;
|
|
50
47
|
})
|
|
51
48
|
.join("\n");
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
export function pluralize(
|
|
55
|
-
count: number,
|
|
56
|
-
noun: string,
|
|
57
|
-
plural?: string,
|
|
58
|
-
): string {
|
|
51
|
+
export function pluralize(count: number, noun: string, plural?: string): string {
|
|
59
52
|
return `${count} ${count === 1 ? noun : (plural ?? `${noun}s`)}`;
|
|
60
53
|
}
|
|
61
54
|
|
|
@@ -74,18 +67,12 @@ function safeHighlightRegex(pattern: string): RegExp | null {
|
|
|
74
67
|
}
|
|
75
68
|
}
|
|
76
69
|
|
|
77
|
-
function dimLineWithHighlight(
|
|
78
|
-
line: string,
|
|
79
|
-
theme: FgTheme,
|
|
80
|
-
re: RegExp | null,
|
|
81
|
-
): string {
|
|
70
|
+
function dimLineWithHighlight(line: string, theme: FgTheme, re: RegExp | null): string {
|
|
82
71
|
if (!re) return theme.fg("dim", line);
|
|
83
72
|
// split with capture group: odd indexes are matches
|
|
84
73
|
return line
|
|
85
74
|
.split(re)
|
|
86
|
-
.map((part, i) =>
|
|
87
|
-
i % 2 ? `${FG_GREEN}${BOLD}${part}${RST}` : theme.fg("dim", part),
|
|
88
|
-
)
|
|
75
|
+
.map((part, i) => (i % 2 ? `${FG_GREEN}${BOLD}${part}${RST}` : theme.fg("dim", part)))
|
|
89
76
|
.join("");
|
|
90
77
|
}
|
|
91
78
|
|
|
@@ -204,15 +191,11 @@ export function humanSize(bytes: number): string {
|
|
|
204
191
|
// Fallback: set PRETTY_ICONS=none to disable icons.
|
|
205
192
|
// ---------------------------------------------------------------------------
|
|
206
193
|
|
|
207
|
-
export function isTextContent(
|
|
208
|
-
content: ToolContent,
|
|
209
|
-
): content is ToolTextContent {
|
|
194
|
+
export function isTextContent(content: ToolContent): content is ToolTextContent {
|
|
210
195
|
return content.type === "text";
|
|
211
196
|
}
|
|
212
197
|
|
|
213
|
-
export function isImageContent(
|
|
214
|
-
content: ToolContent,
|
|
215
|
-
): content is ToolImageContent {
|
|
198
|
+
export function isImageContent(content: ToolContent): content is ToolImageContent {
|
|
216
199
|
return content.type === "image";
|
|
217
200
|
}
|
|
218
201
|
|
package/src/commands/pretty.ts
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pretty.ts — the `/pretty` command: switch the global icon mode.
|
|
3
|
-
*
|
|
4
|
-
* Treats icons like an l10n locale: one global mode (nerd/unicode/ascii)
|
|
5
|
-
* governs every pix-* package that renders via the icon catalog. This command
|
|
6
|
-
* is the single switch — an overlay that previews each mode's glyphs live and
|
|
7
|
-
* persists the choice to ~/.pi/agent/pretty.json. Headless hosts get a notify
|
|
8
|
-
* fallback that cycles the mode without UI.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
getIconMode,
|
|
13
|
-
ICON_MODES,
|
|
14
|
-
type IconKey,
|
|
15
|
-
type IconMode,
|
|
16
|
-
icon,
|
|
17
|
-
setIconMode,
|
|
18
|
-
} from "../icon-catalog.js";
|
|
19
|
-
import { saveIconMode } from "../icon-persist.js";
|
|
20
|
-
import { frameLines } from "../modal-frame.js";
|
|
21
|
-
import type { CommandContextLike, PiPrettyApi } from "../types.js";
|
|
22
|
-
|
|
23
|
-
/** Sample keys shown in the preview, one row per representative role. */
|
|
24
|
-
const PREVIEW: { key: IconKey; label: string }[] = [
|
|
25
|
-
{ key: "model", label: "model" },
|
|
26
|
-
{ key: "cwd", label: "cwd" },
|
|
27
|
-
{ key: "lsp", label: "lsp" },
|
|
28
|
-
{ key: "paste.image", label: "paste" },
|
|
29
|
-
{ key: "opt.caveman", label: "optimizer" },
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
/** Apply + persist a mode in one step. */
|
|
33
|
-
function applyMode(mode: IconMode): void {
|
|
34
|
-
setIconMode(mode);
|
|
35
|
-
saveIconMode(mode);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function registerPrettyCommand(pi: PiPrettyApi): void {
|
|
39
|
-
pi.registerCommand("pretty", {
|
|
40
|
-
description: "pix-pretty: switch icon style (nerd / unicode / ascii)",
|
|
41
|
-
handler: async (_args: string, ctx: CommandContextLike) => {
|
|
42
|
-
const ui = ctx.ui as unknown as {
|
|
43
|
-
theme: {
|
|
44
|
-
fg(c: string, t: string): string;
|
|
45
|
-
bg(c: string, t: string): string;
|
|
46
|
-
bold(t: string): string;
|
|
47
|
-
};
|
|
48
|
-
custom?: <T>(
|
|
49
|
-
f: unknown,
|
|
50
|
-
opts?: {
|
|
51
|
-
overlay?: boolean;
|
|
52
|
-
overlayOptions?: {
|
|
53
|
-
anchor?: string;
|
|
54
|
-
width?: number;
|
|
55
|
-
maxHeight?: string;
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
) => Promise<T>;
|
|
59
|
-
notify(m: string, t?: "info" | "warning" | "error"): void;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Headless / no custom-UI host: cycle to the next mode + notify.
|
|
63
|
-
if (typeof ui.custom !== "function") {
|
|
64
|
-
const cur = ICON_MODES.indexOf(getIconMode());
|
|
65
|
-
const next = ICON_MODES[(cur + 1) % ICON_MODES.length] ?? "nerd";
|
|
66
|
-
applyMode(next);
|
|
67
|
-
ui.notify(`pix-pretty icons: ${next}`, "info");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const boxW = 40;
|
|
72
|
-
|
|
73
|
-
await ui.custom<null>(
|
|
74
|
-
(
|
|
75
|
-
tui: { requestRender(): void },
|
|
76
|
-
theme: typeof ui.theme,
|
|
77
|
-
_kb: unknown,
|
|
78
|
-
done: (v: null) => void,
|
|
79
|
-
) => {
|
|
80
|
-
let selected = ICON_MODES.indexOf(getIconMode());
|
|
81
|
-
if (selected < 0) selected = 0;
|
|
82
|
-
|
|
83
|
-
const choose = (i: number) => {
|
|
84
|
-
selected = (i + ICON_MODES.length) % ICON_MODES.length;
|
|
85
|
-
applyMode(ICON_MODES[selected] ?? "nerd");
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
render: () => {
|
|
90
|
-
const rows = ICON_MODES.map((mode, i) => {
|
|
91
|
-
const sel = i === selected;
|
|
92
|
-
const cursor = sel ? theme.fg("accent", "→") : " ";
|
|
93
|
-
const name = theme.fg(sel ? "accent" : "text", mode.padEnd(8));
|
|
94
|
-
// Live preview: render the sample glyphs in THIS mode.
|
|
95
|
-
const prev = ICON_MODES[i] === getIconMode();
|
|
96
|
-
const samples = prev
|
|
97
|
-
? PREVIEW.map((p) => icon(p.key)).join(" ")
|
|
98
|
-
: "";
|
|
99
|
-
return `${cursor} ${name} ${theme.fg("dim", samples)}`;
|
|
100
|
-
});
|
|
101
|
-
const lines = [
|
|
102
|
-
theme.fg("accent", theme.bold(" Icon style")),
|
|
103
|
-
"",
|
|
104
|
-
...rows,
|
|
105
|
-
"",
|
|
106
|
-
theme.fg("dim", "↑↓ select · esc close"),
|
|
107
|
-
];
|
|
108
|
-
return frameLines({
|
|
109
|
-
width: boxW,
|
|
110
|
-
lines,
|
|
111
|
-
color: (s) => theme.fg("accent", s),
|
|
112
|
-
bg: (s) => theme.bg("customMessageBg", s),
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
invalidate: () => {},
|
|
116
|
-
handleInput: (data: string) => {
|
|
117
|
-
if (data === "k" || data === "\u001b[A") choose(selected - 1);
|
|
118
|
-
else if (data === "j" || data === "\u001b[B")
|
|
119
|
-
choose(selected + 1);
|
|
120
|
-
else if (data === "\u001b" || data === "q" || data === "\r") {
|
|
121
|
-
done(null);
|
|
122
|
-
return;
|
|
123
|
-
} else return;
|
|
124
|
-
tui.requestRender();
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
overlay: true,
|
|
130
|
-
overlayOptions: { anchor: "center", width: boxW, maxHeight: "60%" },
|
|
131
|
-
},
|
|
132
|
-
);
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
}
|