pi-one-ui 0.7.2 → 0.8.0
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 +28 -1
- package/README.en.md +5 -8
- package/README.md +6 -7
- package/extensions/app/config/footer.ts +21 -5
- package/extensions/app/config/renderer.ts +1 -1
- package/extensions/app/config/shell.ts +62 -60
- package/extensions/app/runtime/prototype-patch-registry.ts +13 -9
- package/extensions/app/runtime/tui-runtime.ts +19 -12
- package/extensions/app/settings/controller.ts +2 -2
- package/extensions/layouts/context/index.ts +2 -2
- package/extensions/layouts/context/message/user-message-osc.ts +1 -1
- package/extensions/layouts/context/message/user-message-styles.ts +9 -6
- package/extensions/layouts/context/message/user-message.ts +5 -5
- package/extensions/layouts/context/renderer/compact-mode.ts +2 -2
- package/extensions/layouts/context/renderer/default-mode.ts +10 -10
- package/extensions/layouts/context/renderer/index.ts +0 -1
- package/extensions/layouts/context/renderer/mouse/interaction.ts +5 -5
- package/extensions/layouts/context/renderer/mouse/scroll.ts +4 -4
- package/extensions/layouts/context/renderer/tool/diff/diff-edit-render.ts +1 -1
- package/extensions/layouts/context/renderer/tool/diff/index.ts +1 -1
- package/extensions/layouts/context/renderer/tool/grouping.ts +3 -3
- package/extensions/layouts/context/renderer/tool/message-display.ts +7 -7
- package/extensions/layouts/context/renderer/tool/result.ts +16 -16
- package/extensions/layouts/context/renderer/tool/toggle-render-cache.ts +9 -9
- package/extensions/layouts/editor/controller.ts +17 -17
- package/extensions/layouts/editor/editor-metadata-format.ts +5 -5
- package/extensions/layouts/editor/factory.ts +5 -5
- package/extensions/layouts/editor/minimalist-editor.ts +8 -8
- package/extensions/layouts/editor/ownership.ts +24 -20
- package/extensions/layouts/editor/ui.ts +10 -15
- package/extensions/layouts/footer/controller.ts +4 -4
- package/extensions/layouts/footer/data.ts +2 -2
- package/extensions/layouts/footer/extension-status.ts +4 -4
- package/extensions/layouts/footer/footer.ts +5 -2
- package/extensions/layouts/overlay/selector-border.ts +4 -4
- package/extensions/layouts/overlay/selector-controller.ts +1 -1
- package/extensions/layouts/overlay/settings-previews.ts +2 -2
- package/extensions/layouts/working-line/interaction-summary.ts +3 -1
- package/extensions/layouts/working-line/working-line.ts +4 -4
- package/extensions/services/package-data.ts +1 -1
- package/extensions/shared/style.ts +66 -56
- package/extensions/tools/patch-keys.ts +23 -23
- package/package.json +7 -7
|
@@ -20,6 +20,7 @@ import registerHeader, {
|
|
|
20
20
|
import { SelectorController } from "../../layouts/overlay/selector-controller.ts";
|
|
21
21
|
import { WorkingLineLayoutController } from "../../layouts/working-line/controller.ts";
|
|
22
22
|
import {
|
|
23
|
+
LEGACY_TURN_SUMMARY_ENTRY_TYPE,
|
|
23
24
|
renderTurnSummaryEntry,
|
|
24
25
|
TURN_SUMMARY_ENTRY_TYPE,
|
|
25
26
|
} from "../../layouts/working-line/interaction-summary.ts";
|
|
@@ -192,18 +193,24 @@ export function createLayoutRuntime(
|
|
|
192
193
|
});
|
|
193
194
|
const state = sessionState.state;
|
|
194
195
|
if (typeof pi.registerEntryRenderer === "function") {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
const renderTurnSummary = (
|
|
197
|
+
entry: { data?: unknown },
|
|
198
|
+
entryOptions: unknown,
|
|
199
|
+
theme: Parameters<typeof renderTurnSummaryEntry>[2],
|
|
200
|
+
) => {
|
|
201
|
+
const options = (entryOptions ?? {}) as Record<string, unknown>;
|
|
202
|
+
return renderTurnSummaryEntry(
|
|
203
|
+
entry,
|
|
204
|
+
{
|
|
205
|
+
...options,
|
|
206
|
+
workingLineHigh: currentConfig.colors.workingLineHigh,
|
|
207
|
+
},
|
|
208
|
+
theme,
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
pi.registerEntryRenderer(TURN_SUMMARY_ENTRY_TYPE, renderTurnSummary);
|
|
212
|
+
// 旧 session 的摘要条目继续可渲染。
|
|
213
|
+
pi.registerEntryRenderer(LEGACY_TURN_SUMMARY_ENTRY_TYPE, renderTurnSummary);
|
|
207
214
|
}
|
|
208
215
|
const refresh = (): void => {
|
|
209
216
|
if (sessionLifecycle.isCurrent()) {
|
|
@@ -6,7 +6,7 @@ import { type Config, normalizeConfig } from "../config/renderer.ts";
|
|
|
6
6
|
import {
|
|
7
7
|
mergeConfig,
|
|
8
8
|
mutateComponentsRecord,
|
|
9
|
-
type
|
|
9
|
+
type PolishedTuiConfig,
|
|
10
10
|
} from "../config/shell.ts";
|
|
11
11
|
import {
|
|
12
12
|
type ConfigRecord,
|
|
@@ -19,7 +19,7 @@ import { applyPresetRecord, isPreset } from "./presets.ts";
|
|
|
19
19
|
export type SettingsChange = Readonly<{ id: string; value: string }>;
|
|
20
20
|
|
|
21
21
|
export type SettingsSnapshot = Readonly<{
|
|
22
|
-
shell:
|
|
22
|
+
shell: PolishedTuiConfig;
|
|
23
23
|
renderer: Config;
|
|
24
24
|
panel: PanelOverlayConfig;
|
|
25
25
|
}>;
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
import { type Config, config } from "../../app/config/renderer.ts";
|
|
7
7
|
import {
|
|
8
8
|
hasUnsupportedComponentStyle,
|
|
9
|
-
type
|
|
9
|
+
type PolishedTuiConfig,
|
|
10
10
|
} from "../../app/config/shell.ts";
|
|
11
11
|
import { installUserMessageStyle } from "./message/user-message.ts";
|
|
12
12
|
import registerContextRenderer, {
|
|
@@ -22,7 +22,7 @@ export type ContextRuntimeController = RendererRuntimeController & {
|
|
|
22
22
|
applyConfig(ctx: ExtensionContext, previous: Config): void;
|
|
23
23
|
};
|
|
24
24
|
export type ContextExtensionOptions = {
|
|
25
|
-
readonly getConfig: () =>
|
|
25
|
+
readonly getConfig: () => PolishedTuiConfig;
|
|
26
26
|
readonly services?: RendererExtensionOptions["services"];
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -192,7 +192,7 @@ function filterUserMessageTerminalText(
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// Removing an unterminated introducer prevents it from consuming
|
|
195
|
-
//
|
|
195
|
+
// pi-one-ui's prompt markers. Preserve visible trailing payload, except
|
|
196
196
|
// for a recognized partial OSC 133 command prefix.
|
|
197
197
|
const payloadEnd = boundary?.index ?? text.length;
|
|
198
198
|
const payload = text.slice(payloadStart, payloadEnd);
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
truncateToWidth,
|
|
6
6
|
visibleWidth,
|
|
7
7
|
} from "@earendil-works/pi-tui";
|
|
8
|
-
import type {
|
|
8
|
+
import type { PolishedTuiConfig } from "../../../app/config/shell.ts";
|
|
9
9
|
import {
|
|
10
10
|
EDITOR_ACCENT_FALLBACK,
|
|
11
11
|
EDITOR_BORDER_FALLBACK,
|
|
@@ -17,7 +17,7 @@ export type UserMessageStyleRenderInput = {
|
|
|
17
17
|
text: string;
|
|
18
18
|
width: number;
|
|
19
19
|
theme?: Theme;
|
|
20
|
-
config:
|
|
20
|
+
config: PolishedTuiConfig;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
function themeFg(
|
|
@@ -70,7 +70,7 @@ function fillLine(content: string, width: number): string {
|
|
|
70
70
|
|
|
71
71
|
function accent(
|
|
72
72
|
theme: Theme | undefined,
|
|
73
|
-
config:
|
|
73
|
+
config: PolishedTuiConfig,
|
|
74
74
|
text: string,
|
|
75
75
|
): string {
|
|
76
76
|
return theme
|
|
@@ -85,7 +85,7 @@ function accent(
|
|
|
85
85
|
|
|
86
86
|
function border(
|
|
87
87
|
theme: Theme | undefined,
|
|
88
|
-
config:
|
|
88
|
+
config: PolishedTuiConfig,
|
|
89
89
|
text: string,
|
|
90
90
|
): string {
|
|
91
91
|
return theme
|
|
@@ -98,7 +98,10 @@ function border(
|
|
|
98
98
|
: text;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function renderRail(
|
|
101
|
+
function renderRail(
|
|
102
|
+
theme: Theme | undefined,
|
|
103
|
+
config: PolishedTuiConfig,
|
|
104
|
+
): string {
|
|
102
105
|
return `${accent(theme, config, config.icons.rail)} `;
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -202,7 +205,7 @@ function renderLabeled({
|
|
|
202
205
|
return [top, ...body.map(side), bottom];
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
export function userMessageStyleCacheKey(config:
|
|
208
|
+
export function userMessageStyleCacheKey(config: PolishedTuiConfig): string {
|
|
206
209
|
const messages = config.components.userMessages;
|
|
207
210
|
switch (messages.style) {
|
|
208
211
|
case "framed":
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
UserMessageComponent,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
6
|
-
import type {
|
|
6
|
+
import type { PolishedTuiConfig } from "../../../app/config/shell.ts";
|
|
7
7
|
import {
|
|
8
8
|
installPrototypePatch,
|
|
9
9
|
removePrototypePatch,
|
|
@@ -79,11 +79,11 @@ function getCachedMarkdownText(instance: object): string | undefined {
|
|
|
79
79
|
return text;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function
|
|
82
|
+
function renderPolishedUserMessage(
|
|
83
83
|
instance: PatchableUserMessagePrototype,
|
|
84
84
|
width: number,
|
|
85
85
|
theme: Theme | undefined,
|
|
86
|
-
config:
|
|
86
|
+
config: PolishedTuiConfig,
|
|
87
87
|
): string[] | undefined {
|
|
88
88
|
if (!isRecord(instance)) return undefined;
|
|
89
89
|
|
|
@@ -171,7 +171,7 @@ export function removeUserMessageStyle(): void {
|
|
|
171
171
|
|
|
172
172
|
export function installUserMessageStyle(
|
|
173
173
|
getTheme: () => Theme | undefined,
|
|
174
|
-
getConfig: () =>
|
|
174
|
+
getConfig: () => PolishedTuiConfig,
|
|
175
175
|
): Cleanup {
|
|
176
176
|
const prototype = UserMessageComponent.prototype;
|
|
177
177
|
const cleanupInvalidate = installPrototypePatch(
|
|
@@ -195,7 +195,7 @@ export function installUserMessageStyle(
|
|
|
195
195
|
const width = args[0];
|
|
196
196
|
if (typeof width !== "number") return renderPredecessor();
|
|
197
197
|
try {
|
|
198
|
-
const lines =
|
|
198
|
+
const lines = renderPolishedUserMessage(
|
|
199
199
|
receiver as PatchableUserMessagePrototype,
|
|
200
200
|
width,
|
|
201
201
|
getTheme(),
|
|
@@ -690,7 +690,7 @@ function compactEditWriteLines(
|
|
|
690
690
|
outputText,
|
|
691
691
|
theme,
|
|
692
692
|
isError,
|
|
693
|
-
state.
|
|
693
|
+
state.oneuiIoView,
|
|
694
694
|
component.args,
|
|
695
695
|
component,
|
|
696
696
|
true, // 外层 Box(1,1) 已 pad
|
|
@@ -1380,7 +1380,7 @@ export function installCompactMode(
|
|
|
1380
1380
|
uiRef = ctx?.ui;
|
|
1381
1381
|
resetRounds();
|
|
1382
1382
|
// 始终保持补丁在链上:mode=on/off 走 passThrough,仍写入 lastMessage
|
|
1383
|
-
// 与 tracked 集合,这样 /
|
|
1383
|
+
// 与 tracked 集合,这样 /oneui 切回 compact 时不必 /reload。
|
|
1384
1384
|
patch.assertAssistantOwnership();
|
|
1385
1385
|
if (config.mode === "compact") syncGlobalExpanded(ctx);
|
|
1386
1386
|
else hoveredAssistantComponent = undefined;
|
|
@@ -236,8 +236,8 @@ function renderExpandedTaskResult(
|
|
|
236
236
|
return formatted ? new Text(`↳ ${formatted}`, 0, 0) : undefined;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
/** 用
|
|
240
|
-
function
|
|
239
|
+
/** 用 pi-one-ui call/result 包装任意工具定义。 */
|
|
240
|
+
function createPolishedTool(
|
|
241
241
|
originalTool: any,
|
|
242
242
|
writeExecutionMetadata: WriteExecutionMetadataStore,
|
|
243
243
|
): any {
|
|
@@ -400,7 +400,7 @@ function createCcstyleTool(
|
|
|
400
400
|
true, // mode=on:贴左,由外层 Box(1,1) 提供 1 格 padding
|
|
401
401
|
);
|
|
402
402
|
}
|
|
403
|
-
if (context?.state) context.state.
|
|
403
|
+
if (context?.state) context.state.oneuiIoView = undefined;
|
|
404
404
|
let cachedWidth: number | undefined;
|
|
405
405
|
let cachedLine: string | undefined;
|
|
406
406
|
let cachedHoveredLine: string | undefined;
|
|
@@ -425,7 +425,7 @@ function createCcstyleTool(
|
|
|
425
425
|
cachedWidth = undefined;
|
|
426
426
|
cachedLine = undefined;
|
|
427
427
|
cachedHoveredLine = undefined;
|
|
428
|
-
context?.state?.
|
|
428
|
+
context?.state?.oneuiExpandedIoView?.invalidate?.();
|
|
429
429
|
},
|
|
430
430
|
};
|
|
431
431
|
},
|
|
@@ -465,7 +465,7 @@ function shouldGloballyStyleTool(
|
|
|
465
465
|
const builtInDefinition = component.builtInToolDefinition;
|
|
466
466
|
const definition = extensionDefinition ?? builtInDefinition;
|
|
467
467
|
const toolName = String(component.toolName || definition?.name || "");
|
|
468
|
-
const
|
|
468
|
+
const usePolishedToolStyle =
|
|
469
469
|
patch.mode() === "on" &&
|
|
470
470
|
!DEDICATED_RENDERER_TOOLS.has(toolName) &&
|
|
471
471
|
!preservesOriginalRenderer(
|
|
@@ -473,8 +473,8 @@ function shouldGloballyStyleTool(
|
|
|
473
473
|
toolName,
|
|
474
474
|
builtInDefinition,
|
|
475
475
|
);
|
|
476
|
-
component[COMPONENT_TOOL_RENDER_MODE] =
|
|
477
|
-
return
|
|
476
|
+
component[COMPONENT_TOOL_RENDER_MODE] = usePolishedToolStyle;
|
|
477
|
+
return usePolishedToolStyle;
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
function getGloballyStyledTool(
|
|
@@ -607,7 +607,7 @@ function installGlobalToolRendering(
|
|
|
607
607
|
active: true,
|
|
608
608
|
enabled: () => config.mode === "on",
|
|
609
609
|
mode: () => config.mode,
|
|
610
|
-
wrap: (tool: any) =>
|
|
610
|
+
wrap: (tool: any) => createPolishedTool(tool, writeExecutionMetadata),
|
|
611
611
|
byDefinition: new WeakMap(),
|
|
612
612
|
byName: new Map(),
|
|
613
613
|
downstream,
|
|
@@ -626,8 +626,8 @@ function installGlobalToolRendering(
|
|
|
626
626
|
getRenderShell: function (this: any, ...args: any[]) {
|
|
627
627
|
if (!patch.active)
|
|
628
628
|
return patch.downstream.getRenderShell.apply(this, args);
|
|
629
|
-
const
|
|
630
|
-
const shell =
|
|
629
|
+
const usePolishedToolStyle = shouldGloballyStyleTool(this, patch);
|
|
630
|
+
const shell = usePolishedToolStyle
|
|
631
631
|
? this.expanded
|
|
632
632
|
? "default"
|
|
633
633
|
: "self"
|
|
@@ -243,7 +243,6 @@ export default function (
|
|
|
243
243
|
hooks.toolGrouping.setTheme(ctx.ui.theme);
|
|
244
244
|
hooks.toggleRenderCache.setTheme(ctx.ui.theme);
|
|
245
245
|
setMessageDisplayTheme(ctx.ui.theme);
|
|
246
|
-
ctx.ui.setStatus("ccstyle", undefined);
|
|
247
246
|
// Collect the resumed context before syncing compact patches and expansion state.
|
|
248
247
|
syncCompactMode(ctx);
|
|
249
248
|
// compact-thinking 的 session_start 处理在本 handler 之后执行(在其之上再装
|
|
@@ -97,10 +97,10 @@ type InteractionRegion = {
|
|
|
97
97
|
|
|
98
98
|
type InteractionFrame = { regions: InteractionRegion[] };
|
|
99
99
|
|
|
100
|
-
const TOOL_MOUSE_WIDGET_KEY = "
|
|
100
|
+
const TOOL_MOUSE_WIDGET_KEY = "oneui-tool-mouse";
|
|
101
101
|
const TOOL_MOUSE_MOTION_ENABLE = "\x1b[?1003h\x1b[?1006h";
|
|
102
102
|
const TOOL_MOUSE_MOTION_DISABLE = "\x1b[?1003l";
|
|
103
|
-
const FULLSCREEN_MOTION_ENABLED = Symbol("
|
|
103
|
+
const FULLSCREEN_MOTION_ENABLED = Symbol("pi-one-ui.fullscreen-motion-enabled");
|
|
104
104
|
const DEFAULT_TOOL_MOUSE_OWNER = {};
|
|
105
105
|
export const TOOL_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1003l\x1b[?1000l";
|
|
106
106
|
|
|
@@ -118,7 +118,7 @@ let ownsFullscreenMotion = false;
|
|
|
118
118
|
let sessionRenderTimer: ReturnType<typeof setTimeout> | null = null;
|
|
119
119
|
let latestInteractionFrame: InteractionFrame = { regions: [] };
|
|
120
120
|
|
|
121
|
-
/** Summary markers used by Pi and
|
|
121
|
+
/** Summary markers used by Pi and pi-one-ui; unlike the trailing hint, these survive truncation. */
|
|
122
122
|
const COLLAPSED_TOOL_SUMMARY = /^\s*(?:↳|└|⎿|●|✓|✗|…)/;
|
|
123
123
|
|
|
124
124
|
function interactionRegionAt(packet: SgrMousePacket): InteractionRegion | null {
|
|
@@ -298,9 +298,9 @@ function releaseFullscreenToolMouseMotion(tui?: any): void {
|
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
const FULLSCREEN_VIEWPORT_PATCH = Symbol("
|
|
301
|
+
const FULLSCREEN_VIEWPORT_PATCH = Symbol("pi-one-ui.fullscreen-viewport-patch");
|
|
302
302
|
const FULLSCREEN_WHEEL_SCROLL_ORIGINAL = Symbol(
|
|
303
|
-
"
|
|
303
|
+
"pi-one-ui.fullscreen-wheel-scroll-original",
|
|
304
304
|
);
|
|
305
305
|
|
|
306
306
|
/**
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
} from "../../../../tools/patch-keys.ts";
|
|
15
15
|
import { parseSgrMousePackets } from "./packets.ts";
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const PAGE_UP_INPUT =
|
|
18
18
|
/^\x1b\[5;9(?::[12])?~$|^\x1b\[57421;9(?::[12])?u$|^\x1b\[1;6A$/;
|
|
19
|
-
const
|
|
19
|
+
const PAGE_DOWN_INPUT =
|
|
20
20
|
/^\x1b\[6;9(?::[12])?~$|^\x1b\[57422;9(?::[12])?u$|^\x1b\[1;6B$/;
|
|
21
21
|
const SCROLL_BOTTOM_SHORTCUT = "ctrl+end";
|
|
22
22
|
|
|
@@ -123,8 +123,8 @@ function isScrollNavigationInput(data: string): boolean {
|
|
|
123
123
|
if (
|
|
124
124
|
matchesKey(data, "pageUp") ||
|
|
125
125
|
matchesKey(data, "pageDown") ||
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
PAGE_UP_INPUT.test(data) ||
|
|
127
|
+
PAGE_DOWN_INPUT.test(data) ||
|
|
128
128
|
// 官方 fullscreen viewport 的可滚动键(half-page/prompt/top/bottom)。
|
|
129
129
|
[
|
|
130
130
|
"tui.altScreen.pageUp",
|
|
@@ -119,7 +119,7 @@ export function renderEditDiffResult(
|
|
|
119
119
|
showHashlineAnchors,
|
|
120
120
|
);
|
|
121
121
|
const palette = resolveDiffPalette(theme);
|
|
122
|
-
// Rich diffs use
|
|
122
|
+
// Rich diffs use pi-one-ui's self shell. Keep the panel transparent so the
|
|
123
123
|
// separator cannot leak toolSuccessBg across the entire new column.
|
|
124
124
|
const containerBgAnsi = undefined;
|
|
125
125
|
const language = resolveLanguageFromPath(options.filePath);
|
|
@@ -46,7 +46,7 @@ export function renderRichToolResult(
|
|
|
46
46
|
theme: any,
|
|
47
47
|
context: any,
|
|
48
48
|
writeMetadata: WriteExecutionMetadataStore,
|
|
49
|
-
/** Plain snapshot or live getter — getter lets /
|
|
49
|
+
/** Plain snapshot or live getter — getter lets /oneui panel changes repaint existing diffs. */
|
|
50
50
|
displayConfig: DisplayConfigInput = DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
51
51
|
): any | undefined {
|
|
52
52
|
if (options?.isPartial || options?.isError || context?.isError)
|
|
@@ -213,8 +213,8 @@ type SettledExpandedChildCache = {
|
|
|
213
213
|
|
|
214
214
|
function toolIoView(tool: any): ExpandedToolIoView | undefined {
|
|
215
215
|
for (const candidate of [
|
|
216
|
-
tool?.rendererState?.
|
|
217
|
-
tool?.state?.
|
|
216
|
+
tool?.rendererState?.oneuiIoView,
|
|
217
|
+
tool?.state?.oneuiIoView,
|
|
218
218
|
tool?.resultRendererComponent,
|
|
219
219
|
]) {
|
|
220
220
|
if (isExpandedToolIoView(candidate)) {
|
|
@@ -237,7 +237,7 @@ function ioRenderRevision(
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
export class ToolGroupComponent extends Container {
|
|
240
|
-
readonly toolCallId = `
|
|
240
|
+
readonly toolCallId = `oneui-tool-group-${nextGroupId++}`;
|
|
241
241
|
readonly toolName = "Tool group";
|
|
242
242
|
private _expanded = false;
|
|
243
243
|
/** 分组是否展开(只读;测试与外部读状态用)。 */
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { showMoreHintText } from "./show-more-hint.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* 接管三个消息组件(`<skill>` 块、压缩摘要、分支摘要),
|
|
16
|
+
* 接管三个消息组件(`<skill>` 块、压缩摘要、分支摘要),pi-one-ui on 时渲染为
|
|
17
17
|
* 与工具调用一致的单行风格(● Title · hint),off 时回退原生。
|
|
18
18
|
*
|
|
19
19
|
* 三者结构同构:Box + markdownTheme + updateDisplay,构造/setExpanded/invalidate
|
|
@@ -68,12 +68,12 @@ function ensureHintHover(component: any): void {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function
|
|
71
|
+
function renderPolishedMessage(component: any, kind: DisplayKind): void {
|
|
72
72
|
const theme = displayTheme;
|
|
73
73
|
if (!theme) return; // 主题未就绪时保留原生渲染
|
|
74
74
|
if (component.bgFn) component.setBgFn?.(undefined); // 与工具调用一致,去掉灰底
|
|
75
75
|
if (component.paddingY !== 0) {
|
|
76
|
-
component.
|
|
76
|
+
component._oneuiOriginalPaddingY = component.paddingY;
|
|
77
77
|
component.paddingY = 0; // 工具组件 paddingY=0;原生 Box 默认 1,会上下各留一个空行
|
|
78
78
|
}
|
|
79
79
|
component.clear();
|
|
@@ -128,16 +128,16 @@ export function installMessageDisplayRendering(): () => void {
|
|
|
128
128
|
const installed = function (this: any) {
|
|
129
129
|
if (patch.active && config.mode !== "off") {
|
|
130
130
|
try {
|
|
131
|
-
|
|
131
|
+
renderPolishedMessage(this, kind);
|
|
132
132
|
return;
|
|
133
133
|
} catch {
|
|
134
134
|
// 渲染失败回退原生
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
// 回退原生前恢复 paddingY,避免原生渲染丢失上下内边距
|
|
138
|
-
if (this.
|
|
139
|
-
this.paddingY = this.
|
|
140
|
-
delete this.
|
|
138
|
+
if (this._oneuiOriginalPaddingY !== undefined) {
|
|
139
|
+
this.paddingY = this._oneuiOriginalPaddingY;
|
|
140
|
+
delete this._oneuiOriginalPaddingY;
|
|
141
141
|
}
|
|
142
142
|
original.call(this);
|
|
143
143
|
};
|
|
@@ -247,8 +247,8 @@ const activeAnimationContexts = new Set<any>();
|
|
|
247
247
|
let sharedAnimationTimer: ReturnType<typeof setTimeout> | null = null;
|
|
248
248
|
|
|
249
249
|
function clearAnimation(context: any) {
|
|
250
|
-
if (!context?.state?.
|
|
251
|
-
context.state.
|
|
250
|
+
if (!context?.state?.oneuiAnimationScheduled) return;
|
|
251
|
+
context.state.oneuiAnimationScheduled = false;
|
|
252
252
|
activeAnimationContexts.delete(context);
|
|
253
253
|
if (activeAnimationContexts.size === 0 && sharedAnimationTimer) {
|
|
254
254
|
clearTimeout(sharedAnimationTimer);
|
|
@@ -258,7 +258,7 @@ function clearAnimation(context: any) {
|
|
|
258
258
|
|
|
259
259
|
export function clearAllAnimations() {
|
|
260
260
|
for (const ctx of activeAnimationContexts) {
|
|
261
|
-
ctx.state.
|
|
261
|
+
ctx.state.oneuiAnimationScheduled = false;
|
|
262
262
|
}
|
|
263
263
|
activeAnimationContexts.clear();
|
|
264
264
|
if (sharedAnimationTimer) {
|
|
@@ -272,8 +272,8 @@ export function scheduleAnimation(
|
|
|
272
272
|
intervalMs = TOOL_LOADING_INTERVAL_MS,
|
|
273
273
|
) {
|
|
274
274
|
const state = (context.state ??= {});
|
|
275
|
-
if (state.
|
|
276
|
-
state.
|
|
275
|
+
if (state.oneuiAnimationScheduled) return;
|
|
276
|
+
state.oneuiAnimationScheduled = true;
|
|
277
277
|
activeAnimationContexts.add(context);
|
|
278
278
|
if (!sharedAnimationTimer) {
|
|
279
279
|
sharedAnimationTimer = setTimeout(() => {
|
|
@@ -281,7 +281,7 @@ export function scheduleAnimation(
|
|
|
281
281
|
const contexts = Array.from(activeAnimationContexts);
|
|
282
282
|
activeAnimationContexts.clear();
|
|
283
283
|
for (const ctx of contexts) {
|
|
284
|
-
ctx.state.
|
|
284
|
+
ctx.state.oneuiAnimationScheduled = false;
|
|
285
285
|
ctx.invalidate?.();
|
|
286
286
|
}
|
|
287
287
|
}, intervalMs);
|
|
@@ -307,8 +307,8 @@ export function settledIcon(
|
|
|
307
307
|
export function setToolVisualState(context: any, visualState: ToolVisualState) {
|
|
308
308
|
const state = (context.state ??= {});
|
|
309
309
|
if (visualState !== "pending") clearAnimation(context);
|
|
310
|
-
if (state.
|
|
311
|
-
state.
|
|
310
|
+
if (state.oneuiToolVisualState === visualState) return;
|
|
311
|
+
state.oneuiToolVisualState = visualState;
|
|
312
312
|
// Do not invalidate synchronously from renderResult. Pi is already rendering
|
|
313
313
|
// this tool row; recursively scheduling another render here can retain both
|
|
314
314
|
// the finalized result component and its previous secondary/partial component,
|
|
@@ -317,7 +317,7 @@ export function setToolVisualState(context: any, visualState: ToolVisualState) {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
function getToolVisualState(context: any): ToolVisualState | undefined {
|
|
320
|
-
return context?.state?.
|
|
320
|
+
return context?.state?.oneuiToolVisualState as ToolVisualState | undefined;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
export function resolveToolVisualState(
|
|
@@ -346,7 +346,7 @@ export function toolIconColor(
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
export function isToolExpanded(options: any, context: any): boolean {
|
|
349
|
-
const local = context?.state?.
|
|
349
|
+
const local = context?.state?.oneuiToolExpanded;
|
|
350
350
|
return typeof local === "boolean"
|
|
351
351
|
? local
|
|
352
352
|
: Boolean(options?.expanded ?? context?.expanded);
|
|
@@ -407,7 +407,7 @@ export type ToolIoSection = "input" | "output";
|
|
|
407
407
|
// Module-local token: /reload creates a fresh token, so stale view instances are
|
|
408
408
|
// replaced instead of retaining their old render implementation.
|
|
409
409
|
const EXPANDED_TOOL_IO_VIEW_GENERATION = Symbol(
|
|
410
|
-
"
|
|
410
|
+
"pi-one-ui.expanded-tool-io-view",
|
|
411
411
|
);
|
|
412
412
|
|
|
413
413
|
/**
|
|
@@ -931,7 +931,7 @@ export function renderExpandedToolResult(
|
|
|
931
931
|
|
|
932
932
|
// Prefer structured Input/Output when we have args or non-empty output.
|
|
933
933
|
if (inputBody.trim() || outputBody.trim()) {
|
|
934
|
-
const cached = context?.state?.
|
|
934
|
+
const cached = context?.state?.oneuiExpandedIoView;
|
|
935
935
|
let view: ExpandedToolIoView;
|
|
936
936
|
if (isExpandedToolIoView(lastComponent)) {
|
|
937
937
|
view = lastComponent;
|
|
@@ -962,8 +962,8 @@ export function renderExpandedToolResult(
|
|
|
962
962
|
}
|
|
963
963
|
|
|
964
964
|
if (context?.state) {
|
|
965
|
-
context.state.
|
|
966
|
-
context.state.
|
|
965
|
+
context.state.oneuiIoView = undefined;
|
|
966
|
+
context.state.oneuiExpandedIoView = undefined;
|
|
967
967
|
}
|
|
968
968
|
const color = isError ? "error" : "muted";
|
|
969
969
|
return new Text(theme.fg(color, renderCollapsedToolResult("Done")), 0, 0);
|
|
@@ -1079,8 +1079,8 @@ function rememberIoView(context: any, view: ExpandedToolIoView): void {
|
|
|
1079
1079
|
ioViewInvalidators.set(view, () => context.invalidate());
|
|
1080
1080
|
}
|
|
1081
1081
|
if (!context.state || typeof context.state !== "object") context.state = {};
|
|
1082
|
-
context.state.
|
|
1083
|
-
context.state.
|
|
1082
|
+
context.state.oneuiIoView = view;
|
|
1083
|
+
context.state.oneuiExpandedIoView = view;
|
|
1084
1084
|
}
|
|
1085
1085
|
|
|
1086
1086
|
/** hover 状态变更后刷新上一个/下一个视图(context.invalidate 缓存于 ioViewInvalidators)。 */
|
|
@@ -35,7 +35,7 @@ type RenderSlot = {
|
|
|
35
35
|
shell: "box" | "self" | "text" | null;
|
|
36
36
|
callComponent: unknown;
|
|
37
37
|
resultComponent: unknown;
|
|
38
|
-
/** rendererState 中
|
|
38
|
+
/** rendererState 中 pi-one-ui 渲染层的指针快照(切槽重装时恢复)。 */
|
|
39
39
|
stateSnapshot: [unknown, unknown, unknown];
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -89,7 +89,7 @@ function fingerprintOf(
|
|
|
89
89
|
* 不再排期,长命令期间 loading 图标就会冻结在当前帧。
|
|
90
90
|
*/
|
|
91
91
|
function schedulesPendingAnimation(component: any): boolean {
|
|
92
|
-
const visualState = component?.rendererState?.
|
|
92
|
+
const visualState = component?.rendererState?.oneuiToolVisualState as
|
|
93
93
|
| string
|
|
94
94
|
| undefined;
|
|
95
95
|
const pending =
|
|
@@ -166,9 +166,9 @@ function syncShell(component: any, shell: "box" | "self" | "text"): void {
|
|
|
166
166
|
function stateSnapshotOf(component: any): [unknown, unknown, unknown] {
|
|
167
167
|
const state = component?.rendererState;
|
|
168
168
|
return [
|
|
169
|
-
state?.
|
|
170
|
-
state?.
|
|
171
|
-
state?.
|
|
169
|
+
state?.oneuiIoView,
|
|
170
|
+
state?.oneuiExpandedIoView,
|
|
171
|
+
state?.oneuiToolVisualState,
|
|
172
172
|
];
|
|
173
173
|
}
|
|
174
174
|
|
|
@@ -186,14 +186,14 @@ function restoreStateSnapshot(
|
|
|
186
186
|
const state = (component.rendererState ??= {});
|
|
187
187
|
// ioView/visualState 每槽构建时都会写入,按槽恢复;
|
|
188
188
|
// expandedIoView 是跨槽保留指针(折叠 renderer 不清),只在展开槽恢复。
|
|
189
|
-
state.
|
|
190
|
-
state.
|
|
191
|
-
if (slotKey === "expanded") state.
|
|
189
|
+
state.oneuiIoView = snapshot[0];
|
|
190
|
+
state.oneuiToolVisualState = snapshot[1];
|
|
191
|
+
if (slotKey === "expanded") state.oneuiExpandedIoView = snapshot[2];
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
195
|
* 把缓存槽的 call/result 组件装回目标壳容器并恢复派生字段。
|
|
196
|
-
* 同时恢复
|
|
196
|
+
* 同时恢复 pi-one-ui 渲染层的 rendererState 指针快照(ioView / expandedIoView /
|
|
197
197
|
* visualState),补齐切槽跳过 renderer 调用导致的状态缺失;pending 展开时
|
|
198
198
|
* 重新调度加载动画。text fallback 槽(无容器装载语义)返回 false,调用方
|
|
199
199
|
* 退化为重建。
|