pi-supernova 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -196
- package/{CHANGELOG.md → docs/CHANGELOG.md} +54 -1
- package/index.js +81 -68
- package/package.json +12 -31
- package/{catalog.js → src/bridge/catalog.js} +7 -5
- package/{host-bridge.js → src/bridge/host-bridge.js} +232 -87
- package/src/bridge/native-tools.js +155 -0
- package/src/bridge/pi-extension.ts +2 -0
- package/{config.js → src/config/config.js} +1 -1
- package/{evidence.js → src/context/evidence.js} +23 -12
- package/{outline.js → src/context/outline.js} +1 -1
- package/{repo-index.js → src/context/repo-index.js} +11 -9
- package/{search.js → src/context/search.js} +34 -1
- package/{snap.js → src/context/snap.js} +51 -31
- package/{surface.js → src/context/surface.js} +1 -1
- package/{diff.js → src/fs/diff.js} +12 -9
- package/{patch.js → src/fs/patch.js} +1 -1
- package/{vfs.js → src/fs/vfs.js} +55 -14
- package/{workspace.js → src/fs/workspace.js} +22 -6
- package/{bottleneck.js → src/output/bottleneck.js} +23 -6
- package/{format.js → src/output/format.js} +20 -1
- package/{guest-worker.js → src/runtime/guest-worker.js} +90 -21
- package/{parallel.js → src/runtime/parallel.js} +68 -1
- package/{runtime.js → src/runtime/runtime.js} +14 -5
- package/{omp-frame.js → src/ui/omp-frame.js} +1 -1
- package/{render-measure.js → src/ui/render-measure.js} +27 -1
- package/{render.js → src/ui/render.js} +42 -20
- /package/{config.default.json → src/config/config.default.json} +0 -0
- /package/{fuzzy.js → src/context/fuzzy.js} +0 -0
- /package/{ledger.js → src/context/ledger.js} +0 -0
- /package/{check.js → src/fs/check.js} +0 -0
- /package/{decode.js → src/shared/decode.js} +0 -0
|
@@ -4,8 +4,34 @@ import stringWidth from "string-width";
|
|
|
4
4
|
const ELLIPSIS = "…";
|
|
5
5
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
6
6
|
|
|
7
|
+
const widthCache = new Map();
|
|
8
|
+
let cachedWidthChars = 0;
|
|
9
|
+
const MAX_WIDTH_CACHE_CHARS = 512_000;
|
|
10
|
+
|
|
7
11
|
export function measureWidth(text) {
|
|
8
|
-
|
|
12
|
+
const raw = String(text ?? "");
|
|
13
|
+
const cached = widthCache.get(raw);
|
|
14
|
+
if (cached !== undefined) return cached;
|
|
15
|
+
const normalized = raw.replace(/\t/g, " ");
|
|
16
|
+
// eslint-disable-next-line no-control-regex -- intentional ANSI SGR recognition
|
|
17
|
+
const plain = normalized.replace(/\x1b\[(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?m/g, "");
|
|
18
|
+
// ASCII and these single-column chrome glyphs need no Unicode segmentation.
|
|
19
|
+
// Any other character/control/escape sequence uses the full oracle.
|
|
20
|
+
const width = /^[\x20-\x7e\u2500-\u257f\u00b7\u00d7\u2026\u2713\u2717]*$/.test(plain)
|
|
21
|
+
? plain.length
|
|
22
|
+
: stringWidth(normalized);
|
|
23
|
+
// Cache immutable text only, never host/theme/result objects. Bound both
|
|
24
|
+
// bookkeeping and retained text; unusually long lines bypass retention.
|
|
25
|
+
if (raw.length <= 4096) {
|
|
26
|
+
while (widthCache.size >= 4096 || cachedWidthChars + raw.length > MAX_WIDTH_CACHE_CHARS) {
|
|
27
|
+
const oldest = widthCache.keys().next().value;
|
|
28
|
+
widthCache.delete(oldest);
|
|
29
|
+
cachedWidthChars -= oldest.length;
|
|
30
|
+
}
|
|
31
|
+
widthCache.set(raw, width);
|
|
32
|
+
cachedWidthChars += raw.length;
|
|
33
|
+
}
|
|
34
|
+
return width;
|
|
9
35
|
}
|
|
10
36
|
|
|
11
37
|
function takePrefix(text, width) {
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { stripVTControlCharacters } from "node:util";
|
|
14
|
-
import { isString, isObject, isFunction } from "
|
|
14
|
+
import { isString, isObject, isFunction } from "../shared/decode.js";
|
|
15
15
|
import { measureWidth, hardTruncate, clampLine, fitPath, wrapLine } from "./render-measure.js";
|
|
16
16
|
import { novaFramedBlock, novaStatusLine } from "./omp-frame.js";
|
|
17
|
-
import { formatValue } from "
|
|
17
|
+
import { formatValue } from "../output/format.js";
|
|
18
18
|
|
|
19
19
|
export { measureWidth, hardTruncate, clampLine };
|
|
20
20
|
|
|
@@ -42,14 +42,10 @@ function formatDiffRows(diff, theme, maxShown = 6) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function stripUnsafeControls(value) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const isDeleteOrC1 = codePoint >= 0x7f && codePoint <= 0x9f;
|
|
50
|
-
if (!isC0 && !isDeleteOrC1) clean += character;
|
|
51
|
-
}
|
|
52
|
-
return clean;
|
|
45
|
+
// Exactly the C0/DEL/C1 ranges previously filtered code point by code point.
|
|
46
|
+
// Native replacement avoids rebuilding every already-clean Unicode string.
|
|
47
|
+
// eslint-disable-next-line no-control-regex -- intentional terminal-control filtering
|
|
48
|
+
return value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/g, "");
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
function cleanBlockText(value) {
|
|
@@ -195,11 +191,20 @@ function parseDiffLine(rawLine) {
|
|
|
195
191
|
return null;
|
|
196
192
|
}
|
|
197
193
|
|
|
194
|
+
// Text diffs are immutable, even when hosts replace trace snapshots on each frame.
|
|
195
|
+
// Cache only parsed data, not theme, paths or mutable result objects.
|
|
196
|
+
const textDiffCache = new Map();
|
|
197
|
+
let cachedDiffChars = 0;
|
|
198
|
+
const MAX_CACHED_DIFF_CHARS = 1_000_000;
|
|
199
|
+
|
|
198
200
|
function normalizeTraceDiff(item) {
|
|
199
201
|
const diff = item?.diff;
|
|
200
202
|
if (isObject(diff)) return diff;
|
|
201
203
|
if (!isString(diff) || !diff.trim()) return undefined;
|
|
204
|
+
const cached = textDiffCache.get(diff);
|
|
205
|
+
if (cached) return cached;
|
|
202
206
|
const lines = [];
|
|
207
|
+
let displayLineCount = 0;
|
|
203
208
|
let added = 0;
|
|
204
209
|
let removed = 0;
|
|
205
210
|
for (const rawLine of cleanBlockText(diff).split("\n")) {
|
|
@@ -207,10 +212,21 @@ function normalizeTraceDiff(item) {
|
|
|
207
212
|
if (!parsed) continue;
|
|
208
213
|
if (parsed.type === "add") added += 1;
|
|
209
214
|
else if (parsed.type === "remove") removed += 1;
|
|
210
|
-
|
|
215
|
+
displayLineCount++;
|
|
216
|
+
if (lines.length < 24) lines.push(parsed);
|
|
211
217
|
}
|
|
212
218
|
if (lines.length === 0) return undefined;
|
|
213
|
-
|
|
219
|
+
const parsed = { added, removed, lines, displayLineCount };
|
|
220
|
+
if (diff.length <= MAX_CACHED_DIFF_CHARS) {
|
|
221
|
+
while (textDiffCache.size >= 24 || cachedDiffChars + diff.length > MAX_CACHED_DIFF_CHARS) {
|
|
222
|
+
const oldest = textDiffCache.keys().next().value;
|
|
223
|
+
textDiffCache.delete(oldest);
|
|
224
|
+
cachedDiffChars -= oldest.length;
|
|
225
|
+
}
|
|
226
|
+
textDiffCache.set(diff, parsed);
|
|
227
|
+
cachedDiffChars += diff.length;
|
|
228
|
+
}
|
|
229
|
+
return parsed;
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
function operationsFromTrace(trace) {
|
|
@@ -305,8 +321,9 @@ function formatOpRow(theme, op, width, isPartial, isError) {
|
|
|
305
321
|
return prefix.trimEnd();
|
|
306
322
|
}
|
|
307
323
|
|
|
308
|
-
function
|
|
309
|
-
|
|
324
|
+
function traceFor(payload, context) {
|
|
325
|
+
const trace = payload?.trace || context?.state?.trace;
|
|
326
|
+
return Array.isArray(trace) ? trace : [];
|
|
310
327
|
}
|
|
311
328
|
|
|
312
329
|
function resultLines(value, width) {
|
|
@@ -317,7 +334,7 @@ function resultLines(value, width) {
|
|
|
317
334
|
function appendOps(lines, theme, ops, maxOps, maxDiffLines, width, isPartial, isError) {
|
|
318
335
|
for (const op of ops.slice(0, maxOps)) {
|
|
319
336
|
lines.push(formatOpRow(theme, op, width, isPartial, isError));
|
|
320
|
-
if (!op.diff || !isObject(op.diff)) continue;
|
|
337
|
+
if (maxDiffLines === 0 || !op.diff || !isObject(op.diff)) continue;
|
|
321
338
|
for (const row of formatDiffRows(op.diff, theme, maxDiffLines)) lines.push(" " + row);
|
|
322
339
|
}
|
|
323
340
|
if (ops.length > maxOps) lines.push(theme.fg("dim", ` … ${ops.length - maxOps} more calls`));
|
|
@@ -338,14 +355,19 @@ function appendTail(lines, theme, payload, expanded, isError, width) {
|
|
|
338
355
|
}
|
|
339
356
|
}
|
|
340
357
|
|
|
341
|
-
function buildBodyLines(theme, width, { payload, context,
|
|
342
|
-
const
|
|
358
|
+
function buildBodyLines(theme, width, { payload, context, expanded, isPartial, isError }) {
|
|
359
|
+
const trace = traceFor(payload, context);
|
|
343
360
|
const maxOps = expanded ? 24 : 8;
|
|
344
|
-
const maxDiffLines = expanded ? 24 : 8;
|
|
361
|
+
const maxDiffLines = expanded ? 24 : isPartial ? 0 : 8;
|
|
362
|
+
// Select before parsing diffs: invisible history must not consume a frame.
|
|
363
|
+
// While running, show current activity rather than the first completed calls.
|
|
364
|
+
const visible = isPartial ? trace.slice(-maxOps) : trace.slice(0, maxOps);
|
|
365
|
+
const ops = operationsFromTrace(visible);
|
|
345
366
|
const lines = [];
|
|
346
367
|
appendOps(lines, theme, ops, maxOps, maxDiffLines, width, isPartial, isError);
|
|
368
|
+
if (trace.length > maxOps) lines.push(theme.fg("dim", ` … ${trace.length - maxOps} ${isPartial ? "earlier" : "more"} calls`));
|
|
347
369
|
appendTail(lines, theme, payload, expanded, isError, width);
|
|
348
|
-
return { lines, opCount:
|
|
370
|
+
return { lines, opCount: trace.length };
|
|
349
371
|
}
|
|
350
372
|
|
|
351
373
|
function describeCard(model, opCount) {
|
|
@@ -404,7 +426,7 @@ export function renderSupernovaResult(resultArg, optionsArg, themeArg, contextAr
|
|
|
404
426
|
contextArg,
|
|
405
427
|
);
|
|
406
428
|
|
|
407
|
-
const payload = result?.details;
|
|
429
|
+
const payload = result?.details ?? (result?.isError ? { ok: false, error: result.content?.filter(block => block.type === "text").map(block => block.text).join("\n") } : undefined);
|
|
408
430
|
syncState(context, payload);
|
|
409
431
|
|
|
410
432
|
const isError = result?.isError || payload?.ok === false;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|