pi-one-ui 0.1.1 → 0.2.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.
Files changed (113) hide show
  1. package/README.md +66 -39
  2. package/extensions/{shell → app/commands}/settings-previews.ts +7 -7
  3. package/extensions/app/config/renderer.ts +13 -63
  4. package/extensions/app/config/shell.ts +24 -138
  5. package/extensions/app/config/store.ts +290 -0
  6. package/extensions/app/host/pi-extension-port.ts +25 -0
  7. package/extensions/app/host/pi-ui-port.ts +52 -0
  8. package/extensions/app/host/tui-capabilities.ts +27 -0
  9. package/extensions/app/overlay/input-router.ts +63 -0
  10. package/extensions/app/overlay/overlay-manager.ts +39 -0
  11. package/extensions/{shell → app/overlay}/selector-border.ts +3 -3
  12. package/extensions/app/overlay/selector-controller.ts +104 -0
  13. package/extensions/app/ownership/layout-registry.ts +65 -0
  14. package/extensions/app/ownership.ts +21 -7
  15. package/extensions/app/panel.ts +261 -140
  16. package/extensions/app/runtime/event-coordinator.ts +84 -0
  17. package/extensions/app/runtime/render-scheduler.ts +56 -0
  18. package/extensions/app/runtime/runtime-state.ts +83 -0
  19. package/extensions/app/runtime/tui-runtime.ts +546 -0
  20. package/extensions/features/{context.ts → context-inspector/index.ts} +30 -23
  21. package/extensions/features/{shell/flush-docked-bash.ts → flush-docked-bash.ts} +1 -4
  22. package/extensions/index.ts +2 -48
  23. package/extensions/layouts/context/index.ts +188 -0
  24. package/extensions/{shell → layouts/context/message}/user-message-styles.ts +2 -2
  25. package/extensions/{shell → layouts/context/message}/user-message.ts +2 -2
  26. package/extensions/{transcript → layouts/context}/renderer/compact-mode.ts +17 -13
  27. package/extensions/layouts/context/renderer/context-refresh.ts +79 -0
  28. package/extensions/{transcript → layouts/context}/renderer/default-mode.ts +3 -3
  29. package/extensions/{transcript → layouts/context}/renderer/index.ts +14 -14
  30. package/extensions/{transcript → layouts/context}/renderer/mouse/hover.ts +2 -2
  31. package/extensions/{transcript → layouts/context}/renderer/mouse/interaction.ts +26 -7
  32. package/extensions/{transcript → layouts/context}/renderer/mouse/layout.ts +1 -1
  33. package/extensions/{transcript → layouts/context}/renderer/mouse/packets.ts +3 -3
  34. package/extensions/{transcript → layouts/context}/renderer/mouse/scroll.ts +12 -7
  35. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-component.ts +2 -2
  36. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-edit-render.ts +1 -1
  37. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-highlight.ts +1 -1
  38. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-layout.ts +1 -1
  39. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-limits.ts +1 -1
  40. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-palette.ts +1 -1
  41. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-presentation.ts +1 -1
  42. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-renderer.ts +1 -1
  43. package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-write-render.ts +1 -1
  44. package/extensions/{transcript → layouts/context}/renderer/tool/diff/index.ts +2 -2
  45. package/extensions/{transcript → layouts/context}/renderer/tool/grouping.ts +4 -4
  46. package/extensions/{transcript → layouts/context}/renderer/tool/message-display.ts +6 -4
  47. package/extensions/{transcript → layouts/context}/renderer/tool/names.ts +2 -2
  48. package/extensions/{transcript → layouts/context}/renderer/tool/result.ts +3 -3
  49. package/extensions/{features/agent-summary → layouts/context/summary}/core.ts +1 -1
  50. package/extensions/{features → layouts/context/thinking}/compact-thinking.ts +7 -7
  51. package/extensions/{shell → layouts/editor}/accent-rail-editor.ts +8 -8
  52. package/extensions/{shell → layouts/editor}/completion-menu.ts +3 -6
  53. package/extensions/layouts/editor/controller.ts +613 -0
  54. package/extensions/{shell → layouts/editor}/editor-metadata-format.ts +6 -3
  55. package/extensions/layouts/editor/factory.ts +159 -0
  56. package/extensions/layouts/editor/index.ts +16 -0
  57. package/extensions/{shell → layouts/editor}/minimalist-editor.ts +4 -4
  58. package/extensions/layouts/editor/ownership.ts +103 -0
  59. package/extensions/{shell → layouts/editor}/ui.ts +2 -2
  60. package/extensions/layouts/footer/controller.ts +392 -0
  61. package/extensions/layouts/footer/data.ts +41 -0
  62. package/extensions/{shell → layouts/footer}/extension-status.ts +2 -2
  63. package/extensions/{shell → layouts/footer}/footer-layout.ts +1 -1
  64. package/extensions/{shell → layouts/footer}/footer.ts +11 -8
  65. package/extensions/layouts/footer/index.ts +24 -0
  66. package/extensions/layouts/header/index.ts +5 -0
  67. package/extensions/{features/shell → layouts/header}/startup-header.ts +105 -19
  68. package/extensions/layouts/working-line/controller.ts +291 -0
  69. package/extensions/{shell → layouts/working-line}/interaction-summary.ts +3 -3
  70. package/extensions/{shell → layouts/working-line}/working-line-spinners.ts +1 -1
  71. package/extensions/{shell → layouts/working-line}/working-line.ts +4 -4
  72. package/extensions/{shell → services}/live-context.ts +29 -1
  73. package/extensions/services/project-refresh.ts +374 -0
  74. package/extensions/{shell → services}/project-state.ts +5 -5
  75. package/extensions/{shell/state.ts → services/session-state.ts} +75 -5
  76. package/extensions/{shell → shared}/format.ts +4 -4
  77. package/extensions/tools/component-tree.ts +6 -6
  78. package/package.json +4 -4
  79. package/extensions/shell/index.ts +0 -1659
  80. package/extensions/shell/project-refresh.ts +0 -130
  81. package/extensions/shell/settings-command.ts +0 -2144
  82. package/extensions/transcript/index.ts +0 -65
  83. package/extensions/transcript/renderer/transcript-refresh.ts +0 -79
  84. /package/extensions/{shell → app/ownership}/prototype-patch-registry.ts +0 -0
  85. /package/extensions/{shell → app/runtime}/session-lifecycle.ts +0 -0
  86. /package/extensions/features/{shell/aliases.ts → aliases.ts} +0 -0
  87. /package/extensions/features/{shell → legacy}/working-message.ts +0 -0
  88. /package/extensions/features/{reference → session-reference}/index.ts +0 -0
  89. /package/extensions/features/{reference → session-reference}/session.ts +0 -0
  90. /package/extensions/features/{reference/subagent.ts → subagent-autocomplete.ts} +0 -0
  91. /package/extensions/{shell → layouts/context/message}/user-message-osc.ts +0 -0
  92. /package/extensions/{transcript → layouts/context}/renderer/markdown-enhance.ts +0 -0
  93. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/ATTRIBUTION.md +0 -0
  94. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/ansi-utils.ts +0 -0
  95. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-header.ts +0 -0
  96. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-inline.ts +0 -0
  97. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-parse.ts +0 -0
  98. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-text.ts +0 -0
  99. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/line-width-safety.ts +0 -0
  100. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/shiki-highlight.ts +0 -0
  101. /package/extensions/{transcript → layouts/context}/renderer/tool/diff/write-execution.ts +0 -0
  102. /package/extensions/{transcript → layouts/context}/renderer/tool/show-more-hint.ts +0 -0
  103. /package/extensions/{features/agent-summary → layouts/context/summary}/index.ts +0 -0
  104. /package/extensions/{shell → layouts/editor}/accent-rail-layout-patch.ts +0 -0
  105. /package/extensions/{shell → layouts/editor}/editor-transfer.ts +0 -0
  106. /package/extensions/{shell → layouts/footer}/footer-format.ts +0 -0
  107. /package/extensions/{shell → layouts/working-line}/working-line-messages.ts +0 -0
  108. /package/extensions/{shell/git.ts → services/git-data.ts} +0 -0
  109. /package/extensions/{shell/package-version.ts → services/package-data.ts} +0 -0
  110. /package/extensions/{shell/runtime.ts → services/runtime-data.ts} +0 -0
  111. /package/extensions/{shell → services}/telemetry.ts +0 -0
  112. /package/extensions/{shell → shared}/icons.ts +0 -0
  113. /package/extensions/{shell → shared}/style.ts +0 -0
@@ -1,52 +1,6 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { showOneUiPanel } from "./app/panel.ts";
3
- import registerShell, {
4
- type ShellExtensionOptions,
5
- type ShellRuntimeController,
6
- } from "./shell/index.ts";
7
- import registerTranscript, {
8
- type TranscriptExtensionOptions,
9
- type TranscriptRuntimeController,
10
- } from "./transcript/index.ts";
11
-
12
- type Controllers = {
13
- shell?: ShellRuntimeController;
14
- renderer?: TranscriptRuntimeController;
15
- };
16
-
17
- function registerOneUiCommand(
18
- pi: ExtensionAPI,
19
- controllers: Controllers,
20
- ): void {
21
- pi.registerCommand("oneui", {
22
- description: "Open pi-one-ui settings",
23
- handler: async (_args, ctx) => {
24
- await showOneUiPanel(ctx, controllers);
25
- },
26
- });
27
- }
2
+ import { createTuiRuntime } from "./app/runtime/tui-runtime.ts";
28
3
 
29
4
  export default function (pi: ExtensionAPI): void {
30
- let shellController: ShellRuntimeController | undefined;
31
- let rendererController: TranscriptRuntimeController | undefined;
32
- const shellOptions: ShellExtensionOptions = {
33
- registerCommand: false,
34
- onRuntimeController: (controller) => {
35
- shellController = controller;
36
- },
37
- };
38
- const rendererOptions: TranscriptExtensionOptions = {
39
- onRuntimeController: (controller) => {
40
- rendererController = controller;
41
- },
42
- };
43
-
44
- // One package, one management command. The internal owners remain explicit:
45
- // Shell owns the terminal chrome; transcript owns tool and message rendering.
46
- registerShell(pi, shellOptions);
47
- registerTranscript(pi, rendererOptions);
48
- registerOneUiCommand(pi, {
49
- shell: shellController,
50
- renderer: rendererController,
51
- });
5
+ createTuiRuntime(pi).install();
52
6
  }
@@ -0,0 +1,188 @@
1
+ import type {
2
+ ExtensionAPI,
3
+ ExtensionContext,
4
+ Theme,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import { config } from "../../app/config/renderer.ts";
7
+ import { configStore, type ConfigRecord } from "../../app/config/store.ts";
8
+ import {
9
+ hasUnsupportedComponentStyle,
10
+ loadConfig,
11
+ mergeConfig,
12
+ saveUserMessagesComponentPatch,
13
+ type UserMessagesComponentConfig,
14
+ type ZentuiConfig,
15
+ } from "../../app/config/shell.ts";
16
+ import {
17
+ installUserMessageStyle,
18
+ removeUserMessageStyle,
19
+ } from "./message/user-message.ts";
20
+
21
+ // shell
22
+ import piAliases from "../../features/aliases.ts";
23
+ import { installFlushDockedBash } from "../../features/flush-docked-bash.ts";
24
+
25
+ // feature
26
+ import agentAutocomplete from "../../features/subagent-autocomplete.ts";
27
+ import agentSummary from "./summary/index.ts";
28
+ import context from "../../features/context-inspector/index.ts";
29
+ import sessionReference from "../../features/session-reference/index.ts";
30
+ import { installCompactThinking } from "./thinking/compact-thinking.ts";
31
+
32
+ // renderer
33
+ import registerContextRenderer, {
34
+ getCompactThinkingConfig,
35
+ } from "./renderer/index.ts";
36
+ import markdownEnhance from "./renderer/markdown-enhance.ts";
37
+
38
+ export type ContextRuntimeController = {
39
+ setMode: (mode: "on" | "compact" | "off", ctx: ExtensionContext) => void;
40
+ updateConfig: (
41
+ partial: Partial<typeof config>,
42
+ ctx: ExtensionContext,
43
+ ) => void;
44
+ setUserMessagesComponent: (
45
+ patch: Partial<UserMessagesComponentConfig>,
46
+ ctx: ExtensionContext,
47
+ ) => void;
48
+ };
49
+
50
+ export type ContextExtensionOptions = {
51
+ /**
52
+ * Exposes live context refresh to the unified settings panel.
53
+ */
54
+ onRuntimeController?: (controller: ContextRuntimeController) => void;
55
+ };
56
+
57
+ let contextConfig: ZentuiConfig = loadConfig();
58
+ let activeTheme: Theme | undefined;
59
+ let cleanupUserMessageStyle: () => void = () => {};
60
+ let userMessageStyleInstalled = false;
61
+ let unsubscribeConfig: () => void = () => {};
62
+
63
+ /**
64
+ * Reports whether a session context can safely install TUI-owned patches.
65
+ */
66
+ function isTuiContext(ctx: ExtensionContext): boolean {
67
+ return ctx.mode === "tui" && ctx.hasUI;
68
+ }
69
+
70
+ /**
71
+ * Installs the Context-owned User Message renderer once for the active session.
72
+ */
73
+ function installContextUserMessages(): void {
74
+ if (userMessageStyleInstalled || !activeTheme) return;
75
+ try {
76
+ cleanupUserMessageStyle = installUserMessageStyle(
77
+ () => activeTheme,
78
+ () => contextConfig,
79
+ );
80
+ userMessageStyleInstalled = true;
81
+ } catch {
82
+ cleanupUserMessageStyle = () => {};
83
+ userMessageStyleInstalled = false;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Removes the Context-owned User Message renderer and clears its patch state.
89
+ */
90
+ function uninstallContextUserMessages(): void {
91
+ try {
92
+ cleanupUserMessageStyle();
93
+ } finally {
94
+ cleanupUserMessageStyle = () => {};
95
+ userMessageStyleInstalled = false;
96
+ removeUserMessageStyle();
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Reconciles User Message ownership with the latest shell configuration.
102
+ */
103
+ function reconcileContextUserMessages(): void {
104
+ const enabled =
105
+ contextConfig.components.userMessages.enabled &&
106
+ !hasUnsupportedComponentStyle(contextConfig, "userMessages");
107
+ if (enabled) installContextUserMessages();
108
+ else uninstallContextUserMessages();
109
+ }
110
+
111
+ /**
112
+ * Starts the Context session binding and subscribes it to shared config updates.
113
+ */
114
+ function startContextSession(ctx: ExtensionContext): void {
115
+ if (!isTuiContext(ctx)) return;
116
+ activeTheme = ctx.ui.theme;
117
+ contextConfig = loadConfig();
118
+ unsubscribeConfig();
119
+ unsubscribeConfig = configStore.subscribe((record: ConfigRecord) => {
120
+ contextConfig = mergeConfig(record);
121
+ reconcileContextUserMessages();
122
+ });
123
+ reconcileContextUserMessages();
124
+ }
125
+
126
+ /**
127
+ * Applies a User Message configuration patch owned by Context.
128
+ *
129
+ * @param patch User Message configuration changes.
130
+ * @param _ctx Active Pi extension context.
131
+ */
132
+ function setContextUserMessages(
133
+ patch: Partial<UserMessagesComponentConfig>,
134
+ _ctx: ExtensionContext,
135
+ ): void {
136
+ contextConfig = saveUserMessagesComponentPatch(patch);
137
+ if (patch.enabled !== undefined || patch.style !== undefined)
138
+ reconcileContextUserMessages();
139
+ }
140
+
141
+ /**
142
+ * Releases Context session bindings and restores the original User Message patch.
143
+ */
144
+ function stopContextSession(): void {
145
+ unsubscribeConfig();
146
+ unsubscribeConfig = () => {};
147
+ uninstallContextUserMessages();
148
+ activeTheme = undefined;
149
+ }
150
+
151
+ /**
152
+ * Registers the Context layout, its renderer stack and its feature hooks.
153
+ */
154
+ export default function (
155
+ pi: ExtensionAPI,
156
+ options: ContextExtensionOptions = {},
157
+ ): void {
158
+ // shell chrome
159
+ if (config.enableAliases) piAliases(pi);
160
+ installFlushDockedBash();
161
+ // The thinking controller is queried directly by the context render stack.
162
+ markdownEnhance(pi);
163
+ registerContextRenderer(
164
+ pi,
165
+ undefined,
166
+ installCompactThinking(pi, getCompactThinkingConfig()),
167
+ {
168
+ onRuntimeController: (controller) =>
169
+ options.onRuntimeController?.({
170
+ ...controller,
171
+ setUserMessagesComponent: setContextUserMessages,
172
+ }),
173
+ },
174
+ );
175
+
176
+ // features
177
+ if (config.enableContextCommand) context(pi);
178
+ if (config.enableSessionReference) sessionReference(pi);
179
+ if (config.enableSubagentAutocomplete) agentAutocomplete(pi);
180
+ if (config.enableAgentSummary) agentSummary(pi);
181
+
182
+ pi.on("session_start", (_event, ctx) => {
183
+ startContextSession(ctx);
184
+ });
185
+ pi.on("session_shutdown", () => {
186
+ stopContextSession();
187
+ });
188
+ }
@@ -5,12 +5,12 @@ import {
5
5
  truncateToWidth,
6
6
  visibleWidth,
7
7
  } from "@earendil-works/pi-tui";
8
- import type { ZentuiConfig } from "../app/config/shell.ts";
8
+ import type { ZentuiConfig } from "../../../app/config/shell.ts";
9
9
  import {
10
10
  EDITOR_ACCENT_FALLBACK,
11
11
  EDITOR_BORDER_FALLBACK,
12
12
  renderStyleForSourceOrFallbackStrict,
13
- } from "./style.ts";
13
+ } from "../../../shared/style.ts";
14
14
  import { sanitizeUserMessageSourceText } from "./user-message-osc.ts";
15
15
 
16
16
  export type UserMessageStyleRenderInput = {
@@ -3,11 +3,11 @@ import {
3
3
  UserMessageComponent,
4
4
  } from "@earendil-works/pi-coding-agent";
5
5
  import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
6
- import type { ZentuiConfig } from "../app/config/shell.ts";
6
+ import type { ZentuiConfig } from "../../../app/config/shell.ts";
7
7
  import {
8
8
  installPrototypePatch,
9
9
  removePrototypePatch,
10
- } from "./prototype-patch-registry.ts";
10
+ } from "../../../app/ownership/prototype-patch-registry.ts";
11
11
  import {
12
12
  sanitizeRenderedUserMessageLines,
13
13
  sanitizeRenderedUserMessageText,
@@ -26,10 +26,10 @@ import {
26
26
  Box,
27
27
  Spacer,
28
28
  } from "@earendil-works/pi-tui";
29
- import { config, getToolDisplayConfig } from "../../app/config/renderer.ts";
30
- import { toolLoadingIcon } from "../../tools/tool-loading-icon.ts";
31
- import { sanitizeToolResultText } from "../../tools/tool-result-sanitize.ts";
32
- import { refreshTranscriptComponent } from "./transcript-refresh.ts";
29
+ import { config, getToolDisplayConfig } from "../../../app/config/renderer.ts";
30
+ import { toolLoadingIcon } from "../../../tools/tool-loading-icon.ts";
31
+ import { sanitizeToolResultText } from "../../../tools/tool-result-sanitize.ts";
32
+ import { refreshContextComponent } from "./context-refresh.ts";
33
33
  import { getMessageDisplayTheme } from "./tool/message-display.ts";
34
34
  import { showMoreHintText } from "./tool/show-more-hint.ts";
35
35
  import {
@@ -45,10 +45,13 @@ import {
45
45
  renderExpandedToolResult,
46
46
  scheduleAnimation,
47
47
  } from "./tool/result.ts";
48
- import { oneLine } from "../../tools/format.ts";
48
+ import { oneLine } from "../../../tools/format.ts";
49
49
  import { paddedBackgroundRow } from "./tool/grouping.ts";
50
- import { hasVisibleText, stripBackgroundAnsi } from "../../tools/ansi-text.ts";
51
- import { walkComponentTree } from "../../tools/component-tree.ts";
50
+ import {
51
+ hasVisibleText,
52
+ stripBackgroundAnsi,
53
+ } from "../../../tools/ansi-text.ts";
54
+ import { walkComponentTree } from "../../../tools/component-tree.ts";
52
55
  import {
53
56
  ASSISTANT_REENTRY_DESCRIPTION,
54
57
  ASSISTANT_REENTRY_KEY,
@@ -60,7 +63,7 @@ import {
60
63
  COMPACT_THINKING_PATCH_KEY,
61
64
  patchRegistry,
62
65
  PROTOTYPE_ORIGINAL_KEY,
63
- } from "../../tools/patch-keys.ts";
66
+ } from "../../../tools/patch-keys.ts";
64
67
 
65
68
  /** compact 渲染层对 compact-thinking 的只读查询面(不建第二套计时器)。 */
66
69
  export type CompactThinkingQuery = {
@@ -839,9 +842,10 @@ function collectMountedComponents(root: any): void {
839
842
  tools.add(value);
840
843
  }
841
844
  });
842
- // 扫到组件才替换跟踪表。面板/custom UI 打开时 root 往往扫不到 transcript,
843
- // 若此时清空会丢掉 live updateContent/updateDisplay 已登记的实例,
844
- // /ccstyle 切换就只能靠 /reload 重建。
845
+ // Replace tracking only after scanning components. The root often omits the
846
+ // context while a panel or custom UI is open,
847
+ // so clearing here would lose live updateContent/updateDisplay instances,
848
+ // and mode changes would require a /reload to rebuild them.
845
849
  if (assistants.size > 0 || tools.size > 0) {
846
850
  trackedAssistantComponents.clear();
847
851
  trackedToolComponents.clear();
@@ -1421,7 +1425,7 @@ function refreshTrackedComponents(): void {
1421
1425
  for (const component of [...trackedAssistantComponents]) {
1422
1426
  try {
1423
1427
  if (config.mode !== "compact") detachAssistantExpansion(component);
1424
- refreshTranscriptComponent(component);
1428
+ refreshContextComponent(component);
1425
1429
  } catch {
1426
1430
  trackedAssistantComponents.delete(component);
1427
1431
  }
@@ -1429,7 +1433,7 @@ function refreshTrackedComponents(): void {
1429
1433
  for (const component of [...trackedToolComponents]) {
1430
1434
  try {
1431
1435
  // 共享实现负责 updateDisplay;invalidate 属于 compact-mode 的跟踪语义。
1432
- refreshTranscriptComponent(component);
1436
+ refreshContextComponent(component);
1433
1437
  component.invalidate?.();
1434
1438
  } catch {
1435
1439
  trackedToolComponents.delete(component);
@@ -0,0 +1,79 @@
1
+ import type { AssistantMessage } from "@earendil-works/pi-ai";
2
+ import {
3
+ AssistantMessageComponent,
4
+ ToolExecutionComponent,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import { walkComponentTree } from "../../../tools/component-tree.ts";
7
+ import {
8
+ COMPACT_MODE_PATCH_KEY,
9
+ patchRegistry,
10
+ } from "../../../tools/patch-keys.ts";
11
+
12
+ // pi-coding-agent 类型声明中 AssistantMessageComponent 仅能以 value 形式使用,
13
+ // updateContent/lastMessage 用结构化类型访问。
14
+ type ContextComponentInternals = {
15
+ lastMessage?: AssistantMessage;
16
+ updateContent(message: AssistantMessage): void;
17
+ invalidate?(): void;
18
+ };
19
+
20
+ /**
21
+ * Refreshes one constructed context component.
22
+ *
23
+ * Assistant components receive their latest message, while tool components
24
+ * refresh their display so resumed content returns to the current shell.
25
+ */
26
+ export function refreshContextComponent(value: unknown): boolean {
27
+ if (!value || typeof value !== "object") return false;
28
+ if (value instanceof AssistantMessageComponent) {
29
+ const self = value as unknown as ContextComponentInternals;
30
+ if (self.lastMessage) self.updateContent(self.lastMessage);
31
+ else self.invalidate?.();
32
+ return true;
33
+ }
34
+ if (value instanceof ToolExecutionComponent) {
35
+ // updateDisplay is private, but it is the only resume-time path that
36
+ // restores content to the correct shell container.
37
+ (value as unknown as { updateDisplay(): void }).updateDisplay();
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+
43
+ /**
44
+ * Asserts that compact-mode remains the outer patch in compact mode.
45
+ *
46
+ * Resume and reload can rebuild components without emitting a message update,
47
+ * so the ownership order is checked before any mounted component is refreshed.
48
+ */
49
+ function assertCompactModeOutermost(): void {
50
+ try {
51
+ const patch = patchRegistry.get<{ assertAssistantOwnership?: () => void }>(
52
+ COMPACT_MODE_PATCH_KEY,
53
+ );
54
+ if (patch && typeof patch.assertAssistantOwnership === "function") {
55
+ patch.assertAssistantOwnership();
56
+ }
57
+ } catch {
58
+ // Continue scanning when compact-mode is absent or the assertion fails.
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Scans mounted roots and refreshes all constructed assistant and tool components.
64
+ *
65
+ * This covers reload, resume and compaction paths where Pi rebuilds components
66
+ * before the renderer patches are installed.
67
+ */
68
+ export function refreshMountedContext(tui?: unknown): void {
69
+ if (!tui || typeof (tui as any).getMountedRoots !== "function") return;
70
+ // Fix patch order before refreshing so updateContent runs through the outer patch.
71
+ assertCompactModeOutermost();
72
+ walkComponentTree(tui, (value: any) => {
73
+ try {
74
+ refreshContextComponent(value);
75
+ } catch {
76
+ // Isolate one component failure; a later tree rebuild can retry it.
77
+ }
78
+ });
79
+ }
@@ -10,13 +10,13 @@ import {
10
10
  config,
11
11
  getToolDisplayConfig,
12
12
  type CompactStyleMode,
13
- } from "../../app/config/renderer.ts";
13
+ } from "../../../app/config/renderer.ts";
14
14
  import {
15
15
  COMPONENT_TOOL_RENDER_MODE,
16
16
  GLOBAL_TOOL_RENDER_PATCH,
17
17
  patchRegistry,
18
18
  TOOL_EXPANDED_BACKGROUND_PATCH,
19
- } from "../../tools/patch-keys.ts";
19
+ } from "../../../tools/patch-keys.ts";
20
20
  import { isToolCallHovered } from "./mouse/hover.ts";
21
21
  import {
22
22
  countLines,
@@ -36,7 +36,7 @@ import {
36
36
  toolIconColor,
37
37
  toolViewportWidth,
38
38
  } from "./tool/result.ts";
39
- import { oneLine } from "../../tools/format.ts";
39
+ import { oneLine } from "../../../tools/format.ts";
40
40
  import { showMoreHintText } from "./tool/show-more-hint.ts";
41
41
  import { countWriteDiffStats } from "./tool/diff/diff-renderer.ts";
42
42
  import {
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { CompactThinkingController } from "../../features/compact-thinking.ts";
2
+ import type { CompactThinkingController } from "../thinking/compact-thinking.ts";
3
3
  import {
4
4
  installToolGrouping,
5
5
  type ToolGroupingHooks,
@@ -15,11 +15,11 @@ import {
15
15
  installToolExpandedBackground,
16
16
  type DefaultModeHooks,
17
17
  } from "./default-mode.ts";
18
- import { isLazyProxyTui } from "../../tools/fullscreen-detect.ts";
18
+ import { isLazyProxyTui } from "../../../tools/fullscreen-detect.ts";
19
19
  import {
20
20
  GLOBAL_COMPACTION_RENDER_PATCH,
21
21
  patchRegistry,
22
- } from "../../tools/patch-keys.ts";
22
+ } from "../../../tools/patch-keys.ts";
23
23
  import {
24
24
  config,
25
25
  normalizeConfig,
@@ -27,7 +27,7 @@ import {
27
27
  updateConfig,
28
28
  type CompactStyleMode,
29
29
  type Config,
30
- } from "../../app/config/renderer.ts";
30
+ } from "../../../app/config/renderer.ts";
31
31
  import {
32
32
  installToolMouseInteraction,
33
33
  resetToolHoverState,
@@ -58,7 +58,7 @@ import {
58
58
 
59
59
  let compactModeHooks: CompactModeHooks | undefined;
60
60
 
61
- function refreshCurrentTranscript(
61
+ function refreshCurrentContext(
62
62
  ctx?: any,
63
63
  toolGrouping?: ToolGroupingHooks,
64
64
  ): void {
@@ -94,15 +94,15 @@ function applyStyleMode(
94
94
  tui.terminal?.write?.(TOOL_MOUSE_DISABLE);
95
95
  }
96
96
  } else if (mode === "compact") {
97
- // 切入 compact:先收集当前 transcript,再同步全局展开状态和补丁所有权。
97
+ // Enter compact mode after collecting the current context and syncing ownership.
98
98
  syncCompactMode(ctx);
99
99
  }
100
- // 立即重塑一次;再延迟一帧(与 session_start 同款),等面板/custom
101
- // 卸下、主 transcript 重新挂载后再扫树,避免必须 /reload。
102
- refreshCurrentTranscript(ctx, toolGrouping);
100
+ // Reshape immediately and once more after the panel/custom UI unmounts and
101
+ // the main context is mounted again.
102
+ refreshCurrentContext(ctx, toolGrouping);
103
103
  scheduleSessionRender(() => {
104
104
  if (mode === "compact") syncCompactMode(ctx);
105
- refreshCurrentTranscript(ctx, toolGrouping);
105
+ refreshCurrentContext(ctx, toolGrouping);
106
106
  });
107
107
  ctx.ui.notify(`Claude Code style: ${mode}`, "info");
108
108
  }
@@ -180,7 +180,7 @@ export default function (
180
180
  applyStyleMode(mode, ctx, installation?.toolGrouping),
181
181
  updateConfig: (partial, ctx) => {
182
182
  updateConfig(partial);
183
- refreshCurrentTranscript(ctx, installation?.toolGrouping);
183
+ refreshCurrentContext(ctx, installation?.toolGrouping);
184
184
  },
185
185
  });
186
186
 
@@ -215,7 +215,7 @@ export default function (
215
215
  hooks.toolGrouping.setTheme(ctx.ui.theme);
216
216
  setMessageDisplayTheme(ctx.ui.theme);
217
217
  ctx.ui.setStatus("ccstyle", undefined);
218
- // 先收集 resume transcript,再同步 compact 补丁与全局展开状态。
218
+ // Collect the resumed context before syncing compact patches and expansion state.
219
219
  syncCompactMode(ctx);
220
220
  // compact-thinking 的 session_start 处理在本 handler 之后执行(在其之上再装
221
221
  // 一层 updateContent);延迟再同步一次,保证 compact 补丁最终位于外层。
@@ -225,7 +225,7 @@ export default function (
225
225
 
226
226
  pi.on("session_compact", async (event, ctx) => {
227
227
  const hooks = ensureTuiInstallation(ctx);
228
- // Compaction rebuilds the transcript without session_start. Rebind after
228
+ // Compaction rebuilds the context without session_start. Rebind after
229
229
  // other TUI extensions may have replaced the root input dispatcher.
230
230
  if (ctx?.mode === "tui" && ctx?.hasUI)
231
231
  installToolMouseInteraction(ctx, mouseOwner);
@@ -270,7 +270,7 @@ export default function (
270
270
  }
271
271
 
272
272
  // ---- 对外导出:入口/测试实际消费的符号 ----
273
- export { getCompactThinkingConfig } from "../../app/config/renderer.ts";
273
+ export { getCompactThinkingConfig } from "../../../app/config/renderer.ts";
274
274
  export {
275
275
  humanizeMcpToolName,
276
276
  isMcpToolDefinition,
@@ -1,9 +1,9 @@
1
- import { ThinkingPreviewBlock } from "../../../features/compact-thinking.ts";
1
+ import { ThinkingPreviewBlock } from "../../thinking/compact-thinking.ts";
2
2
  import { setHoveredCompactAssistant } from "../compact-mode.ts";
3
3
  import {
4
4
  patchRegistry,
5
5
  TOOL_HOVER_STATE_KEY,
6
- } from "../../../tools/patch-keys.ts";
6
+ } from "../../../../tools/patch-keys.ts";
7
7
  import {
8
8
  isExpandedToolIoView,
9
9
  invalidateIoView,
@@ -1,20 +1,22 @@
1
1
  import {
2
2
  hasActiveTextPreview,
3
3
  showTextPreview,
4
- } from "../../../features/context.ts";
5
- import { ThinkingPreviewBlock } from "../../../features/compact-thinking.ts";
4
+ } from "../../../../features/context-inspector/index.ts";
5
+ import { ThinkingPreviewBlock } from "../../thinking/compact-thinking.ts";
6
+ import { inputRouter } from "../../../../app/overlay/input-router.ts";
7
+ import { overlayManager } from "../../../../app/overlay/overlay-manager.ts";
6
8
  import {
7
9
  patchRegistry,
8
10
  TOOL_MOUSE_OWNER_KEY,
9
- } from "../../../tools/patch-keys.ts";
11
+ } from "../../../../tools/patch-keys.ts";
10
12
  import { ToolGroupComponent } from "../tool/grouping.ts";
11
13
  import {
12
14
  isCompactAssistantComponent,
13
15
  setHoveredCompactAssistant,
14
16
  } from "../compact-mode.ts";
15
17
  import { isMessageDisplayComponent } from "../tool/message-display.ts";
16
- import { config } from "../../../app/config/renderer.ts";
17
- import { isLazyProxyTui } from "../../../tools/fullscreen-detect.ts";
18
+ import { config } from "../../../../app/config/renderer.ts";
19
+ import { isLazyProxyTui } from "../../../../tools/fullscreen-detect.ts";
18
20
  import { setToolTuiFullscreen } from "../tool/show-more-hint.ts";
19
21
  import {
20
22
  type ExpandedToolIoView,
@@ -934,6 +936,12 @@ export function resetToolHoverState(): void {
934
936
  releaseFullscreenToolMouseMotion(getToolMouseTui());
935
937
  }
936
938
 
939
+ /**
940
+ * Installs the Context mouse route through the shared input router.
941
+ *
942
+ * @param ctx Active TUI extension context.
943
+ * @param owner Ownership token used to make teardown safe across reloads.
944
+ */
937
945
  export function installToolMouseInteraction(
938
946
  ctx: any,
939
947
  owner: object = DEFAULT_TOOL_MOUSE_OWNER,
@@ -979,7 +987,18 @@ export function installToolMouseInteraction(
979
987
  setScrollButtonWidget(widget);
980
988
  return widget;
981
989
  });
982
- toolMouseInputUnsubscribe = ctx.ui.onTerminalInput(handleToolMouseInput);
990
+ const removeMouseRoute = inputRouter.register(
991
+ (data) =>
992
+ overlayManager.hasActive() ? undefined : handleToolMouseInput(data),
993
+ 100,
994
+ );
995
+ const removeHostInput = ctx.ui.onTerminalInput((data: string) =>
996
+ inputRouter.dispatch(data),
997
+ );
998
+ toolMouseInputUnsubscribe = () => {
999
+ removeHostInput();
1000
+ removeMouseRoute();
1001
+ };
983
1002
  }
984
1003
 
985
1004
  function refreshToolRendererComponents(tui: any): void {
@@ -992,7 +1011,7 @@ export function scheduleSessionRender(refresh?: () => void): void {
992
1011
  const tui = getToolMouseTui();
993
1012
  if (!tui || typeof tui.requestRender !== "function") return;
994
1013
  if (sessionRenderTimer) clearTimeout(sessionRenderTimer);
995
- // Restored transcripts are populated at different points for startup, reload,
1014
+ // Restored context components are populated at different points for startup, reload,
996
1015
  // and session replacement. Repaint after session_start and the surrounding UI
997
1016
  // rebuild finish so messages are not left hidden until the next terminal input.
998
1017
  sessionRenderTimer = setTimeout(() => {
@@ -1,5 +1,5 @@
1
1
  import { visibleWidth } from "@earendil-works/pi-tui";
2
- import { ThinkingPreviewBlock } from "../../../features/compact-thinking.ts";
2
+ import { ThinkingPreviewBlock } from "../../thinking/compact-thinking.ts";
3
3
  import { isCompactAssistantComponent } from "../compact-mode.ts";
4
4
  import { ToolGroupComponent } from "../tool/grouping.ts";
5
5
  import { isMessageDisplayComponent } from "../tool/message-display.ts";
@@ -1,14 +1,14 @@
1
1
  import type { ExpandedToolIoView, ToolIoSection } from "../tool/result.ts";
2
- import { walkComponentTree } from "../../../tools/component-tree.ts";
2
+ import { walkComponentTree } from "../../../../tools/component-tree.ts";
3
3
  export {
4
4
  stripTerminalSequences,
5
5
  stripTerminalSequencesPreservingLayout,
6
- } from "../../../tools/ansi-text.ts";
6
+ } from "../../../../tools/ansi-text.ts";
7
7
  export {
8
8
  parseSgrMousePackets,
9
9
  isSgrLeftPress,
10
10
  type SgrMousePacket,
11
- } from "../../../tools/sgr-mouse.ts";
11
+ } from "../../../../tools/sgr-mouse.ts";
12
12
 
13
13
  /** Final painted placement of one outermost tool/group row after parent layout. */
14
14
  export type FrameToolPlacement = {