@heyhuynhgiabuu/pi-pretty 0.6.22 → 0.6.24
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 +20 -0
- package/README.md +25 -7
- package/dist/autocomplete.js +1 -4
- package/dist/autocomplete.js.map +1 -1
- package/dist/config.d.ts +42 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +174 -117
- package/dist/config.js.map +1 -1
- package/dist/fff-helpers.js +2 -7
- package/dist/fff-helpers.js.map +1 -1
- package/dist/fff.js +16 -23
- package/dist/fff.js.map +1 -1
- package/dist/find-glob.js +2 -6
- package/dist/find-glob.js.map +1 -1
- package/dist/helpers.js +11 -21
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -60
- package/dist/index.js.map +1 -1
- package/dist/notices.js +1 -4
- package/dist/notices.js.map +1 -1
- package/dist/render.d.ts +19 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +125 -96
- package/dist/render.js.map +1 -1
- package/dist/tools/bash.js +26 -29
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/find.js +31 -34
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/grep.js +22 -25
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/labels.js +1 -4
- package/dist/tools/labels.js.map +1 -1
- package/dist/tools/ls.js +18 -21
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/metrics.js +4 -7
- package/dist/tools/metrics.js.map +1 -1
- package/dist/tools/read.js +35 -38
- package/dist/tools/read.js.map +1 -1
- package/dist/tui-text.js +5 -9
- package/dist/tui-text.js.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/package.json +7 -6
- package/src/config.ts +90 -19
- package/src/index.ts +9 -10
- package/src/render.ts +56 -9
package/dist/tools/read.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/* pi-pretty: read tool -- file reading with syntax highlighting and inline image support. */
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const tui_text_js_1 = require("../tui-text.js");
|
|
10
|
-
const metrics_js_1 = require("./metrics.js");
|
|
2
|
+
import { basename, dirname } from "node:path";
|
|
3
|
+
import { BG_BASE, BG_ERROR, FG_DIM, FG_LNUM, FG_RULE, RST, resolveBaseBackground, TOOL_RESULT_INDENT, termWidth, } from "../config.js";
|
|
4
|
+
import { normalizeLineEndings, shortPath } from "../helpers.js";
|
|
5
|
+
import { fillToolBackground, renderFileContent, renderToolError } from "../render.js";
|
|
6
|
+
import { resolveTextCtor } from "../tui-text.js";
|
|
7
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
11
8
|
function getSkillName(filePath, content) {
|
|
12
|
-
if (
|
|
9
|
+
if (basename(filePath) !== "SKILL.md")
|
|
13
10
|
return undefined;
|
|
14
11
|
const lines = content.split("\n");
|
|
15
12
|
if (lines[0]?.trim() === "---") {
|
|
@@ -20,12 +17,12 @@ function getSkillName(filePath, content) {
|
|
|
20
17
|
continue;
|
|
21
18
|
const value = match[1].trim();
|
|
22
19
|
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
23
|
-
return value.slice(1, -1).trim() ||
|
|
20
|
+
return value.slice(1, -1).trim() || basename(dirname(filePath));
|
|
24
21
|
}
|
|
25
|
-
return value ||
|
|
22
|
+
return value || basename(dirname(filePath));
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
|
-
return
|
|
25
|
+
return basename(dirname(filePath));
|
|
29
26
|
}
|
|
30
27
|
function renderSkillHeader(skillName, expanded, theme) {
|
|
31
28
|
const label = theme.fg("accent", "[skill]");
|
|
@@ -33,8 +30,8 @@ function renderSkillHeader(skillName, expanded, theme) {
|
|
|
33
30
|
const hint = theme.fg("dim", `ctrl+o to ${expanded ? "collapse" : "expand"}`);
|
|
34
31
|
return `${label} ${name} ${hint}`;
|
|
35
32
|
}
|
|
36
|
-
function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
37
|
-
const TC =
|
|
33
|
+
export function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
34
|
+
const TC = resolveTextCtor(TextComp);
|
|
38
35
|
const home = process.env.HOME ?? "";
|
|
39
36
|
pi.registerTool({
|
|
40
37
|
name: "read",
|
|
@@ -42,7 +39,7 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
42
39
|
description: sdkTool.description ?? "Read file contents",
|
|
43
40
|
parameters: sdkTool.parameters,
|
|
44
41
|
renderShell: "self",
|
|
45
|
-
execute:
|
|
42
|
+
execute: wrapExecuteWithMetrics(async (tid, params, sig, _upd, ctx) => {
|
|
46
43
|
const p = params;
|
|
47
44
|
const result = (await sdkTool.execute(tid, p, sig, undefined, ctx));
|
|
48
45
|
const imageBlock = result.content?.find((c) => c.type === "image");
|
|
@@ -53,7 +50,7 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
53
50
|
};
|
|
54
51
|
return result;
|
|
55
52
|
}
|
|
56
|
-
const tc =
|
|
53
|
+
const tc = normalizeLineEndings(getText(result));
|
|
57
54
|
result.details = {
|
|
58
55
|
_type: "readFile",
|
|
59
56
|
filePath: String(p.path ?? ""),
|
|
@@ -64,7 +61,7 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
64
61
|
return result;
|
|
65
62
|
}),
|
|
66
63
|
renderCall(args, theme, ctx) {
|
|
67
|
-
|
|
64
|
+
resolveBaseBackground(theme);
|
|
68
65
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
69
66
|
if (!ctx.isError) {
|
|
70
67
|
text.setText("");
|
|
@@ -72,14 +69,14 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
72
69
|
}
|
|
73
70
|
const path = String(args.path ?? "");
|
|
74
71
|
const label = theme.fg("error", theme.bold("→ read"));
|
|
75
|
-
text.setText(
|
|
72
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${label} ${theme.fg("toolTitle", path)}\n`, BG_ERROR));
|
|
76
73
|
return text;
|
|
77
74
|
},
|
|
78
75
|
renderResult(result, _opt, theme, ctx) {
|
|
79
|
-
|
|
76
|
+
resolveBaseBackground(theme);
|
|
80
77
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
81
78
|
if (ctx.isError) {
|
|
82
|
-
text.setText(
|
|
79
|
+
text.setText(fillToolBackground(renderToolError(getText(result) || "Error", theme), BG_ERROR));
|
|
83
80
|
return text;
|
|
84
81
|
}
|
|
85
82
|
const d = result.details;
|
|
@@ -88,25 +85,25 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
88
85
|
// or unsupported by the terminal.
|
|
89
86
|
if (d?._type === "readImage") {
|
|
90
87
|
const note = getText(result);
|
|
91
|
-
text.setText(note ?
|
|
88
|
+
text.setText(note ? fillToolBackground(note, BG_BASE) : "");
|
|
92
89
|
return text;
|
|
93
90
|
}
|
|
94
91
|
// File content — line-numbered display
|
|
95
92
|
if (d?._type === "readFile" && d.content) {
|
|
96
|
-
const tw =
|
|
93
|
+
const tw = termWidth();
|
|
97
94
|
const lines = d.content.split("\n");
|
|
98
95
|
const total = lines.length;
|
|
99
96
|
const filePath = String(d.filePath ?? "");
|
|
100
97
|
const skillName = getSkillName(filePath, d.content);
|
|
101
|
-
const p2 =
|
|
98
|
+
const p2 = shortPath(cwd, home, filePath);
|
|
102
99
|
const off2 = typeof d.offset === "number" ? `:${d.offset}` : "";
|
|
103
100
|
if (!ctx.expanded) {
|
|
104
101
|
if (skillName) {
|
|
105
102
|
const header = renderSkillHeader(skillName, false, theme);
|
|
106
|
-
text.setText(
|
|
103
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${header}\n`, BG_BASE));
|
|
107
104
|
return text;
|
|
108
105
|
}
|
|
109
|
-
text.setText(
|
|
106
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${theme.fg("toolTitle", theme.bold("→ read"))} ${theme.fg("toolTitle", p2)}${theme.fg("dim", off2)}\n${TOOL_RESULT_INDENT}${FG_DIM}${total} lines — ctrl+o to expand${RST}\n`, BG_BASE));
|
|
110
107
|
return text;
|
|
111
108
|
}
|
|
112
109
|
const maxShow = lines.length;
|
|
@@ -117,44 +114,44 @@ function registerReadTool(pi, cwd, _fffService, sdkTool, TextComp) {
|
|
|
117
114
|
const header = skillName
|
|
118
115
|
? renderSkillHeader(skillName, true, theme)
|
|
119
116
|
: `${theme.fg("toolTitle", theme.bold("→ read"))} ${theme.fg("toolTitle", p2)}${theme.fg("dim", off2)}`;
|
|
120
|
-
const out = ["", `${
|
|
121
|
-
out.push(`${
|
|
117
|
+
const out = ["", `${TOOL_RESULT_INDENT}${header}`];
|
|
118
|
+
out.push(`${TOOL_RESULT_INDENT}${FG_RULE}${"─".repeat(tw - 1)}${RST}`);
|
|
122
119
|
for (let i = 0; i < show.length; i++) {
|
|
123
120
|
const ln = (d.offset || 0) + i + 1;
|
|
124
121
|
const code = show[i] ?? "";
|
|
125
|
-
const display = code.length > cw ? code.slice(0, Math.max(0, cw - 1)) + `${
|
|
122
|
+
const display = code.length > cw ? code.slice(0, Math.max(0, cw - 1)) + `${FG_DIM}›${RST}` : code;
|
|
126
123
|
const lineNo = String(ln);
|
|
127
|
-
out.push(`${
|
|
124
|
+
out.push(`${TOOL_RESULT_INDENT}${FG_LNUM}${" ".repeat(Math.max(0, nw - lineNo.length))}${lineNo}${RST} ${FG_RULE}│${RST} ${display}${RST}`);
|
|
128
125
|
}
|
|
129
126
|
if (total > maxShow) {
|
|
130
|
-
out.push(`${
|
|
127
|
+
out.push(`${TOOL_RESULT_INDENT}${FG_DIM}… ${total - maxShow} more lines (${total} total)${RST}`);
|
|
131
128
|
}
|
|
132
129
|
out.push("");
|
|
133
130
|
const rendered = out.join("\n");
|
|
134
|
-
text.setText(
|
|
131
|
+
text.setText(fillToolBackground(rendered, BG_BASE));
|
|
135
132
|
ctx.state._rt = rendered;
|
|
136
133
|
// Async syntax highlighting via Shiki
|
|
137
|
-
|
|
134
|
+
renderFileContent(d.content, d.filePath, d.offset || 0, maxShow, cw)
|
|
138
135
|
.then((hl) => {
|
|
139
136
|
const padded = hl
|
|
140
137
|
.split("\n")
|
|
141
138
|
.map((line, index) => {
|
|
142
139
|
const lineNo = String((d.offset || 0) + index + 1);
|
|
143
|
-
return `${
|
|
140
|
+
return `${TOOL_RESULT_INDENT}${FG_LNUM}${" ".repeat(Math.max(0, nw - lineNo.length))}${lineNo}${RST} ${FG_RULE}│${RST} ${line}${RST}`;
|
|
144
141
|
})
|
|
145
142
|
.join("\n");
|
|
146
143
|
const divider = skillName
|
|
147
|
-
? `${
|
|
144
|
+
? `${TOOL_RESULT_INDENT}${FG_RULE}${"─".repeat(Math.max(1, tw - 1))}${RST}\n`
|
|
148
145
|
: "";
|
|
149
|
-
const rendered = `\n${
|
|
150
|
-
text.setText(
|
|
146
|
+
const rendered = `\n${TOOL_RESULT_INDENT}${header}\n${divider}${padded}\n`;
|
|
147
|
+
text.setText(fillToolBackground(rendered, BG_BASE));
|
|
151
148
|
ctx.state._rt = rendered;
|
|
152
149
|
})
|
|
153
150
|
.catch(() => { });
|
|
154
151
|
return text;
|
|
155
152
|
}
|
|
156
153
|
const fc = result.content?.[0];
|
|
157
|
-
text.setText(
|
|
154
|
+
text.setText(fillToolBackground(`${TOOL_RESULT_INDENT}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`, BG_BASE));
|
|
158
155
|
return text;
|
|
159
156
|
},
|
|
160
157
|
});
|
package/dist/tools/read.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,OAAO,EACP,OAAO,EACP,GAAG,EACH,qBAAqB,EACrB,kBAAkB,EAClB,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAItD,SAAS,YAAY,CAAC,QAAgB,EAAE,OAAe;IACtD,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAExD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC;QACjF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACtG,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,KAAK,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,CAAC;IACF,CAAC;IAED,OAAO,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB,EAAE,QAAiB,EAAE,KAAgB;IAChF,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9E,OAAO,GAAG,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC/B,EAAgB,EAChB,GAAW,EACX,WAAoB,EACpB,OAAmB,EACnB,QAAmF;IAEnF,MAAM,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAEpC,EAAE,CAAC,YAAY,CAAC;QACf,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,oBAAoB;QACxD,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,WAAW,EAAE,MAAM;QAEnB,OAAO,EAAE,sBAAsB,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAqB,EAAE,EAAE;YACvF,MAAM,CAAC,GAAG,MAAa,CAAC;YACxB,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAW,CAAC;YAE9E,MAAM,UAAU,GAAI,MAAM,CAAC,OAAiB,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;YACnF,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,CAAC,OAAO,GAAG;oBAChB,KAAK,EAAE,WAAW;oBAClB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;iBACf,CAAC;gBACjB,OAAO,MAAM,CAAC;YACf,CAAC;YAED,MAAM,EAAE,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,OAAO,GAAG;gBAChB,KAAK,EAAE,UAAU;gBACjB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC9B,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACnD,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aAC1B,CAAC;YACjB,OAAO,MAAM,CAAC;QACf,CAAC,CAAC;QAEF,UAAU,CAAC,IAAS,EAAE,KAAgB,EAAE,GAAkB;YACzD,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAE7B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjB,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,kBAAkB,GAAG,KAAK,IAAI,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC/G,OAAO,IAAI,CAAC;QACb,CAAC;QAED,YAAY,CAAC,MAAc,EAAE,IAAa,EAAE,KAAgB,EAAE,GAAkB;YAC/E,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAE7B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAEnD,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAC/F,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,CAAC,GAAG,MAAM,CAAC,OAAkC,CAAC;YAEpD,0EAA0E;YAC1E,6EAA6E;YAC7E,kCAAkC;YAClC,IAAI,CAAC,EAAE,KAAK,KAAK,WAAW,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5D,OAAO,IAAI,CAAC;YACb,CAAC;YAED,uCAAuC;YACvC,IAAI,CAAC,EAAE,KAAK,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1C,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;gBAC1C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBACpD,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;oBACnB,IAAI,SAAS,EAAE,CAAC;wBACf,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;wBAC1D,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,kBAAkB,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;wBAChF,OAAO,IAAI,CAAC;oBACb,CAAC;oBACD,IAAI,CAAC,OAAO,CACX,kBAAkB,CACjB,KAAK,kBAAkB,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,kBAAkB,GAAG,MAAM,GAAG,KAAK,4BAA4B,GAAG,IAAI,EACrM,OAAO,CACP,CACD,CAAC;oBACF,OAAO,IAAI,CAAC;gBACb,CAAC;gBACD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACrC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;gBAC/D,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;gBAClB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;gBAEhC,MAAM,MAAM,GAAG,SAAS;oBACvB,CAAC,CAAC,iBAAiB,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC;oBAC3C,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;gBACzG,MAAM,GAAG,GAAa,CAAC,EAAE,EAAE,GAAG,kBAAkB,GAAG,MAAM,EAAE,CAAC,CAAC;gBAC7D,GAAG,CAAC,IAAI,CAAC,GAAG,kBAAkB,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;gBACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACtC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACnC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;oBAClG,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;oBAC1B,GAAG,CAAC,IAAI,CACP,GAAG,kBAAkB,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,GAAG,IAAI,OAAO,IAAI,GAAG,IAAI,OAAO,GAAG,GAAG,EAAE,CACjI,CAAC;gBACH,CAAC;gBACD,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;oBACrB,GAAG,CAAC,IAAI,CAAC,GAAG,kBAAkB,GAAG,MAAM,KAAK,KAAK,GAAG,OAAO,gBAAgB,KAAK,UAAU,GAAG,EAAE,CAAC,CAAC;gBAClG,CAAC;gBACD,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACb,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAChC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;gBACnD,GAAW,CAAC,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;gBAElC,sCAAsC;gBACtC,iBAAiB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;qBAClE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;oBACZ,MAAM,MAAM,GAAG,EAAE;yBACf,KAAK,CAAC,IAAI,CAAC;yBACX,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBACpB,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;wBACnD,OAAO,GAAG,kBAAkB,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,GAAG,IAAI,OAAO,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;oBACvI,CAAC,CAAC;yBACD,IAAI,CAAC,IAAI,CAAC,CAAC;oBACb,MAAM,OAAO,GAAG,SAAS;wBACxB,CAAC,CAAC,GAAG,kBAAkB,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI;wBAC7E,CAAC,CAAC,EAAE,CAAC;oBACN,MAAM,QAAQ,GAAG,KAAK,kBAAkB,GAAG,MAAM,KAAK,OAAO,GAAG,MAAM,IAAI,CAAC;oBAC3E,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;oBACnD,GAAW,CAAC,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;gBACnC,CAAC,CAAC;qBACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAElB,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,OAAO,CACX,kBAAkB,CACjB,GAAG,kBAAkB,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EACtG,OAAO,CACP,CACD,CAAC;YACF,OAAO,IAAI,CAAC;QACb,CAAC;KAC2C,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,OAAO,CAAC,MAAc;IAC9B,OAAO,CACL,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAmB;SACvC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAClB,CAAC;AACH,CAAC"}
|
package/dist/tui-text.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.resolveTextCtor = resolveTextCtor;
|
|
5
|
-
const pi_tui_1 = require("@earendil-works/pi-tui");
|
|
6
|
-
function getTextCtor() {
|
|
7
|
-
return pi_tui_1.Text;
|
|
1
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
2
|
+
export function getTextCtor() {
|
|
3
|
+
return Text;
|
|
8
4
|
}
|
|
9
|
-
function resolveTextCtor(TextComp) {
|
|
10
|
-
return TextComp ??
|
|
5
|
+
export function resolveTextCtor(TextComp) {
|
|
6
|
+
return TextComp ?? Text;
|
|
11
7
|
}
|
|
12
8
|
//# sourceMappingURL=tui-text.js.map
|
package/dist/tui-text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-text.js","sourceRoot":"","sources":["../src/tui-text.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui-text.js","sourceRoot":"","sources":["../src/tui-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAU9C,MAAM,UAAU,WAAW;IAC1B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAmB;IAClD,OAAO,QAAQ,IAAI,IAAI,CAAC;AACzB,CAAC"}
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyhuynhgiabuu/pi-pretty",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Pretty terminal output for pi
|
|
3
|
+
"version": "0.6.24",
|
|
4
|
+
"description": "Pretty terminal output for pi \u2014 syntax-highlighted file reads, colored bash output, tree-view directory listings, and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
7
7
|
"pi-extension",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@biomejs/biome": "^2.3.5",
|
|
45
|
-
"@earendil-works/pi-ai": "^0.
|
|
46
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
47
|
-
"@earendil-works/pi-tui": "^0.
|
|
45
|
+
"@earendil-works/pi-ai": "^0.84.3",
|
|
46
|
+
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
47
|
+
"@earendil-works/pi-tui": "^0.84.3",
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"typescript": "^5.0.0",
|
|
50
50
|
"vitest": "^4.0.18"
|
|
@@ -58,5 +58,6 @@
|
|
|
58
58
|
"extensions": [
|
|
59
59
|
"./dist/index.js"
|
|
60
60
|
]
|
|
61
|
-
}
|
|
61
|
+
},
|
|
62
|
+
"type": "module"
|
|
62
63
|
}
|
package/src/config.ts
CHANGED
|
@@ -67,37 +67,93 @@ function hexToAnsiBg(hex: string): string | null {
|
|
|
67
67
|
return `\x1b[48;2;${r};${g};${b}m`;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
interface PrettyConfig {
|
|
70
|
+
export interface PrettyConfig {
|
|
71
71
|
background?: {
|
|
72
72
|
tool?: string;
|
|
73
73
|
error?: string;
|
|
74
74
|
};
|
|
75
|
+
theme?: string;
|
|
76
|
+
icons?: string;
|
|
77
|
+
enableTools?: string[];
|
|
78
|
+
disableTools?: string[];
|
|
79
|
+
maxHlChars?: number;
|
|
80
|
+
maxPreviewLines?: number;
|
|
81
|
+
cacheLimit?: number;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
|
|
84
|
+
/** Normalize a comma-separated env value or JSON array into tool names. */
|
|
85
|
+
export function normalizeToolList(value: unknown): string[] {
|
|
86
|
+
if (!Array.isArray(value)) return [];
|
|
87
|
+
return value
|
|
88
|
+
.filter((v): v is string => typeof v === "string")
|
|
89
|
+
.map((v) => v.trim().toLowerCase())
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function positiveInt(value: unknown): number | undefined {
|
|
94
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Read and validate `pi-pretty.json` from the config directory. Invalid
|
|
99
|
+
* fields are silently skipped, matching the historical `background`
|
|
100
|
+
* behavior. The file is re-read on each call; callers that need a stable
|
|
101
|
+
* value (extension activation, theme resolution) cache the result.
|
|
102
|
+
*/
|
|
103
|
+
export function loadConfig(agentDir = getConfigDir()): PrettyConfig {
|
|
78
104
|
if (!agentDir) return {};
|
|
79
105
|
try {
|
|
80
|
-
const
|
|
81
|
-
const
|
|
106
|
+
const parsed = JSON.parse(readFileSync(join(agentDir, "pi-pretty.json"), "utf8")) as PrettyConfig;
|
|
107
|
+
const config: PrettyConfig = {};
|
|
82
108
|
if (parsed.background) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (parsed.background.error && !hexToAnsiBg(parsed.background.error)) {
|
|
87
|
-
parsed.background.error = undefined;
|
|
109
|
+
const background: NonNullable<PrettyConfig["background"]> = {};
|
|
110
|
+
if (parsed.background.tool && hexToAnsiBg(parsed.background.tool)) {
|
|
111
|
+
background.tool = parsed.background.tool;
|
|
88
112
|
}
|
|
89
|
-
if (
|
|
90
|
-
|
|
113
|
+
if (parsed.background.error && hexToAnsiBg(parsed.background.error)) {
|
|
114
|
+
background.error = parsed.background.error;
|
|
91
115
|
}
|
|
116
|
+
if (background.tool || background.error) config.background = background;
|
|
92
117
|
}
|
|
93
|
-
|
|
118
|
+
if (typeof parsed.theme === "string" && parsed.theme.trim() !== "") config.theme = parsed.theme;
|
|
119
|
+
if (typeof parsed.icons === "string") config.icons = parsed.icons;
|
|
120
|
+
const enableTools = normalizeToolList(parsed.enableTools);
|
|
121
|
+
const disableTools = normalizeToolList(parsed.disableTools);
|
|
122
|
+
if (enableTools.length) config.enableTools = enableTools;
|
|
123
|
+
if (disableTools.length) config.disableTools = disableTools;
|
|
124
|
+
const maxHlChars = positiveInt(parsed.maxHlChars);
|
|
125
|
+
const maxPreviewLines = positiveInt(parsed.maxPreviewLines);
|
|
126
|
+
const cacheLimit = positiveInt(parsed.cacheLimit);
|
|
127
|
+
if (maxHlChars) config.maxHlChars = maxHlChars;
|
|
128
|
+
if (maxPreviewLines) config.maxPreviewLines = maxPreviewLines;
|
|
129
|
+
if (cacheLimit) config.cacheLimit = cacheLimit;
|
|
130
|
+
return config;
|
|
94
131
|
} catch {
|
|
95
132
|
return {};
|
|
96
133
|
}
|
|
97
134
|
}
|
|
98
135
|
|
|
136
|
+
export function getConfigDir(): string | undefined {
|
|
137
|
+
return process.env.PRETTY_CONFIG_DIR ?? getDefaultAgentDir();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Merge env tool sets with config-file tool lists: a non-empty env set wins
|
|
142
|
+
* over the config file (12-factor precedence); otherwise config applies.
|
|
143
|
+
*/
|
|
144
|
+
export function resolveToolSets(
|
|
145
|
+
envDisabled: Set<string>,
|
|
146
|
+
envEnabled: Set<string>,
|
|
147
|
+
config: Pick<PrettyConfig, "disableTools" | "enableTools">,
|
|
148
|
+
): { disabledTools: Set<string>; enabledTools: Set<string> } {
|
|
149
|
+
return {
|
|
150
|
+
disabledTools: envDisabled.size > 0 ? envDisabled : new Set(config.disableTools ?? []),
|
|
151
|
+
enabledTools: envEnabled.size > 0 ? envEnabled : new Set(config.enableTools ?? []),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
99
155
|
function applyPrettyConfigBg(agentDir?: string): boolean {
|
|
100
|
-
const config =
|
|
156
|
+
const config = loadConfig(agentDir);
|
|
101
157
|
if (!config.background?.tool) return false;
|
|
102
158
|
const toolBg = hexToAnsiBg(config.background.tool);
|
|
103
159
|
if (!toolBg) return false;
|
|
@@ -108,8 +164,7 @@ function applyPrettyConfigBg(agentDir?: string): boolean {
|
|
|
108
164
|
}
|
|
109
165
|
|
|
110
166
|
export function resolveBaseBackground(theme: BgThemeLike | null | undefined): void {
|
|
111
|
-
const
|
|
112
|
-
const configDir = process.env.PRETTY_CONFIG_DIR ?? (home ? join(home, ".pi/agent") : undefined);
|
|
167
|
+
const configDir = getConfigDir();
|
|
113
168
|
if (applyPrettyConfigBg(configDir)) return;
|
|
114
169
|
if (!theme?.getBgAnsi && !theme?.bg) return;
|
|
115
170
|
BG_BASE =
|
|
@@ -139,7 +194,7 @@ export function termWidth(): number {
|
|
|
139
194
|
// ---------------------------------------------------------------------------
|
|
140
195
|
|
|
141
196
|
const ICONS_MODE = (process.env.PRETTY_ICONS ?? "nerd").toLowerCase();
|
|
142
|
-
export
|
|
197
|
+
export let USE_ICONS = ICONS_MODE !== "none" && ICONS_MODE !== "off";
|
|
143
198
|
|
|
144
199
|
export const NF_DIR = `${FG_BLUE}\ue5ff${RST}`;
|
|
145
200
|
export const NF_DEFAULT = `${FG_DIM}\uf15b${RST}`;
|
|
@@ -284,9 +339,25 @@ export function envInt(name: string, fallback: number): number {
|
|
|
284
339
|
return Number.isFinite(v) && v > 0 ? v : fallback;
|
|
285
340
|
}
|
|
286
341
|
|
|
287
|
-
export
|
|
288
|
-
export
|
|
289
|
-
export
|
|
342
|
+
export let MAX_HL_CHARS = envInt("PRETTY_MAX_HL_CHARS", 80_000);
|
|
343
|
+
export let MAX_PREVIEW_LINES = envInt("PRETTY_MAX_PREVIEW_LINES", 80);
|
|
344
|
+
export let CACHE_LIMIT = envInt("PRETTY_CACHE_LIMIT", 128);
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Apply `pi-pretty.json` values to the env-backed module bindings. An env
|
|
348
|
+
* var wins over the config file whenever it is set to a non-empty value;
|
|
349
|
+
* empty strings are treated as unset. Call once at extension activation,
|
|
350
|
+
* before any render reads the bindings.
|
|
351
|
+
*/
|
|
352
|
+
export function applyConfig(config: PrettyConfig): void {
|
|
353
|
+
if (!process.env.PRETTY_MAX_HL_CHARS && config.maxHlChars) MAX_HL_CHARS = config.maxHlChars;
|
|
354
|
+
if (!process.env.PRETTY_MAX_PREVIEW_LINES && config.maxPreviewLines) MAX_PREVIEW_LINES = config.maxPreviewLines;
|
|
355
|
+
if (!process.env.PRETTY_CACHE_LIMIT && config.cacheLimit) CACHE_LIMIT = config.cacheLimit;
|
|
356
|
+
if (!process.env.PRETTY_ICONS && config.icons) {
|
|
357
|
+
const mode = config.icons.toLowerCase();
|
|
358
|
+
USE_ICONS = mode !== "none" && mode !== "off";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
290
361
|
|
|
291
362
|
// ---------------------------------------------------------------------------
|
|
292
363
|
// Agent directory helpers
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import * as hostSdk from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { createFffAutocompleteProvider } from "./autocomplete.js";
|
|
15
|
-
import { getDefaultAgentDir } from "./config.js";
|
|
15
|
+
import { applyConfig, getDefaultAgentDir, loadConfig, normalizeToolList, resolveToolSets } from "./config.js";
|
|
16
16
|
import { type FffService, getSharedFffService } from "./fff.js";
|
|
17
17
|
import { registerBashTool } from "./tools/bash.js";
|
|
18
18
|
import { registerFindTool } from "./tools/find.js";
|
|
@@ -28,12 +28,7 @@ import type { PiPrettyDeps, SdkTools } from "./types.js";
|
|
|
28
28
|
const DEFAULT_DISABLED_TOOLS = new Set(["ls"]);
|
|
29
29
|
|
|
30
30
|
function envTools(name: "PRETTY_DISABLE_TOOLS" | "PRETTY_ENABLE_TOOLS"): Set<string> {
|
|
31
|
-
return new Set(
|
|
32
|
-
(process.env[name] ?? "")
|
|
33
|
-
.split(",")
|
|
34
|
-
.map((tool) => tool.trim().toLowerCase())
|
|
35
|
-
.filter(Boolean),
|
|
36
|
-
);
|
|
31
|
+
return new Set(normalizeToolList((process.env[name] ?? "").split(",")));
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
// ---------------------------------------------------------------------------
|
|
@@ -43,8 +38,13 @@ function envTools(name: "PRETTY_DISABLE_TOOLS" | "PRETTY_ENABLE_TOOLS"): Set<str
|
|
|
43
38
|
export type { PiPrettyDeps };
|
|
44
39
|
|
|
45
40
|
export default async function piPrettyExtension(pi: ExtensionAPI, deps?: PiPrettyDeps): Promise<void> {
|
|
46
|
-
const
|
|
47
|
-
|
|
41
|
+
const config = loadConfig();
|
|
42
|
+
applyConfig(config);
|
|
43
|
+
const { disabledTools, enabledTools } = resolveToolSets(
|
|
44
|
+
envTools("PRETTY_DISABLE_TOOLS"),
|
|
45
|
+
envTools("PRETTY_ENABLE_TOOLS"),
|
|
46
|
+
config,
|
|
47
|
+
);
|
|
48
48
|
const isToolEnabled = (name: string) => {
|
|
49
49
|
const normalizedName = name.toLowerCase();
|
|
50
50
|
return (
|
|
@@ -216,5 +216,4 @@ export default async function piPrettyExtension(pi: ExtensionAPI, deps?: PiPrett
|
|
|
216
216
|
if (isToolEnabled("grep") && createGrepTool) {
|
|
217
217
|
registerGrepTool(pi, cwd, fffService, createGrepTool(cwd), TextComp);
|
|
218
218
|
}
|
|
219
|
-
|
|
220
219
|
}
|
package/src/render.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
|
11
11
|
// static top-level import/require, not function-body require — see tui-text.ts).
|
|
12
12
|
import { Text as TuiText, truncateToWidth } from "@earendil-works/pi-tui";
|
|
13
13
|
import { codeToANSI } from "@shikijs/cli";
|
|
14
|
-
import type
|
|
14
|
+
import { type BundledLanguage, type BundledTheme, bundledThemes } from "shiki";
|
|
15
15
|
import {
|
|
16
16
|
BG_BASE,
|
|
17
17
|
BG_ERROR,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
FG_LNUM,
|
|
24
24
|
FG_RULE,
|
|
25
25
|
FG_YELLOW,
|
|
26
|
+
loadConfig,
|
|
26
27
|
MAX_HL_CHARS,
|
|
27
28
|
MAX_PREVIEW_LINES,
|
|
28
29
|
RST,
|
|
@@ -51,22 +52,68 @@ function _truncateToWidth(text: string, maxWidth: number, ellipsis?: string, pad
|
|
|
51
52
|
|
|
52
53
|
const DEFAULT_THEME: BundledTheme = "github-dark";
|
|
53
54
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
function isBundledTheme(name: string): name is BundledTheme {
|
|
56
|
+
return Object.hasOwn(bundledThemes, name);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pickValidTheme(candidate: string, source: string): BundledTheme {
|
|
60
|
+
if (isBundledTheme(candidate)) return candidate;
|
|
61
|
+
console.warn(
|
|
62
|
+
`pi-pretty: theme "${candidate}" from ${source} is not a bundled Shiki theme; falling back to "${DEFAULT_THEME}"`,
|
|
63
|
+
);
|
|
64
|
+
return DEFAULT_THEME;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the Shiki theme used for syntax highlighting.
|
|
69
|
+
*
|
|
70
|
+
* `~/.pi/agent/settings.json`'s `theme` field is pi's TUI appearance setting
|
|
71
|
+
* (e.g. `dark`, `light`, or a custom pi theme name), which is not a valid
|
|
72
|
+
* Shiki theme for native users. Validate the candidate against Shiki's
|
|
73
|
+
* bundled themes; warn once and fall back to {@link DEFAULT_THEME} when it is
|
|
74
|
+
* not a bundled theme, instead of silently degrading every highlighted block
|
|
75
|
+
* to plain text.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveTheme(env = process.env.PRETTY_THEME, home = process.env.HOME): BundledTheme {
|
|
78
|
+
if (env) return pickValidTheme(env, "PRETTY_THEME");
|
|
57
79
|
try {
|
|
58
|
-
const home = process.env.HOME;
|
|
59
80
|
if (!home) return DEFAULT_THEME;
|
|
60
81
|
const settings = JSON.parse(
|
|
61
82
|
require("node:fs").readFileSync(require("node:path").join(home, ".pi/agent/settings.json"), "utf8"),
|
|
62
83
|
);
|
|
63
|
-
|
|
84
|
+
const candidate = settings.theme;
|
|
85
|
+
if (typeof candidate === "string") return pickValidTheme(candidate, "~/.pi/agent/settings.json");
|
|
86
|
+
return DEFAULT_THEME;
|
|
64
87
|
} catch {
|
|
65
88
|
return DEFAULT_THEME;
|
|
66
89
|
}
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
|
|
92
|
+
let _theme: BundledTheme | undefined;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve the effective theme once per session: `PRETTY_THEME` env →
|
|
96
|
+
* `pi-pretty.json` → `~/.pi/agent/settings.json` → {@link DEFAULT_THEME}.
|
|
97
|
+
* Resolution is lazy so config-file values applied at extension activation
|
|
98
|
+
* take effect, and the result is cached (at most one warning per source).
|
|
99
|
+
*/
|
|
100
|
+
export function getTheme(): BundledTheme {
|
|
101
|
+
if (_theme) return _theme;
|
|
102
|
+
const env = process.env.PRETTY_THEME;
|
|
103
|
+
let theme: BundledTheme;
|
|
104
|
+
if (env) {
|
|
105
|
+
theme = resolveTheme(env, "");
|
|
106
|
+
} else {
|
|
107
|
+
const configTheme = loadConfig().theme;
|
|
108
|
+
theme =
|
|
109
|
+
typeof configTheme === "string"
|
|
110
|
+
? pickValidTheme(configTheme, "pi-pretty.json")
|
|
111
|
+
: resolveTheme(undefined, process.env.HOME);
|
|
112
|
+
}
|
|
113
|
+
_theme = theme;
|
|
114
|
+
return theme;
|
|
115
|
+
}
|
|
116
|
+
|
|
70
117
|
const _cache = new Map<string, string[]>();
|
|
71
118
|
|
|
72
119
|
function _touch(k: string, v: string[]): string[] {
|
|
@@ -84,12 +131,12 @@ async function hlBlock(code: string, language: BundledLanguage | undefined): Pro
|
|
|
84
131
|
if (!code) return [""];
|
|
85
132
|
if (!language || code.length > MAX_HL_CHARS) return code.split("\n");
|
|
86
133
|
|
|
87
|
-
const k = `${
|
|
134
|
+
const k = `${getTheme()}\0${language}\0${code}`;
|
|
88
135
|
const hit = _cache.get(k);
|
|
89
136
|
if (hit) return _touch(k, hit);
|
|
90
137
|
|
|
91
138
|
try {
|
|
92
|
-
const ansi = normalizeShikiContrast(await codeToANSI(code, language,
|
|
139
|
+
const ansi = normalizeShikiContrast(await codeToANSI(code, language, getTheme()));
|
|
93
140
|
const out = (ansi.endsWith("\n") ? ansi.slice(0, -1) : ansi).split("\n");
|
|
94
141
|
return _touch(k, out);
|
|
95
142
|
} catch {
|