pi-cc-extensions 0.8.53 → 0.8.55
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.en.md +12 -17
- package/README.md +18 -17
- package/assets/readme/hero.en.svg +101 -0
- package/extensions/config/config.ts +25 -17
- package/extensions/config/panel.ts +100 -4
- package/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +39 -47
- package/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
- package/extensions/feature/compact-thinking.ts +8 -3
- package/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
- package/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +0 -1
- package/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +2 -42
- package/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -11
- package/extensions/index.ts +28 -38
- package/extensions/renderer/compact-mode.ts +394 -101
- package/extensions/renderer/default-mode.ts +747 -0
- package/extensions/renderer/index.ts +58 -775
- package/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +8 -6
- package/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +37 -36
- package/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
- package/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
- package/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +49 -36
- package/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +10 -45
- package/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
- package/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
- package/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +20 -16
- package/extensions/renderer/{tool-result.ts → tool/result.ts} +35 -27
- package/extensions/utils/ansi-color.ts +44 -0
- package/extensions/utils/format.ts +21 -0
- package/extensions/utils/fullscreen-detect.ts +0 -27
- package/package.json +4 -2
- package/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
- /package/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
- /package/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
- /package/extensions/{feature → renderer}/markdown-enhance.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
import { config } from "
|
|
4
|
-
import { showMoreHintText } from "
|
|
5
|
-
import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "
|
|
6
|
-
import { sanitizeToolResultText } from "
|
|
3
|
+
import { config } from "../../config/config.ts";
|
|
4
|
+
import { showMoreHintText } from "../show-more-hint.ts";
|
|
5
|
+
import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../../utils/tool-loading-icon.ts";
|
|
6
|
+
import { sanitizeToolResultText } from "../../utils/tool-result-sanitize.ts";
|
|
7
7
|
|
|
8
8
|
const TOOL_VIEWPORT_WIDTH_RATIO = 0.8;
|
|
9
9
|
|
|
@@ -23,13 +23,6 @@ export function insetComponent(component: any): any {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function oneLine(value: unknown, max = 72): string {
|
|
27
|
-
const text = sanitizeToolResultText(String(value ?? ""))
|
|
28
|
-
.replace(/\s+/g, " ")
|
|
29
|
-
.trim();
|
|
30
|
-
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
26
|
function rawTextFromResult(result: any): string {
|
|
34
27
|
return Array.isArray(result?.content)
|
|
35
28
|
? result.content
|
|
@@ -233,6 +226,8 @@ const EXPANDED_TOOL_IO_VIEW_GENERATION = Symbol("ccstyle-expanded-tool-io-view")
|
|
|
233
226
|
* └ Output click to show more
|
|
234
227
|
* result line…
|
|
235
228
|
*
|
|
229
|
+
* flushLeft=true(仅 mode=on 展开卡):去掉树线前导空格,由外层 Box(1,1) 提供 1 格 padding。
|
|
230
|
+
*
|
|
236
231
|
* Reused across re-renders via context.lastComponent when possible.
|
|
237
232
|
*/
|
|
238
233
|
export class ExpandedToolIoView {
|
|
@@ -251,6 +246,9 @@ export class ExpandedToolIoView {
|
|
|
251
246
|
/** 0-based header line indexes that carry show-more after last render. */
|
|
252
247
|
private showMoreHeaderRows: { input?: number; output?: number } = {};
|
|
253
248
|
|
|
249
|
+
/** flushLeft:贴左渲染(mode=on 展开卡);默认 false 保留前导空格(compact 等共用路径)。 */
|
|
250
|
+
private flushLeft: boolean;
|
|
251
|
+
|
|
254
252
|
constructor(
|
|
255
253
|
theme: any,
|
|
256
254
|
inputBody: string,
|
|
@@ -258,6 +256,7 @@ export class ExpandedToolIoView {
|
|
|
258
256
|
isError: boolean,
|
|
259
257
|
maxOutputLines = config.expandedPreviewMaxLines,
|
|
260
258
|
maxInputLines = config.expandedPreviewMaxLines,
|
|
259
|
+
flushLeft = false,
|
|
261
260
|
) {
|
|
262
261
|
this.theme = theme;
|
|
263
262
|
this.inputBody = inputBody;
|
|
@@ -265,6 +264,7 @@ export class ExpandedToolIoView {
|
|
|
265
264
|
this.isError = isError;
|
|
266
265
|
this.maxOutputLines = Math.max(1, maxOutputLines);
|
|
267
266
|
this.maxInputLines = Math.max(1, maxInputLines);
|
|
267
|
+
this.flushLeft = flushLeft;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
setContent(
|
|
@@ -273,16 +273,19 @@ export class ExpandedToolIoView {
|
|
|
273
273
|
isError: boolean,
|
|
274
274
|
maxOutputLines?: number,
|
|
275
275
|
maxInputLines?: number,
|
|
276
|
+
flushLeft?: boolean,
|
|
276
277
|
): void {
|
|
277
278
|
const nextOut =
|
|
278
279
|
maxOutputLines !== undefined ? Math.max(1, maxOutputLines) : this.maxOutputLines;
|
|
279
280
|
const nextIn = maxInputLines !== undefined ? Math.max(1, maxInputLines) : this.maxInputLines;
|
|
281
|
+
const nextFlush = flushLeft !== undefined ? flushLeft : this.flushLeft;
|
|
280
282
|
if (
|
|
281
283
|
this.inputBody === inputBody &&
|
|
282
284
|
this.outputBody === outputBody &&
|
|
283
285
|
this.isError === isError &&
|
|
284
286
|
this.maxOutputLines === nextOut &&
|
|
285
|
-
this.maxInputLines === nextIn
|
|
287
|
+
this.maxInputLines === nextIn &&
|
|
288
|
+
this.flushLeft === nextFlush
|
|
286
289
|
) {
|
|
287
290
|
return;
|
|
288
291
|
}
|
|
@@ -291,6 +294,7 @@ export class ExpandedToolIoView {
|
|
|
291
294
|
this.isError = isError;
|
|
292
295
|
this.maxOutputLines = nextOut;
|
|
293
296
|
this.maxInputLines = nextIn;
|
|
297
|
+
this.flushLeft = nextFlush;
|
|
294
298
|
this.invalidate();
|
|
295
299
|
}
|
|
296
300
|
|
|
@@ -348,7 +352,9 @@ export class ExpandedToolIoView {
|
|
|
348
352
|
|
|
349
353
|
const theme = this.theme;
|
|
350
354
|
const safeWidth = Math.max(1, Math.floor(width));
|
|
351
|
-
|
|
355
|
+
// flushLeft:贴左(外层 Box 负责 1 格 pad);否则保留 1 格前导空格(compact 共用)
|
|
356
|
+
const lead = this.flushLeft ? "" : " ";
|
|
357
|
+
const rail = `${lead}│ `;
|
|
352
358
|
const railWidth = visibleWidth(rail);
|
|
353
359
|
const bodyWidth = toolViewportWidth(safeWidth);
|
|
354
360
|
const contentWidth = Math.max(1, bodyWidth - railWidth);
|
|
@@ -363,7 +369,7 @@ export class ExpandedToolIoView {
|
|
|
363
369
|
section: ToolIoSection,
|
|
364
370
|
showMore: boolean,
|
|
365
371
|
) => {
|
|
366
|
-
const mark = theme.fg("dim",
|
|
372
|
+
const mark = theme.fg("dim", `${lead}${corner} `);
|
|
367
373
|
const title = theme.fg(
|
|
368
374
|
"accent",
|
|
369
375
|
typeof theme.bold === "function" ? theme.bold(label) : label,
|
|
@@ -378,12 +384,13 @@ export class ExpandedToolIoView {
|
|
|
378
384
|
};
|
|
379
385
|
|
|
380
386
|
const pushRailLine = (styledContent: string, continued = true) => {
|
|
381
|
-
|
|
387
|
+
// 续行 rail;Output 正文相对 └ 缩进 2 格(+ 可选 lead)
|
|
388
|
+
const prefix = continued ? rail : `${lead} `;
|
|
382
389
|
lines.push(truncateToWidth(theme.fg("dim", prefix) + styledContent, safeWidth, ""));
|
|
383
390
|
};
|
|
384
391
|
|
|
385
392
|
const pushBlankRail = () => {
|
|
386
|
-
lines.push(truncateToWidth(theme.fg("dim",
|
|
393
|
+
lines.push(truncateToWidth(theme.fg("dim", `${lead}│`), safeWidth, ""));
|
|
387
394
|
};
|
|
388
395
|
|
|
389
396
|
/** Style `key: value` input rows — dim keys, readable values. */
|
|
@@ -654,6 +661,8 @@ export function renderExpandedToolResult(
|
|
|
654
661
|
lastComponent?: unknown,
|
|
655
662
|
args?: unknown,
|
|
656
663
|
context?: any,
|
|
664
|
+
/** mode=on 展开卡贴左;compact 等保持默认前导空格 */
|
|
665
|
+
flushLeft = false,
|
|
657
666
|
): ExpandedToolIoView | ExpandedToolResultText | Text {
|
|
658
667
|
const inputBody = formatToolInputArgs(args);
|
|
659
668
|
const outputBody = body;
|
|
@@ -663,10 +672,18 @@ export function renderExpandedToolResult(
|
|
|
663
672
|
if (inputBody.trim() || outputBody.trim()) {
|
|
664
673
|
let view: ExpandedToolIoView;
|
|
665
674
|
if (isExpandedToolIoView(lastComponent)) {
|
|
666
|
-
lastComponent.setContent(inputBody, outputBody, isError, maxLines, maxLines);
|
|
675
|
+
lastComponent.setContent(inputBody, outputBody, isError, maxLines, maxLines, flushLeft);
|
|
667
676
|
view = lastComponent;
|
|
668
677
|
} else {
|
|
669
|
-
view = new ExpandedToolIoView(
|
|
678
|
+
view = new ExpandedToolIoView(
|
|
679
|
+
theme,
|
|
680
|
+
inputBody,
|
|
681
|
+
outputBody,
|
|
682
|
+
isError,
|
|
683
|
+
maxLines,
|
|
684
|
+
maxLines,
|
|
685
|
+
flushLeft,
|
|
686
|
+
);
|
|
670
687
|
}
|
|
671
688
|
if (context) rememberIoView(context, view);
|
|
672
689
|
return view;
|
|
@@ -677,15 +694,6 @@ export function renderExpandedToolResult(
|
|
|
677
694
|
return new Text(theme.fg(color, renderCollapsedToolResult("Done")), 0, 0);
|
|
678
695
|
}
|
|
679
696
|
|
|
680
|
-
export function formatExpandHint(lineCount: number): string {
|
|
681
|
-
return ` (${lineCount} more line${lineCount === 1 ? "" : "s"} / click)`;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function expandHint(theme: any, lineCount: number, hovered = false): string {
|
|
685
|
-
// Keep interaction guidance neutral; it should not inherit success/error
|
|
686
|
-
// coloring from the tool result surrounding it.
|
|
687
|
-
return theme.fg(hovered ? "text" : "muted", formatExpandHint(lineCount));
|
|
688
|
-
}
|
|
689
697
|
const toolViewMarker = (id: number, section: ToolIoSection) =>
|
|
690
698
|
`_cc:v${id}:${section === "input" ? "i" : "o"}`;
|
|
691
699
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** ANSI 16 色 → RGB(标准 xterm 色序)。 */
|
|
2
|
+
const ANSI_16_RGB: readonly [number, number, number][] = [
|
|
3
|
+
[0, 0, 0],
|
|
4
|
+
[128, 0, 0],
|
|
5
|
+
[0, 128, 0],
|
|
6
|
+
[128, 128, 0],
|
|
7
|
+
[0, 0, 128],
|
|
8
|
+
[128, 0, 128],
|
|
9
|
+
[0, 128, 128],
|
|
10
|
+
[192, 192, 192],
|
|
11
|
+
[128, 128, 128],
|
|
12
|
+
[255, 0, 0],
|
|
13
|
+
[0, 255, 0],
|
|
14
|
+
[255, 255, 0],
|
|
15
|
+
[0, 0, 255],
|
|
16
|
+
[255, 0, 255],
|
|
17
|
+
[0, 255, 255],
|
|
18
|
+
[255, 255, 255],
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/** ANSI 16 色码(0-15)→ RGB;越界回退白。 */
|
|
22
|
+
export function ansi16ToRgb(index: number): [number, number, number] {
|
|
23
|
+
return ANSI_16_RGB[index] ?? [255, 255, 255];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** ANSI 256 色码 → RGB(0-15 标准色 + 232-255 灰阶 + 16-231 六阶立方体)。 */
|
|
27
|
+
export function ansi256ToRgb(code: number): [number, number, number] {
|
|
28
|
+
// SGR 正则可解析任意位数字,入口先规范:取整并钳制到合法区间,
|
|
29
|
+
// 否则灰阶分支对 code > 255 会算出超出 0-255 的非法 RGB。
|
|
30
|
+
if (!Number.isFinite(code)) return [0, 0, 0];
|
|
31
|
+
code = Math.min(255, Math.max(0, Math.trunc(code)));
|
|
32
|
+
if (code < 16) return ansi16ToRgb(code);
|
|
33
|
+
if (code >= 232) {
|
|
34
|
+
const gray = 8 + (code - 232) * 10;
|
|
35
|
+
return [gray, gray, gray];
|
|
36
|
+
}
|
|
37
|
+
const cubeIndex = code - 16;
|
|
38
|
+
const levels = [0, 95, 135, 175, 215, 255];
|
|
39
|
+
return [
|
|
40
|
+
levels[Math.floor(cubeIndex / 36)]!,
|
|
41
|
+
levels[Math.floor((cubeIndex % 36) / 6)]!,
|
|
42
|
+
levels[cubeIndex % 6]!,
|
|
43
|
+
];
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sanitizeToolResultText } from "./tool-result-sanitize.ts";
|
|
2
|
+
|
|
3
|
+
/** 毫秒 → "1h 2m 3s"/"2m 3s"/"3s";低于 1 秒返回 ""(省略)。 */
|
|
4
|
+
export function formatDuration(ms: number): string {
|
|
5
|
+
const totalSec = Math.floor(ms / 1000);
|
|
6
|
+
if (totalSec < 1) return "";
|
|
7
|
+
const hours = Math.floor(totalSec / 3600);
|
|
8
|
+
const minutes = Math.floor((totalSec % 3600) / 60);
|
|
9
|
+
const seconds = totalSec % 60;
|
|
10
|
+
if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
|
|
11
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
12
|
+
return `${seconds}s`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 文本单行化:去换行空白,超宽截断加省略号。 */
|
|
16
|
+
export function oneLine(value: unknown, max = 96): string {
|
|
17
|
+
const text = sanitizeToolResultText(String(value ?? ""), 4096)
|
|
18
|
+
.replace(/\s+/g, " ")
|
|
19
|
+
.trim();
|
|
20
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
21
|
+
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// fullscreen 模式探测:官方 fullscreen TUI(TuiAltScreen)由官方独占布局,
|
|
2
|
-
// 插件渲染层应让位。检测方式是瞬时 setWidget 拿 tui 实例检查 mode。
|
|
3
|
-
const PROBE_KEY = "cc-fullscreen-probe";
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
2
|
* 官方 0.84+ 的 tui 引用是惰性 Proxy(createInteractiveTuiReference):
|
|
7
3
|
* 函数属性每次 get 都返回新包装,执行时才解析到当前实现。
|
|
@@ -13,26 +9,3 @@ export function isLazyProxyTui(tui: any): boolean {
|
|
|
13
9
|
const probe = tui.requestRender;
|
|
14
10
|
return typeof probe === "function" && probe !== tui.requestRender;
|
|
15
11
|
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 探测当前会话的 TUI 是否运行在官方 fullscreen 模式。
|
|
19
|
-
* 无 setWidget 的环境(测试 mock)视为 regular。
|
|
20
|
-
*/
|
|
21
|
-
export function isFullscreenUi(ctx: { ui?: any }): boolean {
|
|
22
|
-
if (typeof ctx?.ui?.setWidget !== "function") return false;
|
|
23
|
-
let fullscreen = false;
|
|
24
|
-
try {
|
|
25
|
-
ctx.ui.setWidget(PROBE_KEY, (tui: any) => {
|
|
26
|
-
fullscreen = tui?.mode === "fullscreen";
|
|
27
|
-
return { render: () => 0, invalidate() {} };
|
|
28
|
-
});
|
|
29
|
-
} catch {
|
|
30
|
-
// 探测失败按 regular 处理,避免误伤
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
ctx.ui.setWidget(PROBE_KEY, undefined);
|
|
34
|
-
} catch {
|
|
35
|
-
// 忽略
|
|
36
|
-
}
|
|
37
|
-
return fullscreen;
|
|
38
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.55",
|
|
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",
|
|
@@ -41,7 +41,9 @@
|
|
|
41
41
|
"typecheck": "node scripts/typecheck.mjs",
|
|
42
42
|
"format": "biome format --write .",
|
|
43
43
|
"format:check": "biome format .",
|
|
44
|
-
"
|
|
44
|
+
"docs:tool-render": "node scripts/generate-tool-render-examples.mjs",
|
|
45
|
+
"lint": "biome check .",
|
|
46
|
+
"preversion": "npm run format && npm run lint && npm run typecheck && npm test && npm run docs:tool-render",
|
|
45
47
|
"release": "npm version patch && npm publish",
|
|
46
48
|
"release:minor": "npm version minor && npm publish",
|
|
47
49
|
"release:major": "npm version major && npm publish"
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface WriteCallSummaryOptions {
|
|
2
|
-
hasContent: boolean;
|
|
3
|
-
hasDetailedResultHeader: boolean;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function splitWriteContentLines(content: string): string[] {
|
|
7
|
-
if (!content) {
|
|
8
|
-
return [];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const normalized = content.replace(/\r/g, "");
|
|
12
|
-
const lines = normalized.split("\n");
|
|
13
|
-
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
14
|
-
lines.pop();
|
|
15
|
-
}
|
|
16
|
-
return lines;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function countWriteContentLines(value: unknown): number {
|
|
20
|
-
return typeof value === "string" ? splitWriteContentLines(value).length : 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function getWriteContentSizeBytes(value: unknown): number {
|
|
24
|
-
return typeof value === "string" ? Buffer.byteLength(value, "utf8") : 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function shouldRenderWriteCallSummary(options: WriteCallSummaryOptions): boolean {
|
|
28
|
-
return options.hasContent && !options.hasDetailedResultHeader;
|
|
29
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|