pi-one-ui 0.7.0 → 0.7.1
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/CHANGELOG.md +25 -1
- package/README.en.md +19 -6
- package/README.md +19 -6
- package/extensions/app/config/context.ts +42 -0
- package/extensions/app/config/editor.ts +80 -0
- package/extensions/app/config/footer.ts +291 -0
- package/extensions/app/config/panel.ts +1 -5
- package/extensions/app/config/renderer.ts +1 -6
- package/extensions/app/config/shell.ts +358 -1435
- package/extensions/app/config/store.ts +22 -1
- package/extensions/app/config/values.ts +33 -0
- package/extensions/app/config/working-line.ts +100 -0
- package/extensions/app/overlay/input-router.ts +56 -32
- package/extensions/app/overlay/overlay-manager.ts +183 -21
- package/extensions/app/runtime/event-coordinator.ts +90 -42
- package/extensions/{features → app/runtime}/flush-docked-bash.ts +12 -2
- package/extensions/app/runtime/render-scheduler.ts +35 -31
- package/extensions/app/runtime/tui-runtime.ts +315 -312
- package/extensions/app/settings/controller.ts +202 -0
- package/extensions/app/settings/panel.ts +81 -0
- package/extensions/app/settings/presets.ts +43 -0
- package/extensions/features/context-inspector/index.ts +83 -662
- package/extensions/layouts/context/index.ts +58 -161
- package/extensions/layouts/context/message/user-message.ts +1 -1
- package/extensions/layouts/context/renderer/index.ts +31 -13
- package/extensions/layouts/context/renderer/mouse/interaction.ts +29 -14
- package/extensions/layouts/editor/controller.ts +9 -18
- package/extensions/layouts/editor/factory.ts +3 -3
- package/extensions/layouts/footer/controller.ts +30 -19
- package/extensions/layouts/footer/footer.ts +2 -2
- package/extensions/layouts/overlay/context-inspector.ts +497 -0
- package/extensions/{app → layouts}/overlay/selector-border.ts +5 -5
- package/extensions/{app → layouts}/overlay/selector-controller.ts +1 -1
- package/extensions/layouts/overlay/settings-panel.ts +359 -0
- package/extensions/{app/commands → layouts/overlay}/settings-previews.ts +4 -4
- package/extensions/layouts/working-line/controller.ts +3 -29
- package/extensions/layouts/working-line/working-line.ts +17 -114
- package/extensions/shared/working-line-text.ts +108 -0
- package/package.json +1 -1
- package/extensions/app/host/pi-extension-port.ts +0 -25
- package/extensions/app/host/pi-ui-port.ts +0 -52
- package/extensions/app/host/tui-capabilities.ts +0 -27
- package/extensions/app/ownership/layout-registry.ts +0 -65
- package/extensions/app/ownership.ts +0 -32
- package/extensions/app/panel.ts +0 -770
- package/extensions/app/presets.ts +0 -35
- package/extensions/app/runtime/runtime-state.ts +0 -83
- /package/extensions/{layouts/working-line → app/config}/working-line-messages.ts +0 -0
- /package/extensions/app/{ownership → runtime}/prototype-patch-registry.ts +0 -0
|
@@ -3,187 +3,84 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
Theme,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { config } from "../../app/config/renderer.ts";
|
|
6
|
+
import { type Config, config } from "../../app/config/renderer.ts";
|
|
7
7
|
import {
|
|
8
8
|
hasUnsupportedComponentStyle,
|
|
9
|
-
loadConfig,
|
|
10
|
-
mergeConfig,
|
|
11
|
-
saveUserMessagesComponentPatch,
|
|
12
|
-
type UserMessagesComponentConfig,
|
|
13
9
|
type ZentuiConfig,
|
|
14
10
|
} from "../../app/config/shell.ts";
|
|
15
|
-
import {
|
|
16
|
-
// shell
|
|
17
|
-
import piAliases from "../../features/aliases.ts";
|
|
18
|
-
import context from "../../features/context-inspector/index.ts";
|
|
19
|
-
import effortCommand from "../../features/effort-command.ts";
|
|
20
|
-
import { installFlushDockedBash } from "../../features/flush-docked-bash.ts";
|
|
21
|
-
import sessionReference from "../../features/session-reference/index.ts";
|
|
22
|
-
// feature
|
|
23
|
-
import agentAutocomplete from "../../features/subagent-autocomplete.ts";
|
|
24
|
-
import {
|
|
25
|
-
installUserMessageStyle,
|
|
26
|
-
removeUserMessageStyle,
|
|
27
|
-
} from "./message/user-message.ts";
|
|
28
|
-
// renderer
|
|
11
|
+
import { installUserMessageStyle } from "./message/user-message.ts";
|
|
29
12
|
import registerContextRenderer, {
|
|
30
13
|
getCompactThinkingConfig,
|
|
14
|
+
type RendererExtensionOptions,
|
|
15
|
+
type RendererRuntimeController,
|
|
31
16
|
} from "./renderer/index.ts";
|
|
32
17
|
import markdownEnhance from "./renderer/markdown-enhance.ts";
|
|
33
18
|
import agentSummary from "./summary/index.ts";
|
|
34
19
|
import { installCompactThinking } from "./thinking/compact-thinking.ts";
|
|
35
20
|
|
|
36
|
-
export type ContextRuntimeController = {
|
|
37
|
-
|
|
38
|
-
updateConfig: (
|
|
39
|
-
partial: Partial<typeof config>,
|
|
40
|
-
ctx: ExtensionContext,
|
|
41
|
-
) => void;
|
|
42
|
-
setUserMessagesComponent: (
|
|
43
|
-
patch: Partial<UserMessagesComponentConfig>,
|
|
44
|
-
ctx: ExtensionContext,
|
|
45
|
-
) => void;
|
|
21
|
+
export type ContextRuntimeController = RendererRuntimeController & {
|
|
22
|
+
applyConfig(ctx: ExtensionContext, previous: Config): void;
|
|
46
23
|
};
|
|
47
|
-
|
|
48
24
|
export type ContextExtensionOptions = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*/
|
|
52
|
-
onRuntimeController?: (controller: ContextRuntimeController) => void;
|
|
25
|
+
readonly getConfig: () => ZentuiConfig;
|
|
26
|
+
readonly services?: RendererExtensionOptions["services"];
|
|
53
27
|
};
|
|
54
28
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
} catch {
|
|
80
|
-
cleanupUserMessageStyle = () => {};
|
|
81
|
-
userMessageStyleInstalled = false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Removes the Context-owned User Message renderer and clears its patch state.
|
|
87
|
-
*/
|
|
88
|
-
function uninstallContextUserMessages(): void {
|
|
89
|
-
try {
|
|
90
|
-
cleanupUserMessageStyle();
|
|
91
|
-
} finally {
|
|
92
|
-
cleanupUserMessageStyle = () => {};
|
|
93
|
-
userMessageStyleInstalled = false;
|
|
94
|
-
removeUserMessageStyle();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Reconciles User Message ownership with the latest shell configuration.
|
|
100
|
-
*/
|
|
101
|
-
function reconcileContextUserMessages(): void {
|
|
102
|
-
const enabled =
|
|
103
|
-
contextConfig.components.userMessages.enabled &&
|
|
104
|
-
!hasUnsupportedComponentStyle(contextConfig, "userMessages");
|
|
105
|
-
if (enabled) installContextUserMessages();
|
|
106
|
-
else uninstallContextUserMessages();
|
|
107
|
-
}
|
|
29
|
+
/** Context 仅组织自身内容组件,Feature 的启用由 app 决定。 */
|
|
30
|
+
export default function registerContext(
|
|
31
|
+
pi: ExtensionAPI,
|
|
32
|
+
options: ContextExtensionOptions,
|
|
33
|
+
): ContextRuntimeController {
|
|
34
|
+
let activeTheme: Theme | undefined;
|
|
35
|
+
let cleanupUserMessages: (() => void) | undefined;
|
|
36
|
+
const reconcile = (): void => {
|
|
37
|
+
const current = options.getConfig();
|
|
38
|
+
const enabled =
|
|
39
|
+
current.components.userMessages.enabled &&
|
|
40
|
+
!hasUnsupportedComponentStyle(current, "userMessages");
|
|
41
|
+
if (!enabled || !activeTheme) {
|
|
42
|
+
cleanupUserMessages?.();
|
|
43
|
+
cleanupUserMessages = undefined;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!cleanupUserMessages) {
|
|
47
|
+
cleanupUserMessages = installUserMessageStyle(
|
|
48
|
+
() => activeTheme,
|
|
49
|
+
options.getConfig,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
108
53
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!isTuiContext(ctx)) return;
|
|
114
|
-
activeTheme = ctx.ui.theme;
|
|
115
|
-
contextConfig = loadConfig();
|
|
116
|
-
unsubscribeConfig();
|
|
117
|
-
unsubscribeConfig = configStore.subscribe((record: ConfigRecord) => {
|
|
118
|
-
contextConfig = mergeConfig(record);
|
|
119
|
-
reconcileContextUserMessages();
|
|
54
|
+
markdownEnhance(pi);
|
|
55
|
+
const thinking = installCompactThinking(pi, getCompactThinkingConfig());
|
|
56
|
+
const renderer = registerContextRenderer(pi, undefined, thinking, {
|
|
57
|
+
services: options.services,
|
|
120
58
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Applies a User Message configuration patch owned by Context.
|
|
126
|
-
*
|
|
127
|
-
* @param patch User Message configuration changes.
|
|
128
|
-
* @param _ctx Active Pi extension context.
|
|
129
|
-
*/
|
|
130
|
-
function setContextUserMessages(
|
|
131
|
-
patch: Partial<UserMessagesComponentConfig>,
|
|
132
|
-
_ctx: ExtensionContext,
|
|
133
|
-
): void {
|
|
134
|
-
contextConfig = saveUserMessagesComponentPatch(patch);
|
|
135
|
-
if (patch.enabled !== undefined || patch.style !== undefined)
|
|
136
|
-
reconcileContextUserMessages();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Releases Context session bindings and restores the original User Message patch.
|
|
141
|
-
*/
|
|
142
|
-
function stopContextSession(): void {
|
|
143
|
-
unsubscribeConfig();
|
|
144
|
-
unsubscribeConfig = () => {};
|
|
145
|
-
uninstallContextUserMessages();
|
|
146
|
-
activeTheme = undefined;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Registers the Context layout, its renderer stack and its feature hooks.
|
|
151
|
-
*/
|
|
152
|
-
export default function (
|
|
153
|
-
pi: ExtensionAPI,
|
|
154
|
-
options: ContextExtensionOptions = {},
|
|
155
|
-
): void {
|
|
156
|
-
// shell chrome
|
|
157
|
-
if (config.enableAliases) piAliases(pi);
|
|
158
|
-
if (config.enableEffortCommand) {
|
|
159
|
-
effortCommand(pi);
|
|
59
|
+
if (config.enableAgentSummary) {
|
|
60
|
+
agentSummary(pi);
|
|
160
61
|
}
|
|
161
|
-
installFlushDockedBash();
|
|
162
|
-
// The thinking controller is queried directly by the context render stack.
|
|
163
|
-
markdownEnhance(pi);
|
|
164
|
-
registerContextRenderer(
|
|
165
|
-
pi,
|
|
166
|
-
undefined,
|
|
167
|
-
installCompactThinking(pi, getCompactThinkingConfig()),
|
|
168
|
-
{
|
|
169
|
-
onRuntimeController: (controller) =>
|
|
170
|
-
options.onRuntimeController?.({
|
|
171
|
-
...controller,
|
|
172
|
-
setUserMessagesComponent: setContextUserMessages,
|
|
173
|
-
}),
|
|
174
|
-
},
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// features
|
|
178
|
-
if (config.enableContextCommand) context(pi);
|
|
179
|
-
if (config.enableSessionReference) sessionReference(pi);
|
|
180
|
-
if (config.enableSubagentAutocomplete) agentAutocomplete(pi);
|
|
181
|
-
if (config.enableAgentSummary) agentSummary(pi);
|
|
182
|
-
|
|
183
62
|
pi.on("session_start", (_event, ctx) => {
|
|
184
|
-
|
|
63
|
+
if (ctx.mode !== "tui" || !ctx.hasUI) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
activeTheme = ctx.ui.theme;
|
|
67
|
+
reconcile();
|
|
185
68
|
});
|
|
186
69
|
pi.on("session_shutdown", () => {
|
|
187
|
-
|
|
70
|
+
cleanupUserMessages?.();
|
|
71
|
+
cleanupUserMessages = undefined;
|
|
72
|
+
activeTheme = undefined;
|
|
188
73
|
});
|
|
74
|
+
return {
|
|
75
|
+
...renderer,
|
|
76
|
+
applyConfig(ctx, previous) {
|
|
77
|
+
reconcile();
|
|
78
|
+
thinking.updateConfig(getCompactThinkingConfig());
|
|
79
|
+
if (config.mode !== previous.mode) {
|
|
80
|
+
renderer.setMode(config.mode, ctx);
|
|
81
|
+
} else {
|
|
82
|
+
renderer.updateConfig(config, ctx);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
189
86
|
}
|
|
@@ -7,7 +7,7 @@ import type { ZentuiConfig } from "../../../app/config/shell.ts";
|
|
|
7
7
|
import {
|
|
8
8
|
installPrototypePatch,
|
|
9
9
|
removePrototypePatch,
|
|
10
|
-
} from "../../../app/
|
|
10
|
+
} from "../../../app/runtime/prototype-patch-registry.ts";
|
|
11
11
|
import {
|
|
12
12
|
sanitizeRenderedUserMessageLines,
|
|
13
13
|
sanitizeRenderedUserMessageText,
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
config,
|
|
6
6
|
normalizeConfig,
|
|
7
7
|
setConfig,
|
|
8
|
-
updateConfig,
|
|
9
8
|
} from "../../../app/config/renderer.ts";
|
|
10
9
|
import { isLazyProxyTui } from "../../../tools/fullscreen-detect.ts";
|
|
11
10
|
import {
|
|
@@ -30,6 +29,7 @@ import {
|
|
|
30
29
|
resetToolHoverState,
|
|
31
30
|
scheduleSessionRender,
|
|
32
31
|
TOOL_MOUSE_DISABLE,
|
|
32
|
+
type ToolMouseServices,
|
|
33
33
|
teardownToolMouseInteraction,
|
|
34
34
|
} from "./mouse/interaction.ts";
|
|
35
35
|
import { getToolMouseTui } from "./mouse/scroll.ts";
|
|
@@ -65,14 +65,19 @@ let compactModeHooks: CompactModeHooks | undefined;
|
|
|
65
65
|
function refreshCurrentContext(
|
|
66
66
|
ctx?: any,
|
|
67
67
|
toolGrouping?: ToolGroupingHooks,
|
|
68
|
+
render?: ToolMouseServices["render"],
|
|
68
69
|
): void {
|
|
69
70
|
const tui = getToolMouseTui();
|
|
70
71
|
toolGrouping?.refresh(tui);
|
|
71
72
|
refreshMessageDisplays(tui);
|
|
72
73
|
refreshCompactModeComponents(tui);
|
|
73
74
|
compactModeHooks?.refresh();
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
if (render) {
|
|
76
|
+
render.request(true);
|
|
77
|
+
} else {
|
|
78
|
+
tui?.requestRender?.(true);
|
|
79
|
+
ctx?.ui?.requestRender?.(true);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
function syncCompactMode(ctx: any): void {
|
|
@@ -84,8 +89,9 @@ function applyStyleMode(
|
|
|
84
89
|
mode: CompactStyleMode,
|
|
85
90
|
ctx: any,
|
|
86
91
|
toolGrouping?: ToolGroupingHooks,
|
|
92
|
+
render?: ToolMouseServices["render"],
|
|
87
93
|
): void {
|
|
88
|
-
|
|
94
|
+
setConfig(normalizeConfig({ ...config, mode }));
|
|
89
95
|
if (mode === "off") {
|
|
90
96
|
// Native rendering:清 hover/click,关闭鼠标上报以恢复终端默认滚轮。
|
|
91
97
|
resetToolHoverState();
|
|
@@ -103,10 +109,10 @@ function applyStyleMode(
|
|
|
103
109
|
}
|
|
104
110
|
// Reshape immediately and once more after the panel/custom UI unmounts and
|
|
105
111
|
// the main context is mounted again.
|
|
106
|
-
refreshCurrentContext(ctx, toolGrouping);
|
|
112
|
+
refreshCurrentContext(ctx, toolGrouping, render);
|
|
107
113
|
scheduleSessionRender(() => {
|
|
108
114
|
if (mode === "compact") syncCompactMode(ctx);
|
|
109
|
-
refreshCurrentContext(ctx, toolGrouping);
|
|
115
|
+
refreshCurrentContext(ctx, toolGrouping, render);
|
|
110
116
|
});
|
|
111
117
|
ctx.ui.notify(`Claude Code style: ${mode}`, "info");
|
|
112
118
|
}
|
|
@@ -129,6 +135,7 @@ export type RendererRuntimeController = {
|
|
|
129
135
|
};
|
|
130
136
|
|
|
131
137
|
export type RendererExtensionOptions = {
|
|
138
|
+
services?: ToolMouseServices;
|
|
132
139
|
onRuntimeController?: (controller: RendererRuntimeController) => void;
|
|
133
140
|
};
|
|
134
141
|
|
|
@@ -183,17 +190,27 @@ export default function (
|
|
|
183
190
|
return installation;
|
|
184
191
|
};
|
|
185
192
|
|
|
186
|
-
|
|
193
|
+
const controller: RendererRuntimeController = {
|
|
187
194
|
setMode: (mode, ctx) =>
|
|
188
|
-
applyStyleMode(
|
|
195
|
+
applyStyleMode(
|
|
196
|
+
mode,
|
|
197
|
+
ctx,
|
|
198
|
+
installation?.toolGrouping,
|
|
199
|
+
options.services?.render,
|
|
200
|
+
),
|
|
189
201
|
updateConfig: (partial, ctx) => {
|
|
190
|
-
|
|
202
|
+
setConfig(normalizeConfig({ ...config, ...partial }));
|
|
191
203
|
// 配置(如 expandedPreviewMaxLines / excludeRenderers)变化可能改变
|
|
192
204
|
// 渲染结果:丢弃工具卡跨 toggle 缓存,下次 updateDisplay 重建。
|
|
193
205
|
installation?.toggleRenderCache.clear();
|
|
194
|
-
refreshCurrentContext(
|
|
206
|
+
refreshCurrentContext(
|
|
207
|
+
ctx,
|
|
208
|
+
installation?.toolGrouping,
|
|
209
|
+
options.services?.render,
|
|
210
|
+
);
|
|
195
211
|
},
|
|
196
|
-
}
|
|
212
|
+
};
|
|
213
|
+
options.onRuntimeController?.(controller);
|
|
197
214
|
|
|
198
215
|
pi.on("message_update", async (event) => {
|
|
199
216
|
// compact-thinking 在 session_start 时于 compact 补丁之上再装一层;
|
|
@@ -221,7 +238,7 @@ export default function (
|
|
|
221
238
|
// 鼠标交互独立于渲染层:fullscreen 渲染层让位(hooks undefined)但
|
|
222
239
|
// 工具点击/回到底部适配仍需安装;保持在渲染层安装之后以维持原顺序。
|
|
223
240
|
if (ctx?.mode === "tui" && ctx?.hasUI)
|
|
224
|
-
installToolMouseInteraction(ctx, mouseOwner);
|
|
241
|
+
installToolMouseInteraction(ctx, mouseOwner, options.services);
|
|
225
242
|
if (!hooks) return;
|
|
226
243
|
hooks.toolGrouping.setTheme(ctx.ui.theme);
|
|
227
244
|
hooks.toggleRenderCache.setTheme(ctx.ui.theme);
|
|
@@ -240,7 +257,7 @@ export default function (
|
|
|
240
257
|
// Compaction rebuilds the context without session_start. Rebind after
|
|
241
258
|
// other TUI extensions may have replaced the root input dispatcher.
|
|
242
259
|
if (ctx?.mode === "tui" && ctx?.hasUI)
|
|
243
|
-
installToolMouseInteraction(ctx, mouseOwner);
|
|
260
|
+
installToolMouseInteraction(ctx, mouseOwner, options.services);
|
|
244
261
|
if (!hooks) return;
|
|
245
262
|
hooks.toolGrouping.setTheme(ctx.ui.theme);
|
|
246
263
|
hooks.toggleRenderCache.setTheme(ctx.ui.theme);
|
|
@@ -282,6 +299,7 @@ export default function (
|
|
|
282
299
|
clearAllAnimations();
|
|
283
300
|
installation = undefined;
|
|
284
301
|
});
|
|
302
|
+
return controller;
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
// ---- 对外导出:入口/测试实际消费的符号 ----
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { config } from "../../../../app/config/renderer.ts";
|
|
2
|
-
import { inputRouter } from "../../../../app/overlay/input-router.ts";
|
|
3
|
-
import { overlayManager } from "../../../../app/overlay/overlay-manager.ts";
|
|
4
2
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../../../../
|
|
3
|
+
type InputRouter,
|
|
4
|
+
inputRouter,
|
|
5
|
+
} from "../../../../app/overlay/input-router.ts";
|
|
6
|
+
import {
|
|
7
|
+
type OverlayManager,
|
|
8
|
+
overlayManager,
|
|
9
|
+
} from "../../../../app/overlay/overlay-manager.ts";
|
|
10
|
+
import type { RenderScheduler } from "../../../../app/runtime/render-scheduler.ts";
|
|
8
11
|
import { isLazyProxyTui } from "../../../../tools/fullscreen-detect.ts";
|
|
9
12
|
import {
|
|
10
13
|
patchRegistry,
|
|
11
14
|
TOOL_MOUSE_OWNER_KEY,
|
|
12
15
|
} from "../../../../tools/patch-keys.ts";
|
|
16
|
+
import { showTextPreview } from "../../../overlay/context-inspector.ts";
|
|
13
17
|
import { ThinkingPreviewBlock } from "../../thinking/compact-thinking.ts";
|
|
14
18
|
import {
|
|
15
19
|
isCompactAssistantComponent,
|
|
@@ -101,6 +105,7 @@ const DEFAULT_TOOL_MOUSE_OWNER = {};
|
|
|
101
105
|
export const TOOL_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1003l\x1b[?1000l";
|
|
102
106
|
|
|
103
107
|
let toolMouseUi: any = null;
|
|
108
|
+
let toolMouseOverlays = overlayManager;
|
|
104
109
|
let toolMouseInputUnsubscribe: (() => void) | null = null;
|
|
105
110
|
let toolMouseRenderPatchTui: any = null;
|
|
106
111
|
let toolMouseRenderPatchOriginal: ((...args: any[]) => any) | null = null;
|
|
@@ -144,7 +149,7 @@ function tryOpenToolIoShowMore(region: InteractionRegion): boolean {
|
|
|
144
149
|
const title = section === "input" ? "Tool Input" : "Tool Output";
|
|
145
150
|
const content =
|
|
146
151
|
section === "input" ? ioView.getInputBody() : ioView.getOutputBody();
|
|
147
|
-
void showTextPreview({ ui }, title, content || "(empty)");
|
|
152
|
+
void showTextPreview({ ui }, title, content || "(empty)", toolMouseOverlays);
|
|
148
153
|
return true;
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -498,7 +503,7 @@ function patchFullscreenViewportInput(tui: any): void {
|
|
|
498
503
|
const packets = parseSgrMousePackets(data);
|
|
499
504
|
// 官方 fullscreen 会消费全部鼠标;文本预览 overlay 活动时放行给 focused
|
|
500
505
|
// custom component,使 [esc] 点击和滚轮可用。
|
|
501
|
-
if (packets && tui.hasOverlay?.() &&
|
|
506
|
+
if (packets && tui.hasOverlay?.() && toolMouseOverlays.hasActive())
|
|
502
507
|
return undefined;
|
|
503
508
|
if (packets && !tui.hasOverlay?.()) {
|
|
504
509
|
for (const packet of packets) {
|
|
@@ -942,9 +947,19 @@ export function resetToolHoverState(): void {
|
|
|
942
947
|
* @param ctx Active TUI extension context.
|
|
943
948
|
* @param owner Ownership token used to make teardown safe across reloads.
|
|
944
949
|
*/
|
|
950
|
+
export type ToolMouseServices = {
|
|
951
|
+
input: InputRouter;
|
|
952
|
+
overlays: OverlayManager;
|
|
953
|
+
render?: RenderScheduler;
|
|
954
|
+
};
|
|
955
|
+
|
|
945
956
|
export function installToolMouseInteraction(
|
|
946
957
|
ctx: any,
|
|
947
958
|
owner: object = DEFAULT_TOOL_MOUSE_OWNER,
|
|
959
|
+
services: ToolMouseServices = {
|
|
960
|
+
input: inputRouter,
|
|
961
|
+
overlays: overlayManager,
|
|
962
|
+
},
|
|
948
963
|
): void {
|
|
949
964
|
teardownToolMouseInteraction(toolMouseInstallationOwner ?? owner);
|
|
950
965
|
if (ctx?.mode !== "tui" || !ctx?.hasUI) return;
|
|
@@ -958,9 +973,11 @@ export function installToolMouseInteraction(
|
|
|
958
973
|
patchRegistry.install(TOOL_MOUSE_OWNER_KEY, owner);
|
|
959
974
|
setHoveredToolCallId(null);
|
|
960
975
|
toolMouseUi = ctx.ui;
|
|
976
|
+
toolMouseOverlays = services.overlays;
|
|
961
977
|
// 0.84+ 的 tui 是惰性 Proxy:regular 保留原生 scrollback;fullscreen
|
|
962
978
|
// 由官方 LayoutFrame 命中,并由扩展补齐 hover 所需的 all-motion 上报。
|
|
963
979
|
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any, theme: any) => {
|
|
980
|
+
services.render?.register(owner, (force) => tui.requestRender(force));
|
|
964
981
|
setToolMouseTui(tui);
|
|
965
982
|
setToolTuiFullscreen(fullscreenLazyTui(tui));
|
|
966
983
|
if (isLazyProxyTui(tui)) {
|
|
@@ -987,17 +1004,15 @@ export function installToolMouseInteraction(
|
|
|
987
1004
|
setScrollButtonWidget(widget);
|
|
988
1005
|
return widget;
|
|
989
1006
|
});
|
|
990
|
-
const
|
|
1007
|
+
const removeInput = services.input.bind(
|
|
1008
|
+
ctx.ui,
|
|
991
1009
|
(data) =>
|
|
992
|
-
|
|
1010
|
+
services.overlays.hasActive() ? undefined : handleToolMouseInput(data),
|
|
993
1011
|
100,
|
|
994
1012
|
);
|
|
995
|
-
const removeHostInput = ctx.ui.onTerminalInput((data: string) =>
|
|
996
|
-
inputRouter.dispatch(data),
|
|
997
|
-
);
|
|
998
1013
|
toolMouseInputUnsubscribe = () => {
|
|
999
|
-
|
|
1000
|
-
|
|
1014
|
+
removeInput();
|
|
1015
|
+
services.render?.register(owner, undefined);
|
|
1001
1016
|
};
|
|
1002
1017
|
}
|
|
1003
1018
|
|
|
@@ -29,12 +29,9 @@ export type EditorChangeResult = { ok: true } | { ok: false; reason: string };
|
|
|
29
29
|
|
|
30
30
|
export type EditorLayoutControllerContext = {
|
|
31
31
|
readonly getConfig: () => PolishedTuiConfig;
|
|
32
|
-
readonly saveComponent: (
|
|
33
|
-
patch: Partial<PolishedTuiConfig["components"]["editor"]>,
|
|
34
|
-
) => PolishedTuiConfig;
|
|
35
32
|
readonly getState: () => FooterState;
|
|
36
33
|
readonly sessionLifecycle: SessionLifecycle;
|
|
37
|
-
readonly render: Pick<RenderScheduler, "request">;
|
|
34
|
+
readonly render: Pick<RenderScheduler, "request" | "register">;
|
|
38
35
|
readonly getThinkingLevel: () => ReturnType<ExtensionAPI["getThinkingLevel"]>;
|
|
39
36
|
readonly getAgentDurationMs: () => number;
|
|
40
37
|
readonly isAgentActive: () => boolean;
|
|
@@ -54,7 +51,6 @@ export class EditorLayoutController {
|
|
|
54
51
|
private readonly context: EditorLayoutControllerContext;
|
|
55
52
|
private readonly ownerToken = Symbol("pi-one-ui-editor-owner");
|
|
56
53
|
private activeTuiContext: ExtensionContext | undefined;
|
|
57
|
-
private requestEditorRender: (() => void) | undefined;
|
|
58
54
|
private editorInstalled = false;
|
|
59
55
|
private editorInstallMode: EditorInstallMode = "none";
|
|
60
56
|
private installedEditorFactory: EditorFactory | undefined;
|
|
@@ -131,17 +127,16 @@ export class EditorLayoutController {
|
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
/**
|
|
134
|
-
*
|
|
130
|
+
* 应用 app 已提交的 Editor 配置并更新组件。
|
|
135
131
|
*
|
|
136
|
-
* @param patch Editor
|
|
137
|
-
* @param ctx
|
|
138
|
-
* @returns
|
|
132
|
+
* @param patch 本次修改的 Editor 字段。
|
|
133
|
+
* @param ctx 当前 Pi session 的上下文。
|
|
134
|
+
* @returns 组件应用结果。
|
|
139
135
|
*/
|
|
140
|
-
|
|
136
|
+
applyConfig(
|
|
141
137
|
patch: Partial<PolishedTuiConfig["components"]["editor"]>,
|
|
142
138
|
ctx: ExtensionContext,
|
|
143
139
|
): { applied: boolean; reason?: string } {
|
|
144
|
-
this.context.saveComponent(patch);
|
|
145
140
|
let result: EditorChangeResult | undefined;
|
|
146
141
|
if (patch.style === "off") {
|
|
147
142
|
// off 透传原生渲染:不替换工厂,既有编辑器实例与 overlay preFocus
|
|
@@ -298,7 +293,6 @@ export class EditorLayoutController {
|
|
|
298
293
|
*/
|
|
299
294
|
cleanup(ctx?: ExtensionContext): void {
|
|
300
295
|
this.resetAgentTimer();
|
|
301
|
-
this.context.sessionLifecycle.shutdown();
|
|
302
296
|
if (ctx && this.isTuiContext(ctx)) {
|
|
303
297
|
try {
|
|
304
298
|
const currentFactory = ctx.ui.getEditorComponent();
|
|
@@ -413,7 +407,7 @@ export class EditorLayoutController {
|
|
|
413
407
|
*/
|
|
414
408
|
private clearEditorOwnership(): void {
|
|
415
409
|
this.setMinimalistDecorationActive(false);
|
|
416
|
-
this.
|
|
410
|
+
this.context.render.register(this, undefined);
|
|
417
411
|
this.wrappedEditorFactory = undefined;
|
|
418
412
|
this.installedEditorFactory = undefined;
|
|
419
413
|
this.editorInstallMode = "none";
|
|
@@ -481,7 +475,7 @@ export class EditorLayoutController {
|
|
|
481
475
|
isAgentActive: this.context.isAgentActive,
|
|
482
476
|
getProjectRoot: this.context.getProjectRoot,
|
|
483
477
|
onRender: (requestRender) => {
|
|
484
|
-
this.
|
|
478
|
+
this.context.render.register(this, requestRender);
|
|
485
479
|
},
|
|
486
480
|
onDecorationActive: (active) =>
|
|
487
481
|
this.setMinimalistDecorationActive(active),
|
|
@@ -532,11 +526,8 @@ export class EditorLayoutController {
|
|
|
532
526
|
});
|
|
533
527
|
}
|
|
534
528
|
|
|
535
|
-
/**
|
|
536
|
-
* Requests both the mounted editor redraw and the shared runtime redraw.
|
|
537
|
-
*/
|
|
529
|
+
/** 向 app 提交共享 TUI 刷新请求。 */
|
|
538
530
|
requestRender(): void {
|
|
539
|
-
this.requestEditorRender?.();
|
|
540
531
|
this.context.render.request();
|
|
541
532
|
}
|
|
542
533
|
}
|
|
@@ -24,7 +24,7 @@ export type EditorFactoryRuntime = {
|
|
|
24
24
|
readonly getAgentDurationMs: () => number;
|
|
25
25
|
readonly isAgentActive: () => boolean;
|
|
26
26
|
readonly getProjectRoot: () => string | undefined;
|
|
27
|
-
readonly onRender: (requestRender: () => void) => void;
|
|
27
|
+
readonly onRender: (requestRender: (force: boolean) => void) => void;
|
|
28
28
|
readonly onDecorationActive: (active: boolean) => void;
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -44,7 +44,7 @@ export function createEditorFactory(
|
|
|
44
44
|
theme: EditorTheme,
|
|
45
45
|
keybindings: KeybindingsManager,
|
|
46
46
|
) => {
|
|
47
|
-
runtime.onRender(() => tui.requestRender());
|
|
47
|
+
runtime.onRender((force) => tui.requestRender(force));
|
|
48
48
|
return new PolishedEditor(
|
|
49
49
|
tui,
|
|
50
50
|
theme,
|
|
@@ -78,7 +78,7 @@ export function createWrappedEditorFactory(
|
|
|
78
78
|
theme: EditorTheme,
|
|
79
79
|
keybindings: KeybindingsManager,
|
|
80
80
|
) => {
|
|
81
|
-
runtime.onRender(() => tui.requestRender());
|
|
81
|
+
runtime.onRender((force) => tui.requestRender(force));
|
|
82
82
|
return new WrappedPolishedEditor(
|
|
83
83
|
baseFactory(tui, theme, keybindings),
|
|
84
84
|
runtime.sessionTheme,
|