@herbertgao/pi-extensions 2026.8.7 → 2026.8.9
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 +8 -6
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +24 -0
- package/node_modules/@czottmann/pi-automode/README.md +78 -112
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +18 -14
- package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +44 -0
- package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +5 -0
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +198 -104
- package/node_modules/@czottmann/pi-automode/docs/configuration.md +155 -0
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +55 -14
- package/node_modules/@czottmann/pi-automode/docs/diagnostics.md +90 -0
- package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +61 -26
- package/node_modules/@czottmann/pi-automode/examples/automode.local.json +5 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +172 -18
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +127 -30
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +3 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +273 -31
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/log.ts +60 -5
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +92 -8
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +203 -13
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/state.ts +1 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +11 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/utils.ts +9 -1
- package/node_modules/@czottmann/pi-automode/package.json +9 -2
- package/node_modules/@czottmann/pi-automode/skills/automode-diagnostics/SKILL.md +63 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +8 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +63 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +56 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +78 -16
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +13 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +30 -17
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +29 -0
- package/node_modules/@herbertgao/pi-handoff/README.md +15 -18
- package/node_modules/@herbertgao/pi-handoff/package.json +11 -4
- package/node_modules/@herbertgao/pi-handoff/src/complete-text.ts +33 -0
- package/node_modules/@herbertgao/pi-handoff/src/index.ts +127 -232
- package/node_modules/@herbertgao/pi-handoff/src/session-query.ts +15 -30
- package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +14 -0
- package/node_modules/@herbertgao/pi-mermaid-open/README.md +21 -4
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +11 -0
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/viewer.mjs +305 -0
- package/node_modules/@herbertgao/pi-mermaid-open/package.json +15 -4
- package/node_modules/@herbertgao/pi-mermaid-open/src/index.ts +351 -101
- package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-preferred-thinking/README.md +30 -14
- package/node_modules/@herbertgao/pi-preferred-thinking/package.json +7 -4
- package/node_modules/@herbertgao/pi-preferred-thinking/src/index.ts +140 -85
- package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-recap/README.md +5 -3
- package/node_modules/@herbertgao/pi-recap/package.json +9 -3
- package/node_modules/@herbertgao/pi-recap/src/index.ts +3 -7
- package/node_modules/@herbertgao/pi-recap/src/model-picker.ts +6 -19
- package/node_modules/@herbertgao/pi-recap/src/models.ts +9 -21
- package/node_modules/@herbertgao/pi-recap/src/tui.ts +1 -1
- package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +49 -0
- package/node_modules/@herbertgao/pi-rename/README.md +13 -11
- package/node_modules/@herbertgao/pi-rename/package.json +13 -3
- package/node_modules/@herbertgao/pi-rename/src/index.ts +135 -59
- package/node_modules/@herbertgao/pi-rename/src/models.ts +5 -13
- package/node_modules/@herbertgao/pi-rename/src/naming.ts +2 -6
- package/node_modules/@herbertgao/pi-stash/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +16 -3
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +15 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +16 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +4 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +154 -96
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +7 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +137 -96
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +44 -32
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +56 -34
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +50 -4
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +90 -0
- package/node_modules/pi-lens/README.md +106 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +97 -6
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +16 -6
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +14 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +22 -1
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +8 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +10 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +10 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +19 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +35 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +8 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +110 -23
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +117 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +16 -14
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +8 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +22 -0
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +5 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +7 -2
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +27 -2
- package/node_modules/pi-lens/dist/clients/lsp/client.js +144 -5
- package/node_modules/pi-lens/dist/clients/lsp/index.js +151 -6
- package/node_modules/pi-lens/dist/clients/module-report.js +14 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +18 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +7 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +88 -19
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +44 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +41 -0
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +47 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/index.js +1224 -393
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +10 -3
- package/node_modules/pi-lens/package.json +18 -5
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-web-access/CHANGELOG.md +27 -0
- package/node_modules/pi-web-access/README.md +8 -7
- package/node_modules/pi-web-access/chrome-cookies.ts +96 -20
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/extract.ts +12 -2
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +28 -10
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +43 -14
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/package.json +19 -18
|
@@ -196,12 +196,60 @@ function cachePreview(key: string, entry: PreviewCacheEntry): void {
|
|
|
196
196
|
|
|
197
197
|
export function clearThinkingPreviewCache(): void {
|
|
198
198
|
previewCache.clear();
|
|
199
|
+
expandedWrapCache.clear();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
type ExpandedWrapEntry = { text: string; lines: string[] };
|
|
203
|
+
const expandedWrapCache = new Map<string, ExpandedWrapEntry>();
|
|
204
|
+
export const THINKING_EXPANDED_WRAP_CACHE_MAX = 64;
|
|
205
|
+
|
|
206
|
+
export function thinkingRunKey(messageTimestamp: number, runStartIndex: number): string {
|
|
207
|
+
return `${messageTimestamp}:${runStartIndex}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function expandedWrapKey(messageTimestamp: number, runStartIndex: number, width: number): string {
|
|
211
|
+
return `${thinkingRunKey(messageTimestamp, runStartIndex)}:${width}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function wrapExpandedThinking(
|
|
215
|
+
messageTimestamp: number,
|
|
216
|
+
runStartIndex: number,
|
|
217
|
+
text: string,
|
|
218
|
+
width: number,
|
|
219
|
+
): string[] {
|
|
220
|
+
const key = expandedWrapKey(messageTimestamp, runStartIndex, width);
|
|
221
|
+
const hit = expandedWrapCache.get(key);
|
|
222
|
+
if (hit && hit.text === text) {
|
|
223
|
+
expandedWrapCache.delete(key);
|
|
224
|
+
expandedWrapCache.set(key, hit);
|
|
225
|
+
return hit.lines;
|
|
226
|
+
}
|
|
227
|
+
const lines = wrapTextWithAnsi(text.replace(/\t/g, " "), Math.max(1, width));
|
|
228
|
+
expandedWrapCache.delete(key);
|
|
229
|
+
while (expandedWrapCache.size >= THINKING_EXPANDED_WRAP_CACHE_MAX) {
|
|
230
|
+
const oldest = expandedWrapCache.keys().next().value;
|
|
231
|
+
if (oldest === undefined) break;
|
|
232
|
+
expandedWrapCache.delete(oldest);
|
|
233
|
+
}
|
|
234
|
+
expandedWrapCache.set(key, { text, lines });
|
|
235
|
+
return lines;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function evictExpandedWraps(messageTimestamp: number, runStartIndex: number): void {
|
|
239
|
+
const prefix = `${thinkingRunKey(messageTimestamp, runStartIndex)}:`;
|
|
240
|
+
for (const key of expandedWrapCache.keys()) {
|
|
241
|
+
if (key.startsWith(prefix)) expandedWrapCache.delete(key);
|
|
242
|
+
}
|
|
199
243
|
}
|
|
200
244
|
|
|
201
245
|
export function thinkingPreviewCacheSize(): number {
|
|
202
246
|
return previewCache.size;
|
|
203
247
|
}
|
|
204
248
|
|
|
249
|
+
export function thinkingExpandedWrapCacheSize(): number {
|
|
250
|
+
return expandedWrapCache.size;
|
|
251
|
+
}
|
|
252
|
+
|
|
205
253
|
/** 按可见宽度估算折行数,不走 Text 全量 wrap。无换行长段也能计到隐藏行。 */
|
|
206
254
|
function countWrappedLines(text: string, contentWidth: number): number {
|
|
207
255
|
if (!text) return 0;
|
|
@@ -274,19 +322,19 @@ function hiddenPreviewHint(
|
|
|
274
322
|
return undefined;
|
|
275
323
|
}
|
|
276
324
|
|
|
277
|
-
const expandedThinking = new Set<
|
|
325
|
+
const expandedThinking = new Set<string>();
|
|
278
326
|
|
|
279
327
|
/** 折叠预览 + 展开全文。fullscreen 点击 hint 展开、双击整块收起,对齐工具卡。 */
|
|
280
328
|
export class ThinkingPreviewBlock implements Component {
|
|
281
329
|
private heading: string;
|
|
282
330
|
private text: string;
|
|
283
331
|
private padding: number;
|
|
284
|
-
|
|
332
|
+
readonly messageTimestamp: number;
|
|
333
|
+
readonly runStartIndex: number;
|
|
285
334
|
private style: (text: string) => string;
|
|
286
335
|
private theme: Theme | undefined;
|
|
287
336
|
private _expanded: boolean;
|
|
288
337
|
private hintHovered = false;
|
|
289
|
-
private expandedBody: { width: number; lines: string[] } | undefined;
|
|
290
338
|
private collapsedMemo:
|
|
291
339
|
| {
|
|
292
340
|
width: number;
|
|
@@ -304,14 +352,16 @@ export class ThinkingPreviewBlock implements Component {
|
|
|
304
352
|
messageTimestamp: number,
|
|
305
353
|
style: (text: string) => string,
|
|
306
354
|
theme?: Theme,
|
|
355
|
+
runStartIndex = 0,
|
|
307
356
|
) {
|
|
308
357
|
this.heading = heading;
|
|
309
358
|
this.text = text;
|
|
310
359
|
this.padding = padding;
|
|
311
360
|
this.messageTimestamp = messageTimestamp;
|
|
361
|
+
this.runStartIndex = runStartIndex;
|
|
312
362
|
this.style = style;
|
|
313
363
|
this.theme = theme;
|
|
314
|
-
this._expanded = expandedThinking.has(messageTimestamp);
|
|
364
|
+
this._expanded = expandedThinking.has(thinkingRunKey(messageTimestamp, runStartIndex));
|
|
315
365
|
}
|
|
316
366
|
|
|
317
367
|
private paint(color: string, text: string): string {
|
|
@@ -327,10 +377,11 @@ export class ThinkingPreviewBlock implements Component {
|
|
|
327
377
|
setExpanded(expanded: boolean): void {
|
|
328
378
|
if (this._expanded !== expanded) this.collapsedMemo = undefined;
|
|
329
379
|
this._expanded = expanded;
|
|
330
|
-
|
|
380
|
+
const key = thinkingRunKey(this.messageTimestamp, this.runStartIndex);
|
|
381
|
+
if (expanded) expandedThinking.add(key);
|
|
331
382
|
else {
|
|
332
|
-
expandedThinking.delete(
|
|
333
|
-
this.
|
|
383
|
+
expandedThinking.delete(key);
|
|
384
|
+
evictExpandedWraps(this.messageTimestamp, this.runStartIndex);
|
|
334
385
|
}
|
|
335
386
|
}
|
|
336
387
|
|
|
@@ -364,13 +415,10 @@ export class ThinkingPreviewBlock implements Component {
|
|
|
364
415
|
return { lines: [], hiddenLines: 0 };
|
|
365
416
|
}
|
|
366
417
|
if (this._expanded) {
|
|
367
|
-
|
|
368
|
-
this.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
return { lines: this.expandedBody.lines, hiddenLines: 0 };
|
|
418
|
+
return {
|
|
419
|
+
lines: wrapExpandedThinking(this.messageTimestamp, this.runStartIndex, this.text, width),
|
|
420
|
+
hiddenLines: 0,
|
|
421
|
+
};
|
|
374
422
|
}
|
|
375
423
|
const preview = layoutThinkingPreview(this.text, width, padding);
|
|
376
424
|
return { lines: preview.visible, hiddenLines: preview.hiddenLines };
|
|
@@ -426,7 +474,6 @@ export class ThinkingPreviewBlock implements Component {
|
|
|
426
474
|
|
|
427
475
|
invalidate() {
|
|
428
476
|
this.collapsedMemo = undefined;
|
|
429
|
-
this.expandedBody = undefined;
|
|
430
477
|
}
|
|
431
478
|
}
|
|
432
479
|
|
|
@@ -710,6 +757,7 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
710
757
|
message.timestamp,
|
|
711
758
|
thinkingStyle,
|
|
712
759
|
activeTheme,
|
|
760
|
+
runStartIndex,
|
|
713
761
|
),
|
|
714
762
|
);
|
|
715
763
|
|
|
@@ -9,7 +9,7 @@ import { Key, Markdown, matchesKey, truncateToWidth, visibleWidth } from "@earen
|
|
|
9
9
|
export type ContextPart = {
|
|
10
10
|
label: string;
|
|
11
11
|
tokens: number;
|
|
12
|
-
color: "accent" | "success" | "warning" | "muted" | "dim";
|
|
12
|
+
color: "accent" | "success" | "warning" | "muted" | "dim" | "error";
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
type PreviewKey = "systemPrompt" | "tools" | "toolResults" | "contextFiles" | "skills";
|
|
@@ -43,11 +43,16 @@ export function escCloseHitbox(bounds: DialogBounds): {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let
|
|
46
|
+
let activeContextOverlays = 0;
|
|
47
47
|
|
|
48
|
-
/** fullscreen
|
|
48
|
+
/** fullscreen 输入包装用于把鼠标事件继续传给当前 context overlay。 */
|
|
49
49
|
export function hasActiveTextPreview(): boolean {
|
|
50
|
-
return
|
|
50
|
+
return activeContextOverlays > 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** fullscreen overlays fall back to 1002; restore motion events for hover states. */
|
|
54
|
+
function ensureFullscreenMouseMotion(tui: any): void {
|
|
55
|
+
if (tui.mode === "fullscreen") tui.terminal?.write?.("\x1b[?1003h\x1b[?1006h");
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export async function showTextPreview(
|
|
@@ -56,10 +61,11 @@ export async function showTextPreview(
|
|
|
56
61
|
rawContent: string,
|
|
57
62
|
): Promise<void> {
|
|
58
63
|
const content = normalizePreviewText(rawContent);
|
|
59
|
-
|
|
64
|
+
activeContextOverlays++;
|
|
60
65
|
try {
|
|
61
66
|
await ctx.ui.custom(
|
|
62
67
|
(tui, theme, _keybindings, done) => {
|
|
68
|
+
ensureFullscreenMouseMotion(tui);
|
|
63
69
|
let scrollOffset = 0;
|
|
64
70
|
let pageSize = 1;
|
|
65
71
|
let totalLines = 1;
|
|
@@ -249,7 +255,7 @@ export async function showTextPreview(
|
|
|
249
255
|
},
|
|
250
256
|
);
|
|
251
257
|
} finally {
|
|
252
|
-
|
|
258
|
+
activeContextOverlays--;
|
|
253
259
|
}
|
|
254
260
|
}
|
|
255
261
|
|
|
@@ -424,7 +430,7 @@ export function collectContextBreakdown(ctx: ExtensionCommandContext): ContextBr
|
|
|
424
430
|
const parts: ContextPart[] = [
|
|
425
431
|
{ label: "System prompt", tokens: baseSystem, color: "accent" },
|
|
426
432
|
{ label: "Tools", tokens: toolTokens, color: "success" },
|
|
427
|
-
{ label: "
|
|
433
|
+
{ label: "Memory", tokens: contextFileTokens, color: "error" },
|
|
428
434
|
{ label: "Skills", tokens: skillTokens, color: "warning" },
|
|
429
435
|
{ label: "User messages", tokens: user, color: "muted" },
|
|
430
436
|
{ label: "Assistant messages", tokens: assistant, color: "accent" },
|
|
@@ -448,10 +454,12 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
448
454
|
const breakdown = collectContextBreakdown(ctx);
|
|
449
455
|
const estimated = breakdown.parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
450
456
|
const fixedParts = breakdown.parts.filter(
|
|
451
|
-
(part) =>
|
|
457
|
+
(part) =>
|
|
458
|
+
part.label === "System prompt" || part.label === "Memory" || part.label === "Tools",
|
|
452
459
|
);
|
|
453
460
|
const variableParts = breakdown.parts.filter(
|
|
454
|
-
(part) =>
|
|
461
|
+
(part) =>
|
|
462
|
+
part.label !== "System prompt" && part.label !== "Memory" && part.label !== "Tools",
|
|
455
463
|
);
|
|
456
464
|
const orderedParts = [...fixedParts, ...variableParts];
|
|
457
465
|
const fixedTokens = fixedParts.reduce((sum, part) => sum + part.tokens, 0);
|
|
@@ -527,9 +535,9 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
527
535
|
},
|
|
528
536
|
{
|
|
529
537
|
key: "contextFiles",
|
|
530
|
-
label: "
|
|
531
|
-
title: "
|
|
532
|
-
content: contextFilesContent || "No
|
|
538
|
+
label: "Memory",
|
|
539
|
+
title: "Memory Files",
|
|
540
|
+
content: contextFilesContent || "No memory files loaded.",
|
|
533
541
|
},
|
|
534
542
|
{
|
|
535
543
|
key: "skills",
|
|
@@ -549,8 +557,10 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
549
557
|
let selectedPreviewIndex = 0;
|
|
550
558
|
|
|
551
559
|
while (true) {
|
|
552
|
-
|
|
560
|
+
activeContextOverlays++;
|
|
561
|
+
const actionPromise = ctx.ui.custom(
|
|
553
562
|
(tui, theme, _keybindings, done) => {
|
|
563
|
+
ensureFullscreenMouseMotion(tui);
|
|
554
564
|
let previewHitboxes: Array<{
|
|
555
565
|
key: PreviewKey;
|
|
556
566
|
row: number;
|
|
@@ -558,6 +568,8 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
558
568
|
endCol: number;
|
|
559
569
|
}> = [];
|
|
560
570
|
let escHitbox: { row: number; startCol: number; endCol: number } | undefined;
|
|
571
|
+
let escHovered = false;
|
|
572
|
+
let hoveredKey: PreviewKey | undefined;
|
|
561
573
|
|
|
562
574
|
const padLine = (text: string, width: number): string => {
|
|
563
575
|
const truncated = truncateToWidth(text, width, "…");
|
|
@@ -588,27 +600,32 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
588
600
|
}
|
|
589
601
|
|
|
590
602
|
const mouse = parseSgrMousePacket(data);
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
mouseBaseButton(mouse.code) !== 0 ||
|
|
594
|
-
(mouse.code & 32) !== 0
|
|
595
|
-
)
|
|
596
|
-
return;
|
|
597
|
-
if (
|
|
603
|
+
if (!mouse || mouse.final !== "M") return;
|
|
604
|
+
const overEsc = Boolean(
|
|
598
605
|
escHitbox &&
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
)
|
|
603
|
-
done(undefined);
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
+
mouse.row === escHitbox.row &&
|
|
607
|
+
mouse.col >= escHitbox.startCol &&
|
|
608
|
+
mouse.col <= escHitbox.endCol,
|
|
609
|
+
);
|
|
606
610
|
const hitbox = previewHitboxes.find(
|
|
607
611
|
(candidate) =>
|
|
608
612
|
mouse.row === candidate.row &&
|
|
609
613
|
mouse.col >= candidate.startCol &&
|
|
610
614
|
mouse.col <= candidate.endCol,
|
|
611
615
|
);
|
|
616
|
+
if ((mouse.code & 32) !== 0) {
|
|
617
|
+
if (overEsc !== escHovered || hitbox?.key !== hoveredKey) {
|
|
618
|
+
escHovered = overEsc;
|
|
619
|
+
hoveredKey = hitbox?.key;
|
|
620
|
+
tui.requestRender();
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
if (mouseBaseButton(mouse.code) !== 0) return;
|
|
625
|
+
if (overEsc) {
|
|
626
|
+
done(undefined);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
612
629
|
if (hitbox) {
|
|
613
630
|
selectedPreviewIndex = Math.max(
|
|
614
631
|
0,
|
|
@@ -643,6 +660,9 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
643
660
|
Math.max(...allParts.map((part) => part.label.length)),
|
|
644
661
|
);
|
|
645
662
|
const selectedLabel = visiblePreviews[selectedPreviewIndex]?.label;
|
|
663
|
+
const hoverLabel = visiblePreviews.find(
|
|
664
|
+
(preview) => preview.key === hoveredKey,
|
|
665
|
+
)?.label;
|
|
646
666
|
const partRows = allParts.map((part) => {
|
|
647
667
|
const pct = contextWindow > 0 ? (part.tokens / contextWindow) * 100 : 0;
|
|
648
668
|
const swatch = theme.fg(part.color, "■");
|
|
@@ -651,12 +671,13 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
651
671
|
const selected = part.label === selectedLabel;
|
|
652
672
|
const prefix = selected ? "› " : " ";
|
|
653
673
|
const row = padLine(`${prefix}${swatch} ${label} ${amount}`, inner);
|
|
654
|
-
|
|
674
|
+
if (selected) return theme.bg("selectedBg", row);
|
|
675
|
+
return part.label === hoverLabel ? theme.bg("customMessageBg", row) : row;
|
|
655
676
|
});
|
|
656
677
|
const border = (text: string) => theme.fg("border", text);
|
|
657
678
|
const lines = [
|
|
658
679
|
border(`╭${"─".repeat(inner)}╮`),
|
|
659
|
-
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner - escWidth)}${theme.fg("muted", "[esc]")}${border("│")}`,
|
|
680
|
+
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner - escWidth)}${theme.fg(escHovered ? "text" : "muted", "[esc]")}${border("│")}`,
|
|
660
681
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
661
682
|
`${border("│")}${padLine(` ${segments}`, inner)}${border("│")}`,
|
|
662
683
|
`${border("│")}${" ".repeat(inner)}${border("│")}`,
|
|
@@ -706,6 +727,12 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
706
727
|
},
|
|
707
728
|
},
|
|
708
729
|
);
|
|
730
|
+
let action;
|
|
731
|
+
try {
|
|
732
|
+
action = await actionPromise;
|
|
733
|
+
} finally {
|
|
734
|
+
activeContextOverlays--;
|
|
735
|
+
}
|
|
709
736
|
|
|
710
737
|
if (!action) break;
|
|
711
738
|
const preview = previewByKey.get(action as PreviewKey);
|
|
@@ -17,6 +17,9 @@ import { showMoreHintText } from "./show-more-hint.ts";
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const MESSAGE_DISPLAY_PATCH = Symbol.for("pi.ccstyle.message-display-patch");
|
|
20
|
+
const MESSAGE_DISPLAY_ORIGINAL_BG = Symbol.for("pi.ccstyle.message-display-original-bg");
|
|
21
|
+
const MESSAGE_DISPLAY_ORIGINAL_PADDING = Symbol.for("pi.ccstyle.message-display-original-padding");
|
|
22
|
+
const MESSAGE_DISPLAY_ORIGINAL_SAVED = Symbol.for("pi.ccstyle.message-display-original-saved");
|
|
20
23
|
|
|
21
24
|
// 与 renderer/index.ts renderCall 的成功勾一致:亮绿 ✓(truecolor ANSI)。
|
|
22
25
|
const BRIGHT_GREEN = "\x1b[38;2;80;220;100m";
|
|
@@ -55,21 +58,54 @@ const BRANCH_KIND: DisplayKind = {
|
|
|
55
58
|
body: (component) => String(component.message?.summary ?? ""),
|
|
56
59
|
};
|
|
57
60
|
|
|
58
|
-
function
|
|
61
|
+
function ensureHintHover(component: any): void {
|
|
62
|
+
if (typeof component.setHintHovered === "function") return;
|
|
63
|
+
component.setHintHovered = function (this: any, hovered: boolean) {
|
|
64
|
+
if (this.hintHovered === hovered) return;
|
|
65
|
+
this.hintHovered = hovered;
|
|
66
|
+
this.invalidate?.();
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function rememberMessageDisplay(component: any): void {
|
|
71
|
+
if (component[MESSAGE_DISPLAY_ORIGINAL_SAVED]) return;
|
|
72
|
+
component[MESSAGE_DISPLAY_ORIGINAL_BG] = component.bgFn;
|
|
73
|
+
component[MESSAGE_DISPLAY_ORIGINAL_PADDING] = [component.paddingX, component.paddingY];
|
|
74
|
+
component[MESSAGE_DISPLAY_ORIGINAL_SAVED] = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function restoreMessageDisplay(component: any): void {
|
|
78
|
+
if (!component[MESSAGE_DISPLAY_ORIGINAL_SAVED]) return;
|
|
79
|
+
const [paddingX, paddingY] = component[MESSAGE_DISPLAY_ORIGINAL_PADDING];
|
|
80
|
+
component.paddingX = paddingX;
|
|
81
|
+
component.paddingY = paddingY;
|
|
82
|
+
component.setBgFn?.(component[MESSAGE_DISPLAY_ORIGINAL_BG]);
|
|
83
|
+
delete component[MESSAGE_DISPLAY_ORIGINAL_BG];
|
|
84
|
+
delete component[MESSAGE_DISPLAY_ORIGINAL_PADDING];
|
|
85
|
+
delete component[MESSAGE_DISPLAY_ORIGINAL_SAVED];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function renderCcstyle(component: any, kind: DisplayKind): boolean {
|
|
59
89
|
const theme = displayTheme;
|
|
60
|
-
if (!theme) return; //
|
|
90
|
+
if (!theme) return false; // 主题未就绪时回退原生渲染
|
|
91
|
+
rememberMessageDisplay(component);
|
|
61
92
|
if (component.bgFn) component.setBgFn?.(undefined); // 与工具调用一致,去掉灰底
|
|
62
|
-
|
|
63
|
-
component._ccstyleOriginalPaddingY = component.paddingY;
|
|
64
|
-
component.paddingY = 0; // 工具组件 paddingY=0;原生 Box 默认 1,会上下各留一个空行
|
|
65
|
-
}
|
|
93
|
+
component.paddingY = 0; // 工具组件 paddingY=0;原生 Box 默认 1,会上下各留一个空行
|
|
66
94
|
component.clear();
|
|
95
|
+
ensureHintHover(component);
|
|
67
96
|
const icon = `${BRIGHT_GREEN}✓${ANSI_FG_RESET}`; // 已完成消息,等同工具成功态
|
|
68
97
|
const title = theme.fg("toolTitle", kind.title(component));
|
|
69
98
|
if (!component.expanded) {
|
|
70
|
-
const
|
|
99
|
+
const hovered = component.hintHovered === true;
|
|
100
|
+
const hint = `${theme.fg("dim", " • ")}${theme.fg(hovered ? "text" : "dim", showMoreHintText())}`;
|
|
71
101
|
component.addChild(new Text(`${icon} ${title}${hint}`, 0, 0));
|
|
72
|
-
return;
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
// 展开卡与 tool 一致:userMessageBg + 上下左右 1 格
|
|
105
|
+
component.paddingX = 1;
|
|
106
|
+
component.paddingY = 1;
|
|
107
|
+
if (typeof theme.bg === "function" && typeof component.setBgFn === "function") {
|
|
108
|
+
component.setBgFn((text: string) => theme.bg("userMessageBg", text));
|
|
73
109
|
}
|
|
74
110
|
component.addChild(new Text(`${icon} ${title}`, 0, 0));
|
|
75
111
|
component.addChild(new Spacer(1));
|
|
@@ -78,6 +114,7 @@ function renderCcstyle(component: any, kind: DisplayKind): void {
|
|
|
78
114
|
color: (text: string) => theme.fg("customMessageText", text),
|
|
79
115
|
}),
|
|
80
116
|
);
|
|
117
|
+
return true;
|
|
81
118
|
}
|
|
82
119
|
|
|
83
120
|
type PatchEntry = {
|
|
@@ -91,28 +128,42 @@ export function installMessageDisplayRendering(): () => void {
|
|
|
91
128
|
const host = globalThis as any;
|
|
92
129
|
const previous = host[MESSAGE_DISPLAY_PATCH];
|
|
93
130
|
if (previous) previous.dispose();
|
|
94
|
-
const patch: {
|
|
131
|
+
const patch: {
|
|
132
|
+
active: boolean;
|
|
133
|
+
entries: PatchEntry[];
|
|
134
|
+
components: Set<WeakRef<any>>;
|
|
135
|
+
tracked: WeakSet<object>;
|
|
136
|
+
dispose: () => void;
|
|
137
|
+
} = {
|
|
95
138
|
active: true,
|
|
96
139
|
entries: [],
|
|
140
|
+
components: new Set(),
|
|
141
|
+
tracked: new WeakSet(),
|
|
97
142
|
dispose: () => {},
|
|
98
143
|
};
|
|
144
|
+
const track = (component: Record<PropertyKey, unknown>): void => {
|
|
145
|
+
for (const ref of patch.components) {
|
|
146
|
+
if (!ref.deref()) patch.components.delete(ref);
|
|
147
|
+
}
|
|
148
|
+
if (patch.tracked.has(component)) return;
|
|
149
|
+
patch.tracked.add(component);
|
|
150
|
+
patch.components.add(new WeakRef(component));
|
|
151
|
+
};
|
|
99
152
|
const installOne = (ComponentClass: any, kind: DisplayKind): void => {
|
|
100
153
|
const prototype = ComponentClass.prototype;
|
|
101
154
|
const original = prototype.updateDisplay;
|
|
102
155
|
const installed = function (this: any) {
|
|
103
156
|
if (patch.active && config.mode !== "off") {
|
|
104
157
|
try {
|
|
105
|
-
renderCcstyle(this, kind)
|
|
106
|
-
|
|
158
|
+
if (renderCcstyle(this, kind)) {
|
|
159
|
+
track(this);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
107
162
|
} catch {
|
|
108
163
|
// 渲染失败回退原生
|
|
109
164
|
}
|
|
110
165
|
}
|
|
111
|
-
|
|
112
|
-
if (this._ccstyleOriginalPaddingY !== undefined) {
|
|
113
|
-
this.paddingY = this._ccstyleOriginalPaddingY;
|
|
114
|
-
delete this._ccstyleOriginalPaddingY;
|
|
115
|
-
}
|
|
166
|
+
restoreMessageDisplay(this);
|
|
116
167
|
original.call(this);
|
|
117
168
|
};
|
|
118
169
|
prototype.updateDisplay = installed;
|
|
@@ -128,6 +179,17 @@ export function installMessageDisplayRendering(): () => void {
|
|
|
128
179
|
entry.prototype.updateDisplay = entry.original;
|
|
129
180
|
}
|
|
130
181
|
}
|
|
182
|
+
for (const ref of patch.components) {
|
|
183
|
+
const component = ref.deref();
|
|
184
|
+
if (!component) continue;
|
|
185
|
+
try {
|
|
186
|
+
restoreMessageDisplay(component);
|
|
187
|
+
component.updateDisplay();
|
|
188
|
+
} catch {
|
|
189
|
+
// 单个陈旧组件不应阻断其余原型与组件恢复
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
patch.components.clear();
|
|
131
193
|
if (host[MESSAGE_DISPLAY_PATCH] === patch) delete host[MESSAGE_DISPLAY_PATCH];
|
|
132
194
|
};
|
|
133
195
|
host[MESSAGE_DISPLAY_PATCH] = patch;
|
|
@@ -54,6 +54,16 @@ export function setHoveredThinking(block: ThinkingPreviewBlock | null): boolean
|
|
|
54
54
|
return true;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
let hoveredMessageDisplay: any = null;
|
|
58
|
+
|
|
59
|
+
export function setHoveredMessageDisplay(component: any): boolean {
|
|
60
|
+
if (component === hoveredMessageDisplay) return false;
|
|
61
|
+
hoveredMessageDisplay?.setHintHovered?.(false);
|
|
62
|
+
hoveredMessageDisplay = component;
|
|
63
|
+
component?.setHintHovered?.(true);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
let hoveredToolIoView: ExpandedToolIoView | null = null;
|
|
58
68
|
let hoveredToolIoSection: ToolIoSection | null = null;
|
|
59
69
|
|
|
@@ -110,6 +120,7 @@ export type FullscreenHoverTarget =
|
|
|
110
120
|
| { kind: "button" }
|
|
111
121
|
| { kind: "group"; component: ToolGroupComponent }
|
|
112
122
|
| { kind: "thinking"; component: ThinkingPreviewBlock }
|
|
123
|
+
| { kind: "message"; component: any }
|
|
113
124
|
| { kind: "assistant"; component: any }
|
|
114
125
|
| {
|
|
115
126
|
kind: "tool";
|
|
@@ -133,6 +144,8 @@ export function applyFullscreenHover(tui: any, target: FullscreenHoverTarget | n
|
|
|
133
144
|
if (setHoveredToolGroup(nextGroup)) changed = true;
|
|
134
145
|
const nextThinking = target?.kind === "thinking" ? target.component : null;
|
|
135
146
|
if (setHoveredThinking(nextThinking)) changed = true;
|
|
147
|
+
const nextMessage = target?.kind === "message" ? target.component : null;
|
|
148
|
+
if (setHoveredMessageDisplay(nextMessage)) changed = true;
|
|
136
149
|
const nextAssistant = target?.kind === "assistant" ? target.component : null;
|
|
137
150
|
if (setHoveredCompactAssistant(nextAssistant)) changed = true;
|
|
138
151
|
const nextView = target?.kind === "tool" ? target.view : null;
|