pi-cc-extensions 0.8.50 → 0.8.52
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 +9 -12
- package/README.md +9 -12
- package/extensions/config/config.ts +10 -10
- package/extensions/config/panel.ts +113 -26
- package/extensions/feature/agent-autocomplete.ts +3 -1
- package/extensions/feature/agent-summary.ts +8 -8
- package/extensions/feature/compact-thinking.ts +116 -55
- package/extensions/feature/context.ts +1 -1
- package/extensions/feature/markdown-enhance.ts +4 -1
- package/extensions/feature/pi-startup-header.ts +3 -8
- package/extensions/feature/session-reference-core.ts +3 -2
- package/extensions/feature/session-reference.ts +49 -21
- package/extensions/index.ts +12 -0
- package/extensions/renderer/compact-mode.ts +949 -0
- package/extensions/renderer/index.ts +73 -17
- package/extensions/renderer/message-display.ts +17 -3
- package/extensions/renderer/mouse-hover.ts +130 -0
- package/extensions/renderer/mouse-interaction.ts +74 -532
- package/extensions/renderer/mouse-layout.ts +160 -0
- package/extensions/renderer/mouse-packets.ts +143 -0
- package/extensions/renderer/mouse-scroll.ts +184 -0
- package/extensions/renderer/show-more-hint.ts +1 -3
- package/extensions/renderer/tool-diff/diff-renderer.ts +59 -2
- package/extensions/renderer/tool-grouping.ts +9 -2
- package/extensions/renderer/tool-result.ts +24 -8
- package/extensions/utils/agent-summary.ts +16 -2
- package/package.json +3 -2
- package/themes/cc-dark.json +3 -2
- package/themes/cc-light.json +3 -3
|
@@ -3,6 +3,11 @@ import { ToolExecutionComponent } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
import type { CompactThinkingController } from "../feature/compact-thinking.ts";
|
|
5
5
|
import { installToolGrouping, type ToolGroupingHooks } from "./tool-grouping.ts";
|
|
6
|
+
import {
|
|
7
|
+
installCompactMode,
|
|
8
|
+
refreshCompactModeComponents,
|
|
9
|
+
type CompactModeHooks,
|
|
10
|
+
} from "./compact-mode.ts";
|
|
6
11
|
import { isLazyProxyTui } from "../utils/fullscreen-detect.ts";
|
|
7
12
|
import { showCcstylePanel } from "../config/panel.ts";
|
|
8
13
|
import {
|
|
@@ -33,6 +38,7 @@ import {
|
|
|
33
38
|
countLines,
|
|
34
39
|
hasExpandableDetail,
|
|
35
40
|
headTruncateToWidth,
|
|
41
|
+
insetComponent,
|
|
36
42
|
isToolExpanded,
|
|
37
43
|
middleTruncateToWidth,
|
|
38
44
|
oneLine,
|
|
@@ -49,6 +55,7 @@ import {
|
|
|
49
55
|
toolViewportWidth,
|
|
50
56
|
} from "./tool-result.ts";
|
|
51
57
|
import { showMoreHintText } from "./show-more-hint.ts";
|
|
58
|
+
import type { CompactThinkingQuery } from "./compact-mode.ts";
|
|
52
59
|
import {
|
|
53
60
|
installWriteOverride,
|
|
54
61
|
renderRichToolResult,
|
|
@@ -74,10 +81,19 @@ const ANSI_FG_RESET = "\x1b[39m";
|
|
|
74
81
|
function refreshCurrentTranscript(ctx?: any, toolGrouping?: ToolGroupingHooks): void {
|
|
75
82
|
toolGrouping?.refresh(toolMouseTui);
|
|
76
83
|
refreshMessageDisplays(toolMouseTui);
|
|
84
|
+
refreshCompactModeComponents(toolMouseTui);
|
|
85
|
+
compactModeHooks?.refresh();
|
|
77
86
|
toolMouseTui?.requestRender?.(true);
|
|
78
87
|
ctx?.ui?.requestRender?.(true);
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
let compactModeHooks: CompactModeHooks | undefined;
|
|
91
|
+
|
|
92
|
+
function syncCompactMode(ctx: any): void {
|
|
93
|
+
refreshCompactModeComponents(toolMouseTui);
|
|
94
|
+
compactModeHooks?.sync(ctx);
|
|
95
|
+
}
|
|
96
|
+
|
|
81
97
|
function applyStyleMode(mode: CompactStyleMode, ctx: any, toolGrouping?: ToolGroupingHooks): void {
|
|
82
98
|
config.mode = mode;
|
|
83
99
|
saveConfig();
|
|
@@ -92,6 +108,9 @@ function applyStyleMode(mode: CompactStyleMode, ctx: any, toolGrouping?: ToolGro
|
|
|
92
108
|
if (toolMouseTui && !isLazyProxyTui(toolMouseTui)) {
|
|
93
109
|
toolMouseTui.terminal?.write?.(TOOL_MOUSE_DISABLE);
|
|
94
110
|
}
|
|
111
|
+
} else if (mode === "compact") {
|
|
112
|
+
// 切入 compact:先收集当前 transcript,再同步全局展开状态和补丁所有权。
|
|
113
|
+
syncCompactMode(ctx);
|
|
95
114
|
}
|
|
96
115
|
refreshCurrentTranscript(ctx, toolGrouping);
|
|
97
116
|
ctx.ui.notify(`Claude Code style: ${mode}`, "info");
|
|
@@ -113,17 +132,6 @@ function singleLine(text: string) {
|
|
|
113
132
|
};
|
|
114
133
|
}
|
|
115
134
|
|
|
116
|
-
function insetComponent(component: any): any {
|
|
117
|
-
return {
|
|
118
|
-
render: (width: number) =>
|
|
119
|
-
component.render(Math.max(1, width - 1)).map((line: string) => {
|
|
120
|
-
const nestedMarker = line.replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*)↳/, "$1 ↳");
|
|
121
|
-
return ` ${nestedMarker}`;
|
|
122
|
-
}),
|
|
123
|
-
invalidate: () => component.invalidate?.(),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
135
|
function humanizeToolLabel(label: string): string {
|
|
128
136
|
return label
|
|
129
137
|
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
@@ -763,7 +771,7 @@ function installToolExpandedBackground(): () => void {
|
|
|
763
771
|
const host = globalThis as any;
|
|
764
772
|
const previous = host[TOOL_EXPANDED_BACKGROUND_PATCH] as ToolExpandedBackgroundPatch | undefined;
|
|
765
773
|
if (previous) previous.dispose();
|
|
766
|
-
const prototype = ToolExecutionComponent.prototype;
|
|
774
|
+
const prototype = ToolExecutionComponent.prototype as unknown as { updateDisplay: () => void };
|
|
767
775
|
const original = prototype.updateDisplay;
|
|
768
776
|
const patch: ToolExpandedBackgroundPatch = {
|
|
769
777
|
active: true,
|
|
@@ -817,6 +825,7 @@ export default function (
|
|
|
817
825
|
| {
|
|
818
826
|
globalToolRendering: GlobalToolRenderPatch;
|
|
819
827
|
toolGrouping: ToolGroupingHooks;
|
|
828
|
+
compactMode: CompactModeHooks;
|
|
820
829
|
disposeMessageDisplay: () => void;
|
|
821
830
|
disposeToolExpandedBackground: () => void;
|
|
822
831
|
}
|
|
@@ -828,12 +837,18 @@ export default function (
|
|
|
828
837
|
if (installation) return installation;
|
|
829
838
|
const globalToolRendering = installGlobalToolRendering(writeExecutionMetadata);
|
|
830
839
|
const toolGrouping = installToolGrouping(() => config.mode === "on");
|
|
840
|
+
const compactMode = installCompactMode({
|
|
841
|
+
query: compactThinking as CompactThinkingQuery | undefined,
|
|
842
|
+
writeMetadata: writeExecutionMetadata,
|
|
843
|
+
});
|
|
844
|
+
compactModeHooks = compactMode;
|
|
831
845
|
const disposeMessageDisplay = installMessageDisplayRendering();
|
|
832
846
|
const disposeToolExpandedBackground = installToolExpandedBackground();
|
|
833
847
|
deactivateLegacyCompactionRendering();
|
|
834
848
|
installation = {
|
|
835
849
|
globalToolRendering,
|
|
836
850
|
toolGrouping,
|
|
851
|
+
compactMode,
|
|
837
852
|
disposeMessageDisplay,
|
|
838
853
|
disposeToolExpandedBackground,
|
|
839
854
|
};
|
|
@@ -845,6 +860,11 @@ export default function (
|
|
|
845
860
|
getArgumentCompletions: (prefix) => {
|
|
846
861
|
const topLevel = [
|
|
847
862
|
{ value: "on", label: "on", description: "Enable Claude Code style" },
|
|
863
|
+
{
|
|
864
|
+
value: "compact",
|
|
865
|
+
label: "compact",
|
|
866
|
+
description: "One summary line per assistant message",
|
|
867
|
+
},
|
|
848
868
|
{ value: "off", label: "off", description: "Use Pi's native renderer" },
|
|
849
869
|
{ value: "status", label: "status", description: "Show full configuration" },
|
|
850
870
|
{ value: "panel", label: "panel", description: "Open interactive settings panel" },
|
|
@@ -853,7 +873,7 @@ export default function (
|
|
|
853
873
|
},
|
|
854
874
|
handler: async (args, ctx) => {
|
|
855
875
|
const arg = args.trim().toLowerCase();
|
|
856
|
-
if (!arg || arg === "panel" || arg === "on" || arg === "off") {
|
|
876
|
+
if (!arg || arg === "panel" || arg === "on" || arg === "compact" || arg === "off") {
|
|
857
877
|
if (ctx?.mode !== "tui" || !ctx?.hasUI) {
|
|
858
878
|
ctx.ui?.notify?.("/ccstyle requires TUI mode", "warning");
|
|
859
879
|
return;
|
|
@@ -868,7 +888,7 @@ export default function (
|
|
|
868
888
|
compactThinking,
|
|
869
889
|
);
|
|
870
890
|
} else {
|
|
871
|
-
applyStyleMode(arg, ctx, hooks.toolGrouping);
|
|
891
|
+
applyStyleMode(arg as CompactStyleMode, ctx, hooks.toolGrouping);
|
|
872
892
|
}
|
|
873
893
|
return;
|
|
874
894
|
}
|
|
@@ -876,10 +896,28 @@ export default function (
|
|
|
876
896
|
ctx.ui.notify(`Claude Code style: ${formatConfigStatus(config)}`, "info");
|
|
877
897
|
return;
|
|
878
898
|
}
|
|
879
|
-
ctx.ui.notify("Usage: /ccstyle [on|off|status|panel]", "warning");
|
|
899
|
+
ctx.ui.notify("Usage: /ccstyle [on|compact|off|status|panel]", "warning");
|
|
880
900
|
},
|
|
881
901
|
});
|
|
882
902
|
|
|
903
|
+
pi.on("message_update", async (event) => {
|
|
904
|
+
// compact-thinking 在 session_start 时于 compact 补丁之上再装一层;
|
|
905
|
+
// 扩展事件先于 interactive-mode 的 updateContent 派发,此处先重新认领。
|
|
906
|
+
if (config.mode === "compact" && event.message?.role === "assistant") {
|
|
907
|
+
compactModeHooks?.assertOwnership();
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
pi.on("tool_execution_end", async (event) => {
|
|
912
|
+
if (config.mode !== "compact") return;
|
|
913
|
+
// Agent 工具在 tool_execution_end 收尾思考:延迟到所有监听器之后刷新,
|
|
914
|
+
// 让 compact-thinking 先写入最终时长,再重绘摘要行。
|
|
915
|
+
const toolCallId: string | undefined = event?.toolCallId;
|
|
916
|
+
setTimeout(() => {
|
|
917
|
+
compactModeHooks?.refreshToolCallMessage(toolCallId);
|
|
918
|
+
}, 0);
|
|
919
|
+
});
|
|
920
|
+
|
|
883
921
|
pi.on("session_start", async (event, ctx) => {
|
|
884
922
|
// 延迟到 session_start 注册 write override:加载阶段 getAllTools 不可用且其他扩展
|
|
885
923
|
// 尚未注册工具,无法检测外部 write 所有者(如 pi-spark),直接注册会与对方撞名。
|
|
@@ -893,6 +931,11 @@ export default function (
|
|
|
893
931
|
hooks.toolGrouping.setTheme(ctx.ui.theme);
|
|
894
932
|
setMessageDisplayTheme(ctx.ui.theme);
|
|
895
933
|
ctx.ui.setStatus("ccstyle", undefined);
|
|
934
|
+
// 先收集 resume transcript,再同步 compact 补丁与全局展开状态。
|
|
935
|
+
syncCompactMode(ctx);
|
|
936
|
+
// compact-thinking 的 session_start 处理在本 handler 之后执行(在其之上再装
|
|
937
|
+
// 一层 updateContent);延迟再同步一次,保证 compact 补丁最终位于外层。
|
|
938
|
+
setTimeout(() => syncCompactMode(ctx), 0);
|
|
896
939
|
scheduleSessionRender(() => hooks.toolGrouping.refresh(toolMouseTui));
|
|
897
940
|
});
|
|
898
941
|
|
|
@@ -904,7 +947,18 @@ export default function (
|
|
|
904
947
|
if (!hooks) return;
|
|
905
948
|
hooks.toolGrouping.setTheme(ctx.ui.theme);
|
|
906
949
|
setMessageDisplayTheme(ctx.ui.theme);
|
|
907
|
-
|
|
950
|
+
syncCompactMode(ctx);
|
|
951
|
+
scheduleSessionRender(() => {
|
|
952
|
+
syncCompactMode(ctx);
|
|
953
|
+
hooks.toolGrouping.refresh(toolMouseTui);
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
pi.on("session_tree", async (event, ctx) => {
|
|
958
|
+
// 会话树重建后在当前帧和下一帧各同步一次,替换旧组件引用。
|
|
959
|
+
if (ctx?.mode !== "tui" || !ctx?.hasUI) return;
|
|
960
|
+
syncCompactMode(ctx);
|
|
961
|
+
scheduleSessionRender(() => syncCompactMode(ctx));
|
|
908
962
|
});
|
|
909
963
|
|
|
910
964
|
pi.on("tool_execution_start", async (event, ctx) => {
|
|
@@ -925,8 +979,10 @@ export default function (
|
|
|
925
979
|
return;
|
|
926
980
|
deactivateGlobalToolRendering(current.globalToolRendering);
|
|
927
981
|
current.toolGrouping.shutdown();
|
|
928
|
-
current.disposeMessageDisplay();
|
|
929
982
|
current.disposeToolExpandedBackground();
|
|
983
|
+
current.compactMode.shutdown();
|
|
984
|
+
compactModeHooks = undefined;
|
|
985
|
+
current.disposeMessageDisplay();
|
|
930
986
|
deactivateLegacyCompactionRendering();
|
|
931
987
|
clearAllAnimations();
|
|
932
988
|
installation = undefined;
|
|
@@ -45,7 +45,8 @@ const SKILL_KIND: DisplayKind = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
const COMPACTION_KIND: DisplayKind = {
|
|
48
|
-
title: (component) =>
|
|
48
|
+
title: (component) =>
|
|
49
|
+
`Compacted from ${Number(component.message?.tokensBefore ?? 0).toLocaleString()} tokens`,
|
|
49
50
|
body: (component) => String(component.message?.summary ?? ""),
|
|
50
51
|
};
|
|
51
52
|
|
|
@@ -58,6 +59,10 @@ function renderCcstyle(component: any, kind: DisplayKind): void {
|
|
|
58
59
|
const theme = displayTheme;
|
|
59
60
|
if (!theme) return; // 主题未就绪时保留原生渲染
|
|
60
61
|
if (component.bgFn) component.setBgFn?.(undefined); // 与工具调用一致,去掉灰底
|
|
62
|
+
if (component.paddingY !== 0) {
|
|
63
|
+
component._ccstyleOriginalPaddingY = component.paddingY;
|
|
64
|
+
component.paddingY = 0; // 工具组件 paddingY=0;原生 Box 默认 1,会上下各留一个空行
|
|
65
|
+
}
|
|
61
66
|
component.clear();
|
|
62
67
|
const icon = `${BRIGHT_GREEN}✓${ANSI_FG_RESET}`; // 已完成消息,等同工具成功态
|
|
63
68
|
const title = theme.fg("toolTitle", kind.title(component));
|
|
@@ -75,7 +80,11 @@ function renderCcstyle(component: any, kind: DisplayKind): void {
|
|
|
75
80
|
);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
type PatchEntry = {
|
|
83
|
+
type PatchEntry = {
|
|
84
|
+
prototype: any;
|
|
85
|
+
installed: (...args: any[]) => void;
|
|
86
|
+
original: (...args: any[]) => void;
|
|
87
|
+
};
|
|
79
88
|
|
|
80
89
|
/** patch 三个消息组件的 updateDisplay,返回统一 dispose(/reload 链安全)。 */
|
|
81
90
|
export function installMessageDisplayRendering(): () => void {
|
|
@@ -91,7 +100,7 @@ export function installMessageDisplayRendering(): () => void {
|
|
|
91
100
|
const prototype = ComponentClass.prototype;
|
|
92
101
|
const original = prototype.updateDisplay;
|
|
93
102
|
const installed = function (this: any) {
|
|
94
|
-
if (patch.active && config.mode
|
|
103
|
+
if (patch.active && config.mode !== "off") {
|
|
95
104
|
try {
|
|
96
105
|
renderCcstyle(this, kind);
|
|
97
106
|
return;
|
|
@@ -99,6 +108,11 @@ export function installMessageDisplayRendering(): () => void {
|
|
|
99
108
|
// 渲染失败回退原生
|
|
100
109
|
}
|
|
101
110
|
}
|
|
111
|
+
// 回退原生前恢复 paddingY,避免原生渲染丢失上下内边距
|
|
112
|
+
if (this._ccstyleOriginalPaddingY !== undefined) {
|
|
113
|
+
this.paddingY = this._ccstyleOriginalPaddingY;
|
|
114
|
+
delete this._ccstyleOriginalPaddingY;
|
|
115
|
+
}
|
|
102
116
|
original.call(this);
|
|
103
117
|
};
|
|
104
118
|
prototype.updateDisplay = installed;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { setHoveredCompactAssistant } from "./compact-mode.ts";
|
|
2
|
+
import {
|
|
3
|
+
isExpandedToolIoView,
|
|
4
|
+
invalidateIoView,
|
|
5
|
+
type ExpandedToolIoView,
|
|
6
|
+
type ToolIoSection,
|
|
7
|
+
} from "./tool-result.ts";
|
|
8
|
+
import type { ToolGroupComponent } from "./tool-grouping.ts";
|
|
9
|
+
import { componentAtLocalRow, type ComponentRowHit } from "./mouse-layout.ts";
|
|
10
|
+
import { setScrollButtonHovered } from "./mouse-scroll.ts";
|
|
11
|
+
|
|
12
|
+
const TOOL_HOVER_STATE_KEY = Symbol.for("pi.ccstyle.tool-hover-state");
|
|
13
|
+
|
|
14
|
+
type SharedToolHoverState = { toolCallId: string | null };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* hover 状态宿主(Symbol.for + globalThis):模块 reload 后新旧实例共享,
|
|
18
|
+
* 测试依赖该跨实例语义。
|
|
19
|
+
*/
|
|
20
|
+
export function sharedToolHoverState(): SharedToolHoverState {
|
|
21
|
+
const host = globalThis as any;
|
|
22
|
+
return (host[TOOL_HOVER_STATE_KEY] ??= { toolCallId: null });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export let hoveredToolCallId: string | null = sharedToolHoverState().toolCallId;
|
|
26
|
+
|
|
27
|
+
export function setHoveredToolCallId(toolCallId: string | null): void {
|
|
28
|
+
hoveredToolCallId = toolCallId;
|
|
29
|
+
sharedToolHoverState().toolCallId = toolCallId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isToolCallHovered(toolCallId: string | null | undefined): boolean {
|
|
33
|
+
return Boolean(toolCallId && sharedToolHoverState().toolCallId === toolCallId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let hoveredToolGroup: ToolGroupComponent | null = null;
|
|
37
|
+
|
|
38
|
+
export function setHoveredToolGroup(group: ToolGroupComponent | null): boolean {
|
|
39
|
+
if (group === hoveredToolGroup) return false;
|
|
40
|
+
hoveredToolGroup?.setHintHovered(false);
|
|
41
|
+
hoveredToolGroup = group;
|
|
42
|
+
group?.setHintHovered(true);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let hoveredToolIoView: ExpandedToolIoView | null = null;
|
|
47
|
+
let hoveredToolIoSection: ToolIoSection | null = null;
|
|
48
|
+
|
|
49
|
+
export function setHoveredToolIo(
|
|
50
|
+
view: ExpandedToolIoView | null,
|
|
51
|
+
section: ToolIoSection | null,
|
|
52
|
+
): boolean {
|
|
53
|
+
// resultRendererComponent 可能是 Text/第三方 renderer;reload 后也可能残留旧实例。
|
|
54
|
+
const nextView = isExpandedToolIoView(view) ? view : null;
|
|
55
|
+
const nextSection = nextView ? section : null;
|
|
56
|
+
if (nextView === hoveredToolIoView && nextSection === hoveredToolIoSection) return false;
|
|
57
|
+
if (isExpandedToolIoView(hoveredToolIoView)) {
|
|
58
|
+
hoveredToolIoView.setHoveredSection(null);
|
|
59
|
+
invalidateIoView(hoveredToolIoView);
|
|
60
|
+
}
|
|
61
|
+
hoveredToolIoView = nextView;
|
|
62
|
+
hoveredToolIoSection = nextSection;
|
|
63
|
+
if (nextView) {
|
|
64
|
+
nextView.setHoveredSection(nextSection);
|
|
65
|
+
invalidateIoView(nextView);
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** hover 与点击共用组件定位;同一布局下按容器/宽度/行缓存,避免 motion 重复渲染。 */
|
|
71
|
+
let fullscreenHoverCacheLayout: unknown = null;
|
|
72
|
+
let fullscreenHoverComponentCache = new WeakMap<object, Map<string, ComponentRowHit | null>>();
|
|
73
|
+
|
|
74
|
+
export function cachedFullscreenComponentAtRow(
|
|
75
|
+
layout: any,
|
|
76
|
+
container: any,
|
|
77
|
+
row: number,
|
|
78
|
+
width: number,
|
|
79
|
+
): ComponentRowHit | null {
|
|
80
|
+
if (!container || typeof container !== "object") return null;
|
|
81
|
+
if (fullscreenHoverCacheLayout !== layout) {
|
|
82
|
+
fullscreenHoverCacheLayout = layout;
|
|
83
|
+
fullscreenHoverComponentCache = new WeakMap();
|
|
84
|
+
}
|
|
85
|
+
let rows = fullscreenHoverComponentCache.get(container);
|
|
86
|
+
if (!rows) {
|
|
87
|
+
rows = new Map();
|
|
88
|
+
fullscreenHoverComponentCache.set(container, rows);
|
|
89
|
+
}
|
|
90
|
+
const key = `${width}:${row}`;
|
|
91
|
+
if (rows.has(key)) return rows.get(key) ?? null;
|
|
92
|
+
const hit = componentAtLocalRow(container, row, width);
|
|
93
|
+
rows.set(key, hit);
|
|
94
|
+
return hit;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** fullscreen 鼠标悬停目标。 */
|
|
98
|
+
export type FullscreenHoverTarget =
|
|
99
|
+
| { kind: "button" }
|
|
100
|
+
| { kind: "group"; component: ToolGroupComponent }
|
|
101
|
+
| { kind: "assistant"; component: any }
|
|
102
|
+
| {
|
|
103
|
+
kind: "tool";
|
|
104
|
+
component: any;
|
|
105
|
+
view: ExpandedToolIoView | null;
|
|
106
|
+
section: ToolIoSection | null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** 悬停状态变化才触发渲染(motion 事件密集,状态不变跳过)。 */
|
|
110
|
+
export function applyFullscreenHover(tui: any, target: FullscreenHoverTarget | null): void {
|
|
111
|
+
let changed = false;
|
|
112
|
+
const nextCallId =
|
|
113
|
+
target?.kind === "tool" && !target.component.expanded
|
|
114
|
+
? (target.component.toolCallId ?? null)
|
|
115
|
+
: null;
|
|
116
|
+
if (nextCallId !== sharedToolHoverState().toolCallId) {
|
|
117
|
+
setHoveredToolCallId(nextCallId);
|
|
118
|
+
changed = true;
|
|
119
|
+
}
|
|
120
|
+
const nextGroup = target?.kind === "group" ? target.component : null;
|
|
121
|
+
if (setHoveredToolGroup(nextGroup)) changed = true;
|
|
122
|
+
const nextAssistant = target?.kind === "assistant" ? target.component : null;
|
|
123
|
+
if (setHoveredCompactAssistant(nextAssistant)) changed = true;
|
|
124
|
+
const nextView = target?.kind === "tool" ? target.view : null;
|
|
125
|
+
const nextSection = target?.kind === "tool" ? target.section : null;
|
|
126
|
+
if (setHoveredToolIo(nextView, nextSection)) changed = true;
|
|
127
|
+
const nextButton = target?.kind === "button";
|
|
128
|
+
if (setScrollButtonHovered(nextButton)) changed = true;
|
|
129
|
+
if (changed) tui.requestRender?.();
|
|
130
|
+
}
|