pi-cc-extensions 0.8.25 → 0.8.26
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/extensions/context.ts +48 -30
- package/package.json +1 -1
package/extensions/context.ts
CHANGED
|
@@ -2,14 +2,9 @@ import {
|
|
|
2
2
|
type ExtensionAPI,
|
|
3
3
|
type ExtensionCommandContext,
|
|
4
4
|
estimateTokens,
|
|
5
|
+
getMarkdownTheme,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import {
|
|
7
|
-
Key,
|
|
8
|
-
matchesKey,
|
|
9
|
-
truncateToWidth,
|
|
10
|
-
visibleWidth,
|
|
11
|
-
wrapTextWithAnsi,
|
|
12
|
-
} from "@earendil-works/pi-tui";
|
|
7
|
+
import { Key, Markdown, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
13
8
|
|
|
14
9
|
export type ContextPart = {
|
|
15
10
|
label: string;
|
|
@@ -33,24 +28,18 @@ function normalizePreviewText(text: string): string {
|
|
|
33
28
|
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
export
|
|
37
|
-
get(width: number): string[];
|
|
38
|
-
invalidate(): void;
|
|
39
|
-
} {
|
|
40
|
-
let cachedWidth: number | undefined;
|
|
41
|
-
let cachedLines: string[] | undefined;
|
|
31
|
+
export type DialogBounds = { left: number; top: number; width: number };
|
|
42
32
|
|
|
33
|
+
/** 1-based terminal hitbox of the [esc] close button on the dialog title row (row 2 of the box). */
|
|
34
|
+
export function escCloseHitbox(bounds: DialogBounds): {
|
|
35
|
+
row: number;
|
|
36
|
+
startCol: number;
|
|
37
|
+
endCol: number;
|
|
38
|
+
} {
|
|
43
39
|
return {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
cachedWidth = width;
|
|
48
|
-
return cachedLines;
|
|
49
|
-
},
|
|
50
|
-
invalidate(): void {
|
|
51
|
-
cachedWidth = undefined;
|
|
52
|
-
cachedLines = undefined;
|
|
53
|
-
},
|
|
40
|
+
row: bounds.top + 2,
|
|
41
|
+
startCol: bounds.left + bounds.width - 5,
|
|
42
|
+
endCol: bounds.left + bounds.width - 1,
|
|
54
43
|
};
|
|
55
44
|
}
|
|
56
45
|
|
|
@@ -65,7 +54,8 @@ export async function showTextPreview(
|
|
|
65
54
|
let scrollOffset = 0;
|
|
66
55
|
let pageSize = 1;
|
|
67
56
|
let totalLines = 1;
|
|
68
|
-
|
|
57
|
+
let escHitbox: { row: number; startCol: number; endCol: number } | undefined;
|
|
58
|
+
const markdownView = new Markdown(content, 0, 0, getMarkdownTheme());
|
|
69
59
|
|
|
70
60
|
const scrollTo = (nextOffset: number): void => {
|
|
71
61
|
scrollOffset = Math.max(0, Math.min(nextOffset, Math.max(0, totalLines - pageSize)));
|
|
@@ -74,7 +64,7 @@ export async function showTextPreview(
|
|
|
74
64
|
|
|
75
65
|
return {
|
|
76
66
|
invalidate() {
|
|
77
|
-
|
|
67
|
+
markdownView.invalidate();
|
|
78
68
|
},
|
|
79
69
|
handleInput(data: string) {
|
|
80
70
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
|
|
@@ -91,12 +81,24 @@ export async function showTextPreview(
|
|
|
91
81
|
const mouse = parseSgrMousePacket(data);
|
|
92
82
|
if (mouse?.final !== "M") return;
|
|
93
83
|
const button = mouseBaseButton(mouse.code);
|
|
84
|
+
if (button === 0 && (mouse.code & 32) === 0) {
|
|
85
|
+
if (
|
|
86
|
+
escHitbox &&
|
|
87
|
+
mouse.row === escHitbox.row &&
|
|
88
|
+
mouse.col >= escHitbox.startCol &&
|
|
89
|
+
mouse.col <= escHitbox.endCol
|
|
90
|
+
) {
|
|
91
|
+
done(undefined);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
94
95
|
if (button === 64) scrollTo(scrollOffset - 3);
|
|
95
96
|
else if (button === 65) scrollTo(scrollOffset + 3);
|
|
96
97
|
}
|
|
97
98
|
},
|
|
98
99
|
render(width: number) {
|
|
99
100
|
const inner = Math.max(1, width - 2);
|
|
101
|
+
const escWidth = visibleWidth("[esc]");
|
|
100
102
|
const bodyInner = Math.max(1, inner - 1);
|
|
101
103
|
const bodyWidth = Math.max(1, bodyInner - 1);
|
|
102
104
|
const terminalHeight = Math.max(1, tui.terminal.rows);
|
|
@@ -107,9 +109,13 @@ export async function showTextPreview(
|
|
|
107
109
|
availableHeight,
|
|
108
110
|
);
|
|
109
111
|
pageSize = Math.max(1, viewportHeight - 6);
|
|
110
|
-
const wrapped =
|
|
112
|
+
const wrapped = markdownView.render(bodyWidth);
|
|
111
113
|
totalLines = wrapped.length;
|
|
112
114
|
scrollOffset = Math.min(scrollOffset, Math.max(0, totalLines - pageSize));
|
|
115
|
+
// Centered overlay with margin 2: mirror TUI resolveOverlayLayout for anchor "center".
|
|
116
|
+
const overlayTop = 2 + Math.floor((availableHeight - viewportHeight) / 2);
|
|
117
|
+
const overlayLeft = Math.floor((Math.max(1, tui.terminal.columns) - width) / 2);
|
|
118
|
+
escHitbox = escCloseHitbox({ left: overlayLeft, top: overlayTop, width });
|
|
113
119
|
const visible = wrapped.slice(scrollOffset, scrollOffset + pageSize);
|
|
114
120
|
const border = (text: string) => theme.fg("border", text);
|
|
115
121
|
const padLine = (text: string, lineWidth = inner): string => {
|
|
@@ -136,11 +142,11 @@ export async function showTextPreview(
|
|
|
136
142
|
});
|
|
137
143
|
const start = totalLines === 0 ? 0 : scrollOffset + 1;
|
|
138
144
|
const end = Math.min(totalLines, scrollOffset + pageSize);
|
|
139
|
-
const status = `${start}-${end} / ${totalLines} lines · ↑↓ PgUp/PgDn Home/End ·
|
|
145
|
+
const status = `${start}-${end} / ${totalLines} lines · ↑↓ PgUp/PgDn Home/End · [esc] close`;
|
|
140
146
|
|
|
141
147
|
return [
|
|
142
148
|
border(`╭${"─".repeat(inner)}╮`),
|
|
143
|
-
`${border("│")}${padLine(` ${theme.bold(theme.fg("accent", title))}
|
|
149
|
+
`${border("│")}${padLine(` ${theme.bold(theme.fg("accent", title))}`, inner - escWidth)}${theme.fg("muted", "[esc]")}${border("│")}`,
|
|
144
150
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
145
151
|
...bodyRows,
|
|
146
152
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
@@ -392,6 +398,7 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
392
398
|
startCol: number;
|
|
393
399
|
endCol: number;
|
|
394
400
|
}> = [];
|
|
401
|
+
let escHitbox: { row: number; startCol: number; endCol: number } | undefined;
|
|
395
402
|
|
|
396
403
|
const padLine = (text: string, width: number): string => {
|
|
397
404
|
const truncated = truncateToWidth(text, width, "…");
|
|
@@ -428,6 +435,15 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
428
435
|
(mouse.code & 32) !== 0
|
|
429
436
|
)
|
|
430
437
|
return;
|
|
438
|
+
if (
|
|
439
|
+
escHitbox &&
|
|
440
|
+
mouse.row === escHitbox.row &&
|
|
441
|
+
mouse.col >= escHitbox.startCol &&
|
|
442
|
+
mouse.col <= escHitbox.endCol
|
|
443
|
+
) {
|
|
444
|
+
done(undefined);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
431
447
|
const hitbox = previewHitboxes.find(
|
|
432
448
|
(candidate) =>
|
|
433
449
|
mouse.row === candidate.row &&
|
|
@@ -444,6 +460,7 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
444
460
|
},
|
|
445
461
|
render(width: number) {
|
|
446
462
|
const inner = Math.max(1, width - 2);
|
|
463
|
+
const escWidth = visibleWidth("[esc]");
|
|
447
464
|
const percent = contextWindow > 0 ? (used / contextWindow) * 100 : 0;
|
|
448
465
|
const title = theme.bold(theme.fg("accent", "Context Usage"));
|
|
449
466
|
const subtitle = `${formatTokens(used)} / ${formatTokens(contextWindow)} tokens (${percent.toFixed(1)}%)`;
|
|
@@ -480,13 +497,13 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
480
497
|
const border = (text: string) => theme.fg("border", text);
|
|
481
498
|
const lines = [
|
|
482
499
|
border(`╭${"─".repeat(inner)}╮`),
|
|
483
|
-
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner)}${border("│")}`,
|
|
500
|
+
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner - escWidth)}${theme.fg("muted", "[esc]")}${border("│")}`,
|
|
484
501
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
485
502
|
`${border("│")}${padLine(` ${segments}`, inner)}${border("│")}`,
|
|
486
503
|
`${border("│")}${" ".repeat(inner)}${border("│")}`,
|
|
487
504
|
...partRows.map((row) => `${border("│")}${row}${border("│")}`),
|
|
488
505
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
489
|
-
`${border("│")}${padLine(theme.fg("dim", " ↑↓ select · Click / Enter to preview ·
|
|
506
|
+
`${border("│")}${padLine(theme.fg("dim", " ↑↓ select · Click / Enter to preview · [esc] close"), inner)}${border("│")}`,
|
|
490
507
|
border(`╰${"─".repeat(inner)}╯`),
|
|
491
508
|
];
|
|
492
509
|
|
|
@@ -499,6 +516,7 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
499
516
|
const overlayTop =
|
|
500
517
|
1 + Math.floor((Math.max(1, terminalHeight - 2) - visibleHeight) / 2);
|
|
501
518
|
const overlayLeft = Math.floor((Math.max(1, tui.terminal.columns) - width) / 2);
|
|
519
|
+
escHitbox = escCloseHitbox({ left: overlayLeft, top: overlayTop, width });
|
|
502
520
|
previewHitboxes = visiblePreviews.flatMap((preview) => {
|
|
503
521
|
const partIndex = allParts.findIndex((part) => part.label === preview.label);
|
|
504
522
|
const line = 5 + partIndex;
|
package/package.json
CHANGED