@quandev104/pi-style 0.2.1 → 0.2.3
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 +21 -0
- package/README.md +8 -3
- package/dist/extensions/pi-style.js +1328 -333
- package/dist/extensions/pi-style.js.map +1 -1
- package/extension-src/pi-style/app/command-service.ts +8 -0
- package/extension-src/pi-style/app/runtime.ts +33 -4
- package/extension-src/pi-style/domain/config-normalization.ts +11 -1
- package/extension-src/pi-style/domain/config-types.ts +12 -0
- package/extension-src/pi-style/domain/status-renderer.ts +41 -9
- package/extension-src/pi-style/domain/status.ts +28 -12
- package/extension-src/pi-style/domain/theme.ts +32 -1
- package/extension-src/pi-style/features/editor/index.ts +144 -5
- package/extension-src/pi-style/features/messages/image-input.ts +205 -0
- package/extension-src/pi-style/features/messages/image-preview.ts +288 -0
- package/extension-src/pi-style/features/messages/index.ts +302 -61
- package/extension-src/pi-style/features/messages/render-config.ts +36 -0
- package/extension-src/pi-style/features/status-line/index.ts +41 -11
- package/extension-src/pi-style/features/tools/boxed/bash.ts +101 -40
- package/extension-src/pi-style/features/tools/boxed/batch.ts +17 -1
- package/extension-src/pi-style/features/tools/boxed/edit.ts +20 -15
- package/extension-src/pi-style/features/tools/boxed/find.ts +9 -4
- package/extension-src/pi-style/features/tools/boxed/git.ts +46 -2
- package/extension-src/pi-style/features/tools/boxed/grep.ts +9 -2
- package/extension-src/pi-style/features/tools/boxed/ls.ts +9 -4
- package/extension-src/pi-style/features/tools/boxed/quick-edit.ts +18 -11
- package/extension-src/pi-style/features/tools/boxed/read.ts +4 -2
- package/extension-src/pi-style/features/tools/boxed/shared.ts +27 -1
- package/extension-src/pi-style/features/tools/boxed/turn-summary.ts +12 -0
- package/extension-src/pi-style/pi/index.ts +55 -2
- package/extension-src/pi-style/pi/session-coordinator.ts +13 -0
- package/extension-src/pi-style/shared/ansi.ts +17 -5
- package/extension-src/pi-style/shared/box.ts +70 -4
- package/extension-src/pi-style/shared/clipboard-path.ts +98 -0
- package/extension-src/pi-style/shared/clipboard-presence.ts +112 -0
- package/extension-src/pi-style/shared/pending-images.ts +199 -0
- package/extension-src/pi-style/shared/split-diff.ts +8 -5
- package/package.json +1 -1
- package/extension-src/pi-style/features/.gitkeep +0 -0
|
@@ -1,4 +1,354 @@
|
|
|
1
|
+
// extension-src/pi-style/features/messages/image-input.ts
|
|
2
|
+
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
3
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
4
|
+
|
|
5
|
+
// extension-src/pi-style/shared/clipboard-path.ts
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
var UUID_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
8
|
+
var CLIPBOARD_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"];
|
|
9
|
+
function clipboardImageMarker(index) {
|
|
10
|
+
return `[Image #${index}]`;
|
|
11
|
+
}
|
|
12
|
+
function escapeRegExp(text) {
|
|
13
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14
|
+
}
|
|
15
|
+
var clipboardPathRegexCache = /* @__PURE__ */ new Map();
|
|
16
|
+
var CLIPBOARD_PATH_REGEX_CACHE_MAX = 16;
|
|
17
|
+
function clipboardPathRegex(tmpRoot = tmpdir()) {
|
|
18
|
+
const cached = clipboardPathRegexCache.get(tmpRoot);
|
|
19
|
+
if (cached) return cached;
|
|
20
|
+
const exts = CLIPBOARD_IMAGE_EXTENSIONS.join("|");
|
|
21
|
+
const regex = new RegExp(
|
|
22
|
+
`(?<![a-zA-Z0-9])(${escapeRegExp(tmpRoot)}/pi-clipboard-${UUID_PATTERN}\\.(${exts}))(?![a-zA-Z0-9])`,
|
|
23
|
+
"g"
|
|
24
|
+
);
|
|
25
|
+
if (clipboardPathRegexCache.size >= CLIPBOARD_PATH_REGEX_CACHE_MAX) {
|
|
26
|
+
const oldest = clipboardPathRegexCache.keys().next().value;
|
|
27
|
+
if (oldest !== void 0) clipboardPathRegexCache.delete(oldest);
|
|
28
|
+
}
|
|
29
|
+
clipboardPathRegexCache.set(tmpRoot, regex);
|
|
30
|
+
return regex;
|
|
31
|
+
}
|
|
32
|
+
function isSingleClipboardImagePath(text, tmpRoot) {
|
|
33
|
+
if (!text || /\s/.test(text)) return false;
|
|
34
|
+
const match = text.match(clipboardPathRegex(tmpRoot ?? tmpdir()));
|
|
35
|
+
return match !== null && match.length === 1 && match[0] === text;
|
|
36
|
+
}
|
|
37
|
+
function extractClipboardImageTokens(text, tmpRoot) {
|
|
38
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const match of text.matchAll(clipboardPathRegex(tmpRoot ?? tmpdir()))) {
|
|
40
|
+
const token = match[1];
|
|
41
|
+
if (token) tokens.add(token);
|
|
42
|
+
}
|
|
43
|
+
return [...tokens];
|
|
44
|
+
}
|
|
45
|
+
var MARKER_AT_END_PATTERN = /\[Image #([0-9]+)\]( ?)$/;
|
|
46
|
+
function clipboardMarkerAtEnd(text) {
|
|
47
|
+
const match = MARKER_AT_END_PATTERN.exec(text);
|
|
48
|
+
if (!match) return void 0;
|
|
49
|
+
return { index: Number(match[1]), length: match[0].length };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// extension-src/pi-style/shared/clipboard-presence.ts
|
|
53
|
+
import { realpathSync } from "fs";
|
|
54
|
+
import { createRequire } from "module";
|
|
55
|
+
import { dirname } from "path";
|
|
56
|
+
var cachedModule;
|
|
57
|
+
function clipboardResolutionRoots() {
|
|
58
|
+
const roots = [];
|
|
59
|
+
const argvMain = process.argv[1];
|
|
60
|
+
if (typeof argvMain === "string" && argvMain.startsWith("/")) {
|
|
61
|
+
try {
|
|
62
|
+
roots.push(dirname(realpathSync(argvMain)));
|
|
63
|
+
} catch {
|
|
64
|
+
roots.push(dirname(argvMain));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
roots.push(import.meta.url);
|
|
68
|
+
return roots;
|
|
69
|
+
}
|
|
70
|
+
function loadClipboardModule() {
|
|
71
|
+
if (cachedModule !== void 0) return cachedModule;
|
|
72
|
+
if (process.env.TERMUX_VERSION) {
|
|
73
|
+
cachedModule = null;
|
|
74
|
+
return cachedModule;
|
|
75
|
+
}
|
|
76
|
+
for (const root of clipboardResolutionRoots()) {
|
|
77
|
+
try {
|
|
78
|
+
const require2 = createRequire(root);
|
|
79
|
+
const resolved = require2("@mariozechner/clipboard");
|
|
80
|
+
if (resolved && typeof resolved.hasImage === "function") {
|
|
81
|
+
cachedModule = resolved;
|
|
82
|
+
return cachedModule;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
cachedModule = null;
|
|
88
|
+
return cachedModule;
|
|
89
|
+
}
|
|
90
|
+
function clipboardHasImageSync() {
|
|
91
|
+
const clipboard = loadClipboardModule();
|
|
92
|
+
if (!clipboard || typeof clipboard.hasImage !== "function") return null;
|
|
93
|
+
try {
|
|
94
|
+
return clipboard.hasImage() === true;
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function readClipboardImageBinary(options = {}) {
|
|
100
|
+
const clipboard = loadClipboardModule();
|
|
101
|
+
if (!clipboard || typeof clipboard.hasImage !== "function" || typeof clipboard.getImageBinary !== "function") {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
if (!options.skipPresenceProbe && !clipboard.hasImage()) return null;
|
|
106
|
+
const imageData = await clipboard.getImageBinary();
|
|
107
|
+
if (!imageData || imageData.length === 0) return null;
|
|
108
|
+
const bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);
|
|
109
|
+
return bytes.length > 0 ? { bytes } : null;
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// extension-src/pi-style/shared/pending-images.ts
|
|
116
|
+
import { readFile, stat } from "fs/promises";
|
|
117
|
+
var MAX_ATTACHABLE_BYTES = 20 * 1024 * 1024;
|
|
118
|
+
var MIME_BY_EXTENSION = Object.freeze({
|
|
119
|
+
png: "image/png",
|
|
120
|
+
jpg: "image/jpeg",
|
|
121
|
+
jpeg: "image/jpeg",
|
|
122
|
+
webp: "image/webp",
|
|
123
|
+
gif: "image/gif"
|
|
124
|
+
});
|
|
125
|
+
var pendingImages = /* @__PURE__ */ new Map();
|
|
126
|
+
var nextPendingIndex = 1;
|
|
127
|
+
function resetPendingImageRegistry() {
|
|
128
|
+
pendingImages.clear();
|
|
129
|
+
nextPendingIndex = 1;
|
|
130
|
+
}
|
|
131
|
+
function allocatePendingImage() {
|
|
132
|
+
const index = nextPendingIndex++;
|
|
133
|
+
let resolveFill = () => {
|
|
134
|
+
};
|
|
135
|
+
const entry = {
|
|
136
|
+
fill: new Promise((resolve3) => {
|
|
137
|
+
resolveFill = resolve3;
|
|
138
|
+
}),
|
|
139
|
+
settled: false
|
|
140
|
+
};
|
|
141
|
+
pendingImages.set(index, entry);
|
|
142
|
+
entry.resolveFill = resolveFill;
|
|
143
|
+
return { index, marker: `${clipboardImageMarker(index)} ` };
|
|
144
|
+
}
|
|
145
|
+
async function fillPendingImageFromPath(index, path, deps = {}) {
|
|
146
|
+
const entry = pendingImages.get(index);
|
|
147
|
+
if (!entry || entry.settled) return;
|
|
148
|
+
const extension = path.split(".").pop() ?? "";
|
|
149
|
+
const mimeType = MIME_BY_EXTENSION[extension];
|
|
150
|
+
if (!mimeType) {
|
|
151
|
+
settle(entry);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const readFileImpl = deps.readFile ?? ((p) => readFile(p));
|
|
155
|
+
const statSizeImpl = deps.statSize ?? (async (p) => {
|
|
156
|
+
try {
|
|
157
|
+
return (await stat(p)).size;
|
|
158
|
+
} catch {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
const size = await statSizeImpl(path);
|
|
164
|
+
if (size === void 0 || size <= 0 || size > MAX_ATTACHABLE_BYTES) {
|
|
165
|
+
settle(entry);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const bytes = await readFileImpl(path);
|
|
169
|
+
if (bytes.length === 0 || bytes.length > MAX_ATTACHABLE_BYTES) {
|
|
170
|
+
settle(entry);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
entry.data = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
174
|
+
entry.mimeType = mimeType;
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
settle(entry);
|
|
178
|
+
}
|
|
179
|
+
function fillPendingImageBytes(index, data, mimeType) {
|
|
180
|
+
const entry = pendingImages.get(index);
|
|
181
|
+
if (!entry || entry.settled) return;
|
|
182
|
+
if (data.length > 0) {
|
|
183
|
+
entry.data = data;
|
|
184
|
+
entry.mimeType = mimeType;
|
|
185
|
+
}
|
|
186
|
+
settle(entry);
|
|
187
|
+
}
|
|
188
|
+
function discardPendingImage(index) {
|
|
189
|
+
const entry = pendingImages.get(index);
|
|
190
|
+
if (!entry) return;
|
|
191
|
+
settle(entry);
|
|
192
|
+
pendingImages.delete(index);
|
|
193
|
+
}
|
|
194
|
+
function isPendingMarkerIndex(index) {
|
|
195
|
+
return pendingImages.has(index);
|
|
196
|
+
}
|
|
197
|
+
function discardPendingMarkerIndex(index) {
|
|
198
|
+
discardPendingImage(index);
|
|
199
|
+
}
|
|
200
|
+
function isPendingImageFilled(index) {
|
|
201
|
+
const entry = pendingImages.get(index);
|
|
202
|
+
return entry !== void 0 && entry.data !== void 0 && entry.mimeType !== void 0;
|
|
203
|
+
}
|
|
204
|
+
function settle(entry) {
|
|
205
|
+
entry.settled = true;
|
|
206
|
+
entry.resolveFill?.();
|
|
207
|
+
}
|
|
208
|
+
async function resolvePendingImagesForSubmit(text) {
|
|
209
|
+
if (pendingImages.size === 0) return void 0;
|
|
210
|
+
const markerRe = /\[Image #([0-9]+)\]/g;
|
|
211
|
+
const found = [];
|
|
212
|
+
for (const match of text.matchAll(markerRe)) {
|
|
213
|
+
const index = Number(match[1]);
|
|
214
|
+
if (Number.isInteger(index) && pendingImages.has(index)) found.push(index);
|
|
215
|
+
}
|
|
216
|
+
const matched = new Set(found);
|
|
217
|
+
await Promise.all(
|
|
218
|
+
[...pendingImages.entries()].filter(([index]) => matched.has(index)).map(([, entry]) => entry.fill)
|
|
219
|
+
);
|
|
220
|
+
const images = [];
|
|
221
|
+
for (const index of [...matched].sort((a, b) => a - b)) {
|
|
222
|
+
const entry = pendingImages.get(index);
|
|
223
|
+
if (entry?.data && entry.mimeType) {
|
|
224
|
+
images.push({ type: "image", data: entry.data, mimeType: entry.mimeType });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const index of [...pendingImages.keys()]) discardPendingImage(index);
|
|
228
|
+
return images.length > 0 ? { images } : void 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// extension-src/pi-style/features/messages/render-config.ts
|
|
232
|
+
var sessionMessagesConfig = {
|
|
233
|
+
showImagePreviews: true,
|
|
234
|
+
clipboardImages: true,
|
|
235
|
+
previewMaxWidth: 30
|
|
236
|
+
};
|
|
237
|
+
function setMessagesRenderConfig(config) {
|
|
238
|
+
sessionMessagesConfig = { ...sessionMessagesConfig, ...config };
|
|
239
|
+
}
|
|
240
|
+
function getMessagesRenderConfig() {
|
|
241
|
+
return sessionMessagesConfig;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// extension-src/pi-style/features/messages/image-input.ts
|
|
245
|
+
var IMAGE_TOKEN = "[image]";
|
|
246
|
+
var MAX_ATTACHABLE_BYTES2 = 20 * 1024 * 1024;
|
|
247
|
+
var MIME_BY_EXTENSION2 = Object.freeze({
|
|
248
|
+
png: "image/png",
|
|
249
|
+
jpg: "image/jpeg",
|
|
250
|
+
jpeg: "image/jpeg",
|
|
251
|
+
webp: "image/webp",
|
|
252
|
+
gif: "image/gif"
|
|
253
|
+
});
|
|
254
|
+
function createClipboardImagePasteSurface(deps = {}) {
|
|
255
|
+
const hasImage = deps.hasImage ?? clipboardHasImageSync;
|
|
256
|
+
const readBinary = deps.readBinary ?? (() => readClipboardImageBinary({ skipPresenceProbe: true }));
|
|
257
|
+
return {
|
|
258
|
+
enabled: () => getMessagesRenderConfig().clipboardImages,
|
|
259
|
+
clipboardHasImage: () => {
|
|
260
|
+
try {
|
|
261
|
+
return hasImage();
|
|
262
|
+
} catch {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
markerFromClipboard() {
|
|
267
|
+
const { index, marker: marker2 } = allocatePendingImage();
|
|
268
|
+
void (async () => {
|
|
269
|
+
try {
|
|
270
|
+
const image = await readBinary();
|
|
271
|
+
if (image) {
|
|
272
|
+
fillPendingImageBytes(
|
|
273
|
+
index,
|
|
274
|
+
Buffer.from(image.bytes.buffer, image.bytes.byteOffset, image.bytes.byteLength).toString("base64"),
|
|
275
|
+
"image/png"
|
|
276
|
+
);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
discardPendingMarkerIndex(index);
|
|
282
|
+
})();
|
|
283
|
+
return marker2;
|
|
284
|
+
},
|
|
285
|
+
async markerFromArtifact(path) {
|
|
286
|
+
const { index, marker: marker2 } = allocatePendingImage();
|
|
287
|
+
await fillPendingImageFromPath(index, path);
|
|
288
|
+
return isPendingImageFilled(index) ? marker2 : path;
|
|
289
|
+
},
|
|
290
|
+
isMarkerIndexRegistered: (index) => isPendingMarkerIndex(index),
|
|
291
|
+
discardMarkerIndex: (index) => discardPendingMarkerIndex(index)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
async function resolvePendingImageMarkers(text) {
|
|
295
|
+
if (!getMessagesRenderConfig().clipboardImages) return void 0;
|
|
296
|
+
return resolvePendingImagesForSubmit(text);
|
|
297
|
+
}
|
|
298
|
+
async function transformClipboardImages(text, deps = {}) {
|
|
299
|
+
if (!getMessagesRenderConfig().clipboardImages) return void 0;
|
|
300
|
+
const tokens = extractClipboardImageTokens(text, deps.tmpRoot);
|
|
301
|
+
if (tokens.length === 0) return void 0;
|
|
302
|
+
const attached = [];
|
|
303
|
+
const rewritten = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const token of tokens) {
|
|
305
|
+
const image = await readAttachable(token, deps);
|
|
306
|
+
if (!image) continue;
|
|
307
|
+
attached.push({ type: "image", data: image.data, mimeType: image.mimeType });
|
|
308
|
+
rewritten.set(token, IMAGE_TOKEN);
|
|
309
|
+
}
|
|
310
|
+
if (attached.length === 0) return void 0;
|
|
311
|
+
return { text: rewriteTokens(text, rewritten, deps.tmpRoot), images: attached };
|
|
312
|
+
}
|
|
313
|
+
async function readAttachable(path, deps) {
|
|
314
|
+
const extension = path.split(".").pop() ?? "";
|
|
315
|
+
const mimeType = MIME_BY_EXTENSION2[extension];
|
|
316
|
+
if (!mimeType) return void 0;
|
|
317
|
+
const readFileImpl = deps?.readFile ?? ((p) => readFile2(p));
|
|
318
|
+
const statSizeImpl = deps?.statSize ?? (async (p) => {
|
|
319
|
+
try {
|
|
320
|
+
return (await stat2(p)).size;
|
|
321
|
+
} catch {
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
try {
|
|
326
|
+
const size = await statSizeImpl(path);
|
|
327
|
+
if (size === void 0 || size <= 0 || size > MAX_ATTACHABLE_BYTES2) return void 0;
|
|
328
|
+
const bytes = await readFileImpl(path);
|
|
329
|
+
if (bytes.length === 0 || bytes.length > MAX_ATTACHABLE_BYTES2) return void 0;
|
|
330
|
+
return { data: Buffer.from(bytes).toString("base64"), mimeType };
|
|
331
|
+
} catch {
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function rewriteTokens(text, rewritten, tmpRoot) {
|
|
336
|
+
if (rewritten.size === 0) return text;
|
|
337
|
+
return text.replace(
|
|
338
|
+
clipboardPathRegex(tmpRoot ?? tmpdir2()),
|
|
339
|
+
(match, token) => rewritten.has(token) ? IMAGE_TOKEN : match
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// extension-src/pi-style/features/messages/image-preview.ts
|
|
344
|
+
import {
|
|
345
|
+
getCapabilities,
|
|
346
|
+
getImageDimensions,
|
|
347
|
+
Image
|
|
348
|
+
} from "@earendil-works/pi-tui";
|
|
349
|
+
|
|
1
350
|
// extension-src/pi-style/shared/ansi.ts
|
|
351
|
+
import { visibleWidth as tuiVisibleWidth } from "@earendil-works/pi-tui";
|
|
2
352
|
function isFinal(byte) {
|
|
3
353
|
return byte >= "@" && byte <= "~";
|
|
4
354
|
}
|
|
@@ -82,7 +432,7 @@ function stripAnsi(value) {
|
|
|
82
432
|
return output;
|
|
83
433
|
}
|
|
84
434
|
function visibleWidth(value) {
|
|
85
|
-
return
|
|
435
|
+
return tuiVisibleWidth(value);
|
|
86
436
|
}
|
|
87
437
|
function resetAnsi(value) {
|
|
88
438
|
return `${value}\x1B[0m`;
|
|
@@ -93,9 +443,10 @@ function fitAnsiWidth(value, width, ellipsis = "\u2026") {
|
|
|
93
443
|
function truncateAnsi(value, width, ellipsis = "\u2026") {
|
|
94
444
|
if (width <= 0) return "";
|
|
95
445
|
if (visibleWidth(value) <= width) return resetAnsi(value);
|
|
446
|
+
const ellipsisWidth = visibleWidth(ellipsis);
|
|
96
447
|
let output = "";
|
|
97
448
|
let visible = 0;
|
|
98
|
-
for (let i = 0; i < value.length && visible < width -
|
|
449
|
+
for (let i = 0; i < value.length && visible < width - ellipsisWidth; i++) {
|
|
99
450
|
if (value.charCodeAt(i) === 27) {
|
|
100
451
|
const start = i;
|
|
101
452
|
i++;
|
|
@@ -212,6 +563,147 @@ function fgHex(theme, hex, text) {
|
|
|
212
563
|
return `${escapes.prefix}${text}${escapes.suffix}`;
|
|
213
564
|
}
|
|
214
565
|
|
|
566
|
+
// extension-src/pi-style/features/messages/image-preview.ts
|
|
567
|
+
var IMAGE_PREVIEW_ENTRY_TYPE = "pi-style-image-preview";
|
|
568
|
+
var IMAGE_PREVIEW_EXPANDED_MAX_WIDTH_CELLS = 60;
|
|
569
|
+
var IMAGE_PREVIEW_MAX_WIDTH_CELLS = 60;
|
|
570
|
+
var GRID_GAP = 2;
|
|
571
|
+
var GRID_MIN_COLUMN = 14;
|
|
572
|
+
function isPreviewableImage(value) {
|
|
573
|
+
if (!value || typeof value !== "object") return false;
|
|
574
|
+
const data = value.data;
|
|
575
|
+
const mimeType = value.mimeType;
|
|
576
|
+
return typeof data === "string" && data.length > 0 && typeof mimeType === "string" && mimeType.length > 0;
|
|
577
|
+
}
|
|
578
|
+
function stageImagePreviewData(images) {
|
|
579
|
+
if (!getMessagesRenderConfig().showImagePreviews) return void 0;
|
|
580
|
+
const valid = images.filter(isPreviewableImage).map((image) => ({ data: image.data, mimeType: image.mimeType }));
|
|
581
|
+
return valid.length > 0 ? { images: valid } : void 0;
|
|
582
|
+
}
|
|
583
|
+
function flushImagePreviewEntry(pi, data) {
|
|
584
|
+
if (!getMessagesRenderConfig().showImagePreviews) return;
|
|
585
|
+
pi.appendEntry(IMAGE_PREVIEW_ENTRY_TYPE, data);
|
|
586
|
+
}
|
|
587
|
+
function previewImagesFromEntryData(data) {
|
|
588
|
+
if (!data || typeof data !== "object") return void 0;
|
|
589
|
+
const images = data.images;
|
|
590
|
+
if (!Array.isArray(images) || images.length === 0) return void 0;
|
|
591
|
+
if (!images.every(isPreviewableImage)) return void 0;
|
|
592
|
+
return images;
|
|
593
|
+
}
|
|
594
|
+
function imageLabel(theme, index, dims) {
|
|
595
|
+
const dimsPart = dims ? ` \xB7 ${dims.widthPx}\xD7${dims.heightPx}` : "";
|
|
596
|
+
return theme.fg("dim", `#${index}${dimsPart}`);
|
|
597
|
+
}
|
|
598
|
+
var __dimensionParsesForTest = { count: 0 };
|
|
599
|
+
function parseImageDimensions(image) {
|
|
600
|
+
__dimensionParsesForTest.count++;
|
|
601
|
+
const fromPrefix = getImageDimensions(image.data.slice(0, 256), image.mimeType);
|
|
602
|
+
return fromPrefix ?? getImageDimensions(image.data, image.mimeType) ?? void 0;
|
|
603
|
+
}
|
|
604
|
+
var previewCache = /* @__PURE__ */ new WeakMap();
|
|
605
|
+
function renderImagePreviewEntry(entry, options, theme) {
|
|
606
|
+
if (!getMessagesRenderConfig().showImagePreviews) return void 0;
|
|
607
|
+
const images = previewImagesFromEntryData(entry?.data);
|
|
608
|
+
if (!images) return void 0;
|
|
609
|
+
const fg = theme?.fg;
|
|
610
|
+
if (typeof fg !== "function") return void 0;
|
|
611
|
+
const expanded = options?.expanded === true;
|
|
612
|
+
try {
|
|
613
|
+
const themed = { fg: fg.bind(theme) };
|
|
614
|
+
let cached = previewCache.get(entry);
|
|
615
|
+
if (!cached) {
|
|
616
|
+
const dims = images.map(parseImageDimensions);
|
|
617
|
+
const components2 = images.map(
|
|
618
|
+
(image, index) => new Image(
|
|
619
|
+
image.data,
|
|
620
|
+
image.mimeType,
|
|
621
|
+
{ fallbackColor: (text) => themed.fg("toolOutput", text) },
|
|
622
|
+
{},
|
|
623
|
+
dims[index]
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
const labels2 = images.map((_image, index) => imageLabel(themed, index + 1, dims[index]));
|
|
627
|
+
cached = { images, components: components2, labels: labels2 };
|
|
628
|
+
previewCache.set(entry, cached);
|
|
629
|
+
}
|
|
630
|
+
const { components, labels } = cached;
|
|
631
|
+
let memo;
|
|
632
|
+
return {
|
|
633
|
+
invalidate() {
|
|
634
|
+
memo = void 0;
|
|
635
|
+
for (const component of components) component.invalidate();
|
|
636
|
+
},
|
|
637
|
+
render(width) {
|
|
638
|
+
const config = getMessagesRenderConfig();
|
|
639
|
+
const kitty = getCapabilities().images === "kitty";
|
|
640
|
+
const key = `${width}|${expanded}|${config.previewMaxWidth}|${config.showImagePreviews}|${kitty}`;
|
|
641
|
+
if (memo && memo.key === key) return memo.lines;
|
|
642
|
+
const maxWidth = expanded ? IMAGE_PREVIEW_EXPANDED_MAX_WIDTH_CELLS : Math.min(config.previewMaxWidth, IMAGE_PREVIEW_MAX_WIDTH_CELLS);
|
|
643
|
+
let lines;
|
|
644
|
+
if (width <= 0) lines = [];
|
|
645
|
+
else if (images.length === 1 || !kitty) lines = renderStacked(width, maxWidth, labels, components);
|
|
646
|
+
else lines = renderGrid(width, maxWidth, labels, components);
|
|
647
|
+
memo = { key, lines };
|
|
648
|
+
return lines;
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
} catch {
|
|
652
|
+
return void 0;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function renderStacked(width, maxWidth, labels, components) {
|
|
656
|
+
const lines = [];
|
|
657
|
+
for (const [index, component] of components.entries()) {
|
|
658
|
+
if (index > 0) lines.push("");
|
|
659
|
+
lines.push(labels[index] ?? "");
|
|
660
|
+
lines.push(...component.render(Math.min(width, Math.max(1, maxWidth + 2))));
|
|
661
|
+
}
|
|
662
|
+
return lines;
|
|
663
|
+
}
|
|
664
|
+
function stripTrailingSpaces(line) {
|
|
665
|
+
if (!line.endsWith(" ")) return line;
|
|
666
|
+
let end = line.length;
|
|
667
|
+
while (end > 0 && line.charCodeAt(end - 1) === 32) end--;
|
|
668
|
+
return line.slice(0, end);
|
|
669
|
+
}
|
|
670
|
+
function renderGrid(width, maxWidth, labels, components) {
|
|
671
|
+
const usable = Math.max(1, width - 2);
|
|
672
|
+
const maxColumns = Math.floor((usable + GRID_GAP) / (GRID_MIN_COLUMN + GRID_GAP));
|
|
673
|
+
const columns = Math.max(1, Math.min(components.length, maxColumns, 3));
|
|
674
|
+
if (columns < 2) return renderStacked(width, maxWidth, labels, components);
|
|
675
|
+
const columnWidth = Math.min(maxWidth, Math.floor((usable - (columns - 1) * GRID_GAP) / columns));
|
|
676
|
+
if (columnWidth < GRID_MIN_COLUMN) return renderStacked(width, maxWidth, labels, components);
|
|
677
|
+
const lines = [];
|
|
678
|
+
for (let start = 0; start < components.length; start += columns) {
|
|
679
|
+
const group = components.slice(start, start + columns);
|
|
680
|
+
const groupLabels = labels.slice(start, start + columns);
|
|
681
|
+
if (start > 0) lines.push("");
|
|
682
|
+
const rendered = group.map((component) => component.render(columnWidth));
|
|
683
|
+
const labelRow = groupLabels.map((label, _index) => {
|
|
684
|
+
const pad = Math.max(0, columnWidth - visibleWidth(stripAnsi(label)));
|
|
685
|
+
return label + " ".repeat(pad);
|
|
686
|
+
}).join(" ".repeat(GRID_GAP)).trimEnd();
|
|
687
|
+
lines.push(labelRow);
|
|
688
|
+
const rows = Math.max(...rendered.map((column) => column.length));
|
|
689
|
+
for (let row = 0; row < rows; row++) {
|
|
690
|
+
let line = "";
|
|
691
|
+
for (let column = 0; column < rendered.length; column++) {
|
|
692
|
+
if (column > 0) {
|
|
693
|
+
const startCol = column * (columnWidth + GRID_GAP);
|
|
694
|
+
line += `\x1B[${startCol + 1}G`;
|
|
695
|
+
}
|
|
696
|
+
line += rendered[column]?.[row] ?? "";
|
|
697
|
+
}
|
|
698
|
+
lines.push(stripTrailingSpaces(line));
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return lines;
|
|
702
|
+
}
|
|
703
|
+
function registerImagePreviewSurface(pi) {
|
|
704
|
+
pi.registerEntryRenderer(IMAGE_PREVIEW_ENTRY_TYPE, renderImagePreviewEntry);
|
|
705
|
+
}
|
|
706
|
+
|
|
215
707
|
// extension-src/pi-style/shared/box.ts
|
|
216
708
|
import { homedir as homedir2 } from "os";
|
|
217
709
|
import { relative, resolve as resolve2 } from "path";
|
|
@@ -233,7 +725,7 @@ function getElapsedMs(result) {
|
|
|
233
725
|
// extension-src/pi-style/shared/render-budget.ts
|
|
234
726
|
import {
|
|
235
727
|
truncateToWidth as tuiTruncateToWidth,
|
|
236
|
-
visibleWidth as
|
|
728
|
+
visibleWidth as tuiVisibleWidth2,
|
|
237
729
|
wrapTextWithAnsi
|
|
238
730
|
} from "@earendil-works/pi-tui";
|
|
239
731
|
var MAX_RENDER_LINE_CHARS = 2e3;
|
|
@@ -455,7 +947,7 @@ function safeVisibleWidth(text) {
|
|
|
455
947
|
const fastWidth = knownVisibleWidth(text);
|
|
456
948
|
if (fastWidth) return fastWidth.visibleWidth;
|
|
457
949
|
if (text === TRUNCATE_ELLIPSIS) return 1;
|
|
458
|
-
return
|
|
950
|
+
return tuiVisibleWidth2(text);
|
|
459
951
|
}
|
|
460
952
|
function safeTruncateToWidth(text, maxWidth, ellipsis = "...", pad = false) {
|
|
461
953
|
const width = Math.floor(maxWidth);
|
|
@@ -509,9 +1001,9 @@ function boxedResultRenderBudget(rawLineBudget = DEFAULT_COLLAPSED_RENDER_LINES)
|
|
|
509
1001
|
// extension-src/pi-style/shared/theme-extras.ts
|
|
510
1002
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
511
1003
|
import { homedir } from "os";
|
|
512
|
-
import { dirname, join, resolve } from "path";
|
|
1004
|
+
import { dirname as dirname2, join, resolve } from "path";
|
|
513
1005
|
import { fileURLToPath } from "url";
|
|
514
|
-
var extensionDir =
|
|
1006
|
+
var extensionDir = dirname2(fileURLToPath(import.meta.url));
|
|
515
1007
|
var THEME_EXTRA_DEFAULTS = Object.freeze({
|
|
516
1008
|
assistantPrefix: "\u2022",
|
|
517
1009
|
assistantPrefixColor: "",
|
|
@@ -743,12 +1235,48 @@ function getTextOutput(result) {
|
|
|
743
1235
|
});
|
|
744
1236
|
return textBlocks.map((contentBlock) => String(contentBlock.text ?? "")).join("\n").replace(/\r/g, "");
|
|
745
1237
|
}
|
|
1238
|
+
var WORD_CP_UNKNOWN = 255;
|
|
1239
|
+
var WORD_CP_SURROGATE = 254;
|
|
1240
|
+
var WORD_CP_CLASS = new Uint8Array(65536).fill(WORD_CP_UNKNOWN);
|
|
1241
|
+
for (let code = 48; code <= 57; code++) WORD_CP_CLASS[code] = 1;
|
|
1242
|
+
for (let code = 65; code <= 90; code++) WORD_CP_CLASS[code] = 1;
|
|
1243
|
+
for (let code = 97; code <= 122; code++) WORD_CP_CLASS[code] = 1;
|
|
1244
|
+
WORD_CP_CLASS[39] = 1;
|
|
1245
|
+
WORD_CP_CLASS[45] = 1;
|
|
1246
|
+
WORD_CP_CLASS[95] = 1;
|
|
1247
|
+
WORD_CP_CLASS.fill(WORD_CP_SURROGATE, 55296, 57344);
|
|
1248
|
+
var NON_ASCII_WORD_RE = /[\p{L}\p{N}]/u;
|
|
1249
|
+
var ASTRAL_WORD_CP = /* @__PURE__ */ new Map();
|
|
1250
|
+
function astralWordMembership(codePoint) {
|
|
1251
|
+
const cached = ASTRAL_WORD_CP.get(codePoint);
|
|
1252
|
+
if (cached !== void 0) return cached;
|
|
1253
|
+
const membership = NON_ASCII_WORD_RE.test(String.fromCodePoint(codePoint)) ? 1 : 0;
|
|
1254
|
+
ASTRAL_WORD_CP.set(codePoint, membership);
|
|
1255
|
+
return membership;
|
|
1256
|
+
}
|
|
746
1257
|
function countWords(text) {
|
|
1258
|
+
const len = text.length;
|
|
747
1259
|
let count = 0;
|
|
748
|
-
let inWord =
|
|
749
|
-
for (
|
|
750
|
-
const
|
|
751
|
-
|
|
1260
|
+
let inWord = 0;
|
|
1261
|
+
for (let i = 0; i < len; i++) {
|
|
1262
|
+
const code = text.charCodeAt(i);
|
|
1263
|
+
const membership = WORD_CP_CLASS[code] ?? 0;
|
|
1264
|
+
let isWord;
|
|
1265
|
+
if (membership <= 1) {
|
|
1266
|
+
isWord = membership;
|
|
1267
|
+
} else if (membership === WORD_CP_SURROGATE) {
|
|
1268
|
+
const next = i + 1 < len ? text.charCodeAt(i + 1) : 0;
|
|
1269
|
+
if (code <= 56319 && next >= 56320 && next <= 57343) {
|
|
1270
|
+
isWord = astralWordMembership(65536 + (code - 55296 << 10) + (next - 56320));
|
|
1271
|
+
i++;
|
|
1272
|
+
} else {
|
|
1273
|
+
isWord = 0;
|
|
1274
|
+
}
|
|
1275
|
+
} else {
|
|
1276
|
+
isWord = NON_ASCII_WORD_RE.test(String.fromCharCode(code)) ? 1 : 0;
|
|
1277
|
+
WORD_CP_CLASS[code] = isWord;
|
|
1278
|
+
}
|
|
1279
|
+
count += isWord & (inWord ^ 1);
|
|
752
1280
|
inWord = isWord;
|
|
753
1281
|
}
|
|
754
1282
|
return count;
|
|
@@ -1615,7 +2143,7 @@ function registerBatchResult(meta, data, context) {
|
|
|
1615
2143
|
if (member) {
|
|
1616
2144
|
const nextStatus = data.isPartial ? "running" : "done";
|
|
1617
2145
|
const nextIsError = !data.isPartial && data.isError;
|
|
1618
|
-
const changed = member.status !== nextStatus || member.isError !== nextIsError || member.errorText !== (nextIsError ? data.errorText : void 0) || member.outputEntries !== data.entries;
|
|
2146
|
+
const changed = member.status !== nextStatus || member.isError !== nextIsError || member.errorText !== (nextIsError ? data.errorText : void 0) || data.entries !== void 0 && member.outputEntries !== data.entries;
|
|
1619
2147
|
member.status = nextStatus;
|
|
1620
2148
|
member.isError = nextIsError;
|
|
1621
2149
|
if (member.isError && data.errorText !== void 0) member.errorText = data.errorText;
|
|
@@ -1629,6 +2157,12 @@ function registerBatchResult(meta, data, context) {
|
|
|
1629
2157
|
}
|
|
1630
2158
|
return { batch, isLeader: batch.leaderId === context.toolCallId };
|
|
1631
2159
|
}
|
|
2160
|
+
function hasFinalBatchOutput(toolCallId) {
|
|
2161
|
+
const batch = batchByCallId.get(toolCallId);
|
|
2162
|
+
if (!batch) return false;
|
|
2163
|
+
const member = batch.members.find((entry) => entry.toolCallId === toolCallId);
|
|
2164
|
+
return member !== void 0 && member.outputEntries !== void 0 && member.status === "done" && !member.isError;
|
|
2165
|
+
}
|
|
1632
2166
|
function batchStatus(batch) {
|
|
1633
2167
|
let done = 0;
|
|
1634
2168
|
let failed = 0;
|
|
@@ -1988,6 +2522,9 @@ function invalidateTurnMembers(turn) {
|
|
|
1988
2522
|
}
|
|
1989
2523
|
}
|
|
1990
2524
|
}
|
|
2525
|
+
function releaseTurnInvalidators(turn) {
|
|
2526
|
+
for (const member of turn.members) invalidateByCallId.delete(member.toolCallId);
|
|
2527
|
+
}
|
|
1991
2528
|
function noteTurnMemberElapsed(toolCallId, elapsedMs) {
|
|
1992
2529
|
if (elapsedMs === void 0) return;
|
|
1993
2530
|
const entry = memberByCallId.get(toolCallId);
|
|
@@ -2879,7 +3416,9 @@ import { getLanguageFromPath } from "@earendil-works/pi-coding-agent";
|
|
|
2879
3416
|
import { highlightCode } from "@earendil-works/pi-coding-agent";
|
|
2880
3417
|
var ESC2 = "\x1B";
|
|
2881
3418
|
var BG_ANSI_PATTERN = new RegExp(`${ESC2}\\[(?:4\\d|10\\d|48;5;\\d{1,3}|48;2;\\d{1,3};\\d{1,3};\\d{1,3}|49)m`, "g");
|
|
3419
|
+
var ANSI_SGR_PATTERN = new RegExp(`${ESC2}\\[([0-9;]*)m`, "g");
|
|
2882
3420
|
var CONTROL_CHARS = "\0-\b\v\f-\x7F";
|
|
3421
|
+
var CONTROL_CHARS_PATTERN = new RegExp(`[${CONTROL_CHARS}]`, "g");
|
|
2883
3422
|
var ADD_ROW_BACKGROUND_MIX_RATIO = 0.24;
|
|
2884
3423
|
var REMOVE_ROW_BACKGROUND_MIX_RATIO = 0.12;
|
|
2885
3424
|
var ADD_INLINE_EMPHASIS_MIX_RATIO = 0.44;
|
|
@@ -2971,7 +3510,7 @@ function resolveDiffPalette(theme) {
|
|
|
2971
3510
|
}
|
|
2972
3511
|
function keepBackgroundAcrossResets(text, rowBgAnsi) {
|
|
2973
3512
|
if (!text) return text;
|
|
2974
|
-
return text.replace(
|
|
3513
|
+
return text.replace(ANSI_SGR_PATTERN, (sequence, rawCodes) => {
|
|
2975
3514
|
const split = String(rawCodes ?? "").split(";").filter(Boolean);
|
|
2976
3515
|
const codes = split.length > 0 ? split : ["0"];
|
|
2977
3516
|
const hasGlobalReset = codes.includes("0");
|
|
@@ -3013,7 +3552,7 @@ function applyBackgroundToVisibleRange(ansiText, start, end, backgroundAnsi, res
|
|
|
3013
3552
|
return output;
|
|
3014
3553
|
}
|
|
3015
3554
|
function sanitizeSingleLineText(value) {
|
|
3016
|
-
return value.replace(/\r/g, "").replace(/\n/g, "").replace(
|
|
3555
|
+
return value.replace(/\r/g, "").replace(/\n/g, "").replace(CONTROL_CHARS_PATTERN, "");
|
|
3017
3556
|
}
|
|
3018
3557
|
function stripInlineBreaksPreserveAnsi(value) {
|
|
3019
3558
|
return value.replace(/\r/g, "").replace(/\n/g, "");
|
|
@@ -3610,6 +4149,101 @@ var AdaptiveDiffComponent = class {
|
|
|
3610
4149
|
}
|
|
3611
4150
|
};
|
|
3612
4151
|
|
|
4152
|
+
// extension-src/pi-style/features/tools/boxed/shared.ts
|
|
4153
|
+
function pendingFlag(context) {
|
|
4154
|
+
return Boolean(context.isPartial);
|
|
4155
|
+
}
|
|
4156
|
+
function displayPath(rawPath, context) {
|
|
4157
|
+
const path = String(rawPath ?? "");
|
|
4158
|
+
if (!path) return "(unknown)";
|
|
4159
|
+
return shortenPath(resolveRelativePath(path, context.cwd));
|
|
4160
|
+
}
|
|
4161
|
+
function pathRangeDetail(rawPath, offset, limit, context) {
|
|
4162
|
+
const path = displayPath(rawPath, context);
|
|
4163
|
+
let range = "";
|
|
4164
|
+
if (offset !== void 0 || limit !== void 0) {
|
|
4165
|
+
const start = offset ?? 1;
|
|
4166
|
+
const end = limit !== void 0 ? Number(start) + Number(limit) - 1 : "";
|
|
4167
|
+
range = `:${start}${end ? `-${end}` : ""}`;
|
|
4168
|
+
}
|
|
4169
|
+
return path ? `${path}${range}` : "(unknown)";
|
|
4170
|
+
}
|
|
4171
|
+
function compactCall(theme, toolName, detailLine, options) {
|
|
4172
|
+
return renderCompactBoxedToolCall(theme, toolName, detailLine, {
|
|
4173
|
+
widthKey: boxedToolWidthKey(toolName, options.detailKey),
|
|
4174
|
+
state: options.context.state,
|
|
4175
|
+
isError: Boolean(options.context.isError),
|
|
4176
|
+
isPartial: Boolean(options.context.isPartial),
|
|
4177
|
+
isPending: pendingFlag(options.context),
|
|
4178
|
+
running: Boolean(options.context.executionStarted)
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
4181
|
+
function noteExecutionStart(context) {
|
|
4182
|
+
recordExecutionStarted(context.state, context.executionStarted);
|
|
4183
|
+
}
|
|
4184
|
+
function noteBoxedCallState(context) {
|
|
4185
|
+
if (!context.executionStarted) return;
|
|
4186
|
+
if (context.isPartial) startElapsedTicker(context.state, context.invalidate);
|
|
4187
|
+
else {
|
|
4188
|
+
recordExecutionEnded(context.state);
|
|
4189
|
+
stopElapsedTicker(context.state);
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
function noteBoxedResultPhase(context, isPartial) {
|
|
4193
|
+
const firstResultPass = !isResultSeen(context.state);
|
|
4194
|
+
markResultSeen(context.state);
|
|
4195
|
+
if (isPartial) startElapsedTicker(context.state, context.invalidate);
|
|
4196
|
+
else {
|
|
4197
|
+
recordExecutionEnded(context.state);
|
|
4198
|
+
stopElapsedTicker(context.state);
|
|
4199
|
+
}
|
|
4200
|
+
return firstResultPass;
|
|
4201
|
+
}
|
|
4202
|
+
function stateElapsedMs(context) {
|
|
4203
|
+
return getStateElapsedMs(context.state);
|
|
4204
|
+
}
|
|
4205
|
+
function compactFooterWithState(theme, result, context, options = {}) {
|
|
4206
|
+
const elapsedMs = stateElapsedMs(context);
|
|
4207
|
+
return renderCompactBoxedFooter(theme, result, {
|
|
4208
|
+
state: context.state,
|
|
4209
|
+
isError: Boolean(options.isError ?? context.isError),
|
|
4210
|
+
isPartial: Boolean(options.isPartial ?? context.isPartial),
|
|
4211
|
+
...elapsedMs === void 0 ? {} : { elapsedMs }
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
4214
|
+
function resultFooterLines(theme, result, context, extraParts = []) {
|
|
4215
|
+
return [formatBoxedFooter(theme, result, extraParts, stateElapsedMs(context))];
|
|
4216
|
+
}
|
|
4217
|
+
var CACHE_KEY_LONG_PART_THRESHOLD = 64;
|
|
4218
|
+
function fnv1aHex(text) {
|
|
4219
|
+
let hash = 2166136261;
|
|
4220
|
+
for (let i = 0; i < text.length; i++) {
|
|
4221
|
+
hash ^= text.charCodeAt(i);
|
|
4222
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
4223
|
+
}
|
|
4224
|
+
return hash.toString(16).padStart(8, "0");
|
|
4225
|
+
}
|
|
4226
|
+
function boundedCacheKeyPart(part) {
|
|
4227
|
+
if (typeof part !== "string" || part.length <= CACHE_KEY_LONG_PART_THRESHOLD) return part;
|
|
4228
|
+
return `${part.length}:${fnv1aHex(part)}`;
|
|
4229
|
+
}
|
|
4230
|
+
function getRenderCacheKey(prefix, theme, ...parts) {
|
|
4231
|
+
const pieces = [prefix, themeCacheKey(theme), getToolsRenderCacheSignature()];
|
|
4232
|
+
for (const part of parts) pieces.push(boundedCacheKeyPart(part));
|
|
4233
|
+
return pieces.join("|");
|
|
4234
|
+
}
|
|
4235
|
+
function memoizedStateComponent(state, slot, key, build) {
|
|
4236
|
+
if (!state || typeof state !== "object") return build();
|
|
4237
|
+
const cached = state[slot];
|
|
4238
|
+
if (cached && cached.key === key) return cached.component;
|
|
4239
|
+
const component = build();
|
|
4240
|
+
state[slot] = { key, component };
|
|
4241
|
+
return component;
|
|
4242
|
+
}
|
|
4243
|
+
function clearFooterState(context) {
|
|
4244
|
+
clearCompactBoxedFooter(context.state);
|
|
4245
|
+
}
|
|
4246
|
+
|
|
3613
4247
|
// extension-src/pi-style/features/tools/boxed/git.ts
|
|
3614
4248
|
var GIT_SHORT_STATUS_FLAGS = /* @__PURE__ */ new Set(["-s", "--short", "--porcelain"]);
|
|
3615
4249
|
var GIT_DIFF_FORMAT_REJECT = /* @__PURE__ */ new Set([
|
|
@@ -4068,16 +4702,16 @@ function parseGitShowStat(text) {
|
|
|
4068
4702
|
}
|
|
4069
4703
|
break;
|
|
4070
4704
|
}
|
|
4071
|
-
const
|
|
4072
|
-
if (!
|
|
4705
|
+
const stat3 = parseGitDiffStat(lines.slice(i).join("\n"));
|
|
4706
|
+
if (!stat3) return null;
|
|
4073
4707
|
return {
|
|
4074
4708
|
kind: "show-stat",
|
|
4075
4709
|
hash,
|
|
4076
4710
|
subject,
|
|
4077
|
-
files:
|
|
4078
|
-
...
|
|
4079
|
-
...
|
|
4080
|
-
...
|
|
4711
|
+
files: stat3.files,
|
|
4712
|
+
...stat3.filesChanged !== void 0 ? { filesChanged: stat3.filesChanged } : {},
|
|
4713
|
+
...stat3.insertions !== void 0 ? { insertions: stat3.insertions } : {},
|
|
4714
|
+
...stat3.deletions !== void 0 ? { deletions: stat3.deletions } : {}
|
|
4081
4715
|
};
|
|
4082
4716
|
}
|
|
4083
4717
|
var DIFF_GIT_HEADER = /^diff --git a\/(.*) b\/(.*)$/;
|
|
@@ -4404,17 +5038,17 @@ function parseGitPull(text) {
|
|
|
4404
5038
|
return null;
|
|
4405
5039
|
}
|
|
4406
5040
|
if (!range || ffIndex < 0) return null;
|
|
4407
|
-
const
|
|
4408
|
-
if (!
|
|
5041
|
+
const stat3 = parseGitDiffStat(lines.slice(ffIndex + 1).join("\n"));
|
|
5042
|
+
if (!stat3) return null;
|
|
4409
5043
|
return {
|
|
4410
5044
|
kind: "action",
|
|
4411
5045
|
command: "pull",
|
|
4412
|
-
files:
|
|
5046
|
+
files: stat3.files,
|
|
4413
5047
|
status: "Fast-forward",
|
|
4414
5048
|
range,
|
|
4415
|
-
...
|
|
4416
|
-
...
|
|
4417
|
-
...
|
|
5049
|
+
...stat3.filesChanged !== void 0 ? { filesChanged: stat3.filesChanged } : {},
|
|
5050
|
+
...stat3.insertions !== void 0 ? { insertions: stat3.insertions } : {},
|
|
5051
|
+
...stat3.deletions !== void 0 ? { deletions: stat3.deletions } : {}
|
|
4418
5052
|
};
|
|
4419
5053
|
}
|
|
4420
5054
|
function parseGitFetch(text) {
|
|
@@ -4556,30 +5190,30 @@ function parseGitMerge(text) {
|
|
|
4556
5190
|
}
|
|
4557
5191
|
const marker2 = body[idx] ?? "";
|
|
4558
5192
|
if (range && marker2 === "Fast-forward") {
|
|
4559
|
-
const
|
|
4560
|
-
if (!
|
|
5193
|
+
const stat3 = parseGitDiffStatTolerant(body.slice(idx + 1).join("\n"));
|
|
5194
|
+
if (!stat3) return null;
|
|
4561
5195
|
return {
|
|
4562
5196
|
kind: "action",
|
|
4563
5197
|
command: "merge",
|
|
4564
|
-
files:
|
|
5198
|
+
files: stat3.files,
|
|
4565
5199
|
status: "Fast-forward",
|
|
4566
5200
|
range,
|
|
4567
|
-
...
|
|
4568
|
-
...
|
|
4569
|
-
...
|
|
5201
|
+
...stat3.filesChanged !== void 0 ? { filesChanged: stat3.filesChanged } : {},
|
|
5202
|
+
...stat3.insertions !== void 0 ? { insertions: stat3.insertions } : {},
|
|
5203
|
+
...stat3.deletions !== void 0 ? { deletions: stat3.deletions } : {}
|
|
4570
5204
|
};
|
|
4571
5205
|
}
|
|
4572
5206
|
if (MERGE_MADE.exec(marker2)) {
|
|
4573
|
-
const
|
|
4574
|
-
if (!
|
|
5207
|
+
const stat3 = parseGitDiffStatTolerant(body.slice(idx + 1).join("\n"));
|
|
5208
|
+
if (!stat3) return null;
|
|
4575
5209
|
return {
|
|
4576
5210
|
kind: "action",
|
|
4577
5211
|
command: "merge",
|
|
4578
|
-
files:
|
|
5212
|
+
files: stat3.files,
|
|
4579
5213
|
status: marker2,
|
|
4580
|
-
...
|
|
4581
|
-
...
|
|
4582
|
-
...
|
|
5214
|
+
...stat3.filesChanged !== void 0 ? { filesChanged: stat3.filesChanged } : {},
|
|
5215
|
+
...stat3.insertions !== void 0 ? { insertions: stat3.insertions } : {},
|
|
5216
|
+
...stat3.deletions !== void 0 ? { deletions: stat3.deletions } : {}
|
|
4583
5217
|
};
|
|
4584
5218
|
}
|
|
4585
5219
|
return null;
|
|
@@ -4916,6 +5550,34 @@ function binaryBodyLine(theme, status) {
|
|
|
4916
5550
|
}
|
|
4917
5551
|
function renderGitDiffResult(theme, parsed, options, context) {
|
|
4918
5552
|
const expanded = Boolean(options.expanded);
|
|
5553
|
+
let totalAdditions = 0;
|
|
5554
|
+
let totalRemovals = 0;
|
|
5555
|
+
const sigParts = [];
|
|
5556
|
+
for (const file of parsed.files) {
|
|
5557
|
+
totalAdditions += file.additions;
|
|
5558
|
+
totalRemovals += file.removals;
|
|
5559
|
+
sigParts.push(
|
|
5560
|
+
`${file.path}:${file.body.length}:${file.additions}:${file.removals}:${file.binary ? 1 : 0}:${file.status ?? ""}`
|
|
5561
|
+
);
|
|
5562
|
+
}
|
|
5563
|
+
const sig = sigParts.join(";").slice(0, 2048);
|
|
5564
|
+
return memoizedStateComponent(
|
|
5565
|
+
context.state,
|
|
5566
|
+
"__piStyleGitDiffResult",
|
|
5567
|
+
getRenderCacheKey(
|
|
5568
|
+
"git-diff-result",
|
|
5569
|
+
theme,
|
|
5570
|
+
String(parsed.show),
|
|
5571
|
+
String(expanded),
|
|
5572
|
+
parsed.files.length,
|
|
5573
|
+
totalAdditions,
|
|
5574
|
+
totalRemovals,
|
|
5575
|
+
sig
|
|
5576
|
+
),
|
|
5577
|
+
() => buildGitDiffResultComponent(theme, parsed, expanded, context)
|
|
5578
|
+
);
|
|
5579
|
+
}
|
|
5580
|
+
function buildGitDiffResultComponent(theme, parsed, expanded, context) {
|
|
4919
5581
|
const elapsedMs = getStateElapsedMs(context.state);
|
|
4920
5582
|
const fileCount = parsed.files.length;
|
|
4921
5583
|
const footerParts = [];
|
|
@@ -4963,108 +5625,28 @@ function renderGitDiffResult(theme, parsed, options, context) {
|
|
|
4963
5625
|
});
|
|
4964
5626
|
}
|
|
4965
5627
|
}
|
|
4966
|
-
let cacheWidth;
|
|
4967
|
-
let cacheLines;
|
|
4968
|
-
return {
|
|
4969
|
-
invalidate() {
|
|
4970
|
-
cacheWidth = void 0;
|
|
4971
|
-
cacheLines = void 0;
|
|
4972
|
-
for (const box of fileBoxes) box.resultComponent.invalidate();
|
|
4973
|
-
},
|
|
4974
|
-
render(width) {
|
|
4975
|
-
if (cacheWidth === width && cacheLines) return cacheLines;
|
|
4976
|
-
const renderedWidth = boxWidth(width);
|
|
4977
|
-
const lines = [];
|
|
4978
|
-
for (const box of fileBoxes) {
|
|
4979
|
-
lines.push(boxLabeledBorder(theme, "\u256D", "\u256E", box.topLabel, void 0, renderedWidth));
|
|
4980
|
-
lines.push(boxBlankLine(theme, renderedWidth));
|
|
4981
|
-
lines.push(...box.resultComponent.render(width));
|
|
4982
|
-
}
|
|
4983
|
-
cacheWidth = width;
|
|
4984
|
-
cacheLines = lines;
|
|
4985
|
-
return lines;
|
|
4986
|
-
}
|
|
4987
|
-
};
|
|
4988
|
-
}
|
|
4989
|
-
|
|
4990
|
-
// extension-src/pi-style/features/tools/boxed/shared.ts
|
|
4991
|
-
function pendingFlag(context) {
|
|
4992
|
-
return Boolean(context.isPartial);
|
|
4993
|
-
}
|
|
4994
|
-
function displayPath(rawPath, context) {
|
|
4995
|
-
const path = String(rawPath ?? "");
|
|
4996
|
-
if (!path) return "(unknown)";
|
|
4997
|
-
return shortenPath(resolveRelativePath(path, context.cwd));
|
|
4998
|
-
}
|
|
4999
|
-
function pathRangeDetail(rawPath, offset, limit, context) {
|
|
5000
|
-
const path = displayPath(rawPath, context);
|
|
5001
|
-
let range = "";
|
|
5002
|
-
if (offset !== void 0 || limit !== void 0) {
|
|
5003
|
-
const start = offset ?? 1;
|
|
5004
|
-
const end = limit !== void 0 ? Number(start) + Number(limit) - 1 : "";
|
|
5005
|
-
range = `:${start}${end ? `-${end}` : ""}`;
|
|
5006
|
-
}
|
|
5007
|
-
return path ? `${path}${range}` : "(unknown)";
|
|
5008
|
-
}
|
|
5009
|
-
function compactCall(theme, toolName, detailLine, options) {
|
|
5010
|
-
return renderCompactBoxedToolCall(theme, toolName, detailLine, {
|
|
5011
|
-
widthKey: boxedToolWidthKey(toolName, options.detailKey),
|
|
5012
|
-
state: options.context.state,
|
|
5013
|
-
isError: Boolean(options.context.isError),
|
|
5014
|
-
isPartial: Boolean(options.context.isPartial),
|
|
5015
|
-
isPending: pendingFlag(options.context),
|
|
5016
|
-
running: Boolean(options.context.executionStarted)
|
|
5017
|
-
});
|
|
5018
|
-
}
|
|
5019
|
-
function noteExecutionStart(context) {
|
|
5020
|
-
recordExecutionStarted(context.state, context.executionStarted);
|
|
5021
|
-
}
|
|
5022
|
-
function noteBoxedCallState(context) {
|
|
5023
|
-
if (!context.executionStarted) return;
|
|
5024
|
-
if (context.isPartial) startElapsedTicker(context.state, context.invalidate);
|
|
5025
|
-
else {
|
|
5026
|
-
recordExecutionEnded(context.state);
|
|
5027
|
-
stopElapsedTicker(context.state);
|
|
5028
|
-
}
|
|
5029
|
-
}
|
|
5030
|
-
function noteBoxedResultPhase(context, isPartial) {
|
|
5031
|
-
const firstResultPass = !isResultSeen(context.state);
|
|
5032
|
-
markResultSeen(context.state);
|
|
5033
|
-
if (isPartial) startElapsedTicker(context.state, context.invalidate);
|
|
5034
|
-
else {
|
|
5035
|
-
recordExecutionEnded(context.state);
|
|
5036
|
-
stopElapsedTicker(context.state);
|
|
5037
|
-
}
|
|
5038
|
-
return firstResultPass;
|
|
5039
|
-
}
|
|
5040
|
-
function stateElapsedMs(context) {
|
|
5041
|
-
return getStateElapsedMs(context.state);
|
|
5042
|
-
}
|
|
5043
|
-
function compactFooterWithState(theme, result, context, options = {}) {
|
|
5044
|
-
const elapsedMs = stateElapsedMs(context);
|
|
5045
|
-
return renderCompactBoxedFooter(theme, result, {
|
|
5046
|
-
state: context.state,
|
|
5047
|
-
isError: Boolean(options.isError ?? context.isError),
|
|
5048
|
-
isPartial: Boolean(options.isPartial ?? context.isPartial),
|
|
5049
|
-
...elapsedMs === void 0 ? {} : { elapsedMs }
|
|
5050
|
-
});
|
|
5051
|
-
}
|
|
5052
|
-
function resultFooterLines(theme, result, context, extraParts = []) {
|
|
5053
|
-
return [formatBoxedFooter(theme, result, extraParts, stateElapsedMs(context))];
|
|
5054
|
-
}
|
|
5055
|
-
function getRenderCacheKey(prefix, theme, ...parts) {
|
|
5056
|
-
return [prefix, themeCacheKey(theme), getToolsRenderCacheSignature(), ...parts].join("|");
|
|
5057
|
-
}
|
|
5058
|
-
function memoizedStateComponent(state, slot, key, build) {
|
|
5059
|
-
if (!state || typeof state !== "object") return build();
|
|
5060
|
-
const cached = state[slot];
|
|
5061
|
-
if (cached && cached.key === key) return cached.component;
|
|
5062
|
-
const component = build();
|
|
5063
|
-
state[slot] = { key, component };
|
|
5064
|
-
return component;
|
|
5065
|
-
}
|
|
5066
|
-
function clearFooterState(context) {
|
|
5067
|
-
clearCompactBoxedFooter(context.state);
|
|
5628
|
+
let cacheWidth;
|
|
5629
|
+
let cacheLines;
|
|
5630
|
+
return {
|
|
5631
|
+
invalidate() {
|
|
5632
|
+
cacheWidth = void 0;
|
|
5633
|
+
cacheLines = void 0;
|
|
5634
|
+
for (const box of fileBoxes) box.resultComponent.invalidate();
|
|
5635
|
+
},
|
|
5636
|
+
render(width) {
|
|
5637
|
+
if (cacheWidth === width && cacheLines) return cacheLines;
|
|
5638
|
+
const renderedWidth = boxWidth(width);
|
|
5639
|
+
const lines = [];
|
|
5640
|
+
for (const box of fileBoxes) {
|
|
5641
|
+
lines.push(boxLabeledBorder(theme, "\u256D", "\u256E", box.topLabel, void 0, renderedWidth));
|
|
5642
|
+
lines.push(boxBlankLine(theme, renderedWidth));
|
|
5643
|
+
lines.push(...box.resultComponent.render(width));
|
|
5644
|
+
}
|
|
5645
|
+
cacheWidth = width;
|
|
5646
|
+
cacheLines = lines;
|
|
5647
|
+
return lines;
|
|
5648
|
+
}
|
|
5649
|
+
};
|
|
5068
5650
|
}
|
|
5069
5651
|
|
|
5070
5652
|
// extension-src/pi-style/features/tools/boxed/bash.ts
|
|
@@ -5167,6 +5749,25 @@ function countNewlines(text, from, to) {
|
|
|
5167
5749
|
}
|
|
5168
5750
|
return count;
|
|
5169
5751
|
}
|
|
5752
|
+
function findBackwardLineStart(text, need, end = text.length) {
|
|
5753
|
+
let found = 0;
|
|
5754
|
+
for (let i = end - 1; i >= 0; i--) {
|
|
5755
|
+
if (text.charCodeAt(i) === 10 && ++found >= need) return i + 1;
|
|
5756
|
+
}
|
|
5757
|
+
return 0;
|
|
5758
|
+
}
|
|
5759
|
+
function isOutputWhitespaceCode(code) {
|
|
5760
|
+
if (code === 32 || code >= 9 && code <= 13) return true;
|
|
5761
|
+
if (code === 133 || code === 160 || code === 5760) return true;
|
|
5762
|
+
if (code >= 8192 && code <= 8202) return true;
|
|
5763
|
+
return code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
|
|
5764
|
+
}
|
|
5765
|
+
function lastVisibleEnd(text) {
|
|
5766
|
+
for (let i = text.length - 1; i >= 0; i--) {
|
|
5767
|
+
if (!isOutputWhitespaceCode(text.charCodeAt(i))) return i + 1;
|
|
5768
|
+
}
|
|
5769
|
+
return 0;
|
|
5770
|
+
}
|
|
5170
5771
|
function stripBashToolNoticeLines(text) {
|
|
5171
5772
|
const filteredLines = text.replace(/\r/g, "").split("\n").filter((line) => !BASH_TOOL_NOTICE_PATTERN.test(line.trim()));
|
|
5172
5773
|
return filteredLines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
@@ -5297,8 +5898,10 @@ function bashBodyComponent(preview, emptyLines) {
|
|
|
5297
5898
|
};
|
|
5298
5899
|
}
|
|
5299
5900
|
function renderBashStreamingResult(theme, raw, options, context) {
|
|
5300
|
-
const
|
|
5301
|
-
const hasOutput =
|
|
5901
|
+
const contentEnd = lastVisibleEnd(raw);
|
|
5902
|
+
const hasOutput = contentEnd > 0;
|
|
5903
|
+
const tailStart = hasOutput ? findBackwardLineStart(raw, getToolsRenderConfig().maxCollapsedLines + 10, contentEnd) : 0;
|
|
5904
|
+
const body = stripBashToolNoticeLines(stripAnsi(raw.slice(tailStart, contentEnd)));
|
|
5302
5905
|
const elapsed = getStateElapsedMs(context.state);
|
|
5303
5906
|
const emptyLines = [theme.fg("dim", "No output received yet")];
|
|
5304
5907
|
if (!hasOutput && isInteractiveCommand(context?.args?.command) && (elapsed ?? 0) >= 1e3) {
|
|
@@ -5329,17 +5932,7 @@ function renderBashFinalResult(theme, raw, options, context) {
|
|
|
5329
5932
|
const referenceLines = rawCommand.split("\n").map((line, index) => `${index === 0 ? "$ " : "> "}${line}`);
|
|
5330
5933
|
if (!options.expanded) {
|
|
5331
5934
|
const scanLines = getToolsRenderConfig().maxCollapsedLines + 10;
|
|
5332
|
-
|
|
5333
|
-
let tailStart = 0;
|
|
5334
|
-
for (let i = statusStripped.length - 1; i >= 0; i--) {
|
|
5335
|
-
if (statusStripped.charCodeAt(i) === 10) {
|
|
5336
|
-
nlCount++;
|
|
5337
|
-
if (nlCount >= scanLines) {
|
|
5338
|
-
tailStart = i + 1;
|
|
5339
|
-
break;
|
|
5340
|
-
}
|
|
5341
|
-
}
|
|
5342
|
-
}
|
|
5935
|
+
const tailStart = findBackwardLineStart(statusStripped, scanLines);
|
|
5343
5936
|
const tail = stripBashToolNoticeLines(stripAnsi(statusStripped.slice(tailStart)));
|
|
5344
5937
|
const totalLinesBefore = tailStart > 0 ? countNewlines(statusStripped, 0, tailStart) : 0;
|
|
5345
5938
|
const preview2 = createBashResultPreview(theme, tail, options, outputColor);
|
|
@@ -5397,17 +5990,7 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5397
5990
|
if (cacheLines && cacheKey2 === cacheId) return cacheLines;
|
|
5398
5991
|
if (!expanded) {
|
|
5399
5992
|
const needed = cfg.maxCollapsedLines;
|
|
5400
|
-
|
|
5401
|
-
let scanFrom = 0;
|
|
5402
|
-
for (let i = text.length - 1; i >= 0; i--) {
|
|
5403
|
-
if (text.charCodeAt(i) === 10) {
|
|
5404
|
-
totalNewlines++;
|
|
5405
|
-
if (totalNewlines === needed) {
|
|
5406
|
-
scanFrom = i + 1;
|
|
5407
|
-
break;
|
|
5408
|
-
}
|
|
5409
|
-
}
|
|
5410
|
-
}
|
|
5993
|
+
const scanFrom = findBackwardLineStart(text, needed);
|
|
5411
5994
|
if (text.length === 0) {
|
|
5412
5995
|
cacheKey2 = cacheId;
|
|
5413
5996
|
cacheLines = [];
|
|
@@ -5430,26 +6013,26 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5430
6013
|
return cacheLines;
|
|
5431
6014
|
}
|
|
5432
6015
|
const normalized = replaceTabs(text);
|
|
5433
|
-
const
|
|
5434
|
-
const
|
|
6016
|
+
const rawLines = normalized.split("\n");
|
|
6017
|
+
const totalLines = rawLines.length;
|
|
6018
|
+
const hasOutput = !(totalLines === 1 && rawLines[0] === "");
|
|
5435
6019
|
if (!hasOutput) {
|
|
5436
6020
|
cacheKey2 = cacheId;
|
|
5437
6021
|
cacheLines = [];
|
|
5438
6022
|
return cacheLines;
|
|
5439
6023
|
}
|
|
5440
|
-
const truncatedLines = logicalLines.map((line) => safeTruncateToWidth(line, bodyWidth, "\u2026"));
|
|
5441
|
-
const expandedLines = truncatedLines.length === 1 && truncatedLines[0] === "" ? [] : truncatedLines;
|
|
5442
6024
|
const applyColor = (l) => color === "error" ? formatToolOutputLine(theme, l, "error") : cfg.dimOutput ? formatToolOutputLine(theme, l) : formatToolOutputLine(theme, l, "text");
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
const
|
|
6025
|
+
const renderRawLine = (line) => safeTruncateToWidth(clampLineLength(line), bodyWidth, "\u2026");
|
|
6026
|
+
if (cfg.maxExpandedLines > 0 && totalLines > cfg.maxExpandedLines) {
|
|
6027
|
+
const truncated = rawLines.slice(-cfg.maxExpandedLines).map((line) => applyColor(renderRawLine(line)));
|
|
6028
|
+
const remaining = totalLines - cfg.maxExpandedLines;
|
|
5446
6029
|
truncated.unshift(theme.fg("dim", `\u2026 ${remaining} earlier lines`));
|
|
5447
6030
|
cacheKey2 = cacheId;
|
|
5448
6031
|
cacheLines = truncated;
|
|
5449
6032
|
return cacheLines;
|
|
5450
6033
|
}
|
|
5451
6034
|
cacheKey2 = cacheId;
|
|
5452
|
-
cacheLines =
|
|
6035
|
+
cacheLines = rawLines.map((line) => applyColor(renderRawLine(line)));
|
|
5453
6036
|
return cacheLines;
|
|
5454
6037
|
}
|
|
5455
6038
|
};
|
|
@@ -5616,6 +6199,7 @@ function isGitDiffClass(cls) {
|
|
|
5616
6199
|
function isGitActionClass(cls) {
|
|
5617
6200
|
return !isBashTreeClass(cls) && cls.kind === "action";
|
|
5618
6201
|
}
|
|
6202
|
+
var PARTIAL_REPARSE_THRESHOLD = 4096;
|
|
5619
6203
|
function parseSemanticOutput(cls, output) {
|
|
5620
6204
|
if (isBashTreeClass(cls)) return parseBashTreeOutput(cls, output);
|
|
5621
6205
|
if (isGhClass(cls)) return parseGhOutput(cls, output);
|
|
@@ -5708,6 +6292,7 @@ var bashTool = {
|
|
|
5708
6292
|
existing.finished = false;
|
|
5709
6293
|
existing.revision++;
|
|
5710
6294
|
delete existing.renderCache;
|
|
6295
|
+
delete existing.lastParsedLength;
|
|
5711
6296
|
}
|
|
5712
6297
|
existing.command = command;
|
|
5713
6298
|
existing.cls = cls;
|
|
@@ -5736,21 +6321,24 @@ var bashTool = {
|
|
|
5736
6321
|
}
|
|
5737
6322
|
if (!options.isPartial || isBashTreeClass(cls)) {
|
|
5738
6323
|
const output = stripBashToolNoticeLines(stripAnsi(getTextOutput(result)));
|
|
5739
|
-
const parsed = parseSemanticOutput(cls, output);
|
|
5740
6324
|
const state = semanticStates.get(context.toolCallId);
|
|
6325
|
+
const shouldParse = !options.isPartial || state === void 0 || state.lastParsedLength === void 0 || output.length - state.lastParsedLength >= PARTIAL_REPARSE_THRESHOLD;
|
|
6326
|
+
const parsed = shouldParse ? parseSemanticOutput(cls, output) : void 0;
|
|
5741
6327
|
if (parsed) {
|
|
5742
6328
|
if (state) {
|
|
5743
6329
|
state.parsed = parsed;
|
|
5744
6330
|
state.finished = !options.isPartial;
|
|
5745
6331
|
state.revision++;
|
|
5746
6332
|
delete state.renderCache;
|
|
6333
|
+
if (options.isPartial) state.lastParsedLength = output.length;
|
|
5747
6334
|
} else
|
|
5748
6335
|
semanticStates.set(context.toolCallId, {
|
|
5749
6336
|
cls,
|
|
5750
6337
|
command: String(context?.args?.command ?? ""),
|
|
5751
6338
|
parsed,
|
|
5752
6339
|
finished: !options.isPartial,
|
|
5753
|
-
revision: 0
|
|
6340
|
+
revision: 0,
|
|
6341
|
+
...options.isPartial ? { lastParsedLength: output.length } : {}
|
|
5754
6342
|
});
|
|
5755
6343
|
if (isGitDiffClass(cls)) {
|
|
5756
6344
|
return renderGitDiffResult(theme, parsed, options, context);
|
|
@@ -5760,11 +6348,17 @@ var bashTool = {
|
|
|
5760
6348
|
}
|
|
5761
6349
|
return EMPTY_BASH_TREE_RESULT;
|
|
5762
6350
|
}
|
|
5763
|
-
if (
|
|
5764
|
-
state
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
6351
|
+
if (!shouldParse) {
|
|
6352
|
+
if (state?.parsed !== void 0) return EMPTY_BASH_TREE_RESULT;
|
|
6353
|
+
}
|
|
6354
|
+
if (shouldParse) {
|
|
6355
|
+
if (state) {
|
|
6356
|
+
state.fallback = true;
|
|
6357
|
+
state.finished = !options.isPartial;
|
|
6358
|
+
state.revision++;
|
|
6359
|
+
delete state.renderCache;
|
|
6360
|
+
if (options.isPartial) state.lastParsedLength = output.length;
|
|
6361
|
+
}
|
|
5768
6362
|
}
|
|
5769
6363
|
}
|
|
5770
6364
|
} else if (options.isPartial) {
|
|
@@ -5863,14 +6457,8 @@ var editTool = {
|
|
|
5863
6457
|
const message = firstText(result.content);
|
|
5864
6458
|
const argPath = String(context?.args?.path ?? context?.args?.file_path ?? "");
|
|
5865
6459
|
const sourcePath = details?.path ?? (argPath || extractEditedPath(message));
|
|
5866
|
-
const language = sourcePath ? getLanguageFromPath2(sourcePath) : void 0;
|
|
5867
|
-
const rows = buildSplitRows(diff);
|
|
5868
6460
|
const expanded = options.expanded;
|
|
5869
|
-
const shouldHighlight = Boolean(language) && diff.length <= MAX_HIGHLIGHT_DIFF_CHARS && rows.length <= MAX_HIGHLIGHT_DIFF_ROWS;
|
|
5870
6461
|
const stats = countDiffStats(diff);
|
|
5871
|
-
const maxRows = expanded ? 160 : 36;
|
|
5872
|
-
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
5873
|
-
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
5874
6462
|
return memoizedStateComponent(
|
|
5875
6463
|
context.state,
|
|
5876
6464
|
"__piStyleEditDiffResult",
|
|
@@ -5882,22 +6470,30 @@ var editTool = {
|
|
|
5882
6470
|
sourcePath ?? "",
|
|
5883
6471
|
editDiffFooter(theme, result, context, stats)
|
|
5884
6472
|
),
|
|
5885
|
-
() =>
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
6473
|
+
() => {
|
|
6474
|
+
const language = sourcePath ? getLanguageFromPath2(sourcePath) : void 0;
|
|
6475
|
+
const rows = buildSplitRows(diff);
|
|
6476
|
+
const shouldHighlight = Boolean(language) && diff.length <= MAX_HIGHLIGHT_DIFF_CHARS && rows.length <= MAX_HIGHLIGHT_DIFF_ROWS;
|
|
6477
|
+
const maxRows = expanded ? 160 : 36;
|
|
6478
|
+
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
6479
|
+
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
6480
|
+
return renderBoxedToolResult(
|
|
6481
|
+
theme,
|
|
6482
|
+
{
|
|
6483
|
+
render(width) {
|
|
6484
|
+
return diffView.render(width);
|
|
6485
|
+
},
|
|
6486
|
+
invalidate() {
|
|
6487
|
+
diffView.invalidate();
|
|
6488
|
+
}
|
|
5890
6489
|
},
|
|
5891
|
-
|
|
5892
|
-
|
|
6490
|
+
{
|
|
6491
|
+
dividerLabel: diffDividerLabel2(theme, stats),
|
|
6492
|
+
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
6493
|
+
footerLines: [editDiffFooter(theme, result, context, stats)]
|
|
5893
6494
|
}
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
dividerLabel: diffDividerLabel2(theme, stats),
|
|
5897
|
-
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
5898
|
-
footerLines: [editDiffFooter(theme, result, context, stats)]
|
|
5899
|
-
}
|
|
5900
|
-
)
|
|
6495
|
+
);
|
|
6496
|
+
}
|
|
5901
6497
|
);
|
|
5902
6498
|
}
|
|
5903
6499
|
};
|
|
@@ -6002,14 +6598,16 @@ var findTool = {
|
|
|
6002
6598
|
return renderBatchAwareCall(theme, batch);
|
|
6003
6599
|
},
|
|
6004
6600
|
result(result, options, _theme, context) {
|
|
6005
|
-
const
|
|
6006
|
-
const
|
|
6601
|
+
const isError = Boolean(context.isError);
|
|
6602
|
+
const settled = !options.isPartial && !isError && hasFinalBatchOutput(context.toolCallId);
|
|
6603
|
+
const output = settled ? "" : stripAnsi(getTextOutput(result)).trimEnd();
|
|
6604
|
+
const entries = settled || isError ? void 0 : parseFindOutput(output);
|
|
6007
6605
|
registerBatchResult(
|
|
6008
6606
|
FIND_META,
|
|
6009
6607
|
{
|
|
6010
6608
|
isPartial: Boolean(options.isPartial),
|
|
6011
|
-
isError
|
|
6012
|
-
errorText:
|
|
6609
|
+
isError,
|
|
6610
|
+
errorText: isError ? output || void 0 : void 0,
|
|
6013
6611
|
...entries !== void 0 ? { entries } : {}
|
|
6014
6612
|
},
|
|
6015
6613
|
context
|
|
@@ -6120,14 +6718,19 @@ var grepTool = {
|
|
|
6120
6718
|
return renderGrepPanel(theme, context.toolCallId);
|
|
6121
6719
|
},
|
|
6122
6720
|
result(result, options, _theme, context) {
|
|
6123
|
-
const output = stripAnsi(getTextOutput(result)).trimEnd();
|
|
6124
6721
|
const isError = Boolean(context.isError);
|
|
6722
|
+
const isPartial = Boolean(options.isPartial);
|
|
6723
|
+
const state = grepPanels.get(context.toolCallId);
|
|
6724
|
+
if (!isPartial && !isError && state !== void 0 && state.matches !== void 0 && !state.isPartial) {
|
|
6725
|
+
return EMPTY_GREP_RESULT;
|
|
6726
|
+
}
|
|
6727
|
+
const output = stripAnsi(getTextOutput(result)).trimEnd();
|
|
6125
6728
|
const matches = isError ? [] : parseGrepOutput(output);
|
|
6126
6729
|
registerGrepResult(context.toolCallId, {
|
|
6127
6730
|
matches,
|
|
6128
6731
|
isError,
|
|
6129
6732
|
errorText: isError ? output || void 0 : void 0,
|
|
6130
|
-
isPartial
|
|
6733
|
+
isPartial
|
|
6131
6734
|
});
|
|
6132
6735
|
return EMPTY_GREP_RESULT;
|
|
6133
6736
|
}
|
|
@@ -6154,14 +6757,16 @@ var lsTool = {
|
|
|
6154
6757
|
return renderBatchAwareCall(theme, batch);
|
|
6155
6758
|
},
|
|
6156
6759
|
result(result, options, _theme, context) {
|
|
6157
|
-
const
|
|
6158
|
-
const
|
|
6760
|
+
const isError = Boolean(context.isError);
|
|
6761
|
+
const settled = !options.isPartial && !isError && hasFinalBatchOutput(context.toolCallId);
|
|
6762
|
+
const output = settled ? "" : stripAnsi(getTextOutput(result)).trimEnd();
|
|
6763
|
+
const entries = settled || isError ? void 0 : parseLsOutput(output);
|
|
6159
6764
|
registerBatchResult(
|
|
6160
6765
|
LIST_META,
|
|
6161
6766
|
{
|
|
6162
6767
|
isPartial: Boolean(options.isPartial),
|
|
6163
|
-
isError
|
|
6164
|
-
errorText:
|
|
6768
|
+
isError,
|
|
6769
|
+
errorText: isError ? output || void 0 : void 0,
|
|
6165
6770
|
...entries !== void 0 ? { entries } : {}
|
|
6166
6771
|
},
|
|
6167
6772
|
context
|
|
@@ -6285,15 +6890,9 @@ function renderQuickEditResult(_toolName, result, options, theme, context, confi
|
|
|
6285
6890
|
footerLines: [quickEditFooter(theme, context)]
|
|
6286
6891
|
});
|
|
6287
6892
|
}
|
|
6288
|
-
const rows = buildSplitRows(diff);
|
|
6289
6893
|
const expanded = options.expanded;
|
|
6290
6894
|
const argPath = String(context?.args?.path ?? "");
|
|
6291
|
-
const language = argPath ? getLanguageFromPath3(argPath) : void 0;
|
|
6292
|
-
const shouldHighlight = Boolean(language) && diff.length <= MAX_HIGHLIGHT_DIFF_CHARS2 && rows.length <= MAX_HIGHLIGHT_DIFF_ROWS2;
|
|
6293
6895
|
const stats = countDiffStats(diff);
|
|
6294
|
-
const maxRows = expanded ? 160 : 36;
|
|
6295
|
-
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
6296
|
-
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
6297
6896
|
return memoizedStateComponent(
|
|
6298
6897
|
context.state,
|
|
6299
6898
|
"__piStyleQuickEditDiffResult",
|
|
@@ -6306,22 +6905,30 @@ function renderQuickEditResult(_toolName, result, options, theme, context, confi
|
|
|
6306
6905
|
argPath,
|
|
6307
6906
|
quickEditDiffFooter(theme, result, context, stats)
|
|
6308
6907
|
),
|
|
6309
|
-
() =>
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6908
|
+
() => {
|
|
6909
|
+
const rows = buildSplitRows(diff);
|
|
6910
|
+
const language = argPath ? getLanguageFromPath3(argPath) : void 0;
|
|
6911
|
+
const shouldHighlight = Boolean(language) && diff.length <= MAX_HIGHLIGHT_DIFF_CHARS2 && rows.length <= MAX_HIGHLIGHT_DIFF_ROWS2;
|
|
6912
|
+
const maxRows = expanded ? 160 : 36;
|
|
6913
|
+
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
6914
|
+
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
6915
|
+
return renderBoxedToolResult(
|
|
6916
|
+
theme,
|
|
6917
|
+
{
|
|
6918
|
+
render(width) {
|
|
6919
|
+
return diffView.render(width);
|
|
6920
|
+
},
|
|
6921
|
+
invalidate() {
|
|
6922
|
+
diffView.invalidate();
|
|
6923
|
+
}
|
|
6314
6924
|
},
|
|
6315
|
-
|
|
6316
|
-
|
|
6925
|
+
{
|
|
6926
|
+
dividerLabel: quickEditDividerLabel(theme, stats),
|
|
6927
|
+
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
6928
|
+
footerLines: [quickEditDiffFooter(theme, result, context, stats)]
|
|
6317
6929
|
}
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
dividerLabel: quickEditDividerLabel(theme, stats),
|
|
6321
|
-
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
6322
|
-
footerLines: [quickEditDiffFooter(theme, result, context, stats)]
|
|
6323
|
-
}
|
|
6324
|
-
)
|
|
6930
|
+
);
|
|
6931
|
+
}
|
|
6325
6932
|
);
|
|
6326
6933
|
}
|
|
6327
6934
|
function quickEditFooter(theme, context) {
|
|
@@ -6367,13 +6974,13 @@ var readTool = {
|
|
|
6367
6974
|
return renderBatchAwareCall(theme, batch);
|
|
6368
6975
|
},
|
|
6369
6976
|
result(result, options, _theme, context) {
|
|
6370
|
-
const
|
|
6977
|
+
const errorText = context.isError ? stripAnsi(getTextOutput(result)).trimEnd() || void 0 : void 0;
|
|
6371
6978
|
registerBatchResult(
|
|
6372
6979
|
READ_META,
|
|
6373
6980
|
{
|
|
6374
6981
|
isPartial: Boolean(options.isPartial),
|
|
6375
6982
|
isError: Boolean(context.isError),
|
|
6376
|
-
errorText
|
|
6983
|
+
errorText
|
|
6377
6984
|
},
|
|
6378
6985
|
context
|
|
6379
6986
|
);
|
|
@@ -6978,7 +7585,7 @@ var DEFAULT_CONFIG = Object.freeze({
|
|
|
6978
7585
|
startup: Object.freeze({ mode: "compact", showResources: false, alwaysExpanded: false }),
|
|
6979
7586
|
statusLine: Object.freeze({
|
|
6980
7587
|
enabled: true,
|
|
6981
|
-
separator: "
|
|
7588
|
+
separator: "|",
|
|
6982
7589
|
layout: Object.freeze({
|
|
6983
7590
|
left: ["path", "git", "context_bar", "cost"],
|
|
6984
7591
|
right: ["model_effort"],
|
|
@@ -6995,7 +7602,10 @@ var DEFAULT_CONFIG = Object.freeze({
|
|
|
6995
7602
|
assistantPrefix: true,
|
|
6996
7603
|
specialBlocks: true,
|
|
6997
7604
|
hideThinkingLabel: true,
|
|
6998
|
-
hideInterimText: true
|
|
7605
|
+
hideInterimText: true,
|
|
7606
|
+
showImagePreviews: true,
|
|
7607
|
+
clipboardImages: true,
|
|
7608
|
+
previewMaxWidth: 30
|
|
6999
7609
|
}),
|
|
7000
7610
|
tools: Object.freeze({
|
|
7001
7611
|
enabled: true,
|
|
@@ -7112,7 +7722,10 @@ function normalizeConfig(input, defaults = DEFAULT_CONFIG) {
|
|
|
7112
7722
|
assistantPrefix: bool(messages.assistantPrefix, defaults.messages.assistantPrefix),
|
|
7113
7723
|
specialBlocks: bool(messages.specialBlocks, defaults.messages.specialBlocks),
|
|
7114
7724
|
hideThinkingLabel: bool(messages.hideThinkingLabel, defaults.messages.hideThinkingLabel),
|
|
7115
|
-
hideInterimText: bool(messages.hideInterimText, defaults.messages.hideInterimText)
|
|
7725
|
+
hideInterimText: bool(messages.hideInterimText, defaults.messages.hideInterimText),
|
|
7726
|
+
showImagePreviews: bool(messages.showImagePreviews, defaults.messages.showImagePreviews),
|
|
7727
|
+
clipboardImages: bool(messages.clipboardImages, defaults.messages.clipboardImages),
|
|
7728
|
+
previewMaxWidth: boundedInt(messages.previewMaxWidth, defaults.messages.previewMaxWidth, 8, 60)
|
|
7116
7729
|
}),
|
|
7117
7730
|
tools: Object.freeze({
|
|
7118
7731
|
enabled: bool(tools.enabled, defaults.tools.enabled),
|
|
@@ -7166,6 +7779,8 @@ var BOOL_PATHS = /* @__PURE__ */ new Set([
|
|
|
7166
7779
|
"messages.specialBlocks",
|
|
7167
7780
|
"messages.hideThinkingLabel",
|
|
7168
7781
|
"messages.hideInterimText",
|
|
7782
|
+
"messages.showImagePreviews",
|
|
7783
|
+
"messages.clipboardImages",
|
|
7169
7784
|
"tools.enabled",
|
|
7170
7785
|
"tools.showElapsed",
|
|
7171
7786
|
"tools.dimOutput",
|
|
@@ -7211,6 +7826,8 @@ function validLeaf(path, value) {
|
|
|
7211
7826
|
return typeof value === "string";
|
|
7212
7827
|
if (path === "tools.maxCollapsedLines" || path === "tools.maxExpandedLines")
|
|
7213
7828
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
7829
|
+
if (path === "messages.previewMaxWidth")
|
|
7830
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 8 && value <= 60;
|
|
7214
7831
|
if (path === "statusLine.bottomMargin" || path === "statusLine.contextBarWidth")
|
|
7215
7832
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
7216
7833
|
if (STRING_ARRAY_PATHS.has(path)) return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
@@ -7634,6 +8251,9 @@ var allowedPaths = /* @__PURE__ */ new Set([
|
|
|
7634
8251
|
"messages.specialBlocks",
|
|
7635
8252
|
"messages.hideThinkingLabel",
|
|
7636
8253
|
"messages.hideInterimText",
|
|
8254
|
+
"messages.showImagePreviews",
|
|
8255
|
+
"messages.clipboardImages",
|
|
8256
|
+
"messages.previewMaxWidth",
|
|
7637
8257
|
"tools.enabled",
|
|
7638
8258
|
"tools.style",
|
|
7639
8259
|
"tools.maxCollapsedLines",
|
|
@@ -7666,6 +8286,9 @@ function validatePathValue(path, value) {
|
|
|
7666
8286
|
"messages.specialBlocks",
|
|
7667
8287
|
"messages.hideThinkingLabel",
|
|
7668
8288
|
"messages.hideInterimText",
|
|
8289
|
+
"messages.showImagePreviews",
|
|
8290
|
+
"messages.clipboardImages",
|
|
8291
|
+
"messages.previewMaxWidth",
|
|
7669
8292
|
"tools.enabled",
|
|
7670
8293
|
"tools.showElapsed",
|
|
7671
8294
|
"tools.dimOutput",
|
|
@@ -7688,6 +8311,8 @@ function validatePathValue(path, value) {
|
|
|
7688
8311
|
return typeof value === "string";
|
|
7689
8312
|
if (path === "tools.maxCollapsedLines" || path === "tools.maxExpandedLines")
|
|
7690
8313
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
8314
|
+
if (path === "messages.previewMaxWidth")
|
|
8315
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 8 && value <= 60;
|
|
7691
8316
|
if (path === "statusLine.bottomMargin" || path === "statusLine.contextBarWidth")
|
|
7692
8317
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
7693
8318
|
if (path.endsWith(".colors") || path.endsWith(".glyphs"))
|
|
@@ -7820,8 +8445,8 @@ function runCommand(args, host, app, storage) {
|
|
|
7820
8445
|
}
|
|
7821
8446
|
|
|
7822
8447
|
// extension-src/pi-style/pi/config-host.ts
|
|
7823
|
-
import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
|
|
7824
|
-
import { dirname as
|
|
8448
|
+
import { mkdir, readFile as readFile3, rename, unlink, writeFile } from "fs/promises";
|
|
8449
|
+
import { dirname as dirname3, join as join2 } from "path";
|
|
7825
8450
|
import { CONFIG_DIR_NAME, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7826
8451
|
var locks = /* @__PURE__ */ new Map();
|
|
7827
8452
|
function serialize(path, operation) {
|
|
@@ -7834,14 +8459,14 @@ function serialize(path, operation) {
|
|
|
7834
8459
|
}
|
|
7835
8460
|
function createPiConfigFilePort(fs = {}) {
|
|
7836
8461
|
const fsMkdir = fs.mkdir ?? mkdir;
|
|
7837
|
-
const fsReadFile = fs.readFile ??
|
|
8462
|
+
const fsReadFile = fs.readFile ?? readFile3;
|
|
7838
8463
|
const fsRename = fs.rename ?? rename;
|
|
7839
8464
|
const fsUnlink = fs.unlink ?? unlink;
|
|
7840
8465
|
const fsWriteFile = fs.writeFile ?? writeFile;
|
|
7841
8466
|
return {
|
|
7842
8467
|
read: (path) => fsReadFile(path, "utf8"),
|
|
7843
8468
|
writeAtomic: async (path, content) => serialize(path, async () => {
|
|
7844
|
-
await fsMkdir(
|
|
8469
|
+
await fsMkdir(dirname3(path), { recursive: true });
|
|
7845
8470
|
const temporary = `${path}.pi-style-${process.pid}-${Date.now()}.tmp`;
|
|
7846
8471
|
try {
|
|
7847
8472
|
await fsWriteFile(temporary, content, { mode: 384 });
|
|
@@ -8030,12 +8655,17 @@ function createBuiltinSegments() {
|
|
|
8030
8655
|
const percent = contextPercent(snapshot.context ?? {});
|
|
8031
8656
|
if (percent === void 0) return { visible: false, content: "" };
|
|
8032
8657
|
const token = contextBarToken(percent);
|
|
8033
|
-
const window = snapshot.context?.windowTokens;
|
|
8034
|
-
const label = `ctx${window !== void 0 ? ` (${formatTokens(window)})` : ""}:`;
|
|
8035
8658
|
const width = options.context_bar?.width ?? CONTEXT_BAR_WIDTH;
|
|
8659
|
+
const pipe = theme.apply("separator", "|");
|
|
8660
|
+
const bar = `${theme.apply("muted", "[")}${theme.apply(token, contextBar(percent, width))}${theme.apply("muted", "]")}`;
|
|
8661
|
+
const parts = [bar, `${theme.apply(token, `${Math.round(percent)}%`)}${theme.apply("muted", " used")}`];
|
|
8662
|
+
const current = snapshot.context?.currentTokens;
|
|
8663
|
+
const window = snapshot.context?.windowTokens;
|
|
8664
|
+
if (current !== void 0 && window !== void 0)
|
|
8665
|
+
parts.push(theme.apply("muted", `${formatTokens(current)}/${formatTokens(window)}`));
|
|
8036
8666
|
return {
|
|
8037
8667
|
visible: true,
|
|
8038
|
-
content:
|
|
8668
|
+
content: parts.join(` ${pipe} `),
|
|
8039
8669
|
compactContent: theme.apply(token, `${Math.round(percent)}%`)
|
|
8040
8670
|
};
|
|
8041
8671
|
}),
|
|
@@ -8077,11 +8707,10 @@ function createBuiltinSegments() {
|
|
|
8077
8707
|
content: snapshot.sessionStartedAt === void 0 ? "" : theme.apply("time", formatElapsed2(Date.now() - snapshot.sessionStartedAt)),
|
|
8078
8708
|
compactContent: snapshot.sessionStartedAt === void 0 ? "" : theme.apply("time", formatElapsed2(Date.now() - snapshot.sessionStartedAt))
|
|
8079
8709
|
})),
|
|
8080
|
-
segment("time", 20, ({ theme }) =>
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
})),
|
|
8710
|
+
segment("time", 20, ({ theme }) => {
|
|
8711
|
+
const text = theme.apply("time", clockTime());
|
|
8712
|
+
return { visible: true, content: text, compactContent: text };
|
|
8713
|
+
}),
|
|
8085
8714
|
segment("hostname", 20, ({ snapshot, theme }) => ({
|
|
8086
8715
|
visible: Boolean(snapshot.hostname),
|
|
8087
8716
|
content: theme.apply("muted", snapshot.hostname ?? "")
|
|
@@ -8099,6 +8728,15 @@ function createBuiltinSegments() {
|
|
|
8099
8728
|
];
|
|
8100
8729
|
return new Map(segments.map((item) => [item.id, item]));
|
|
8101
8730
|
}
|
|
8731
|
+
var clockCache;
|
|
8732
|
+
function clockTime() {
|
|
8733
|
+
const now = /* @__PURE__ */ new Date();
|
|
8734
|
+
const minuteKey = now.getHours() * 60 + now.getMinutes();
|
|
8735
|
+
if (clockCache?.minuteKey !== minuteKey) {
|
|
8736
|
+
clockCache = { minuteKey, value: now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) };
|
|
8737
|
+
}
|
|
8738
|
+
return clockCache.value;
|
|
8739
|
+
}
|
|
8102
8740
|
var CONTEXT_BAR_WIDTH = 10;
|
|
8103
8741
|
function contextBar(percent, width = CONTEXT_BAR_WIDTH) {
|
|
8104
8742
|
const filled = Math.max(0, Math.min(width, Math.round(percent / 100 * width)));
|
|
@@ -8110,10 +8748,7 @@ function contextBarToken(percent) {
|
|
|
8110
8748
|
return "success";
|
|
8111
8749
|
}
|
|
8112
8750
|
function formatTokens(value) {
|
|
8113
|
-
if (value >= 1e6) {
|
|
8114
|
-
const millions = value / 1e6;
|
|
8115
|
-
return `${Number.isInteger(millions) ? millions : millions.toFixed(1)}M`;
|
|
8116
|
-
}
|
|
8751
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
8117
8752
|
if (value >= 1e3) {
|
|
8118
8753
|
const thousands = value / 1e3;
|
|
8119
8754
|
return `${Number.isInteger(thousands) ? thousands : thousands.toFixed(1)}K`;
|
|
@@ -8284,7 +8919,24 @@ function colorPrefixFor(active, config, noColor, token) {
|
|
|
8284
8919
|
}
|
|
8285
8920
|
return "";
|
|
8286
8921
|
}
|
|
8922
|
+
function envKeyFor(env) {
|
|
8923
|
+
return `${env.PI_STYLE_NERD_FONTS ?? ""}\0${env.GHOSTTY_RESOURCES_DIR ? "1" : "0"}\0${env.TERM_PROGRAM ?? ""}`;
|
|
8924
|
+
}
|
|
8925
|
+
var glyphModeCache = /* @__PURE__ */ new WeakMap();
|
|
8287
8926
|
function detectGlyphMode(config, env = {}) {
|
|
8927
|
+
let byEnv = glyphModeCache.get(config);
|
|
8928
|
+
if (!byEnv) {
|
|
8929
|
+
byEnv = /* @__PURE__ */ new Map();
|
|
8930
|
+
glyphModeCache.set(config, byEnv);
|
|
8931
|
+
}
|
|
8932
|
+
const envKey = envKeyFor(env);
|
|
8933
|
+
const cached = byEnv.get(envKey);
|
|
8934
|
+
if (cached !== void 0) return cached;
|
|
8935
|
+
const mode = computeGlyphMode(config, env);
|
|
8936
|
+
byEnv.set(envKey, mode);
|
|
8937
|
+
return mode;
|
|
8938
|
+
}
|
|
8939
|
+
function computeGlyphMode(config, env) {
|
|
8288
8940
|
if (env.PI_STYLE_NERD_FONTS === "1") return "nerd";
|
|
8289
8941
|
if (env.PI_STYLE_NERD_FONTS === "0") return "unicode";
|
|
8290
8942
|
if (config.theme.nerdFonts === "on") return "nerd";
|
|
@@ -8299,7 +8951,14 @@ function detectGlyphMode(config, env = {}) {
|
|
|
8299
8951
|
function resolveTheme(active, config, env = {}) {
|
|
8300
8952
|
const noColor = Object.hasOwn(env, "NO_COLOR") && env.NO_COLOR !== "" && config.theme.colors.colorOverride !== "on";
|
|
8301
8953
|
const mode = config.preset === "ascii" ? "ascii" : detectGlyphMode(config, env);
|
|
8302
|
-
const
|
|
8954
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
8955
|
+
const color = (token) => {
|
|
8956
|
+
const cached = prefixes.get(token);
|
|
8957
|
+
if (cached !== void 0) return cached;
|
|
8958
|
+
const prefix = colorPrefixFor(active, config, noColor, token);
|
|
8959
|
+
prefixes.set(token, prefix);
|
|
8960
|
+
return prefix;
|
|
8961
|
+
};
|
|
8303
8962
|
return {
|
|
8304
8963
|
mode,
|
|
8305
8964
|
noColor,
|
|
@@ -8317,9 +8976,13 @@ function resolveTheme(active, config, env = {}) {
|
|
|
8317
8976
|
var widthOf = visibleWidth3;
|
|
8318
8977
|
function widthSafe(value, width) {
|
|
8319
8978
|
if (width <= 0) return "";
|
|
8320
|
-
const
|
|
8321
|
-
|
|
8322
|
-
|
|
8979
|
+
const measured = widthOf(value);
|
|
8980
|
+
if (measured > width) {
|
|
8981
|
+
const fitted = truncateAnsi(value, width, "");
|
|
8982
|
+
const current = widthOf(fitted);
|
|
8983
|
+
return current < width ? fitted + " ".repeat(width - current) : fitted;
|
|
8984
|
+
}
|
|
8985
|
+
return measured < width ? value + " ".repeat(width - measured) : value;
|
|
8323
8986
|
}
|
|
8324
8987
|
function isNativeBorderLine(line) {
|
|
8325
8988
|
const stripped = stripAnsi(line);
|
|
@@ -8389,8 +9052,14 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8389
9052
|
fullTheme;
|
|
8390
9053
|
onSnapshot;
|
|
8391
9054
|
semantic;
|
|
9055
|
+
/** Set when config changes; invalidate() then rebuilds the (otherwise stable) semantic theme. */
|
|
9056
|
+
semanticDirty = false;
|
|
8392
9057
|
disposed = false;
|
|
8393
9058
|
renderPlanCache;
|
|
9059
|
+
clipboardImagePaste;
|
|
9060
|
+
/** Keybinding matcher (the factory's KeybindingsManager) for keystroke-level
|
|
9061
|
+
* interception: the paste keybinding and backspace checks below. */
|
|
9062
|
+
editorKeybindings;
|
|
8394
9063
|
constructor(tui, theme, keybindings, options) {
|
|
8395
9064
|
super(tui, theme, keybindings);
|
|
8396
9065
|
this.config = options.config;
|
|
@@ -8398,6 +9067,8 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8398
9067
|
this.piTheme = theme;
|
|
8399
9068
|
this.fullTheme = options.fullTheme;
|
|
8400
9069
|
this.onSnapshot = options.onSnapshot;
|
|
9070
|
+
this.clipboardImagePaste = options.clipboardImagePaste;
|
|
9071
|
+
this.editorKeybindings = keybindings;
|
|
8401
9072
|
this.semantic = semanticTheme(theme, options.config);
|
|
8402
9073
|
this.setPaddingX(0);
|
|
8403
9074
|
}
|
|
@@ -8409,17 +9080,99 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8409
9080
|
configure(config) {
|
|
8410
9081
|
if (this.disposed) return;
|
|
8411
9082
|
this.config = config;
|
|
8412
|
-
this.
|
|
9083
|
+
this.semanticDirty = true;
|
|
8413
9084
|
this.invalidate();
|
|
8414
9085
|
}
|
|
8415
9086
|
handleInput(data) {
|
|
8416
9087
|
if (this.disposed) return;
|
|
9088
|
+
if (this.handleClipboardImagePasteKeystroke(data)) return;
|
|
9089
|
+
if (this.handleAtomicMarkerBackspace(data)) return;
|
|
8417
9090
|
super.handleInput(data);
|
|
8418
9091
|
this.onSnapshot({ ...this.snapshot });
|
|
8419
9092
|
}
|
|
9093
|
+
/**
|
|
9094
|
+
* Instant marker (ADR 0009): when the built-in paste keybinding fires, the
|
|
9095
|
+
* clipboard holds an image (sync native probe), and the surface is enabled,
|
|
9096
|
+
* the keystroke is owned here — an `[Image #N] ` marker is inserted
|
|
9097
|
+
* immediately (zero-latency feedback) and the bytes fill the registry
|
|
9098
|
+
* entry asynchronously. Pi's own paste handler never runs for this
|
|
9099
|
+
* keystroke, so no temp artifact is written. Text pastes (no image) and
|
|
9100
|
+
* probe-unavailable hosts fall through to the native path untouched.
|
|
9101
|
+
*/
|
|
9102
|
+
handleClipboardImagePasteKeystroke(data) {
|
|
9103
|
+
const surface = this.clipboardImagePaste;
|
|
9104
|
+
if (!surface?.enabled()) return false;
|
|
9105
|
+
if (!this.editorKeybindings.matches(data, "app.clipboard.pasteImage")) return false;
|
|
9106
|
+
if (surface.clipboardHasImage() !== true) return false;
|
|
9107
|
+
super.insertTextAtCursor(surface.markerFromClipboard());
|
|
9108
|
+
this.onSnapshot({ ...this.snapshot });
|
|
9109
|
+
return true;
|
|
9110
|
+
}
|
|
9111
|
+
/**
|
|
9112
|
+
* Atomic marker backspace (ADR 0009): backspacing directly after a
|
|
9113
|
+
* registered `[Image #N] ` marker deletes the whole marker as one unit —
|
|
9114
|
+
* the image is discarded (image-paste semantics). Direct state surgery
|
|
9115
|
+
* (O(1)) mirroring pi-tui's handleBackspace mutation protocol: no setText
|
|
9116
|
+
* round-trip (it clears the pastes registry, and undo snapshots store that
|
|
9117
|
+
* Map by reference — silently breaking `[paste #N]` atomicity on undo) and
|
|
9118
|
+
* no per-column left-arrow inputs (O(K·L) through the keybinding chain and
|
|
9119
|
+
* visual line maps). No undo snapshot is pushed: the discarded image
|
|
9120
|
+
* cannot be restored, so the deletion is intentionally non-undoable.
|
|
9121
|
+
*/
|
|
9122
|
+
handleAtomicMarkerBackspace(data) {
|
|
9123
|
+
const surface = this.clipboardImagePaste;
|
|
9124
|
+
if (!surface?.enabled()) return false;
|
|
9125
|
+
const isBackspace = this.editorKeybindings.matches(data, "tui.editor.deleteCharBackward") || data === "\x7F";
|
|
9126
|
+
if (!isBackspace) return false;
|
|
9127
|
+
const lines = this.getLines();
|
|
9128
|
+
const cursor = this.getCursor();
|
|
9129
|
+
const current = lines[cursor.line];
|
|
9130
|
+
if (current === void 0) return false;
|
|
9131
|
+
const before = current.slice(0, cursor.col);
|
|
9132
|
+
const hit = clipboardMarkerAtEnd(before);
|
|
9133
|
+
if (!hit || !surface.isMarkerIndexRegistered(hit.index)) return false;
|
|
9134
|
+
const editor = this;
|
|
9135
|
+
const targetCol = cursor.col - hit.length;
|
|
9136
|
+
editor.exitHistoryBrowsing();
|
|
9137
|
+
editor.lastAction = null;
|
|
9138
|
+
editor.cancelAutocomplete();
|
|
9139
|
+
editor.state.lines[cursor.line] = current.slice(0, targetCol) + current.slice(cursor.col);
|
|
9140
|
+
editor.setCursorCol(targetCol);
|
|
9141
|
+
editor.onChange?.(this.getText());
|
|
9142
|
+
surface.discardMarkerIndex(hit.index);
|
|
9143
|
+
this.invalidate();
|
|
9144
|
+
this.onSnapshot({ ...this.snapshot });
|
|
9145
|
+
return true;
|
|
9146
|
+
}
|
|
9147
|
+
/**
|
|
9148
|
+
* Artifact-path fallback (ADR 0009): when the keystroke was not owned
|
|
9149
|
+
* (probe unavailable, native editor path, config toggled) Pi's built-in
|
|
9150
|
+
* paste still inserts a `<tmpdir>/pi-clipboard-<uuid>.<ext>` path through
|
|
9151
|
+
* this method. Convert it to a marker when the artifact reads successfully;
|
|
9152
|
+
* every failure inserts the original text verbatim (exact native behavior).
|
|
9153
|
+
*/
|
|
9154
|
+
insertTextAtCursor(text) {
|
|
9155
|
+
const surface = this.clipboardImagePaste;
|
|
9156
|
+
if (!surface?.enabled() || !isSingleClipboardImagePath(text)) {
|
|
9157
|
+
super.insertTextAtCursor(text);
|
|
9158
|
+
return;
|
|
9159
|
+
}
|
|
9160
|
+
void (async () => {
|
|
9161
|
+
let replacement = text;
|
|
9162
|
+
try {
|
|
9163
|
+
replacement = await surface.markerFromArtifact(text);
|
|
9164
|
+
} catch {
|
|
9165
|
+
}
|
|
9166
|
+
super.insertTextAtCursor(replacement);
|
|
9167
|
+
this.tui.requestRender();
|
|
9168
|
+
})();
|
|
9169
|
+
}
|
|
8420
9170
|
invalidate() {
|
|
8421
9171
|
super.invalidate();
|
|
8422
|
-
|
|
9172
|
+
if (this.semanticDirty) {
|
|
9173
|
+
this.semantic = semanticTheme(this.piTheme, this.config);
|
|
9174
|
+
this.semanticDirty = false;
|
|
9175
|
+
}
|
|
8423
9176
|
this.renderPlanCache = void 0;
|
|
8424
9177
|
this.tui.requestRender();
|
|
8425
9178
|
}
|
|
@@ -8629,6 +9382,7 @@ function installEditor(options) {
|
|
|
8629
9382
|
snapshot,
|
|
8630
9383
|
theme,
|
|
8631
9384
|
...options.host.theme ? { fullTheme: options.host.theme } : {},
|
|
9385
|
+
...options.clipboardImagePaste ? { clipboardImagePaste: options.clipboardImagePaste } : {},
|
|
8632
9386
|
onSnapshot: (next) => {
|
|
8633
9387
|
if (!disposed && options.isCurrent?.() !== false) snapshot = next;
|
|
8634
9388
|
}
|
|
@@ -9203,12 +9957,9 @@ function uniqueLayout(layout) {
|
|
|
9203
9957
|
function renderGroup(items, separator, padding) {
|
|
9204
9958
|
return items.map((item) => item.content).filter(Boolean).join(`${padding}${separator}${padding}`);
|
|
9205
9959
|
}
|
|
9206
|
-
function widthOf2(items, separator, padding) {
|
|
9207
|
-
return visibleWidth(renderGroup(items, separator, padding));
|
|
9208
|
-
}
|
|
9209
9960
|
function renderStatus(layout, snapshot, width, options) {
|
|
9210
9961
|
if (width <= 0) return { primary: "", lines: [], visibleSegments: [] };
|
|
9211
|
-
const separator = options.separator ?? "
|
|
9962
|
+
const separator = options.separator ?? "|";
|
|
9212
9963
|
const padding = options.padding ?? " ";
|
|
9213
9964
|
const normalized = uniqueLayout(layout);
|
|
9214
9965
|
const context = { snapshot, theme: options.theme, options: options.options ?? {}, width };
|
|
@@ -9220,7 +9971,15 @@ function renderStatus(layout, snapshot, width, options) {
|
|
|
9220
9971
|
try {
|
|
9221
9972
|
const result = segment2.render(context);
|
|
9222
9973
|
if (!result.visible || !result.content) continue;
|
|
9223
|
-
candidates.set(id, {
|
|
9974
|
+
candidates.set(id, {
|
|
9975
|
+
id,
|
|
9976
|
+
segment: segment2,
|
|
9977
|
+
result,
|
|
9978
|
+
content: result.content,
|
|
9979
|
+
contentWidth: visibleWidth(result.content),
|
|
9980
|
+
compact: false,
|
|
9981
|
+
moved: false
|
|
9982
|
+
});
|
|
9224
9983
|
} catch {
|
|
9225
9984
|
}
|
|
9226
9985
|
}
|
|
@@ -9230,13 +9989,28 @@ function renderStatus(layout, snapshot, width, options) {
|
|
|
9230
9989
|
const secondary = normalized.secondary.map((id) => candidates.get(id)).filter((candidate) => candidate !== void 0);
|
|
9231
9990
|
const visible = [];
|
|
9232
9991
|
const overflow = [];
|
|
9992
|
+
const gapWidth = visibleWidth(`${padding}${separator}${padding}`);
|
|
9993
|
+
let groupWidth = 0;
|
|
9994
|
+
let groupCount = 0;
|
|
9995
|
+
const widthAfter = (baseWidth, count, candidate) => count === 0 ? candidate.contentWidth : baseWidth + gapWidth + candidate.contentWidth;
|
|
9233
9996
|
for (const candidate of primary) {
|
|
9234
9997
|
visible.push(candidate);
|
|
9235
|
-
|
|
9998
|
+
const pushedWidth = widthAfter(groupWidth, groupCount, candidate);
|
|
9999
|
+
if (pushedWidth <= width) {
|
|
10000
|
+
groupWidth = pushedWidth;
|
|
10001
|
+
groupCount++;
|
|
10002
|
+
continue;
|
|
10003
|
+
}
|
|
9236
10004
|
if (candidate.result.compactContent && !candidate.compact) {
|
|
9237
10005
|
candidate.content = candidate.result.compactContent;
|
|
9238
10006
|
candidate.compact = true;
|
|
9239
|
-
|
|
10007
|
+
candidate.contentWidth = visibleWidth(candidate.content);
|
|
10008
|
+
const compactedWidth = widthAfter(groupWidth, groupCount, candidate);
|
|
10009
|
+
if (compactedWidth <= width) {
|
|
10010
|
+
groupWidth = compactedWidth;
|
|
10011
|
+
groupCount++;
|
|
10012
|
+
continue;
|
|
10013
|
+
}
|
|
9240
10014
|
}
|
|
9241
10015
|
visible.pop();
|
|
9242
10016
|
if (candidate.segment.overflow !== "drop" && candidate.segment.overflow !== "primary") {
|
|
@@ -9262,9 +10036,14 @@ function renderStatus(layout, snapshot, width, options) {
|
|
|
9262
10036
|
}
|
|
9263
10037
|
if (visibleWidth(primaryText) > width) primaryText = truncateAnsi(primaryText, width);
|
|
9264
10038
|
const secondaryVisible = [];
|
|
10039
|
+
let secondaryWidth = 0;
|
|
10040
|
+
let secondaryCount = 0;
|
|
9265
10041
|
for (const candidate of [...secondary].sort((a, b) => b.segment.defaultPriority - a.segment.defaultPriority)) {
|
|
10042
|
+
const pushedWidth = widthAfter(secondaryWidth, secondaryCount, candidate);
|
|
10043
|
+
if (pushedWidth > width) continue;
|
|
9266
10044
|
secondaryVisible.push(candidate);
|
|
9267
|
-
|
|
10045
|
+
secondaryWidth = pushedWidth;
|
|
10046
|
+
secondaryCount++;
|
|
9268
10047
|
}
|
|
9269
10048
|
const secondaryText = renderGroup(secondaryVisible, separator, padding);
|
|
9270
10049
|
const lines = secondaryText ? [primaryText, secondaryText] : primaryText ? [primaryText] : [];
|
|
@@ -9317,6 +10096,28 @@ function separatorsFor(config, theme) {
|
|
|
9317
10096
|
if (style === "none") return " ";
|
|
9318
10097
|
return theme.apply("separator", style);
|
|
9319
10098
|
}
|
|
10099
|
+
var themeAssetsCache = /* @__PURE__ */ new WeakMap();
|
|
10100
|
+
function themeAssetsFor(activeTheme, config) {
|
|
10101
|
+
let byConfig = themeAssetsCache.get(activeTheme);
|
|
10102
|
+
if (!byConfig) {
|
|
10103
|
+
byConfig = /* @__PURE__ */ new WeakMap();
|
|
10104
|
+
themeAssetsCache.set(activeTheme, byConfig);
|
|
10105
|
+
}
|
|
10106
|
+
let assets = byConfig.get(config);
|
|
10107
|
+
if (!assets) {
|
|
10108
|
+
const resolved = resolveTheme(
|
|
10109
|
+
activeTheme.colors || activeTheme.fg ? {
|
|
10110
|
+
...activeTheme.colors ? { colors: activeTheme.colors } : {},
|
|
10111
|
+
// Call through the theme instance so `this` binds correctly inside Pi's fg().
|
|
10112
|
+
...activeTheme.fg ? { fg: (color, text) => activeTheme.fg?.(color, text) ?? text } : {}
|
|
10113
|
+
} : void 0,
|
|
10114
|
+
config
|
|
10115
|
+
);
|
|
10116
|
+
assets = { resolved, separator: separatorsFor(config, resolved) };
|
|
10117
|
+
byConfig.set(config, assets);
|
|
10118
|
+
}
|
|
10119
|
+
return assets;
|
|
10120
|
+
}
|
|
9320
10121
|
function installStatusLine(options) {
|
|
9321
10122
|
const existing = installationMap(options.host).get(options.generation);
|
|
9322
10123
|
if (existing) return existing;
|
|
@@ -9349,16 +10150,9 @@ function installStatusLine(options) {
|
|
|
9349
10150
|
}
|
|
9350
10151
|
const render = (activeTheme, width, secondary) => {
|
|
9351
10152
|
if (width <= 0 || !config.enabled || !config.statusLine.enabled) return [];
|
|
9352
|
-
const resolved =
|
|
9353
|
-
activeTheme.colors || activeTheme.fg ? {
|
|
9354
|
-
...activeTheme.colors ? { colors: activeTheme.colors } : {},
|
|
9355
|
-
// Call through the theme instance so `this` binds correctly inside Pi's fg().
|
|
9356
|
-
...activeTheme.fg ? { fg: (color, text) => activeTheme.fg?.(color, text) ?? text } : {}
|
|
9357
|
-
} : void 0,
|
|
9358
|
-
config
|
|
9359
|
-
);
|
|
10153
|
+
const { resolved, separator } = themeAssetsFor(activeTheme, config);
|
|
9360
10154
|
const result = renderStatus(config.statusLine.layout, effectiveSnapshot(snapshot), width, {
|
|
9361
|
-
separator
|
|
10155
|
+
separator,
|
|
9362
10156
|
segments,
|
|
9363
10157
|
theme: resolved,
|
|
9364
10158
|
options: {
|
|
@@ -9439,12 +10233,16 @@ function installStatusLine(options) {
|
|
|
9439
10233
|
};
|
|
9440
10234
|
const factory = (secondary) => (tui, theme) => {
|
|
9441
10235
|
const currentTheme = theme;
|
|
10236
|
+
let renderCache;
|
|
9442
10237
|
const component = {
|
|
9443
10238
|
render(width) {
|
|
10239
|
+
if (renderCache?.width === width) return renderCache.lines;
|
|
9444
10240
|
const lines = render(currentTheme, width, secondary);
|
|
10241
|
+
renderCache = { width, lines };
|
|
9445
10242
|
return lines;
|
|
9446
10243
|
},
|
|
9447
10244
|
invalidate() {
|
|
10245
|
+
renderCache = void 0;
|
|
9448
10246
|
primaryComponent = secondary ? primaryComponent : component;
|
|
9449
10247
|
secondaryComponent = secondary ? component : secondaryComponent;
|
|
9450
10248
|
if (tui.requestRender) tui.requestRender();
|
|
@@ -9862,6 +10660,7 @@ function isPlainRecord(value) {
|
|
|
9862
10660
|
}
|
|
9863
10661
|
|
|
9864
10662
|
// extension-src/pi-style/app/runtime.ts
|
|
10663
|
+
var GIT_INVALIDATE_DEBOUNCE_MS = 250;
|
|
9865
10664
|
function createPiStyleRuntime(host, generation2, requestRender = host.requestRender ?? (() => {
|
|
9866
10665
|
})) {
|
|
9867
10666
|
let disposed = false;
|
|
@@ -9887,6 +10686,7 @@ function createPiStyleRuntime(host, generation2, requestRender = host.requestRen
|
|
|
9887
10686
|
const disposables = new DisposableStore();
|
|
9888
10687
|
const scheduler = new RenderScheduler({ requestRender }, generation2, () => !disposed);
|
|
9889
10688
|
const git = new CachedGitProvider(host.gitRunner);
|
|
10689
|
+
let pendingGitRefresh;
|
|
9890
10690
|
const contextProvider = new InMemoryContextProvider();
|
|
9891
10691
|
const usageProvider = new InMemoryUsageProvider();
|
|
9892
10692
|
const initialContext = contextSnapshot();
|
|
@@ -9976,7 +10776,10 @@ function createPiStyleRuntime(host, generation2, requestRender = host.requestRen
|
|
|
9976
10776
|
config: currentConfig,
|
|
9977
10777
|
generation: generation2,
|
|
9978
10778
|
initialSnapshot: currentSnapshot,
|
|
9979
|
-
isCurrent: () => !disposed
|
|
10779
|
+
isCurrent: () => !disposed,
|
|
10780
|
+
// Clipboard image paste surface (ADR 0009): instant `[Image #N] `
|
|
10781
|
+
// markers at keystroke time, artifact fallback, atomic backspace.
|
|
10782
|
+
clipboardImagePaste: createClipboardImagePasteSurface()
|
|
9980
10783
|
});
|
|
9981
10784
|
if (editor) {
|
|
9982
10785
|
disposables.add(editor);
|
|
@@ -10119,11 +10922,18 @@ function createPiStyleRuntime(host, generation2, requestRender = host.requestRen
|
|
|
10119
10922
|
},
|
|
10120
10923
|
invalidateGit() {
|
|
10121
10924
|
if (disposed || !host.cwd || !currentConfig.enabled || !currentConfig.statusLine.enabled) return;
|
|
10122
|
-
|
|
10123
|
-
|
|
10925
|
+
const cwd = host.cwd;
|
|
10926
|
+
git.invalidate(cwd);
|
|
10927
|
+
if (pendingGitRefresh !== void 0) return;
|
|
10928
|
+
pendingGitRefresh = setTimeout(() => {
|
|
10929
|
+
pendingGitRefresh = void 0;
|
|
10124
10930
|
if (disposed) return;
|
|
10125
|
-
|
|
10126
|
-
|
|
10931
|
+
void git.get(cwd).then((value) => {
|
|
10932
|
+
if (disposed) return;
|
|
10933
|
+
if (updateSnapshot(withSnapshotPatch({ git: value }))) requestRender();
|
|
10934
|
+
});
|
|
10935
|
+
}, GIT_INVALIDATE_DEBOUNCE_MS);
|
|
10936
|
+
pendingGitRefresh.unref?.();
|
|
10127
10937
|
},
|
|
10128
10938
|
get disposed() {
|
|
10129
10939
|
return disposed;
|
|
@@ -10131,6 +10941,10 @@ function createPiStyleRuntime(host, generation2, requestRender = host.requestRen
|
|
|
10131
10941
|
dispose() {
|
|
10132
10942
|
if (disposed) return;
|
|
10133
10943
|
disposed = true;
|
|
10944
|
+
if (pendingGitRefresh !== void 0) {
|
|
10945
|
+
clearTimeout(pendingGitRefresh);
|
|
10946
|
+
pendingGitRefresh = void 0;
|
|
10947
|
+
}
|
|
10134
10948
|
scheduler.cancel();
|
|
10135
10949
|
disposeStatus();
|
|
10136
10950
|
disposeEditor();
|
|
@@ -10663,7 +11477,7 @@ function isTierCAuthorized(input) {
|
|
|
10663
11477
|
|
|
10664
11478
|
// extension-src/pi-style/pi/compatibility-probe.ts
|
|
10665
11479
|
import { readFileSync as readFileSync2 } from "fs";
|
|
10666
|
-
import { dirname as
|
|
11480
|
+
import { dirname as dirname4, join as join3 } from "path";
|
|
10667
11481
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10668
11482
|
import {
|
|
10669
11483
|
AssistantMessageComponent,
|
|
@@ -10684,6 +11498,8 @@ var MAX_RENDER_CACHE_KEYS_PER_INSTANCE = 8;
|
|
|
10684
11498
|
var MAX_LINE_ANALYSIS_ENTRIES = 4096;
|
|
10685
11499
|
var renderCacheByInstance = /* @__PURE__ */ new WeakMap();
|
|
10686
11500
|
var lineAnalysisCache = /* @__PURE__ */ new Map();
|
|
11501
|
+
var lineCacheEvictionCursor;
|
|
11502
|
+
var childrenScanByInstance = /* @__PURE__ */ new WeakMap();
|
|
10687
11503
|
var messageDecorationTestState = {
|
|
10688
11504
|
decoratePasses: 0,
|
|
10689
11505
|
cacheHits: 0,
|
|
@@ -10706,7 +11522,7 @@ function splitLeadingMarkers(line) {
|
|
|
10706
11522
|
if (end === -1) break;
|
|
10707
11523
|
index = end + 1;
|
|
10708
11524
|
}
|
|
10709
|
-
return { head: line.slice(0, index), rest: line.slice(index) };
|
|
11525
|
+
return index === 0 ? { head: "", rest: line } : { head: line.slice(0, index), rest: line.slice(index) };
|
|
10710
11526
|
}
|
|
10711
11527
|
function leadingSgr(line) {
|
|
10712
11528
|
if (!line.startsWith("\x1B[")) return "";
|
|
@@ -10720,41 +11536,153 @@ function leadingSgr(line) {
|
|
|
10720
11536
|
}
|
|
10721
11537
|
function isBackgroundSgr(sequence) {
|
|
10722
11538
|
if (!sequence.startsWith("\x1B[") || !sequence.endsWith("m")) return false;
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
11539
|
+
let value = 0;
|
|
11540
|
+
let empty = true;
|
|
11541
|
+
let valid = true;
|
|
11542
|
+
for (let index = 2; index < sequence.length - 1; index++) {
|
|
11543
|
+
const code = sequence.charCodeAt(index);
|
|
11544
|
+
if (code === 59) {
|
|
11545
|
+
if (valid && matchesBackgroundCode(empty ? 0 : value)) return true;
|
|
11546
|
+
value = 0;
|
|
11547
|
+
empty = true;
|
|
11548
|
+
valid = true;
|
|
11549
|
+
continue;
|
|
11550
|
+
}
|
|
11551
|
+
if (code < 48 || code > 57) {
|
|
11552
|
+
valid = false;
|
|
11553
|
+
continue;
|
|
11554
|
+
}
|
|
11555
|
+
value = value * 10 + (code - 48);
|
|
11556
|
+
empty = false;
|
|
10728
11557
|
}
|
|
10729
|
-
return
|
|
11558
|
+
return valid && matchesBackgroundCode(empty ? 0 : value);
|
|
11559
|
+
}
|
|
11560
|
+
function matchesBackgroundCode(value) {
|
|
11561
|
+
return value === 48 || value === 49 || value >= 40 && value <= 47 || value >= 100 && value <= 107;
|
|
11562
|
+
}
|
|
11563
|
+
function certifiedAsciiWidth(value) {
|
|
11564
|
+
let width = 0;
|
|
11565
|
+
let index = 0;
|
|
11566
|
+
const length = value.length;
|
|
11567
|
+
while (index < length) {
|
|
11568
|
+
const code = value.charCodeAt(index);
|
|
11569
|
+
if (code === 27) {
|
|
11570
|
+
const next = index + 1 < length ? value.charCodeAt(index + 1) : -1;
|
|
11571
|
+
if (next === 91) {
|
|
11572
|
+
let scan = index + 2;
|
|
11573
|
+
while (scan < length) {
|
|
11574
|
+
const terminator = value.charCodeAt(scan);
|
|
11575
|
+
if (terminator === 109 || // m
|
|
11576
|
+
terminator === 71 || // G
|
|
11577
|
+
terminator === 75 || // K
|
|
11578
|
+
terminator === 72 || // H
|
|
11579
|
+
terminator === 74) {
|
|
11580
|
+
index = scan + 1;
|
|
11581
|
+
break;
|
|
11582
|
+
}
|
|
11583
|
+
scan++;
|
|
11584
|
+
}
|
|
11585
|
+
if (scan >= length) return void 0;
|
|
11586
|
+
continue;
|
|
11587
|
+
}
|
|
11588
|
+
if (next === 93 || next === 95) {
|
|
11589
|
+
let scan = index + 2;
|
|
11590
|
+
let end = -1;
|
|
11591
|
+
while (scan < length) {
|
|
11592
|
+
const terminator = value.charCodeAt(scan);
|
|
11593
|
+
if (terminator === 7) {
|
|
11594
|
+
end = scan + 1;
|
|
11595
|
+
break;
|
|
11596
|
+
}
|
|
11597
|
+
if (terminator === 27 && scan + 1 < length && value.charCodeAt(scan + 1) === 92) {
|
|
11598
|
+
end = scan + 2;
|
|
11599
|
+
break;
|
|
11600
|
+
}
|
|
11601
|
+
scan++;
|
|
11602
|
+
}
|
|
11603
|
+
if (end < 0) return void 0;
|
|
11604
|
+
index = end;
|
|
11605
|
+
continue;
|
|
11606
|
+
}
|
|
11607
|
+
return void 0;
|
|
11608
|
+
}
|
|
11609
|
+
if (code < 32 || code > 126) return void 0;
|
|
11610
|
+
width++;
|
|
11611
|
+
index++;
|
|
11612
|
+
}
|
|
11613
|
+
return width;
|
|
11614
|
+
}
|
|
11615
|
+
function certifiedVisibleWidth(value) {
|
|
11616
|
+
return certifiedAsciiWidth(value) ?? visibleWidth(value);
|
|
11617
|
+
}
|
|
11618
|
+
var prefixWidthMemo;
|
|
11619
|
+
function prefixWidthOf(prefix) {
|
|
11620
|
+
if (prefixWidthMemo?.prefix === prefix) return prefixWidthMemo.width;
|
|
11621
|
+
const width = certifiedVisibleWidth(prefix);
|
|
11622
|
+
prefixWidthMemo = { prefix, width };
|
|
11623
|
+
return width;
|
|
10730
11624
|
}
|
|
10731
11625
|
function contentText(line) {
|
|
11626
|
+
if (!line.includes("\x1B")) return line;
|
|
10732
11627
|
let output = "";
|
|
11628
|
+
let sliceStart = 0;
|
|
10733
11629
|
for (let index = 0; index < line.length; index++) {
|
|
10734
|
-
if (line.charCodeAt(index) !== 27)
|
|
10735
|
-
|
|
10736
|
-
continue;
|
|
10737
|
-
}
|
|
11630
|
+
if (line.charCodeAt(index) !== 27) continue;
|
|
11631
|
+
output += line.slice(sliceStart, index);
|
|
10738
11632
|
const next = line[index + 1];
|
|
10739
11633
|
if (next === "]") {
|
|
10740
11634
|
index += 2;
|
|
10741
11635
|
while (index < line.length && line.charCodeAt(index) !== 7) index++;
|
|
10742
|
-
|
|
10743
|
-
}
|
|
10744
|
-
if (next === "[") {
|
|
11636
|
+
} else if (next === "[") {
|
|
10745
11637
|
index += 2;
|
|
10746
11638
|
while (index < line.length && (line.charCodeAt(index) < 64 || line.charCodeAt(index) > 126)) index++;
|
|
10747
11639
|
}
|
|
11640
|
+
sliceStart = index + 1;
|
|
11641
|
+
}
|
|
11642
|
+
output += line.slice(sliceStart);
|
|
11643
|
+
if (output.includes("\x1B]133;")) {
|
|
11644
|
+
return output.replaceAll(OSC133_ZONE_START, "").replaceAll(OSC133_ZONE_END, "").replaceAll(OSC133_ZONE_FINAL, "");
|
|
10748
11645
|
}
|
|
10749
|
-
return output
|
|
11646
|
+
return output;
|
|
11647
|
+
}
|
|
11648
|
+
function isWhitespaceCode(code) {
|
|
11649
|
+
if (code === 32 || code >= 9 && code <= 13) return true;
|
|
11650
|
+
if (code < 128) return false;
|
|
11651
|
+
return code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
|
|
10750
11652
|
}
|
|
10751
11653
|
function hasContent(line) {
|
|
10752
|
-
|
|
11654
|
+
const length = line.length;
|
|
11655
|
+
let index = 0;
|
|
11656
|
+
while (index < length) {
|
|
11657
|
+
const code = line.charCodeAt(index);
|
|
11658
|
+
if (code === 27) {
|
|
11659
|
+
const next = index + 1 < length ? line.charCodeAt(index + 1) : -1;
|
|
11660
|
+
if (next === 93) {
|
|
11661
|
+
index += 2;
|
|
11662
|
+
while (index < length && line.charCodeAt(index) !== 7) index++;
|
|
11663
|
+
} else if (next === 91) {
|
|
11664
|
+
index += 2;
|
|
11665
|
+
while (index < length) {
|
|
11666
|
+
const inner = line.charCodeAt(index);
|
|
11667
|
+
if (inner >= 64 && inner <= 126) break;
|
|
11668
|
+
index++;
|
|
11669
|
+
}
|
|
11670
|
+
}
|
|
11671
|
+
index++;
|
|
11672
|
+
continue;
|
|
11673
|
+
}
|
|
11674
|
+
if (code >= 55296 && code <= 57343) return true;
|
|
11675
|
+
if (!isWhitespaceCode(code)) return true;
|
|
11676
|
+
index++;
|
|
11677
|
+
}
|
|
11678
|
+
return false;
|
|
10753
11679
|
}
|
|
10754
11680
|
function getLineAnalysis(line) {
|
|
10755
11681
|
const cached = lineAnalysisCache.get(line);
|
|
10756
11682
|
if (cached) {
|
|
10757
11683
|
messageDecorationTestState.lineCacheHits++;
|
|
11684
|
+
lineAnalysisCache.delete(line);
|
|
11685
|
+
lineAnalysisCache.set(line, cached);
|
|
10758
11686
|
return cached;
|
|
10759
11687
|
}
|
|
10760
11688
|
messageDecorationTestState.lineCacheMisses++;
|
|
@@ -10762,21 +11690,29 @@ function getLineAnalysis(line) {
|
|
|
10762
11690
|
const backgroundAnsi = leadingSgr(leadingMarkers.rest);
|
|
10763
11691
|
const isBackgroundWrapped = backgroundAnsi !== "" && isBackgroundSgr(backgroundAnsi) && leadingMarkers.rest.endsWith(BG_RESET);
|
|
10764
11692
|
const backgroundBody = isBackgroundWrapped ? leadingMarkers.rest.slice(backgroundAnsi.length, leadingMarkers.rest.length - BG_RESET.length) : void 0;
|
|
11693
|
+
const hasOscStart = line.startsWith(OSC133_ZONE_START);
|
|
10765
11694
|
const analysis = {
|
|
10766
|
-
visibleWidth:
|
|
11695
|
+
visibleWidth: certifiedVisibleWidth(line),
|
|
10767
11696
|
hasContent: hasContent(line),
|
|
10768
|
-
oscEnvelope: extractOscEnvelope(line),
|
|
10769
|
-
hasOscStart
|
|
11697
|
+
oscEnvelope: hasOscStart ? extractOscEnvelope(line) : void 0,
|
|
11698
|
+
hasOscStart,
|
|
10770
11699
|
leadingMarkers,
|
|
10771
11700
|
isBackgroundWrapped,
|
|
10772
11701
|
backgroundAnsi,
|
|
10773
11702
|
backgroundBody,
|
|
10774
|
-
backgroundBodyWidth: backgroundBody === void 0 ? void 0 :
|
|
11703
|
+
backgroundBodyWidth: backgroundBody === void 0 ? void 0 : certifiedVisibleWidth(backgroundBody)
|
|
10775
11704
|
};
|
|
10776
11705
|
lineAnalysisCache.set(line, analysis);
|
|
10777
11706
|
if (lineAnalysisCache.size > MAX_LINE_ANALYSIS_ENTRIES) {
|
|
10778
|
-
|
|
10779
|
-
if (
|
|
11707
|
+
let cursor = lineCacheEvictionCursor;
|
|
11708
|
+
if (cursor === void 0) cursor = lineAnalysisCache.keys();
|
|
11709
|
+
let oldest = cursor.next();
|
|
11710
|
+
if (oldest.done) {
|
|
11711
|
+
cursor = lineAnalysisCache.keys();
|
|
11712
|
+
oldest = cursor.next();
|
|
11713
|
+
}
|
|
11714
|
+
lineCacheEvictionCursor = cursor;
|
|
11715
|
+
if (!oldest.done && oldest.value !== void 0) lineAnalysisCache.delete(oldest.value);
|
|
10780
11716
|
}
|
|
10781
11717
|
return analysis;
|
|
10782
11718
|
}
|
|
@@ -10789,8 +11725,8 @@ function rebuildAtWidth(line, width, lead, leadWidth, analysis = getLineAnalysis
|
|
|
10789
11725
|
return `${lead}${line}${pad}`;
|
|
10790
11726
|
}
|
|
10791
11727
|
function decorateMessageLine(line, index, lastIndex, contentIndex, width, options, analysis = getLineAnalysis(line)) {
|
|
10792
|
-
const { firstEnvelope, firstHasStart, multilineEnvelope, prefix, prefixWidth } = options;
|
|
10793
|
-
const lead = index === contentIndex ? prefix : index > contentIndex ?
|
|
11728
|
+
const { firstEnvelope, firstHasStart, multilineEnvelope, prefix, prefixWidth, continuationLead } = options;
|
|
11729
|
+
const lead = index === contentIndex ? prefix : index > contentIndex ? continuationLead : "";
|
|
10794
11730
|
const leadWidth = index < contentIndex ? 0 : prefixWidth;
|
|
10795
11731
|
if (index === contentIndex && firstEnvelope)
|
|
10796
11732
|
return `${firstEnvelope.start}${rebuildAtWidth(firstEnvelope.body, width, prefix, prefixWidth)}${firstEnvelope.end}`;
|
|
@@ -10846,32 +11782,58 @@ function prefixNative(lines, width, prefix) {
|
|
|
10846
11782
|
if (!Array.isArray(lines) || lines.length === 0 || !lines.every((line) => typeof line === "string")) return void 0;
|
|
10847
11783
|
messageDecorationTestState.decoratePasses++;
|
|
10848
11784
|
const nativeLines = lines;
|
|
10849
|
-
const prefixWidth =
|
|
11785
|
+
const prefixWidth = prefixWidthOf(prefix);
|
|
10850
11786
|
if (width <= prefixWidth) return void 0;
|
|
10851
11787
|
const bodyWidth = width - prefixWidth;
|
|
10852
|
-
const
|
|
11788
|
+
const analyses = nativeLines.map((line) => getLineAnalysis(line));
|
|
10853
11789
|
const last = nativeLines.at(-1) ?? "";
|
|
10854
|
-
const lastAnalysis = getLineAnalysis(last);
|
|
11790
|
+
const lastAnalysis = analyses[analyses.length - 1] ?? getLineAnalysis(last);
|
|
10855
11791
|
const multilineEnvelope = nativeLines.length > 1 && last.startsWith(OSC133_ZONE_END + OSC133_ZONE_FINAL);
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
11792
|
+
let firstContentIndex = -1;
|
|
11793
|
+
for (let index = 0; index < nativeLines.length; index++) {
|
|
11794
|
+
const analysis = analyses[index] ?? getLineAnalysis(nativeLines[index] ?? "");
|
|
11795
|
+
if (index !== nativeLines.length - 1 || !multilineEnvelope) {
|
|
11796
|
+
if (analysis.hasContent) {
|
|
11797
|
+
firstContentIndex = index;
|
|
11798
|
+
break;
|
|
11799
|
+
}
|
|
11800
|
+
continue;
|
|
11801
|
+
}
|
|
11802
|
+
let earlierHasContent = false;
|
|
11803
|
+
for (let earlier = 0; earlier < index; earlier++) {
|
|
11804
|
+
if ((analyses[earlier] ?? getLineAnalysis(nativeLines[earlier] ?? "")).hasContent) {
|
|
11805
|
+
earlierHasContent = true;
|
|
11806
|
+
break;
|
|
11807
|
+
}
|
|
11808
|
+
}
|
|
11809
|
+
if (!earlierHasContent && lastAnalysis.hasContent) firstContentIndex = index;
|
|
11810
|
+
break;
|
|
11811
|
+
}
|
|
10860
11812
|
if (firstContentIndex < 0) return nativeLines;
|
|
10861
|
-
const firstAnalysis = getLineAnalysis(
|
|
11813
|
+
const firstAnalysis = analyses[0] ?? getLineAnalysis(nativeLines[0] ?? "");
|
|
10862
11814
|
const firstEnvelope = firstContentIndex === 0 ? firstAnalysis.oscEnvelope : void 0;
|
|
10863
11815
|
const firstHasStart = firstContentIndex === 0 && firstAnalysis.hasOscStart;
|
|
11816
|
+
const continuationLead = " ".repeat(prefixWidth);
|
|
10864
11817
|
const decorated = nativeLines.map(
|
|
10865
|
-
(line, index) => decorateMessageLine(
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
11818
|
+
(line, index) => decorateMessageLine(
|
|
11819
|
+
line,
|
|
11820
|
+
index,
|
|
11821
|
+
nativeLines.length - 1,
|
|
11822
|
+
firstContentIndex,
|
|
11823
|
+
width,
|
|
11824
|
+
{
|
|
11825
|
+
firstEnvelope,
|
|
11826
|
+
firstHasStart,
|
|
11827
|
+
multilineEnvelope,
|
|
11828
|
+
prefix,
|
|
11829
|
+
prefixWidth,
|
|
11830
|
+
continuationLead
|
|
11831
|
+
},
|
|
11832
|
+
analyses[index]
|
|
11833
|
+
)
|
|
10872
11834
|
);
|
|
10873
|
-
if (!decorated.every((line) =>
|
|
10874
|
-
if (!
|
|
11835
|
+
if (!decorated.every((line) => certifiedVisibleWidth(line) <= width)) return void 0;
|
|
11836
|
+
if (!analyses.every((analysis) => analysis.visibleWidth <= bodyWidth)) return void 0;
|
|
10875
11837
|
return decorated;
|
|
10876
11838
|
}
|
|
10877
11839
|
function decorateMessageRender(original, instance, args, snapshot = {
|
|
@@ -10884,7 +11846,7 @@ function decorateMessageRender(original, instance, args, snapshot = {
|
|
|
10884
11846
|
const width = typeof args[0] === "number" ? args[0] : 0;
|
|
10885
11847
|
const prefix = snapshot.assistantPrefix;
|
|
10886
11848
|
if (!snapshot.assistantEnabled) return Reflect.apply(original, instance, args);
|
|
10887
|
-
const prefixWidth =
|
|
11849
|
+
const prefixWidth = prefixWidthOf(prefix);
|
|
10888
11850
|
if (width <= prefixWidth) return Reflect.apply(original, instance, args);
|
|
10889
11851
|
const reducedWidth = width - prefixWidth;
|
|
10890
11852
|
const native = Reflect.apply(original, instance, [reducedWidth, ...args.slice(1)]);
|
|
@@ -10916,8 +11878,16 @@ function hasToolCallItems(message) {
|
|
|
10916
11878
|
function isBlankTextChild(child) {
|
|
10917
11879
|
const candidate = child;
|
|
10918
11880
|
if (typeof candidate?.setCustomBgFn !== "function" || typeof candidate.render !== "function") return false;
|
|
11881
|
+
if (typeof candidate.text === "string") return contentText(candidate.text).trim() === "";
|
|
10919
11882
|
return contentText(candidate.render(0).join("\n")).trim() === "";
|
|
10920
11883
|
}
|
|
11884
|
+
function childrenUnchangedSinceScan(instance, children) {
|
|
11885
|
+
const state = childrenScanByInstance.get(instance);
|
|
11886
|
+
return state !== void 0 && state.childrenRef === children && state.length === children.length;
|
|
11887
|
+
}
|
|
11888
|
+
function markChildrenScanned(instance, children) {
|
|
11889
|
+
childrenScanByInstance.set(instance, { childrenRef: children, length: children.length });
|
|
11890
|
+
}
|
|
10921
11891
|
function decorateMessageUpdate(original, instance, args, snapshot = {
|
|
10922
11892
|
assistantPrefix: "\u2502 ",
|
|
10923
11893
|
assistantEnabled: true,
|
|
@@ -10927,24 +11897,22 @@ function decorateMessageUpdate(original, instance, args, snapshot = {
|
|
|
10927
11897
|
if (typeof original !== "function") return void 0;
|
|
10928
11898
|
const result = Reflect.apply(original, instance, args);
|
|
10929
11899
|
const target = instance;
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
if (
|
|
11900
|
+
const children = target.contentContainer?.children;
|
|
11901
|
+
if (children && !childrenUnchangedSinceScan(instance, children)) {
|
|
11902
|
+
if (snapshot.collapseHiddenThinking && target.hideThinkingBlock === true && target.hiddenThinkingLabel === "") {
|
|
10933
11903
|
for (let index = children.length - 1; index >= 0; index--) {
|
|
10934
11904
|
if (!isBlankTextChild(children[index])) continue;
|
|
10935
11905
|
children.splice(index, 1);
|
|
10936
11906
|
if (isSpacerChild(children[index])) children.splice(index, 1);
|
|
10937
11907
|
}
|
|
10938
11908
|
}
|
|
10939
|
-
|
|
10940
|
-
if (snapshot.hideInterimText && hasToolCallItems(args[0])) {
|
|
10941
|
-
const children = target.contentContainer?.children;
|
|
10942
|
-
if (children) {
|
|
11909
|
+
if (snapshot.hideInterimText && hasToolCallItems(args[0])) {
|
|
10943
11910
|
for (let index = children.length - 1; index >= 0; index--) {
|
|
10944
11911
|
if (isInterimTextChild(children[index])) children.splice(index, 1);
|
|
10945
11912
|
}
|
|
10946
11913
|
if (children.every((child) => isSpacerChild(child))) children.length = 0;
|
|
10947
11914
|
}
|
|
11915
|
+
markChildrenScanned(instance, children);
|
|
10948
11916
|
}
|
|
10949
11917
|
return result;
|
|
10950
11918
|
}
|
|
@@ -11306,19 +12274,19 @@ function detectPiVersion(resolution = {}) {
|
|
|
11306
12274
|
);
|
|
11307
12275
|
}
|
|
11308
12276
|
});
|
|
11309
|
-
const
|
|
12277
|
+
const readFile4 = resolution.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
11310
12278
|
try {
|
|
11311
12279
|
const entry = resolvePackageEntry("@earendil-works/pi-coding-agent");
|
|
11312
|
-
let directory =
|
|
12280
|
+
let directory = dirname4(entry);
|
|
11313
12281
|
for (; ; ) {
|
|
11314
12282
|
const packagePath = join3(directory, "package.json");
|
|
11315
12283
|
try {
|
|
11316
|
-
const packageJson = JSON.parse(
|
|
12284
|
+
const packageJson = JSON.parse(readFile4(packagePath));
|
|
11317
12285
|
if (packageJson.name === "@earendil-works/pi-coding-agent" && typeof packageJson.version === "string")
|
|
11318
12286
|
return { version: packageJson.version };
|
|
11319
12287
|
} catch {
|
|
11320
12288
|
}
|
|
11321
|
-
const parent =
|
|
12289
|
+
const parent = dirname4(directory);
|
|
11322
12290
|
if (parent === directory) break;
|
|
11323
12291
|
directory = parent;
|
|
11324
12292
|
}
|
|
@@ -11981,6 +12949,11 @@ function createPiStyleSessionCoordinator(pi, hooks = {}) {
|
|
|
11981
12949
|
};
|
|
11982
12950
|
const applyMessagesConfig = (config) => {
|
|
11983
12951
|
sessionUi?.setHiddenThinkingLabel?.(config.messages.hideThinkingLabel ? "" : void 0);
|
|
12952
|
+
setMessagesRenderConfig({
|
|
12953
|
+
showImagePreviews: config.messages.showImagePreviews,
|
|
12954
|
+
clipboardImages: config.messages.clipboardImages,
|
|
12955
|
+
previewMaxWidth: config.messages.previewMaxWidth
|
|
12956
|
+
});
|
|
11984
12957
|
};
|
|
11985
12958
|
const applyAutoTheme = (config, ctx) => {
|
|
11986
12959
|
const target = config.theme.autoApply;
|
|
@@ -12040,6 +13013,7 @@ function createPiStyleSessionCoordinator(pi, hooks = {}) {
|
|
|
12040
13013
|
resetBatchRegistry();
|
|
12041
13014
|
resetGrepRegistry();
|
|
12042
13015
|
resetBashTreeRegistry();
|
|
13016
|
+
resetPendingImageRegistry();
|
|
12043
13017
|
resetTurnRegistry();
|
|
12044
13018
|
rebuildTurnRegistryFromEntries(ctx.sessionManager.getEntries());
|
|
12045
13019
|
stopAllElapsedTickers();
|
|
@@ -12105,6 +13079,7 @@ function createPiStyleSessionCoordinator(pi, hooks = {}) {
|
|
|
12105
13079
|
resetBatchRegistry();
|
|
12106
13080
|
resetGrepRegistry();
|
|
12107
13081
|
resetBashTreeRegistry();
|
|
13082
|
+
resetPendingImageRegistry();
|
|
12108
13083
|
resetTurnRegistry();
|
|
12109
13084
|
stopAllElapsedTickers();
|
|
12110
13085
|
app.sessionShutdown();
|
|
@@ -12308,6 +13283,12 @@ function piStyleExtension(pi) {
|
|
|
12308
13283
|
pi.registerFlag("pi-style-ascii", { type: "boolean", description: "Use ASCII pi-style markers" });
|
|
12309
13284
|
const coordinator = createPiStyleSessionCoordinator(pi, compatibilityTestHooks);
|
|
12310
13285
|
registerPiStyleCommand(pi, coordinator.app);
|
|
13286
|
+
registerImagePreviewSurface(pi);
|
|
13287
|
+
let stagedImagePreview;
|
|
13288
|
+
pi.on("before_agent_start", (event) => {
|
|
13289
|
+
const staged = stageImagePreviewData(event.images ?? []);
|
|
13290
|
+
if (staged) stagedImagePreview = staged;
|
|
13291
|
+
});
|
|
12311
13292
|
pi.on("session_start", async (event, ctx) => {
|
|
12312
13293
|
resetUsageFromSessionCache(ctx.sessionManager);
|
|
12313
13294
|
if (pi.getFlag("pi-style-readonly-tools") === true) {
|
|
@@ -12319,7 +13300,7 @@ function piStyleExtension(pi) {
|
|
|
12319
13300
|
coordinator.app.runtime.current?.dismissStartup();
|
|
12320
13301
|
beginAgentRun();
|
|
12321
13302
|
});
|
|
12322
|
-
pi.on("input", (event, _ctx) => {
|
|
13303
|
+
pi.on("input", async (event, _ctx) => {
|
|
12323
13304
|
coordinator.app.runtime.current?.dismissStartup();
|
|
12324
13305
|
if (event.source === "interactive") {
|
|
12325
13306
|
const trimmed = event.text.trimStart();
|
|
@@ -12328,6 +13309,15 @@ function piStyleExtension(pi) {
|
|
|
12328
13309
|
if (trimmed.slice(bangLength).trim() === "") return { action: "handled" };
|
|
12329
13310
|
}
|
|
12330
13311
|
}
|
|
13312
|
+
const markerResult = await resolvePendingImageMarkers(event.text);
|
|
13313
|
+
const pathResult = event.source === "interactive" ? await transformClipboardImages(event.text) : void 0;
|
|
13314
|
+
if (markerResult || pathResult) {
|
|
13315
|
+
return {
|
|
13316
|
+
action: "transform",
|
|
13317
|
+
text: pathResult?.text ?? event.text,
|
|
13318
|
+
images: [...markerResult?.images ?? [], ...pathResult?.images ?? []]
|
|
13319
|
+
};
|
|
13320
|
+
}
|
|
12331
13321
|
return void 0;
|
|
12332
13322
|
});
|
|
12333
13323
|
pi.on("tool_execution_start", () => coordinator.app.runtime.current?.dismissStartup());
|
|
@@ -12347,8 +13337,12 @@ function piStyleExtension(pi) {
|
|
|
12347
13337
|
"session_info_changed",
|
|
12348
13338
|
(event) => coordinator.app.update({ sessionName: event.name }, "coalesced", { refreshExtensionStatuses: true })
|
|
12349
13339
|
);
|
|
12350
|
-
pi.on("message_start", () => {
|
|
13340
|
+
pi.on("message_start", (event) => {
|
|
12351
13341
|
closeActiveBatch();
|
|
13342
|
+
if (stagedImagePreview && event.message?.role === "assistant") {
|
|
13343
|
+
flushImagePreviewEntry(pi, stagedImagePreview);
|
|
13344
|
+
stagedImagePreview = void 0;
|
|
13345
|
+
}
|
|
12352
13346
|
});
|
|
12353
13347
|
pi.on("message_update", () => coordinator.app.update({}, "coalesced"));
|
|
12354
13348
|
pi.on(
|
|
@@ -12363,6 +13357,7 @@ function piStyleExtension(pi) {
|
|
|
12363
13357
|
const run = finishAgentRun();
|
|
12364
13358
|
if (run) {
|
|
12365
13359
|
invalidateTurnMembers(run);
|
|
13360
|
+
releaseTurnInvalidators(run);
|
|
12366
13361
|
requestToolPresentationRender();
|
|
12367
13362
|
}
|
|
12368
13363
|
});
|