@herbertgao/pi-extensions 2026.8.7 → 2026.8.8
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 +7 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +63 -15
- 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 +17 -6
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- 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 +14 -0
- package/node_modules/pi-web-access/README.md +6 -5
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- 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/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +19 -4
- 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 +9 -9
package/README.md
CHANGED
|
@@ -11,18 +11,20 @@ pi install npm:@herbertgao/pi-extensions
|
|
|
11
11
|
The package bundles 11 active `@herbertgao/*` child packages, including `@herbertgao/pi-cc-extensions`, plus the following upstream packages under their original names:
|
|
12
12
|
|
|
13
13
|
- `@dietrichgebert/ponytail@4.9.0`
|
|
14
|
-
- `@juicesharp/rpiv-ask-user-question@2.
|
|
15
|
-
- `@narumitw/pi-btw@0.55.
|
|
16
|
-
- `@pi-plugins/fast-mode@0.1.
|
|
14
|
+
- `@juicesharp/rpiv-ask-user-question@2.7.0`
|
|
15
|
+
- `@narumitw/pi-btw@0.55.1`
|
|
16
|
+
- `@pi-plugins/fast-mode@0.1.10`
|
|
17
17
|
- `pi-mcp-adapter@2.27.0`
|
|
18
18
|
- `pi-footer@0.5.1`
|
|
19
|
-
- `pi-lens@4.1.
|
|
20
|
-
- `pi-web-access@0.24.
|
|
19
|
+
- `pi-lens@4.1.1`
|
|
20
|
+
- `pi-web-access@0.24.1`
|
|
21
21
|
- `remote-pi@0.7.0`
|
|
22
22
|
- `@czottmann/pi-automode@1.11.0`
|
|
23
23
|
|
|
24
24
|
Pi loads their extensions and skills through `node_modules/` paths inside one package root. The upstream companions are pinned and bundled, not forked or renamed.
|
|
25
25
|
|
|
26
|
+
`pi-lens@4.1.1` improves diagnostics, cache, and LSP observability; prevents session-switch crashes; restores oxlint warnings; speeds large ignored-tree scans; and retries failed log writes once.
|
|
27
|
+
|
|
26
28
|
If `pi-footer` was installed separately before upgrading to an aggregate release that includes it, remove the standalone source shown by `pi list` so only the bundled copy loads. For the pinned standalone install used while preparing this integration:
|
|
27
29
|
|
|
28
30
|
```bash
|
|
@@ -101,5 +101,5 @@ bun run typecheck
|
|
|
101
101
|
|
|
102
102
|
## Credits
|
|
103
103
|
|
|
104
|
-
- Derived from [`minuque/pi-cc-extensions`](https://github.com/minuque/pi-cc-extensions) under the MIT license.
|
|
104
|
+
- Derived from [`minuque/pi-cc-extensions` v0.8.64](https://github.com/minuque/pi-cc-extensions/releases/tag/v0.8.64) (`4709081`) under the MIT license.
|
|
105
105
|
- Rich diffs are adapted from [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display) (MIT). See [`extensions/renderer/tool/diff/ATTRIBUTION.md`](./extensions/renderer/tool/diff/ATTRIBUTION.md).
|
|
@@ -101,5 +101,5 @@ bun run typecheck
|
|
|
101
101
|
|
|
102
102
|
## 致谢
|
|
103
103
|
|
|
104
|
-
- 基于 MIT 许可的 [`minuque/pi-cc-extensions`](https://github.com/minuque/pi-cc-extensions)
|
|
104
|
+
- 基于 MIT 许可的 [`minuque/pi-cc-extensions` v0.8.64](https://github.com/minuque/pi-cc-extensions/releases/tag/v0.8.64)(`4709081`)衍生维护。
|
|
105
105
|
- Rich diff 改编自 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display)(MIT);详见 [`extensions/renderer/tool/diff/ATTRIBUTION.md`](./extensions/renderer/tool/diff/ATTRIBUTION.md)。
|
|
@@ -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
|
|
|
@@ -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: object): 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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ThinkingPreviewBlock } from "../../feature/compact-thinking.ts";
|
|
1
|
+
import { ThinkingPreviewBlock, thinkingRunKey } from "../../feature/compact-thinking.ts";
|
|
2
2
|
import { hasActiveTextPreview, showTextPreview } from "../../feature/context.ts";
|
|
3
3
|
import { ToolGroupComponent } from "../tool/grouping.ts";
|
|
4
4
|
import { isCompactAssistantComponent, setHoveredCompactAssistant } from "../compact-mode.ts";
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
setHoveredToolCallId,
|
|
59
59
|
setHoveredToolGroup,
|
|
60
60
|
setHoveredThinking,
|
|
61
|
+
setHoveredMessageDisplay,
|
|
61
62
|
setHoveredToolIo,
|
|
62
63
|
type FullscreenHoverTarget,
|
|
63
64
|
} from "./hover.ts";
|
|
@@ -155,13 +156,20 @@ function updateToolSummaryHover(tui: any, packet: SgrMousePacket): void {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
const EXPAND_PANEL_DOUBLE_CLICK_MS = 400;
|
|
158
|
-
let lastExpandPanelClick: {
|
|
159
|
+
let lastExpandPanelClick: { id: unknown; at: number } | null = null;
|
|
160
|
+
|
|
161
|
+
function expandPanelIdentity(card: any): unknown {
|
|
162
|
+
return card instanceof ThinkingPreviewBlock
|
|
163
|
+
? thinkingRunKey(card.messageTimestamp, card.runStartIndex)
|
|
164
|
+
: card;
|
|
165
|
+
}
|
|
159
166
|
|
|
160
167
|
function isExpandPanelDoubleClick(card: any): boolean {
|
|
161
168
|
const now = Date.now();
|
|
169
|
+
const id = expandPanelIdentity(card);
|
|
162
170
|
const prev = lastExpandPanelClick;
|
|
163
|
-
lastExpandPanelClick = {
|
|
164
|
-
return Boolean(prev && prev.
|
|
171
|
+
lastExpandPanelClick = { id, at: now };
|
|
172
|
+
return Boolean(prev && prev.id === id && now - prev.at <= EXPAND_PANEL_DOUBLE_CLICK_MS);
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
function clearExpandPanelDoubleClick(): void {
|
|
@@ -174,6 +182,7 @@ function collapseExpandedCard(tui: any, card: any): boolean {
|
|
|
174
182
|
setHoveredToolCallId(null);
|
|
175
183
|
setHoveredToolGroup(null);
|
|
176
184
|
setHoveredThinking(null);
|
|
185
|
+
setHoveredMessageDisplay(null);
|
|
177
186
|
setHoveredToolIo(null, null);
|
|
178
187
|
setHoveredCompactAssistant(null);
|
|
179
188
|
card.invalidate?.();
|
|
@@ -346,6 +355,7 @@ function handleFullscreenToolClick(tui: any, packet: SgrMousePacket): boolean {
|
|
|
346
355
|
setHoveredToolCallId(null);
|
|
347
356
|
setHoveredToolGroup(null);
|
|
348
357
|
setHoveredThinking(null);
|
|
358
|
+
setHoveredMessageDisplay(null);
|
|
349
359
|
setHoveredToolIo(null, null);
|
|
350
360
|
setHoveredCompactAssistant(null);
|
|
351
361
|
card.invalidate?.();
|
|
@@ -417,8 +427,7 @@ function handleFullscreenToolHover(tui: any, packet: SgrMousePacket): void {
|
|
|
417
427
|
// compact 摘要行:折叠时仅提示文字高亮,展开卡整体高亮。
|
|
418
428
|
if (component.expanded || overHint) target = { kind: "assistant", component };
|
|
419
429
|
} else if (isMessageDisplayComponent(component)) {
|
|
420
|
-
|
|
421
|
-
if (component.expanded || overHint) target = { kind: "assistant", component };
|
|
430
|
+
if (overHint) target = { kind: "message", component };
|
|
422
431
|
}
|
|
423
432
|
}
|
|
424
433
|
}
|
|
@@ -815,6 +824,7 @@ export function teardownToolMouseInteraction(
|
|
|
815
824
|
setHoveredToolCallId(null);
|
|
816
825
|
setHoveredToolGroup(null);
|
|
817
826
|
setHoveredThinking(null);
|
|
827
|
+
setHoveredMessageDisplay(null);
|
|
818
828
|
setHoveredToolIo(null, null);
|
|
819
829
|
setHoveredCompactAssistant(null);
|
|
820
830
|
clearExpandPanelDoubleClick();
|
|
@@ -842,6 +852,7 @@ export function teardownToolMouseInteraction(
|
|
|
842
852
|
export function resetToolHoverState(): void {
|
|
843
853
|
setHoveredToolCallId(null);
|
|
844
854
|
setHoveredThinking(null);
|
|
855
|
+
setHoveredMessageDisplay(null);
|
|
845
856
|
setHoveredCompactAssistant(null);
|
|
846
857
|
setScrollButtonVisible(false);
|
|
847
858
|
setScrollButtonHovered(false);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbertgao/pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.57",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A Pi productivity suite with Claude Code-style UI, context inspection, and agent/session references",
|
|
6
6
|
"type": "module",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
},
|
|
70
70
|
"x-upstream": {
|
|
71
71
|
"package": "pi-cc-extensions",
|
|
72
|
-
"version": "0.8.
|
|
72
|
+
"version": "0.8.64",
|
|
73
73
|
"repository": "https://github.com/minuque/pi-cc-extensions",
|
|
74
|
-
"commit": "
|
|
74
|
+
"commit": "4709081a28255b8bdfe610620a2a9ee8951013d6"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -25,7 +25,7 @@ Nothing to set up — the tool is live as soon as Pi restarts. Hand the model a
|
|
|
25
25
|
|
|
26
26
|
> Add caching to the API client.
|
|
27
27
|
|
|
28
|
-
Rather than picking a strategy on your behalf, the model calls `ask_user_question` and a dialog takes over the bottom of your terminal. Move with `↑`/`↓`, choose with `Enter`, press `n` to attach a note
|
|
28
|
+
Rather than picking a strategy on your behalf, the model calls `ask_user_question` and a dialog takes over the bottom of your terminal. Move with `↑`/`↓`, choose with `Enter`, press `n` to attach a note to a question — or a global note to the whole questionnaire from the Submit tab — or land on the `Type something.` row to answer in your own words. While typing, `Shift+Enter` adds a line, `Ctrl+G` opens Pi's configured external editor, and `Ctrl+U` clears the draft; browsing another option and returning keeps what you wrote. `Esc` abandons the questionnaire entirely.
|
|
29
29
|
|
|
30
30
|
When the questionnaire begins waiting in an interactive TTY, it emits one standard terminal BEL (`\x07`). Your terminal configuration determines whether that appears as an audible alert, a visual alert, or nothing; redirected and non-TTY output is untouched.
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ When the questionnaire begins waiting in an interactive TTY, it emits one standa
|
|
|
33
33
|
|
|
34
34
|
When the model asks several things at once, `Tab` moves between them and a Submit tab reviews everything before it goes back:
|
|
35
35
|
|
|
36
|
-

|
|
37
37
|
|
|
38
38
|
## What you get
|
|
39
39
|
|
|
@@ -41,9 +41,9 @@ When the model asks several things at once, `Tab` moves between them and a Submi
|
|
|
41
41
|
- **You can always answer in your own words** — a `Type something.` row is appended to every question, single- or multi-select, widens to the full pane while you type, keeps its multiline draft visible in that row while you browse, and supports Pi's `Shift+Enter` newline and `Ctrl+G` external-editor flows.
|
|
42
42
|
- **Compare real artifacts, not just labels** — an option can carry a markdown `preview` (ASCII mockup, code, diagram, config) that renders in a bordered box beside the option list.
|
|
43
43
|
- **One interruption, not five** — up to four questions arrive in a single tabbed dialog, and the Submit tab lists your answers and names anything still blank before you commit.
|
|
44
|
-
- **Notes on any answer** — `n` opens a multiline note editor on any question tab
|
|
44
|
+
- **Notes on any answer — or on all of them** — `n` opens a multiline note editor on any question tab, and on the Submit tab it opens one global note for the whole questionnaire. Per-question notes reach the model as `user notes: <text>`, the global note as `global note: <text>`; neither marks a question answered.
|
|
45
45
|
- **Read the transcript behind the dialog** — `Ctrl+]` collapses the overlay so you can scroll the conversation, then brings it back with your answers intact.
|
|
46
|
-
- **Works outside the terminal too** — in RPC and ACP hosts such as the VS Code pendant or Zed the questionnaire walks through the host's native dialogs, and in non-interactive runs the tool is removed from the model's tool list instead of failing every call.
|
|
46
|
+
- **Works outside the terminal too** — in RPC and ACP hosts such as the VS Code pendant or Zed the questionnaire walks through the host's native dialogs (notes are terminal-only and do not carry over), and in non-interactive runs the tool is removed from the model's tool list instead of failing every call.
|
|
47
47
|
|
|
48
48
|
## Configuration
|
|
49
49
|
|