@herbertgao/pi-extensions 2026.9.18 → 2026.9.20
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 +20 -28
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
- package/node_modules/pi-antigravity/README.md +29 -6
- package/node_modules/pi-antigravity/package.json +12 -7
- package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
- package/node_modules/pi-antigravity/src/client/client.ts +1 -1
- package/node_modules/pi-antigravity/src/image/image.ts +4 -4
- package/node_modules/pi-antigravity/src/index.ts +203 -13
- package/node_modules/pi-antigravity/src/search/index.ts +1 -0
- package/node_modules/pi-antigravity/src/search/search.ts +325 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
- package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
- package/node_modules/pi-antigravity/src/types/types.ts +5 -12
- package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
- package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
- package/node_modules/pi-typesafe/README.md +2 -0
- package/node_modules/pi-typesafe/package.json +2 -1
- package/node_modules/pi-web-access/CHANGELOG.md +72 -0
- package/node_modules/pi-web-access/README.md +34 -13
- package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
- package/node_modules/pi-web-access/curator-page.ts +5 -1
- package/node_modules/pi-web-access/curator-server.ts +2 -0
- package/node_modules/pi-web-access/dist/index.js +1337 -1009
- package/node_modules/pi-web-access/exa.ts +20 -52
- package/node_modules/pi-web-access/extract.ts +41 -4
- package/node_modules/pi-web-access/gemini-search.ts +12 -4
- package/node_modules/pi-web-access/index.ts +29 -37
- package/node_modules/pi-web-access/openai-search.ts +35 -7
- package/node_modules/pi-web-access/package.json +6 -5
- package/node_modules/pi-web-access/page-query.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +2 -3
- package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
- package/node_modules/pi-web-access/storage.ts +48 -77
- package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
- package/node_modules/pi-web-access/summary-review.ts +3 -4
- package/node_modules/pi-web-access/tavily.ts +67 -20
- package/node_modules/pi-web-access/tool-activation.ts +53 -33
- package/node_modules/pi-web-access/utils.ts +1 -1
- package/node_modules/pi-web-access/you.ts +164 -0
- package/node_modules/pi-web-access/zai.ts +161 -0
- package/package.json +7 -11
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- package/node_modules/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type BuildSystemPromptOptions,
|
|
2
3
|
type ExtensionAPI,
|
|
3
4
|
type ExtensionCommandContext,
|
|
4
|
-
type
|
|
5
|
+
type ToolInfo,
|
|
5
6
|
estimateTokens,
|
|
6
7
|
formatSkillsForPrompt,
|
|
7
8
|
getMarkdownTheme,
|
|
8
9
|
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { Key, Markdown, matchesKey,
|
|
10
|
+
import { Key, Markdown, matchesKey, visibleWidth } from "@earendil-works/pi-tui";
|
|
11
|
+
import { mouseBaseButton, parseSgrMousePacket } from "../utils/sgr-mouse.ts";
|
|
12
|
+
import { padLine } from "../utils/format.ts";
|
|
10
13
|
|
|
11
14
|
export type ContextPart = {
|
|
12
15
|
label: string;
|
|
13
16
|
tokens: number;
|
|
14
|
-
color: "accent" | "success" | "warning" | "muted" | "dim" | "error";
|
|
17
|
+
color: "accent" | "success" | "warning" | "customMessageLabel" | "muted" | "dim" | "error";
|
|
15
18
|
};
|
|
16
19
|
|
|
17
|
-
type PreviewKey =
|
|
20
|
+
type PreviewKey =
|
|
21
|
+
| "systemPrompt"
|
|
22
|
+
| "memoryFiles"
|
|
23
|
+
| "skills"
|
|
24
|
+
| "tools"
|
|
25
|
+
| "toolResults"
|
|
26
|
+
| "contextFiles";
|
|
18
27
|
|
|
19
28
|
type ContextPreview = {
|
|
20
29
|
key: PreviewKey;
|
|
@@ -47,12 +56,12 @@ export function escCloseHitbox(bounds: DialogBounds): {
|
|
|
47
56
|
|
|
48
57
|
let activeContextOverlays = 0;
|
|
49
58
|
|
|
50
|
-
/** fullscreen 输入包装用于把鼠标事件继续传给当前 context overlay。 */
|
|
59
|
+
/** fullscreen 输入包装用于把鼠标事件继续传给当前 context 主弹框或文本预览 overlay。 */
|
|
51
60
|
export function hasActiveTextPreview(): boolean {
|
|
52
61
|
return activeContextOverlays > 0;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
/** fullscreen
|
|
64
|
+
/** 官方 fullscreen 打开 overlay 时会退回 1002;重新启用 1003 才能收到无按键 hover。 */
|
|
56
65
|
function ensureFullscreenMouseMotion(tui: any): void {
|
|
57
66
|
if (tui.mode === "fullscreen") tui.terminal?.write?.("\x1b[?1003h\x1b[?1006h");
|
|
58
67
|
}
|
|
@@ -175,6 +184,7 @@ export async function showTextPreview(
|
|
|
175
184
|
else if (button === 65) scrollTo(scrollOffset + 3);
|
|
176
185
|
}
|
|
177
186
|
},
|
|
187
|
+
|
|
178
188
|
render(width: number) {
|
|
179
189
|
const inner = Math.max(1, width - 2);
|
|
180
190
|
const escWidth = visibleWidth("[esc]");
|
|
@@ -197,10 +207,6 @@ export async function showTextPreview(
|
|
|
197
207
|
escHitbox = escCloseHitbox({ left: overlayLeft, top: overlayTop, width });
|
|
198
208
|
const visible = wrapped.slice(scrollOffset, scrollOffset + pageSize);
|
|
199
209
|
const border = (text: string) => theme.fg("border", text);
|
|
200
|
-
const padLine = (text: string, lineWidth = inner): string => {
|
|
201
|
-
const truncated = truncateToWidth(text, lineWidth, "…");
|
|
202
|
-
return truncated + " ".repeat(Math.max(0, lineWidth - visibleWidth(truncated)));
|
|
203
|
-
};
|
|
204
210
|
const scrollable = totalLines > pageSize;
|
|
205
211
|
const thumbSize = scrollable
|
|
206
212
|
? Math.max(1, Math.floor((pageSize * pageSize) / totalLines))
|
|
@@ -239,7 +245,7 @@ export async function showTextPreview(
|
|
|
239
245
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
240
246
|
...bodyRows,
|
|
241
247
|
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
242
|
-
`${border("│")}${padLine(theme.fg("dim", ` ${status}`))}${border("│")}`,
|
|
248
|
+
`${border("│")}${padLine(theme.fg("dim", ` ${status}`), inner)}${border("│")}`,
|
|
243
249
|
border(`╰${"─".repeat(inner)}╯`),
|
|
244
250
|
];
|
|
245
251
|
},
|
|
@@ -261,80 +267,18 @@ export async function showTextPreview(
|
|
|
261
267
|
}
|
|
262
268
|
}
|
|
263
269
|
|
|
264
|
-
type SgrMousePacket = {
|
|
265
|
-
code: number;
|
|
266
|
-
col: number;
|
|
267
|
-
row: number;
|
|
268
|
-
final: "M" | "m";
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
function parseSgrMousePacket(data: string): SgrMousePacket | null {
|
|
272
|
-
const match = data.match(/^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/);
|
|
273
|
-
if (!match) return null;
|
|
274
|
-
return {
|
|
275
|
-
code: Number(match[1]),
|
|
276
|
-
col: Number(match[2]),
|
|
277
|
-
row: Number(match[3]),
|
|
278
|
-
final: match[4] as "M" | "m",
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function mouseBaseButton(code: number): number {
|
|
283
|
-
return code & ~(4 | 8 | 16 | 32);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
270
|
const tokenEstimate = (value: unknown): number => {
|
|
287
271
|
if (!value) return 0;
|
|
288
272
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
289
273
|
return Math.max(0, Math.ceil(text.length / 4));
|
|
290
274
|
};
|
|
291
275
|
|
|
276
|
+
/** 只统计确实嵌进 system prompt 的片段,避免源文件预览把占用加两遍。 */
|
|
292
277
|
function embeddedTokens(prompt: string, chunk: string): number {
|
|
293
278
|
if (!chunk || !prompt.includes(chunk)) return 0;
|
|
294
279
|
return tokenEstimate(chunk);
|
|
295
280
|
}
|
|
296
281
|
|
|
297
|
-
type ToolPreviewInfo = {
|
|
298
|
-
name: string;
|
|
299
|
-
description?: string;
|
|
300
|
-
promptGuidelines?: string[];
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
export function scaleParts(parts: ContextPart[], target: number): ContextPart[] {
|
|
304
|
-
const estimated = parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
305
|
-
if (estimated === 0 || target <= 0) return parts;
|
|
306
|
-
const scaled = parts.map((part) => ({
|
|
307
|
-
...part,
|
|
308
|
-
tokens: Math.round((part.tokens / estimated) * target),
|
|
309
|
-
}));
|
|
310
|
-
const delta = target - scaled.reduce((sum, part) => sum + part.tokens, 0);
|
|
311
|
-
const largest = scaled.reduce(
|
|
312
|
-
(best, part, index) => (part.tokens > scaled[best]!.tokens ? index : best),
|
|
313
|
-
0,
|
|
314
|
-
);
|
|
315
|
-
scaled[largest]!.tokens += delta;
|
|
316
|
-
return scaled;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export function resolveUsedTokens(
|
|
320
|
-
usage: { tokens: number | null; percent: number | null } | undefined,
|
|
321
|
-
estimated: number,
|
|
322
|
-
contextWindow: number,
|
|
323
|
-
): number {
|
|
324
|
-
const reported = usage?.tokens;
|
|
325
|
-
const fromPercent =
|
|
326
|
-
usage?.percent !== null && usage?.percent !== undefined && contextWindow > 0
|
|
327
|
-
? Math.round((usage.percent / 100) * contextWindow)
|
|
328
|
-
: undefined;
|
|
329
|
-
let resolved = reported ?? fromPercent ?? estimated;
|
|
330
|
-
if (reported !== null && reported !== undefined && fromPercent !== undefined) {
|
|
331
|
-
const tolerance = Math.max(32, Math.round(contextWindow * 0.001));
|
|
332
|
-
if (Math.abs(reported - fromPercent) > tolerance) resolved = fromPercent;
|
|
333
|
-
}
|
|
334
|
-
if (estimated > 0 && resolved < estimated * 0.25) return estimated;
|
|
335
|
-
return resolved;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
282
|
export function capParts(parts: ContextPart[], target: number, fixedPrefix = 0): ContextPart[] {
|
|
339
283
|
const fixed = parts.slice(0, fixedPrefix);
|
|
340
284
|
const variable = parts.slice(fixedPrefix);
|
|
@@ -342,7 +286,10 @@ export function capParts(parts: ContextPart[], target: number, fixedPrefix = 0):
|
|
|
342
286
|
const variableTarget = Math.max(0, target - fixedTokens);
|
|
343
287
|
const estimated = variable.reduce((sum, part) => sum + part.tokens, 0);
|
|
344
288
|
if (estimated <= variableTarget || estimated === 0) return parts;
|
|
345
|
-
if (variableTarget === 0)
|
|
289
|
+
if (variableTarget === 0) {
|
|
290
|
+
return [...fixed, ...variable.map((part) => ({ ...part, tokens: 0 }))];
|
|
291
|
+
}
|
|
292
|
+
|
|
346
293
|
let previous = 0;
|
|
347
294
|
let cumulative = 0;
|
|
348
295
|
const capped = variable.map((part, index) => {
|
|
@@ -364,78 +311,137 @@ export function formatTokens(tokens: number): string {
|
|
|
364
311
|
return `${Math.round(tokens / 1_000)}k`;
|
|
365
312
|
}
|
|
366
313
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
314
|
+
export function resolveUsedTokens(
|
|
315
|
+
usage: { tokens: number | null; percent: number | null } | undefined,
|
|
316
|
+
estimated: number,
|
|
317
|
+
contextWindow: number,
|
|
318
|
+
): number {
|
|
319
|
+
const reported = usage?.tokens;
|
|
320
|
+
const fromPercent =
|
|
321
|
+
usage?.percent !== null && usage?.percent !== undefined && contextWindow > 0
|
|
322
|
+
? Math.round((usage.percent / 100) * contextWindow)
|
|
323
|
+
: undefined;
|
|
324
|
+
let resolved = reported ?? fromPercent ?? estimated;
|
|
325
|
+
if (reported !== null && reported !== undefined && fromPercent !== undefined) {
|
|
326
|
+
// 某些 Provider 会返回异常 totalTokens;百分比与底部状态栏不一致时优先采用百分比。
|
|
327
|
+
const tolerance = Math.max(32, Math.round(contextWindow * 0.001));
|
|
328
|
+
if (Math.abs(reported - fromPercent) > tolerance) resolved = fromPercent;
|
|
329
|
+
}
|
|
330
|
+
// tokens 与 percent 可能同时源自异常 usage;数量级明显偏小时回退到实际内容估算。
|
|
331
|
+
if (estimated > 0 && resolved < estimated * 0.25) return estimated;
|
|
332
|
+
return resolved;
|
|
333
|
+
}
|
|
374
334
|
|
|
375
335
|
type ContextBreakdown = {
|
|
376
336
|
parts: ContextPart[];
|
|
377
|
-
|
|
378
|
-
systemPrompt: string;
|
|
379
|
-
toolResults: string;
|
|
337
|
+
previews: Record<PreviewKey, string>;
|
|
380
338
|
};
|
|
381
339
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
340
|
+
function previewValue(value: unknown): string {
|
|
341
|
+
if (typeof value === "string") return value;
|
|
342
|
+
return `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** 按真实请求的 systemPrompt、tools、messages 三部分同步组装计数与预览。 */
|
|
346
|
+
export function collectContextBreakdown(
|
|
347
|
+
ctx: ExtensionCommandContext,
|
|
348
|
+
allTools: ToolInfo[],
|
|
349
|
+
): ContextBreakdown {
|
|
350
|
+
const options = (ctx.getSystemPromptOptions?.() ?? {}) as BuildSystemPromptOptions;
|
|
388
351
|
const systemPrompt = typeof ctx.getSystemPrompt === "function" ? ctx.getSystemPrompt() : "";
|
|
352
|
+
const selectedTools = new Set(options.selectedTools ?? ["read", "bash", "edit", "write"]);
|
|
353
|
+
const toolDefinitionPreview: string[] = [];
|
|
354
|
+
const toolResultPreview: string[] = [];
|
|
355
|
+
const contextPreview: string[] = [];
|
|
356
|
+
let toolDefinitionTokens = 0;
|
|
357
|
+
let toolResultTokens = 0;
|
|
358
|
+
let contextTokens = 0;
|
|
359
|
+
|
|
360
|
+
const memoryPreview: string[] = [];
|
|
361
|
+
let memoryTokens = 0;
|
|
362
|
+
for (const file of options.contextFiles ?? []) {
|
|
363
|
+
memoryTokens += embeddedTokens(systemPrompt, file.content);
|
|
364
|
+
memoryPreview.push(`## ${file.path}\n\n${previewValue(file.content)}`);
|
|
365
|
+
}
|
|
389
366
|
|
|
390
|
-
const contextFileTokens = (options.contextFiles ?? []).reduce(
|
|
391
|
-
(sum, file) => sum + embeddedTokens(systemPrompt, file.content),
|
|
392
|
-
0,
|
|
393
|
-
);
|
|
394
367
|
const skillsText = formatSkillsForPrompt(options.skills ?? []).trim();
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
368
|
+
const skillsTokens = embeddedTokens(systemPrompt, skillsText);
|
|
369
|
+
|
|
370
|
+
for (const tool of allTools) {
|
|
371
|
+
if (!selectedTools.has(tool.name)) continue;
|
|
372
|
+
const definition = {
|
|
373
|
+
name: tool.name,
|
|
374
|
+
description: tool.description,
|
|
375
|
+
parameters: tool.parameters,
|
|
376
|
+
};
|
|
377
|
+
toolDefinitionTokens += tokenEstimate(definition);
|
|
378
|
+
toolDefinitionPreview.push(`## Definition: ${tool.name}\n\n${previewValue(definition)}`);
|
|
401
379
|
}
|
|
402
380
|
|
|
403
|
-
let user = 0;
|
|
404
|
-
let assistant = 0;
|
|
405
|
-
let toolResults = 0;
|
|
406
|
-
let summaries = 0;
|
|
407
|
-
const toolResultPreview: string[] = [];
|
|
408
381
|
for (const entry of ctx.sessionManager.buildContextEntries()) {
|
|
409
382
|
if (entry.type === "message") {
|
|
410
|
-
const
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
383
|
+
const message = entry.message;
|
|
384
|
+
if (message.role === "assistant") {
|
|
385
|
+
for (const block of message.content) {
|
|
386
|
+
if (block.type === "toolCall") {
|
|
387
|
+
contextTokens += tokenEstimate(block.name) + tokenEstimate(block.arguments);
|
|
388
|
+
contextPreview.push(
|
|
389
|
+
`## Assistant tool call: ${block.name}\n\n${previewValue(block.arguments)}`,
|
|
390
|
+
);
|
|
391
|
+
} else if (block.type === "text") {
|
|
392
|
+
contextTokens += tokenEstimate(block.text);
|
|
393
|
+
contextPreview.push(`## Assistant\n\n${block.text}`);
|
|
394
|
+
} else if (block.type === "thinking") {
|
|
395
|
+
contextTokens += tokenEstimate(block.thinking);
|
|
396
|
+
contextPreview.push(`## Assistant thinking\n\n${block.thinking}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} else if (message.role === "toolResult") {
|
|
400
|
+
toolResultTokens += estimateTokens(message);
|
|
401
|
+
toolResultPreview.push(
|
|
402
|
+
`## Result: ${message.toolName}\n\n${previewValue(message.content)}`,
|
|
403
|
+
);
|
|
404
|
+
} else if (message.role === "bashExecution") {
|
|
405
|
+
toolResultTokens += estimateTokens(message);
|
|
406
|
+
toolResultPreview.push(
|
|
407
|
+
`## Bash\n\nCommand:\n\n${previewValue(message.command)}\n\nOutput:\n\n${previewValue(message.output)}`,
|
|
408
|
+
);
|
|
409
|
+
} else if (message.role === "branchSummary" || message.role === "compactionSummary") {
|
|
410
|
+
contextTokens += estimateTokens(message);
|
|
411
|
+
contextPreview.push(`## ${message.role}\n\n${message.summary}`);
|
|
412
|
+
} else {
|
|
413
|
+
contextTokens += estimateTokens(message);
|
|
414
|
+
contextPreview.push(`## ${message.role}\n\n${previewValue(message.content)}`);
|
|
416
415
|
}
|
|
417
416
|
} else if (entry.type === "compaction" || entry.type === "branch_summary") {
|
|
418
|
-
|
|
417
|
+
contextTokens += tokenEstimate(entry.summary);
|
|
418
|
+
contextPreview.push(
|
|
419
|
+
`## ${entry.type === "compaction" ? "Compaction" : "Branch summary"}\n\n${entry.summary}`,
|
|
420
|
+
);
|
|
421
|
+
} else if (entry.type === "custom_message") {
|
|
422
|
+
contextTokens += tokenEstimate(entry.content);
|
|
423
|
+
contextPreview.push(`## Custom: ${entry.customType}\n\n${previewValue(entry.content)}`);
|
|
419
424
|
}
|
|
420
425
|
}
|
|
421
426
|
|
|
422
|
-
const
|
|
423
|
-
const baseSystem = Math.max(0, systemTotal - contextFileTokens - skillTokens - toolTokens);
|
|
424
|
-
const parts: ContextPart[] = [
|
|
425
|
-
{ label: "System prompt", tokens: baseSystem, color: "accent" },
|
|
426
|
-
{ label: "Tools", tokens: toolTokens, color: "success" },
|
|
427
|
-
{ label: "Memory", tokens: contextFileTokens, color: "error" },
|
|
428
|
-
{ label: "Skills", tokens: skillTokens, color: "warning" },
|
|
429
|
-
{ label: "User messages", tokens: user, color: "muted" },
|
|
430
|
-
{ label: "Assistant messages", tokens: assistant, color: "accent" },
|
|
431
|
-
{ label: "Tool results", tokens: toolResults, color: "dim" },
|
|
432
|
-
{ label: "Compaction summaries", tokens: summaries, color: "success" },
|
|
433
|
-
];
|
|
427
|
+
const systemTokens = Math.max(0, tokenEstimate(systemPrompt) - memoryTokens - skillsTokens);
|
|
434
428
|
return {
|
|
435
|
-
parts:
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
429
|
+
parts: [
|
|
430
|
+
{ label: "System prompt", tokens: systemTokens, color: "accent" },
|
|
431
|
+
{ label: "Memory", tokens: memoryTokens, color: "error" },
|
|
432
|
+
{ label: "Skills", tokens: skillsTokens, color: "warning" },
|
|
433
|
+
{ label: "Tools definition", tokens: toolDefinitionTokens, color: "success" },
|
|
434
|
+
{ label: "Tool results", tokens: toolResultTokens, color: "customMessageLabel" },
|
|
435
|
+
{ label: "Context", tokens: contextTokens, color: "warning" },
|
|
436
|
+
] satisfies ContextPart[],
|
|
437
|
+
previews: {
|
|
438
|
+
systemPrompt: systemPrompt || "No system prompt.",
|
|
439
|
+
memoryFiles: memoryPreview.join("\n\n") || "No memory files in context.",
|
|
440
|
+
skills: skillsText || "No skills in context.",
|
|
441
|
+
tools: toolDefinitionPreview.join("\n\n") || "No active tool definitions.",
|
|
442
|
+
toolResults: toolResultPreview.join("\n\n") || "No tool results in the current context.",
|
|
443
|
+
contextFiles: contextPreview.join("\n\n") || "No conversation context.",
|
|
444
|
+
},
|
|
439
445
|
};
|
|
440
446
|
}
|
|
441
447
|
|
|
@@ -445,26 +451,13 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
445
451
|
handler: async (_args, ctx) => {
|
|
446
452
|
const usage = ctx.getContextUsage();
|
|
447
453
|
const contextWindow = usage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
448
|
-
const
|
|
454
|
+
const tools = pi.getAllTools();
|
|
455
|
+
const breakdown = collectContextBreakdown(ctx, tools);
|
|
449
456
|
const estimated = breakdown.parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
part.label === "System prompt" ||
|
|
453
|
-
part.label === "Memory" ||
|
|
454
|
-
part.label === "Skills" ||
|
|
455
|
-
part.label === "Tools",
|
|
456
|
-
);
|
|
457
|
-
const variableParts = breakdown.parts.filter(
|
|
458
|
-
(part) =>
|
|
459
|
-
part.label !== "System prompt" &&
|
|
460
|
-
part.label !== "Memory" &&
|
|
461
|
-
part.label !== "Skills" &&
|
|
462
|
-
part.label !== "Tools",
|
|
463
|
-
);
|
|
464
|
-
const orderedParts = [...fixedParts, ...variableParts];
|
|
465
|
-
const fixedTokens = fixedParts.reduce((sum, part) => sum + part.tokens, 0);
|
|
457
|
+
// System / Memory / Skills / Tools definition 为固定项,capParts 时保持原值不压缩。
|
|
458
|
+
const fixedTokens = breakdown.parts.slice(0, 4).reduce((sum, part) => sum + part.tokens, 0);
|
|
466
459
|
const used = Math.max(resolveUsedTokens(usage, estimated, contextWindow), fixedTokens);
|
|
467
|
-
const parts = capParts(
|
|
460
|
+
const parts = capParts(breakdown.parts, used, 4);
|
|
468
461
|
const attributed = parts.reduce((sum, part) => sum + part.tokens, 0);
|
|
469
462
|
const other = Math.max(0, used - attributed);
|
|
470
463
|
const free = Math.max(0, contextWindow - used);
|
|
@@ -480,70 +473,42 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
480
473
|
return;
|
|
481
474
|
}
|
|
482
475
|
|
|
483
|
-
const options = breakdown.options;
|
|
484
|
-
const toolByName = new Map<string, ToolPreviewInfo>(
|
|
485
|
-
(pi.getAllTools() as ToolPreviewInfo[]).map((tool) => [tool.name, tool] as const),
|
|
486
|
-
);
|
|
487
|
-
const toolContent = (options.selectedTools ?? []).map((name) => {
|
|
488
|
-
const tool = toolByName.get(name);
|
|
489
|
-
const lines = [`## ${name}`];
|
|
490
|
-
if (tool?.description) lines.push(tool.description);
|
|
491
|
-
if (options.toolSnippets?.[name]) lines.push(`Prompt: ${options.toolSnippets[name]}`);
|
|
492
|
-
if (tool?.promptGuidelines?.length) {
|
|
493
|
-
lines.push("Guidelines:", ...tool.promptGuidelines.map((guideline) => `- ${guideline}`));
|
|
494
|
-
}
|
|
495
|
-
return lines.join("\n");
|
|
496
|
-
});
|
|
497
|
-
if (options.promptGuidelines?.length) {
|
|
498
|
-
toolContent.push(
|
|
499
|
-
`## Shared prompt guidelines\n${options.promptGuidelines.map((guideline) => `- ${guideline}`).join("\n")}`,
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
const contextFilesContent = (options.contextFiles ?? [])
|
|
503
|
-
.map((file) => `===== ${file.path} =====\n${file.content}`)
|
|
504
|
-
.join("\n\n");
|
|
505
|
-
const skillsContent = (options.skills ?? [])
|
|
506
|
-
.map((skill) =>
|
|
507
|
-
[
|
|
508
|
-
`## ${skill.name}`,
|
|
509
|
-
skill.description,
|
|
510
|
-
`Path: ${skill.filePath}`,
|
|
511
|
-
`Model invocation: ${skill.disableModelInvocation ? "disabled" : "enabled"}`,
|
|
512
|
-
]
|
|
513
|
-
.filter(Boolean)
|
|
514
|
-
.join("\n"),
|
|
515
|
-
)
|
|
516
|
-
.join("\n\n");
|
|
517
476
|
const rawPreviews: ContextPreview[] = [
|
|
518
477
|
{
|
|
519
478
|
key: "systemPrompt",
|
|
520
479
|
label: "System prompt",
|
|
521
480
|
title: "System Prompt",
|
|
522
|
-
content: breakdown.systemPrompt,
|
|
481
|
+
content: breakdown.previews.systemPrompt,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
key: "memoryFiles",
|
|
485
|
+
label: "Memory",
|
|
486
|
+
title: "Memory Files",
|
|
487
|
+
content: breakdown.previews.memoryFiles,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
key: "skills",
|
|
491
|
+
label: "Skills",
|
|
492
|
+
title: "Skills",
|
|
493
|
+
content: breakdown.previews.skills,
|
|
523
494
|
},
|
|
524
495
|
{
|
|
525
496
|
key: "tools",
|
|
526
|
-
label: "Tools",
|
|
527
|
-
title: "Tools",
|
|
528
|
-
content:
|
|
497
|
+
label: "Tools definition",
|
|
498
|
+
title: "Tools definition",
|
|
499
|
+
content: breakdown.previews.tools,
|
|
529
500
|
},
|
|
530
501
|
{
|
|
531
502
|
key: "toolResults",
|
|
532
503
|
label: "Tool results",
|
|
533
504
|
title: "Tool Results",
|
|
534
|
-
content: breakdown.toolResults,
|
|
505
|
+
content: breakdown.previews.toolResults,
|
|
535
506
|
},
|
|
536
507
|
{
|
|
537
508
|
key: "contextFiles",
|
|
538
|
-
label: "
|
|
539
|
-
title: "
|
|
540
|
-
content:
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
key: "skills",
|
|
544
|
-
label: "Skills",
|
|
545
|
-
title: "Skills",
|
|
546
|
-
content: skillsContent || "No skills loaded.",
|
|
509
|
+
label: "Context",
|
|
510
|
+
title: "Context",
|
|
511
|
+
content: breakdown.previews.contextFiles,
|
|
547
512
|
},
|
|
548
513
|
];
|
|
549
514
|
const previews = rawPreviews.map((preview) => ({
|
|
@@ -557,179 +522,184 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
557
522
|
let selectedPreviewIndex = 0;
|
|
558
523
|
|
|
559
524
|
while (true) {
|
|
525
|
+
// 主弹框也计入活动 overlay,fullscreen 下官方输入链才会把鼠标包放行给行点击。
|
|
560
526
|
activeContextOverlays++;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
return
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
done(visiblePreviews[selectedPreviewIndex]?.key);
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
527
|
+
let action;
|
|
528
|
+
try {
|
|
529
|
+
action = await ctx.ui.custom(
|
|
530
|
+
(tui, theme, _keybindings, done) => {
|
|
531
|
+
ensureFullscreenMouseMotion(tui);
|
|
532
|
+
let previewHitboxes: Array<{
|
|
533
|
+
key: PreviewKey;
|
|
534
|
+
row: number;
|
|
535
|
+
startCol: number;
|
|
536
|
+
endCol: number;
|
|
537
|
+
}> = [];
|
|
538
|
+
let escHitbox: { row: number; startCol: number; endCol: number } | undefined;
|
|
539
|
+
let escHovered = false;
|
|
540
|
+
let hoveredKey: PreviewKey | undefined;
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
invalidate() {},
|
|
544
|
+
handleInput(data: string) {
|
|
545
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
|
|
546
|
+
done(undefined);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (matchesKey(data, Key.up) && visiblePreviews.length > 0) {
|
|
550
|
+
selectedPreviewIndex =
|
|
551
|
+
(selectedPreviewIndex - 1 + visiblePreviews.length) % visiblePreviews.length;
|
|
552
|
+
tui.requestRender();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (matchesKey(data, Key.down) && visiblePreviews.length > 0) {
|
|
556
|
+
selectedPreviewIndex = (selectedPreviewIndex + 1) % visiblePreviews.length;
|
|
557
|
+
tui.requestRender();
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (matchesKey(data, Key.enter)) {
|
|
561
|
+
done(visiblePreviews[selectedPreviewIndex]?.key);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
601
564
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
565
|
+
const mouse = parseSgrMousePacket(data);
|
|
566
|
+
if (!mouse || mouse.final !== "M") return;
|
|
567
|
+
if ((mouse.code & 32) !== 0) {
|
|
568
|
+
// SGR 1003 的无按键 hover code 为 35,不能按左键事件过滤。
|
|
569
|
+
const overEsc = Boolean(
|
|
570
|
+
escHitbox &&
|
|
571
|
+
mouse.row === escHitbox.row &&
|
|
572
|
+
mouse.col >= escHitbox.startCol &&
|
|
573
|
+
mouse.col <= escHitbox.endCol,
|
|
574
|
+
);
|
|
575
|
+
const hovered = previewHitboxes.find(
|
|
576
|
+
(candidate) =>
|
|
577
|
+
mouse.row === candidate.row &&
|
|
578
|
+
mouse.col >= candidate.startCol &&
|
|
579
|
+
mouse.col <= candidate.endCol,
|
|
580
|
+
);
|
|
581
|
+
if (overEsc !== escHovered || hovered?.key !== hoveredKey) {
|
|
582
|
+
escHovered = overEsc;
|
|
583
|
+
hoveredKey = hovered?.key;
|
|
584
|
+
tui.requestRender();
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (mouseBaseButton(mouse.code) !== 0) return;
|
|
589
|
+
if (
|
|
590
|
+
escHitbox &&
|
|
606
591
|
mouse.row === escHitbox.row &&
|
|
607
592
|
mouse.col >= escHitbox.startCol &&
|
|
608
|
-
mouse.col <= escHitbox.endCol
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
mouse.row === candidate.row &&
|
|
613
|
-
mouse.col >= candidate.startCol &&
|
|
614
|
-
mouse.col <= candidate.endCol,
|
|
615
|
-
);
|
|
616
|
-
if ((mouse.code & 32) !== 0) {
|
|
617
|
-
if (overEsc !== escHovered || hitbox?.key !== hoveredKey) {
|
|
618
|
-
escHovered = overEsc;
|
|
619
|
-
hoveredKey = hitbox?.key;
|
|
620
|
-
tui.requestRender();
|
|
593
|
+
mouse.col <= escHitbox.endCol
|
|
594
|
+
) {
|
|
595
|
+
done(undefined);
|
|
596
|
+
return;
|
|
621
597
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
if (hitbox) {
|
|
630
|
-
selectedPreviewIndex = Math.max(
|
|
631
|
-
0,
|
|
632
|
-
visiblePreviews.findIndex((preview) => preview.key === hitbox.key),
|
|
598
|
+
const hitbox = previewHitboxes.find(
|
|
599
|
+
(candidate) =>
|
|
600
|
+
mouse.row === candidate.row &&
|
|
601
|
+
mouse.col >= candidate.startCol &&
|
|
602
|
+
mouse.col <= candidate.endCol,
|
|
633
603
|
);
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
.
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
Math.max(1,
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
604
|
+
if (hitbox) {
|
|
605
|
+
selectedPreviewIndex = Math.max(
|
|
606
|
+
0,
|
|
607
|
+
visiblePreviews.findIndex((preview) => preview.key === hitbox.key),
|
|
608
|
+
);
|
|
609
|
+
done(hitbox.key);
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
render(width: number) {
|
|
613
|
+
const inner = Math.max(1, width - 2);
|
|
614
|
+
const escWidth = visibleWidth("[esc]");
|
|
615
|
+
const percent = contextWindow > 0 ? (used / contextWindow) * 100 : 0;
|
|
616
|
+
const title = theme.bold(theme.fg("accent", "Context Usage"));
|
|
617
|
+
const subtitle = `${formatTokens(used)} / ${formatTokens(contextWindow)} tokens (${percent.toFixed(1)}%)`;
|
|
618
|
+
const barWidth = Math.max(1, Math.min(60, inner - 2));
|
|
619
|
+
let remaining = barWidth;
|
|
620
|
+
const segments = allParts
|
|
621
|
+
.map((part, index) => {
|
|
622
|
+
const cells =
|
|
623
|
+
index === allParts.length - 1
|
|
624
|
+
? remaining
|
|
625
|
+
: Math.min(
|
|
626
|
+
remaining,
|
|
627
|
+
Math.round((part.tokens / Math.max(1, contextWindow)) * barWidth),
|
|
628
|
+
);
|
|
629
|
+
remaining -= cells;
|
|
630
|
+
return theme.fg(part.color, "█".repeat(Math.max(0, cells)));
|
|
631
|
+
})
|
|
632
|
+
.join("");
|
|
633
|
+
const labelWidth = Math.min(
|
|
634
|
+
24,
|
|
635
|
+
Math.max(...allParts.map((part) => part.label.length)),
|
|
636
|
+
);
|
|
637
|
+
const selectedLabel = visiblePreviews[selectedPreviewIndex]?.label;
|
|
638
|
+
const partRows = allParts.map((part) => {
|
|
639
|
+
const pct = contextWindow > 0 ? (part.tokens / contextWindow) * 100 : 0;
|
|
640
|
+
const swatch = theme.fg(part.color, "■");
|
|
641
|
+
const label = part.label.padEnd(labelWidth);
|
|
642
|
+
const amount = `${formatTokens(part.tokens).padStart(7)} ${pct.toFixed(1).padStart(5)}%`;
|
|
643
|
+
const selected = part.label === selectedLabel;
|
|
644
|
+
const hoverLabel = visiblePreviews.find((p) => p.key === hoveredKey)?.label;
|
|
645
|
+
const prefix = selected ? "› " : " ";
|
|
646
|
+
const row = padLine(`${prefix}${swatch} ${label} ${amount}`, inner);
|
|
647
|
+
if (selected) return theme.bg("selectedBg", row);
|
|
648
|
+
return part.label === hoverLabel ? theme.bg("customMessageBg", row) : row;
|
|
649
|
+
});
|
|
650
|
+
const border = (text: string) => theme.fg("border", text);
|
|
651
|
+
const lines = [
|
|
652
|
+
border(`╭${"─".repeat(inner)}╮`),
|
|
653
|
+
`${border("│")}${padLine(` ${title} ${theme.fg("muted", subtitle)}`, inner - escWidth)}${theme.fg(escHovered ? "text" : "muted", "[esc]")}${border("│")}`,
|
|
654
|
+
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
655
|
+
`${border("│")}${padLine(` ${segments}`, inner)}${border("│")}`,
|
|
656
|
+
`${border("│")}${" ".repeat(inner)}${border("│")}`,
|
|
657
|
+
...partRows.map((row) => `${border("│")}${row}${border("│")}`),
|
|
658
|
+
`${border("├")}${border("─".repeat(inner))}${border("┤")}`,
|
|
659
|
+
`${border("│")}${padLine(theme.fg("dim", " ↑↓ select · Click / Enter to preview · [esc] close"), inner)}${border("│")}`,
|
|
660
|
+
border(`╰${"─".repeat(inner)}╯`),
|
|
661
|
+
];
|
|
662
|
+
|
|
663
|
+
const terminalHeight = Math.max(1, tui.terminal.rows);
|
|
664
|
+
const maxHeight = Math.min(
|
|
665
|
+
Math.max(1, Math.floor(terminalHeight * 0.9)),
|
|
666
|
+
Math.max(1, terminalHeight - 2),
|
|
667
|
+
);
|
|
668
|
+
const visibleHeight = Math.min(lines.length, maxHeight);
|
|
669
|
+
const overlayTop =
|
|
670
|
+
1 + Math.floor((Math.max(1, terminalHeight - 2) - visibleHeight) / 2);
|
|
671
|
+
const overlayLeft = Math.floor((Math.max(1, tui.terminal.columns) - width) / 2);
|
|
672
|
+
escHitbox = escCloseHitbox({ left: overlayLeft, top: overlayTop, width });
|
|
673
|
+
previewHitboxes = visiblePreviews.flatMap((preview) => {
|
|
674
|
+
const partIndex = allParts.findIndex((part) => part.label === preview.label);
|
|
675
|
+
const line = 5 + partIndex;
|
|
676
|
+
return partIndex >= 0 && line < visibleHeight
|
|
677
|
+
? [
|
|
678
|
+
{
|
|
679
|
+
key: preview.key,
|
|
680
|
+
row: overlayTop + line + 1,
|
|
681
|
+
startCol: overlayLeft + 1,
|
|
682
|
+
endCol: overlayLeft + width,
|
|
683
|
+
},
|
|
684
|
+
]
|
|
685
|
+
: [];
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
return lines;
|
|
689
|
+
},
|
|
690
|
+
};
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
overlay: true,
|
|
694
|
+
overlayOptions: {
|
|
695
|
+
anchor: "center",
|
|
696
|
+
width: 64,
|
|
697
|
+
minWidth: 44,
|
|
698
|
+
maxHeight: "90%",
|
|
699
|
+
margin: 1,
|
|
716
700
|
},
|
|
717
|
-
};
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
overlay: true,
|
|
721
|
-
overlayOptions: {
|
|
722
|
-
anchor: "center",
|
|
723
|
-
width: 64,
|
|
724
|
-
minWidth: 44,
|
|
725
|
-
maxHeight: "90%",
|
|
726
|
-
margin: 1,
|
|
727
701
|
},
|
|
728
|
-
|
|
729
|
-
);
|
|
730
|
-
let action;
|
|
731
|
-
try {
|
|
732
|
-
action = await actionPromise;
|
|
702
|
+
);
|
|
733
703
|
} finally {
|
|
734
704
|
activeContextOverlays--;
|
|
735
705
|
}
|