pi-cc-extensions 0.8.64 → 0.8.66
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 +30 -16
- package/README.md +32 -24
- package/extensions/config/panel.ts +6 -3
- package/extensions/feature/context.ts +209 -158
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -40,7 +40,7 @@ Run `/reload` after installation.
|
|
|
40
40
|
| Claude Code Output | Tool summaries, expand/collapse, rich edit/write diffs, and`on` / `compact` / `off` modes | `/ccstyle` |
|
|
41
41
|
| Markdown enhancements | Mermaid art, admonitions, URL linking, and more | Automatic |
|
|
42
42
|
| Fullscreen mode | Tool card/group click to expand, double-click to collapse, previews, hover highlight, and a back-to-bottom button | `TUIMODE=fullscreen` or `--tui-mode fullscreen` |
|
|
43
|
-
| Settings panel | `Style / Diff / Thinking / Feature` tabs
|
|
43
|
+
| Settings panel | `Style / Diff / Thinking / UI / Feature` tabs | `/ccstyle` |
|
|
44
44
|
| Context inspection | Usage breakdown and previews for the system prompt, tools, skills, and messages | `/context` |
|
|
45
45
|
| Session/Subagent references | Search and inject effective context from previous Sessions or existing SubAgents | `@` |
|
|
46
46
|
| Theme | Bundled CC Dark and CC Light themes | `/theme` |
|
|
@@ -51,24 +51,38 @@ Run `/reload` after installation.
|
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
53
|
{
|
|
54
|
-
"mode": "on", // on
|
|
55
|
-
"excludeRenderers": [], //
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
54
|
+
"mode": "on", // on / compact / off
|
|
55
|
+
"excludeRenderers": [], // tools keeping the native renderer; Agent always keeps its dedicated renderer
|
|
56
|
+
|
|
57
|
+
// diff
|
|
58
|
+
"diffViewMode": "auto", // layout: auto / split / unified
|
|
59
|
+
"diffIndicatorMode": "bars", // change indicators: bars / classic / none
|
|
60
|
+
"diffSplitMinWidth": 120, // min terminal width for side-by-side columns
|
|
61
|
+
"editDiffCollapsedLines": 24, // Edit collapse lines; beyond that shows the expand hint
|
|
62
|
+
"writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
|
|
63
|
+
"diffWordWrap": true, // wrap long diff lines
|
|
64
|
+
"expandedPreviewMaxLines": 40, // max lines for expanded bodies
|
|
65
|
+
|
|
66
|
+
// thinking
|
|
67
|
+
"useSummaryTitlesAsThinkingTitle": true, // use latest summary as thinking title
|
|
68
|
+
"previewLines": 3, // preview lines; 0 hides
|
|
69
|
+
"animationIntervalMs": 90, // title animation interval (ms)
|
|
70
|
+
|
|
71
|
+
// ui
|
|
72
|
+
"showStartupHeader": true, // startup header (logo + tips) toggle
|
|
73
|
+
"scrollStepLines": 3, // fullscreen wheel scroll step
|
|
74
|
+
|
|
75
|
+
// features
|
|
76
|
+
"enableSessionReference": true, // @ session references
|
|
77
|
+
"enableSubagentAutocomplete": true, // @ subagent completion and delegation hints
|
|
78
|
+
"enableContextCommand": true, // /context usage check
|
|
79
|
+
"enableAgentSummary": true, // per-turn tool summary
|
|
80
|
+
"enableWorkingMessage": true, // Working... bottom token/elapsed
|
|
81
|
+
"enableAliases": true // /clear, /exit aliases
|
|
68
82
|
}
|
|
69
83
|
```
|
|
70
84
|
|
|
71
|
-
> **Fullscreen
|
|
85
|
+
> **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. Double-click an expanded panel to collapse it.
|
|
72
86
|
>
|
|
73
87
|
> **Tip**: set `markdown.mermaid` to `final` via `~/.pi/agent/settings.json` or the Mermaid diagrams option in `/settings`. Default `streaming` redraws per frame; `final` renders once at completion.
|
|
74
88
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="./assets/readme/hero.svg" width="100%"
|
|
2
|
+
<img src="./assets/readme/hero.svg" width="100%">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
@@ -40,7 +40,7 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
40
40
|
| Claude Code UI | 工具摘要、折叠展开、rich edit/write diff,以及`on` / `compact` / `off` 三种模式 | `/ccstyle` |
|
|
41
41
|
| Markdown 增强 | Mermaid 图、提示框、URL 链接化等 | 自动生效 |
|
|
42
42
|
| Fullscreen mode | 工具卡/group 单击展开、双击收起、预览、hover 高亮、回到底部按钮 | `TUIMODE=fullscreen` 或 `--tui-mode fullscreen` |
|
|
43
|
-
| 配置面板 | `Style / Diff / Thinking / Feature`
|
|
43
|
+
| 配置面板 | `Style / Diff / Thinking / UI / Feature` 五页签 | `/ccstyle` |
|
|
44
44
|
| 上下文检查 | 查看上下文占用,并预览 System prompt、Tools、Skills 和消息内容 | `/context` |
|
|
45
45
|
| Session/Subagent 引用 | 搜索并注入历史 Session 或现有 SubAgent 的有效上下文 | `@` |
|
|
46
46
|
| 主题 | 随包提供内置 CC Dark、CC Light 主题 | `/theme` |
|
|
@@ -51,30 +51,38 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
53
|
{
|
|
54
|
-
"mode": "on", // on
|
|
55
|
-
"excludeRenderers": [], //
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
54
|
+
"mode": "on", // on / compact / off
|
|
55
|
+
"excludeRenderers": [], // 走原生渲染的工具名;Agent 始终保留专用渲染器
|
|
56
|
+
|
|
57
|
+
// diff
|
|
58
|
+
"diffViewMode": "auto", // 布局:auto / split / unified
|
|
59
|
+
"diffIndicatorMode": "bars", // 变更指示:bars / classic / none
|
|
60
|
+
"diffSplitMinWidth": 120, // 左右分栏的最小终端宽度
|
|
61
|
+
"editDiffCollapsedLines": 24, // Edit 折叠行数,超出显示展开提示
|
|
62
|
+
"writeDiffCollapsedLines": 0, // write 折叠行数,0 仅显示创建摘要
|
|
63
|
+
"diffWordWrap": true, // 长 diff 行换行
|
|
64
|
+
"expandedPreviewMaxLines": 40, // 展开正文最大行数
|
|
65
|
+
|
|
66
|
+
// thinking
|
|
67
|
+
"useSummaryTitlesAsThinkingTitle": true, // 用最新摘要作思考标题
|
|
68
|
+
"previewLines": 3, // 预览行数,0 隐藏
|
|
69
|
+
"animationIntervalMs": 90, // 标题动画间隔(毫秒)
|
|
70
|
+
|
|
71
|
+
// ui
|
|
72
|
+
"showStartupHeader": true, // 启动头(logo + tips)开关
|
|
73
|
+
"scrollStepLines": 3, // fullscreen 滚轮步进
|
|
74
|
+
|
|
75
|
+
// features
|
|
76
|
+
"enableSessionReference": true, // @ session 引用
|
|
77
|
+
"enableSubagentAutocomplete": true, // @ subagent 补全与委派提示
|
|
78
|
+
"enableContextCommand": true, // /context 上下文检查
|
|
79
|
+
"enableAgentSummary": true, // 每回合工具摘要
|
|
80
|
+
"enableWorkingMessage": true, // Working... 底部 token/耗时
|
|
81
|
+
"enableAliases": true // /clear、/exit 别名
|
|
74
82
|
}
|
|
75
83
|
```
|
|
76
84
|
|
|
77
|
-
> **Fullscreen
|
|
85
|
+
> **Fullscreen**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要,双击展开面板收起。
|
|
78
86
|
>
|
|
79
87
|
> **建议**:`markdown.mermaid` 设为 `final`(`~/.pi/agent/settings.json` 或 `/settings` 面板的 Mermaid diagrams 选项)。默认 `streaming` 逐帧重绘,`final` 渲染最终版更稳定。
|
|
80
88
|
|
|
@@ -88,7 +96,7 @@ npm run typecheck
|
|
|
88
96
|
|
|
89
97
|
## 兼容性
|
|
90
98
|
|
|
91
|
-
- Node.js `>=22.19.0`,Pi `^0.84.0
|
|
99
|
+
- Node.js `>=22.19.0`,Pi `^0.84.0`
|
|
92
100
|
|
|
93
101
|
## 推荐搭配
|
|
94
102
|
|
|
@@ -182,7 +182,7 @@ function buildNumberInputSubmenu(
|
|
|
182
182
|
|
|
183
183
|
/** Section tabs for /ccstyle — matches Zentui-style "A / B / C" headers. */
|
|
184
184
|
type CcstyleSection = {
|
|
185
|
-
id: "style" | "
|
|
185
|
+
id: "style" | "diff" | "thinking" | "ui" | "feature";
|
|
186
186
|
label: string;
|
|
187
187
|
items: any[];
|
|
188
188
|
};
|
|
@@ -556,12 +556,15 @@ export async function showCcstylePanel(
|
|
|
556
556
|
label: "Thinking",
|
|
557
557
|
items: [thinkingTitleSetting, thinkingPreviewSetting, thinkingAnimationSetting],
|
|
558
558
|
},
|
|
559
|
+
{
|
|
560
|
+
id: "ui",
|
|
561
|
+
label: "UI",
|
|
562
|
+
items: [startupHeaderSetting, scrollStepSetting],
|
|
563
|
+
},
|
|
559
564
|
{
|
|
560
565
|
id: "feature",
|
|
561
566
|
label: "Feature",
|
|
562
567
|
items: [
|
|
563
|
-
startupHeaderSetting,
|
|
564
|
-
scrollStepSetting,
|
|
565
568
|
sessionReferenceToggle.setting,
|
|
566
569
|
subagentAutocompleteToggle.setting,
|
|
567
570
|
contextCommandToggle.setting,
|
|
@@ -13,10 +13,10 @@ import { padLine } from "../utils/format.ts";
|
|
|
13
13
|
export type ContextPart = {
|
|
14
14
|
label: string;
|
|
15
15
|
tokens: number;
|
|
16
|
-
color: "accent" | "success" | "warning" | "customMessageLabel" | "muted" | "dim";
|
|
16
|
+
color: "accent" | "success" | "warning" | "customMessageLabel" | "muted" | "dim" | "error";
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
type PreviewKey = "systemPrompt" | "tools" | "toolResults" | "contextFiles";
|
|
19
|
+
type PreviewKey = "systemPrompt" | "memoryFiles" | "tools" | "toolResults" | "contextFiles";
|
|
20
20
|
|
|
21
21
|
type ContextPreview = {
|
|
22
22
|
key: PreviewKey;
|
|
@@ -47,11 +47,16 @@ export function escCloseHitbox(bounds: DialogBounds): {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
let
|
|
50
|
+
let activeContextOverlays = 0;
|
|
51
51
|
|
|
52
|
-
/** fullscreen
|
|
52
|
+
/** fullscreen 输入包装用于把鼠标事件继续传给当前 context 主弹框或文本预览 overlay。 */
|
|
53
53
|
export function hasActiveTextPreview(): boolean {
|
|
54
|
-
return
|
|
54
|
+
return activeContextOverlays > 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 官方 fullscreen 打开 overlay 时会退回 1002;重新启用 1003 才能收到无按键 hover。 */
|
|
58
|
+
function ensureFullscreenMouseMotion(tui: any): void {
|
|
59
|
+
if (tui.mode === "fullscreen") tui.terminal?.write?.("\x1b[?1003h\x1b[?1006h");
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
export async function showTextPreview(
|
|
@@ -60,10 +65,11 @@ export async function showTextPreview(
|
|
|
60
65
|
rawContent: string,
|
|
61
66
|
): Promise<void> {
|
|
62
67
|
const content = normalizePreviewText(rawContent);
|
|
63
|
-
|
|
68
|
+
activeContextOverlays++;
|
|
64
69
|
try {
|
|
65
70
|
await ctx.ui.custom(
|
|
66
71
|
(tui, theme, _keybindings, done) => {
|
|
72
|
+
ensureFullscreenMouseMotion(tui);
|
|
67
73
|
let scrollOffset = 0;
|
|
68
74
|
let pageSize = 1;
|
|
69
75
|
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
|
|
|
@@ -337,6 +343,13 @@ export function collectContextBreakdown(
|
|
|
337
343
|
let toolResultTokens = 0;
|
|
338
344
|
let contextTokens = 0;
|
|
339
345
|
|
|
346
|
+
const memoryPreview: string[] = [];
|
|
347
|
+
let memoryTokens = 0;
|
|
348
|
+
for (const file of options.contextFiles ?? []) {
|
|
349
|
+
memoryTokens += tokenEstimate(file.content);
|
|
350
|
+
memoryPreview.push(`## ${file.path}\n\n${previewValue(file.content)}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
340
353
|
for (const tool of allTools) {
|
|
341
354
|
if (!selectedTools.has(tool.name)) continue;
|
|
342
355
|
const definition = {
|
|
@@ -397,12 +410,14 @@ export function collectContextBreakdown(
|
|
|
397
410
|
return {
|
|
398
411
|
parts: [
|
|
399
412
|
{ label: "System prompt", tokens: tokenEstimate(systemPrompt), color: "accent" },
|
|
413
|
+
{ label: "Memory", tokens: memoryTokens, color: "error" },
|
|
400
414
|
{ label: "Tools", tokens: toolDefinitionTokens, color: "success" },
|
|
401
415
|
{ label: "Tool results", tokens: toolResultTokens, color: "customMessageLabel" },
|
|
402
416
|
{ label: "Context", tokens: contextTokens, color: "warning" },
|
|
403
417
|
] satisfies ContextPart[],
|
|
404
418
|
previews: {
|
|
405
419
|
systemPrompt: systemPrompt || "No system prompt.",
|
|
420
|
+
memoryFiles: memoryPreview.join("\n\n") || "No memory files in context.",
|
|
406
421
|
tools: toolDefinitionPreview.join("\n\n") || "No active tool definitions.",
|
|
407
422
|
toolResults: toolResultPreview.join("\n\n") || "No tool results in the current context.",
|
|
408
423
|
contextFiles: contextPreview.join("\n\n") || "No conversation context.",
|
|
@@ -419,9 +434,10 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
419
434
|
const tools = pi.getAllTools();
|
|
420
435
|
const breakdown = collectContextBreakdown(ctx, tools);
|
|
421
436
|
const estimated = breakdown.parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
422
|
-
|
|
437
|
+
// System prompt、Memory、Tools 为固定项,capParts 时保持原值不压缩。
|
|
438
|
+
const fixedTokens = breakdown.parts.slice(0, 3).reduce((sum, part) => sum + part.tokens, 0);
|
|
423
439
|
const used = Math.max(resolveUsedTokens(usage, estimated, contextWindow), fixedTokens);
|
|
424
|
-
const parts = capParts(breakdown.parts, used,
|
|
440
|
+
const parts = capParts(breakdown.parts, used, 3);
|
|
425
441
|
const attributed = parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
426
442
|
const other = Math.max(0, used - attributed);
|
|
427
443
|
const free = Math.max(0, contextWindow - used);
|
|
@@ -444,6 +460,12 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
444
460
|
title: "System Prompt",
|
|
445
461
|
content: breakdown.previews.systemPrompt,
|
|
446
462
|
},
|
|
463
|
+
{
|
|
464
|
+
key: "memoryFiles",
|
|
465
|
+
label: "Memory",
|
|
466
|
+
title: "Memory Files",
|
|
467
|
+
content: breakdown.previews.memoryFiles,
|
|
468
|
+
},
|
|
447
469
|
{
|
|
448
470
|
key: "tools",
|
|
449
471
|
label: "Tools",
|
|
@@ -474,158 +496,187 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
474
496
|
let selectedPreviewIndex = 0;
|
|
475
497
|
|
|
476
498
|
while (true) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
mouse.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
499
|
+
// 主弹框也计入活动 overlay,fullscreen 下官方输入链才会把鼠标包放行给行点击。
|
|
500
|
+
activeContextOverlays++;
|
|
501
|
+
let action;
|
|
502
|
+
try {
|
|
503
|
+
action = await ctx.ui.custom(
|
|
504
|
+
(tui, theme, _keybindings, done) => {
|
|
505
|
+
ensureFullscreenMouseMotion(tui);
|
|
506
|
+
let previewHitboxes: Array<{
|
|
507
|
+
key: PreviewKey;
|
|
508
|
+
row: number;
|
|
509
|
+
startCol: number;
|
|
510
|
+
endCol: number;
|
|
511
|
+
}> = [];
|
|
512
|
+
let escHitbox: { row: number; startCol: number; endCol: number } | undefined;
|
|
513
|
+
let escHovered = false;
|
|
514
|
+
let hoveredKey: PreviewKey | undefined;
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
invalidate() {},
|
|
518
|
+
handleInput(data: string) {
|
|
519
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
|
|
520
|
+
done(undefined);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (matchesKey(data, Key.up) && visiblePreviews.length > 0) {
|
|
524
|
+
selectedPreviewIndex =
|
|
525
|
+
(selectedPreviewIndex - 1 + visiblePreviews.length) % visiblePreviews.length;
|
|
526
|
+
tui.requestRender();
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (matchesKey(data, Key.down) && visiblePreviews.length > 0) {
|
|
530
|
+
selectedPreviewIndex = (selectedPreviewIndex + 1) % visiblePreviews.length;
|
|
531
|
+
tui.requestRender();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (matchesKey(data, Key.enter)) {
|
|
535
|
+
done(visiblePreviews[selectedPreviewIndex]?.key);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const mouse = parseSgrMousePacket(data);
|
|
540
|
+
if (!mouse || mouse.final !== "M") return;
|
|
541
|
+
if ((mouse.code & 32) !== 0) {
|
|
542
|
+
// SGR 1003 的无按键 hover code 为 35,不能按左键事件过滤。
|
|
543
|
+
const overEsc = Boolean(
|
|
544
|
+
escHitbox &&
|
|
545
|
+
mouse.row === escHitbox.row &&
|
|
546
|
+
mouse.col >= escHitbox.startCol &&
|
|
547
|
+
mouse.col <= escHitbox.endCol,
|
|
548
|
+
);
|
|
549
|
+
const hovered = previewHitboxes.find(
|
|
550
|
+
(candidate) =>
|
|
551
|
+
mouse.row === candidate.row &&
|
|
552
|
+
mouse.col >= candidate.startCol &&
|
|
553
|
+
mouse.col <= candidate.endCol,
|
|
554
|
+
);
|
|
555
|
+
if (overEsc !== escHovered || hovered?.key !== hoveredKey) {
|
|
556
|
+
escHovered = overEsc;
|
|
557
|
+
hoveredKey = hovered?.key;
|
|
558
|
+
tui.requestRender();
|
|
559
|
+
}
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (mouseBaseButton(mouse.code) !== 0) return;
|
|
563
|
+
if (
|
|
564
|
+
escHitbox &&
|
|
565
|
+
mouse.row === escHitbox.row &&
|
|
566
|
+
mouse.col >= escHitbox.startCol &&
|
|
567
|
+
mouse.col <= escHitbox.endCol
|
|
568
|
+
) {
|
|
569
|
+
done(undefined);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const hitbox = previewHitboxes.find(
|
|
573
|
+
(candidate) =>
|
|
574
|
+
mouse.row === candidate.row &&
|
|
575
|
+
mouse.col >= candidate.startCol &&
|
|
576
|
+
mouse.col <= candidate.endCol,
|
|
536
577
|
);
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
578
|
+
if (hitbox) {
|
|
579
|
+
selectedPreviewIndex = Math.max(
|
|
580
|
+
0,
|
|
581
|
+
visiblePreviews.findIndex((preview) => preview.key === hitbox.key),
|
|
582
|
+
);
|
|
583
|
+
done(hitbox.key);
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
render(width: number) {
|
|
587
|
+
const inner = Math.max(1, width - 2);
|
|
588
|
+
const escWidth = visibleWidth("[esc]");
|
|
589
|
+
const percent = contextWindow > 0 ? (used / contextWindow) * 100 : 0;
|
|
590
|
+
const title = theme.bold(theme.fg("accent", "Context Usage"));
|
|
591
|
+
const subtitle = `${formatTokens(used)} / ${formatTokens(contextWindow)} tokens (${percent.toFixed(1)}%)`;
|
|
592
|
+
const barWidth = Math.max(1, Math.min(60, inner - 2));
|
|
593
|
+
let remaining = barWidth;
|
|
594
|
+
const segments = allParts
|
|
595
|
+
.map((part, index) => {
|
|
596
|
+
const cells =
|
|
597
|
+
index === allParts.length - 1
|
|
598
|
+
? remaining
|
|
599
|
+
: Math.min(
|
|
600
|
+
remaining,
|
|
601
|
+
Math.round((part.tokens / Math.max(1, contextWindow)) * barWidth),
|
|
602
|
+
);
|
|
603
|
+
remaining -= cells;
|
|
604
|
+
return theme.fg(part.color, "█".repeat(Math.max(0, cells)));
|
|
605
|
+
})
|
|
606
|
+
.join("");
|
|
607
|
+
const labelWidth = Math.min(
|
|
608
|
+
24,
|
|
609
|
+
Math.max(...allParts.map((part) => part.label.length)),
|
|
610
|
+
);
|
|
611
|
+
const selectedLabel = visiblePreviews[selectedPreviewIndex]?.label;
|
|
612
|
+
const partRows = allParts.map((part) => {
|
|
613
|
+
const pct = contextWindow > 0 ? (part.tokens / contextWindow) * 100 : 0;
|
|
614
|
+
const swatch = theme.fg(part.color, "■");
|
|
615
|
+
const label = part.label.padEnd(labelWidth);
|
|
616
|
+
const amount = `${formatTokens(part.tokens).padStart(7)} ${pct.toFixed(1).padStart(5)}%`;
|
|
617
|
+
const selected = part.label === selectedLabel;
|
|
618
|
+
const hoverLabel = visiblePreviews.find((p) => p.key === hoveredKey)?.label;
|
|
619
|
+
const prefix = selected ? "› " : " ";
|
|
620
|
+
const row = padLine(`${prefix}${swatch} ${label} ${amount}`, inner);
|
|
621
|
+
if (selected) return theme.bg("selectedBg", row);
|
|
622
|
+
return part.label === hoverLabel ? theme.bg("customMessageBg", row) : row;
|
|
623
|
+
});
|
|
624
|
+
const border = (text: string) => theme.fg("border", text);
|
|
625
|
+
const lines = [
|
|
626
|
+
border(`╭${"─".repeat(inner)}╮`),
|
|
627
|
+
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner - escWidth)}${theme.fg(escHovered ? "text" : "muted", "[esc]")}${border("│")}`,
|
|
628
|
+
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
629
|
+
`${border("│")}${padLine(` ${segments}`, inner)}${border("│")}`,
|
|
630
|
+
`${border("│")}${" ".repeat(inner)}${border("│")}`,
|
|
631
|
+
...partRows.map((row) => `${border("│")}${row}${border("│")}`),
|
|
632
|
+
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
633
|
+
`${border("│")}${padLine(theme.fg("dim", " ↑↓ select · Click / Enter to preview · [esc] close"), inner)}${border("│")}`,
|
|
634
|
+
border(`╰${"─".repeat(inner)}╯`),
|
|
635
|
+
];
|
|
636
|
+
|
|
637
|
+
const terminalHeight = Math.max(1, tui.terminal.rows);
|
|
638
|
+
const maxHeight = Math.min(
|
|
639
|
+
Math.max(1, Math.floor(terminalHeight * 0.9)),
|
|
640
|
+
Math.max(1, terminalHeight - 2),
|
|
641
|
+
);
|
|
642
|
+
const visibleHeight = Math.min(lines.length, maxHeight);
|
|
643
|
+
const overlayTop =
|
|
644
|
+
1 + Math.floor((Math.max(1, terminalHeight - 2) - visibleHeight) / 2);
|
|
645
|
+
const overlayLeft = Math.floor((Math.max(1, tui.terminal.columns) - width) / 2);
|
|
646
|
+
escHitbox = escCloseHitbox({ left: overlayLeft, top: overlayTop, width });
|
|
647
|
+
previewHitboxes = visiblePreviews.flatMap((preview) => {
|
|
648
|
+
const partIndex = allParts.findIndex((part) => part.label === preview.label);
|
|
649
|
+
const line = 5 + partIndex;
|
|
650
|
+
return partIndex >= 0 && line < visibleHeight
|
|
651
|
+
? [
|
|
652
|
+
{
|
|
653
|
+
key: preview.key,
|
|
654
|
+
row: overlayTop + line + 1,
|
|
655
|
+
startCol: overlayLeft + 1,
|
|
656
|
+
endCol: overlayLeft + width,
|
|
657
|
+
},
|
|
658
|
+
]
|
|
659
|
+
: [];
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
return lines;
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
overlay: true,
|
|
668
|
+
overlayOptions: {
|
|
669
|
+
anchor: "center",
|
|
670
|
+
width: 64,
|
|
671
|
+
minWidth: 44,
|
|
672
|
+
maxHeight: "90%",
|
|
673
|
+
margin: 1,
|
|
615
674
|
},
|
|
616
|
-
};
|
|
617
|
-
},
|
|
618
|
-
{
|
|
619
|
-
overlay: true,
|
|
620
|
-
overlayOptions: {
|
|
621
|
-
anchor: "center",
|
|
622
|
-
width: 64,
|
|
623
|
-
minWidth: 44,
|
|
624
|
-
maxHeight: "90%",
|
|
625
|
-
margin: 1,
|
|
626
675
|
},
|
|
627
|
-
|
|
628
|
-
|
|
676
|
+
);
|
|
677
|
+
} finally {
|
|
678
|
+
activeContextOverlays--;
|
|
679
|
+
}
|
|
629
680
|
|
|
630
681
|
if (!action) break;
|
|
631
682
|
const preview = previewByKey.get(action as PreviewKey);
|