pi-one-ui 0.3.0 → 0.4.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 (63) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.en.md +39 -35
  3. package/README.md +20 -16
  4. package/extensions/app/commands/settings-previews.ts +4 -16
  5. package/extensions/app/config/renderer.ts +7 -6
  6. package/extensions/app/config/shell.ts +15 -137
  7. package/extensions/app/overlay/selector-border.ts +5 -5
  8. package/extensions/app/ownership.ts +1 -1
  9. package/extensions/app/panel.ts +19 -25
  10. package/extensions/app/runtime/tui-runtime.ts +31 -56
  11. package/extensions/features/context-inspector/index.ts +3 -3
  12. package/extensions/features/session-reference/index.ts +5 -5
  13. package/extensions/features/subagent-autocomplete.ts +1 -1
  14. package/extensions/layouts/context/index.ts +9 -12
  15. package/extensions/layouts/context/renderer/compact-mode.ts +24 -24
  16. package/extensions/layouts/context/renderer/default-mode.ts +21 -16
  17. package/extensions/layouts/context/renderer/index.ts +24 -24
  18. package/extensions/layouts/context/renderer/mouse/hover.ts +6 -6
  19. package/extensions/layouts/context/renderer/mouse/interaction.ts +35 -35
  20. package/extensions/layouts/context/renderer/mouse/packets.ts +3 -2
  21. package/extensions/layouts/context/renderer/mouse/scroll.ts +2 -2
  22. package/extensions/layouts/context/renderer/tool/diff/diff-component.ts +2 -1
  23. package/extensions/layouts/context/renderer/tool/diff/diff-edit-render.ts +21 -21
  24. package/extensions/layouts/context/renderer/tool/diff/diff-header.ts +4 -4
  25. package/extensions/layouts/context/renderer/tool/diff/diff-highlight.ts +6 -6
  26. package/extensions/layouts/context/renderer/tool/diff/diff-inline.ts +1 -1
  27. package/extensions/layouts/context/renderer/tool/diff/diff-layout.ts +18 -18
  28. package/extensions/layouts/context/renderer/tool/diff/diff-limits.ts +5 -5
  29. package/extensions/layouts/context/renderer/tool/diff/diff-palette.ts +3 -3
  30. package/extensions/layouts/context/renderer/tool/diff/diff-presentation.ts +1 -1
  31. package/extensions/layouts/context/renderer/tool/diff/diff-renderer.ts +2 -1
  32. package/extensions/layouts/context/renderer/tool/diff/diff-write-render.ts +31 -31
  33. package/extensions/layouts/context/renderer/tool/diff/index.ts +4 -4
  34. package/extensions/layouts/context/renderer/tool/diff/line-width-safety.ts +1 -1
  35. package/extensions/layouts/context/renderer/tool/diff/write-execution.ts +1 -1
  36. package/extensions/layouts/context/renderer/tool/grouping.ts +12 -12
  37. package/extensions/layouts/context/renderer/tool/message-display.ts +2 -2
  38. package/extensions/layouts/context/renderer/tool/result.ts +199 -19
  39. package/extensions/layouts/context/summary/core.ts +1 -0
  40. package/extensions/layouts/context/summary/index.ts +3 -3
  41. package/extensions/layouts/context/thinking/compact-thinking.ts +12 -13
  42. package/extensions/layouts/editor/controller.ts +3 -57
  43. package/extensions/layouts/editor/editor-metadata-format.ts +4 -4
  44. package/extensions/layouts/editor/factory.ts +5 -23
  45. package/extensions/layouts/editor/index.ts +2 -8
  46. package/extensions/layouts/editor/minimalist-editor.ts +1 -52
  47. package/extensions/layouts/editor/ui.ts +23 -255
  48. package/extensions/layouts/footer/controller.ts +3 -3
  49. package/extensions/layouts/footer/footer.ts +21 -21
  50. package/extensions/layouts/footer/index.ts +8 -8
  51. package/extensions/layouts/header/index.ts +1 -1
  52. package/extensions/layouts/header/startup-header.ts +3 -2
  53. package/extensions/layouts/working-line/controller.ts +6 -6
  54. package/extensions/layouts/working-line/interaction-summary.ts +47 -7
  55. package/extensions/layouts/working-line/working-line.ts +24 -3
  56. package/extensions/services/project-refresh.ts +2 -2
  57. package/extensions/services/session-state.ts +1 -1
  58. package/extensions/shared/format.ts +2 -2
  59. package/extensions/shared/icons.ts +0 -4
  60. package/package.json +9 -12
  61. package/extensions/features/legacy/working-message.ts +0 -226
  62. package/extensions/layouts/editor/accent-rail-editor.ts +0 -112
  63. package/extensions/layouts/editor/accent-rail-layout-patch.ts +0 -530
@@ -1,17 +1,17 @@
1
+ import { inspect } from "node:util";
1
2
  import {
2
3
  Text,
3
4
  truncateToWidth,
4
5
  visibleWidth,
5
6
  wrapTextWithAnsi,
6
7
  } from "@earendil-works/pi-tui";
7
- import { inspect } from "node:util";
8
8
  import { config } from "../../../../app/config/renderer.ts";
9
- import { showMoreHintText } from "./show-more-hint.ts";
10
9
  import {
11
10
  TOOL_LOADING_INTERVAL_MS,
12
11
  toolLoadingIcon,
13
12
  } from "../../../../tools/tool-loading-icon.ts";
14
13
  import { sanitizeToolResultText } from "../../../../tools/tool-result-sanitize.ts";
14
+ import { showMoreHintText } from "./show-more-hint.ts";
15
15
 
16
16
  const TOOL_VIEWPORT_WIDTH_RATIO = 0.8;
17
17
 
@@ -43,21 +43,185 @@ function rawTextFromResult(result: any): string {
43
43
  : "";
44
44
  }
45
45
 
46
+ const MAX_TOOL_DETAILS_DEPTH = 8;
47
+ const MAX_TOOL_DETAILS_ENTRIES = 64;
48
+ const MAX_TOOL_DETAILS_NODES = 512;
49
+ const MAX_TOOL_DETAILS_STRING_LENGTH = 4096;
50
+ const TOOL_DETAILS_TRUNCATED = "[truncated]";
51
+
52
+ type ToolDetailsProjectionState = {
53
+ remainingNodes: number;
54
+ readonly seen: WeakSet<object>;
55
+ };
56
+
57
+ function projectToolDetailsDescriptor(
58
+ descriptor: PropertyDescriptor | undefined,
59
+ state: ToolDetailsProjectionState,
60
+ depth: number,
61
+ ): unknown {
62
+ if (!descriptor) return "[empty]";
63
+ if ("value" in descriptor)
64
+ return projectToolDetailsValue(descriptor.value, state, depth);
65
+ if (descriptor.get && descriptor.set) return "[Getter/Setter]";
66
+ if (descriptor.get) return "[Getter]";
67
+ return "[Setter]";
68
+ }
69
+
70
+ function projectToolDetailsValue(
71
+ value: unknown,
72
+ state: ToolDetailsProjectionState,
73
+ depth = 0,
74
+ ): unknown {
75
+ if (typeof value === "string")
76
+ return value.length > MAX_TOOL_DETAILS_STRING_LENGTH
77
+ ? `${value.slice(0, MAX_TOOL_DETAILS_STRING_LENGTH)}…`
78
+ : value;
79
+ if (
80
+ value === null ||
81
+ (typeof value !== "object" && typeof value !== "function")
82
+ )
83
+ return value;
84
+ if (typeof value === "function")
85
+ return `[Function${value.name ? `: ${value.name}` : ""}]`;
86
+ if (state.seen.has(value)) return "[Circular]";
87
+ if (state.remainingNodes <= 0) return "[node budget exhausted]";
88
+ if (depth >= MAX_TOOL_DETAILS_DEPTH) return "[depth limit]";
89
+
90
+ state.remainingNodes--;
91
+ state.seen.add(value);
92
+ try {
93
+ if (Array.isArray(value)) {
94
+ const output: unknown[] = [];
95
+ const limit = Math.min(value.length, MAX_TOOL_DETAILS_ENTRIES);
96
+ let index = 0;
97
+ for (; index < limit && state.remainingNodes > 0; index++) {
98
+ output.push(
99
+ projectToolDetailsDescriptor(
100
+ Object.getOwnPropertyDescriptor(value, String(index)),
101
+ state,
102
+ depth + 1,
103
+ ),
104
+ );
105
+ }
106
+ if (index < value.length)
107
+ output.push(`... ${value.length - index} more items`);
108
+ return output;
109
+ }
110
+
111
+ if (value instanceof Map) {
112
+ const output: unknown[] = [];
113
+ let processed = 0;
114
+ for (const [key, entry] of value) {
115
+ if (processed === MAX_TOOL_DETAILS_ENTRIES || state.remainingNodes <= 0)
116
+ break;
117
+ output.push([
118
+ projectToolDetailsValue(key, state, depth + 1),
119
+ projectToolDetailsValue(entry, state, depth + 1),
120
+ ]);
121
+ processed++;
122
+ }
123
+ if (processed < value.size)
124
+ output.push(`... ${value.size - processed} more entries`);
125
+ return { Map: output };
126
+ }
127
+
128
+ if (value instanceof Set) {
129
+ const output: unknown[] = [];
130
+ let processed = 0;
131
+ for (const entry of value) {
132
+ if (processed === MAX_TOOL_DETAILS_ENTRIES || state.remainingNodes <= 0)
133
+ break;
134
+ output.push(projectToolDetailsValue(entry, state, depth + 1));
135
+ processed++;
136
+ }
137
+ if (processed < value.size)
138
+ output.push(`... ${value.size - processed} more entries`);
139
+ return { Set: output };
140
+ }
141
+
142
+ if (ArrayBuffer.isView(value)) {
143
+ const length =
144
+ "length" in value && typeof value.length === "number"
145
+ ? value.length
146
+ : 0;
147
+ const output: unknown[] = [];
148
+ const limit = Math.min(length, MAX_TOOL_DETAILS_ENTRIES);
149
+ for (let index = 0; index < limit; index++)
150
+ output.push(Reflect.get(value, index));
151
+ if (limit < length) output.push(`... ${length - limit} more items`);
152
+ return output;
153
+ }
154
+
155
+ if (value instanceof Date || value instanceof RegExp) return value;
156
+ if (value instanceof Error) {
157
+ return {
158
+ name: value.name,
159
+ message: projectToolDetailsValue(value.message, state, depth + 1),
160
+ };
161
+ }
162
+
163
+ const output: Record<string, unknown> = {};
164
+ let processed = 0;
165
+ for (const key in value) {
166
+ if (!Object.hasOwn(value, key)) continue;
167
+ if (processed === MAX_TOOL_DETAILS_ENTRIES || state.remainingNodes <= 0) {
168
+ Object.defineProperty(output, "... more properties", {
169
+ value: TOOL_DETAILS_TRUNCATED,
170
+ enumerable: true,
171
+ });
172
+ break;
173
+ }
174
+ Object.defineProperty(output, key, {
175
+ value: projectToolDetailsDescriptor(
176
+ Object.getOwnPropertyDescriptor(value, key),
177
+ state,
178
+ depth + 1,
179
+ ),
180
+ enumerable: true,
181
+ });
182
+ processed++;
183
+ }
184
+ return output;
185
+ } catch (error) {
186
+ return `[uninspectable: ${error instanceof Error ? error.message : String(error)}]`;
187
+ }
188
+ }
189
+
46
190
  function detailsFromResult(result: any): string {
47
- if (result?.details === undefined) return "";
48
- const details =
49
- typeof result.details === "string"
50
- ? result.details
51
- : inspect(result.details, { depth: 8, breakLength: 100, compact: false });
191
+ const rawDetails = result?.details;
192
+ if (rawDetails === undefined) return "";
193
+ if (typeof rawDetails === "string")
194
+ return sanitizeToolResultText(rawDetails, 16_384);
195
+ const projected = projectToolDetailsValue(rawDetails, {
196
+ remainingNodes: MAX_TOOL_DETAILS_NODES,
197
+ seen: new WeakSet(),
198
+ });
199
+ const details = inspect(projected, {
200
+ depth: MAX_TOOL_DETAILS_DEPTH,
201
+ breakLength: 100,
202
+ compact: false,
203
+ customInspect: false,
204
+ getters: false,
205
+ maxArrayLength: MAX_TOOL_DETAILS_ENTRIES + 1,
206
+ maxStringLength: MAX_TOOL_DETAILS_STRING_LENGTH,
207
+ });
52
208
  return sanitizeToolResultText(details, 16_384);
53
209
  }
54
210
 
55
211
  export function textFromResult(result: any, expanded = false): string {
56
- // Compact previews only need short text; bound sanitize work.
212
+ // Compact previews only need visible text; avoid traversing large custom details.
57
213
  const content = sanitizeToolResultText(rawTextFromResult(result), 16_384);
214
+ if (content && !expanded) {
215
+ return content;
216
+ }
217
+
58
218
  const details = detailsFromResult(result);
59
- if (!content) return details;
60
- if (!expanded || !details || details === content) return content;
219
+ if (!content) {
220
+ return details;
221
+ }
222
+ if (!details || details === content) {
223
+ return content;
224
+ }
61
225
  return `${content}\nDetails:\n${details}`;
62
226
  }
63
227
 
@@ -301,6 +465,14 @@ export class ExpandedToolIoView {
301
465
  this.flushLeft = flushLeft;
302
466
  }
303
467
 
468
+ setTheme(theme: any): void {
469
+ if (this.theme === theme) {
470
+ return;
471
+ }
472
+ this.theme = theme;
473
+ this.invalidate();
474
+ }
475
+
304
476
  setContent(
305
477
  inputBody: string,
306
478
  outputBody: string,
@@ -759,17 +931,12 @@ export function renderExpandedToolResult(
759
931
 
760
932
  // Prefer structured Input/Output when we have args or non-empty output.
761
933
  if (inputBody.trim() || outputBody.trim()) {
934
+ const cached = context?.state?.ccstyleExpandedIoView;
762
935
  let view: ExpandedToolIoView;
763
936
  if (isExpandedToolIoView(lastComponent)) {
764
- lastComponent.setContent(
765
- inputBody,
766
- outputBody,
767
- isError,
768
- maxLines,
769
- maxLines,
770
- flushLeft,
771
- );
772
937
  view = lastComponent;
938
+ } else if (isExpandedToolIoView(cached)) {
939
+ view = cached;
773
940
  } else {
774
941
  view = new ExpandedToolIoView(
775
942
  theme,
@@ -781,11 +948,23 @@ export function renderExpandedToolResult(
781
948
  flushLeft,
782
949
  );
783
950
  }
951
+ view.setTheme(theme);
952
+ view.setContent(
953
+ inputBody,
954
+ outputBody,
955
+ isError,
956
+ maxLines,
957
+ maxLines,
958
+ flushLeft,
959
+ );
784
960
  if (context) rememberIoView(context, view);
785
961
  return view;
786
962
  }
787
963
 
788
- if (context?.state) context.state.ccstyleIoView = undefined;
964
+ if (context?.state) {
965
+ context.state.ccstyleIoView = undefined;
966
+ context.state.ccstyleExpandedIoView = undefined;
967
+ }
789
968
  const color = isError ? "error" : "muted";
790
969
  return new Text(theme.fg(color, renderCollapsedToolResult("Done")), 0, 0);
791
970
  }
@@ -901,6 +1080,7 @@ function rememberIoView(context: any, view: ExpandedToolIoView): void {
901
1080
  }
902
1081
  if (!context.state || typeof context.state !== "object") context.state = {};
903
1082
  context.state.ccstyleIoView = view;
1083
+ context.state.ccstyleExpandedIoView = view;
904
1084
  }
905
1085
 
906
1086
  /** hover 状态变更后刷新上一个/下一个视图(context.invalidate 缓存于 ioViewInvalidators)。 */
@@ -10,6 +10,7 @@
10
10
 
11
11
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
12
  import { formatDuration } from "../../../tools/format.ts";
13
+
13
14
  export { formatDuration };
14
15
 
15
16
  /** 工具分类。 */
@@ -7,13 +7,13 @@
7
7
  * 引用块文字色取主题 mdQuote(cc 主题下为 muted 灰),内容用 *斜体* 语法。
8
8
  */
9
9
 
10
- import { Markdown } from "@earendil-works/pi-tui";
11
- import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
12
10
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
+ import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
12
+ import { Markdown } from "@earendil-works/pi-tui";
13
13
  import {
14
+ type AgentSummaryData,
14
15
  bindAgentSummary,
15
16
  summaryMarkdown,
16
- type AgentSummaryData,
17
17
  } from "./core.ts";
18
18
 
19
19
  export const AGENT_SUMMARY_ENTRY_TYPE = "agent-summary";
@@ -12,31 +12,30 @@ import {
12
12
  type Theme,
13
13
  } from "@earendil-works/pi-coding-agent";
14
14
  import {
15
+ Box,
16
+ type Component,
15
17
  getKeybindings,
18
+ Markdown,
19
+ Spacer,
20
+ Text,
16
21
  truncateToWidth,
17
22
  visibleWidth,
18
23
  wrapTextWithAnsi,
19
24
  } from "@earendil-works/pi-tui";
20
25
  import {
21
- Box,
22
- Markdown,
23
- Spacer,
24
- Text,
25
- type Component,
26
- } from "@earendil-works/pi-tui";
27
- import { isToolTuiFullscreen } from "../renderer/tool/show-more-hint.ts";
26
+ COMPACT_THINKING_OWNER,
27
+ COMPACT_THINKING_PATCH_KEY,
28
+ PROTOTYPE_ORIGINAL_KEY,
29
+ patchRegistry,
30
+ } from "../../../tools/patch-keys.ts";
28
31
  import {
29
32
  animateCompactThinkingText,
30
33
  formatThoughtDuration,
31
34
  styleCompactThinkingText,
32
35
  } from "../renderer/compact-mode.ts";
33
36
  import { refreshMountedContext } from "../renderer/context-refresh.ts";
34
- import {
35
- COMPACT_THINKING_OWNER,
36
- COMPACT_THINKING_PATCH_KEY,
37
- patchRegistry,
38
- PROTOTYPE_ORIGINAL_KEY,
39
- } from "../../../tools/patch-keys.ts";
37
+ import { isToolTuiFullscreen } from "../renderer/tool/show-more-hint.ts";
38
+
40
39
  // 保持导出兼容:渲染函数已并入 renderer/compact-mode.ts,这里 re-export。
41
40
  export {
42
41
  animateCompactThinkingText,
@@ -6,17 +6,12 @@ import {
6
6
  hasUnsupportedComponentStyle,
7
7
  type PolishedTuiConfig,
8
8
  } from "../../app/config/shell.ts";
9
- import { SessionLifecycle } from "../../app/runtime/session-lifecycle.ts";
10
9
  import type { RenderScheduler } from "../../app/runtime/render-scheduler.ts";
10
+ import type { SessionLifecycle } from "../../app/runtime/session-lifecycle.ts";
11
11
  import type { FooterState } from "../footer/index.ts";
12
12
  import {
13
- installHostAccentRailLayoutPatch,
14
- retainAccentRailLayoutPatchInstallation,
15
- type AccentRailLayoutPatchDiagnostic,
16
- } from "./accent-rail-layout-patch.ts";
17
- import {
18
- replaceEditorComponentWithExpandedText,
19
13
  type EditorTransferFailureReason,
14
+ replaceEditorComponentWithExpandedText,
20
15
  } from "./editor-transfer.ts";
21
16
  import {
22
17
  createEditorFactory,
@@ -24,10 +19,10 @@ import {
24
19
  type EditorFactoryRuntime,
25
20
  } from "./factory.ts";
26
21
  import {
22
+ type EditorFactory,
27
23
  getZentuiEditorBaseFactory,
28
24
  isOwnedEditorFactory,
29
25
  isZentuiEditorFactory,
30
- type EditorFactory,
31
26
  } from "./ownership.ts";
32
27
 
33
28
  export type EditorChangeResult = { ok: true } | { ok: false; reason: string };
@@ -41,8 +36,6 @@ export type EditorLayoutControllerContext = {
41
36
  readonly sessionLifecycle: SessionLifecycle;
42
37
  readonly render: Pick<RenderScheduler, "request">;
43
38
  readonly getThinkingLevel: () => ReturnType<ExtensionAPI["getThinkingLevel"]>;
44
- readonly getContextWindow: (ctx: ExtensionContext) => number | undefined;
45
- readonly getContextPercent: (ctx: ExtensionContext) => number | undefined;
46
39
  readonly getAgentDurationMs: () => number;
47
40
  readonly isAgentActive: () => boolean;
48
41
  readonly isAgentDurationActive: () => boolean;
@@ -50,10 +43,6 @@ export type EditorLayoutControllerContext = {
50
43
  readonly getProjectRoot: () => string | undefined;
51
44
  readonly onProjectRequirementChanged: () => void;
52
45
  readonly onModelLabelChanged: (ctx: ExtensionContext) => void;
53
- readonly recordLayoutDiagnostic: (
54
- diagnostic: AccentRailLayoutPatchDiagnostic,
55
- version?: string,
56
- ) => void;
57
46
  };
58
47
 
59
48
  type EditorInstallMode = "none" | "standalone" | "wrapper";
@@ -73,8 +62,6 @@ export class EditorLayoutController {
73
62
  private minimalistDecorationActive = false;
74
63
  private minimalistDurationUpdatesActive = false;
75
64
  private stopMinimalistDurationUpdates: () => void = () => {};
76
- private accentRailLayoutPatchCleanup: () => void = () => {};
77
- private accentRailLayoutPatchInstallSerial = 0;
78
65
 
79
66
  /**
80
67
  * Creates an Editor controller with shared runtime selectors.
@@ -85,37 +72,6 @@ export class EditorLayoutController {
85
72
  this.context = context;
86
73
  }
87
74
 
88
- /**
89
- * Installs the host Accent Rail patch for a new session generation.
90
- *
91
- * @param ctx Active Pi extension context.
92
- * @returns A promise that settles after patch retention is attempted.
93
- */
94
- async startSession(ctx: ExtensionContext): Promise<void> {
95
- const lifecycleGeneration =
96
- this.context.sessionLifecycle.currentGeneration();
97
- const installSerial = ++this.accentRailLayoutPatchInstallSerial;
98
- this.accentRailLayoutPatchCleanup();
99
- this.accentRailLayoutPatchCleanup = () => {};
100
- if (!this.isTuiContext(ctx)) return;
101
-
102
- const result = await retainAccentRailLayoutPatchInstallation(
103
- () => installHostAccentRailLayoutPatch(this.ownerToken),
104
- () =>
105
- this.context.sessionLifecycle.isCurrent(lifecycleGeneration) &&
106
- installSerial === this.accentRailLayoutPatchInstallSerial,
107
- (layoutPatch) => {
108
- this.accentRailLayoutPatchCleanup = layoutPatch.cleanup;
109
- this.context.recordLayoutDiagnostic(
110
- layoutPatch.diagnostic,
111
- layoutPatch.version,
112
- );
113
- },
114
- );
115
- if (result === "failed")
116
- this.context.recordLayoutDiagnostic("host-module-unavailable");
117
- }
118
-
119
75
  /**
120
76
  * Installs or reconciles the configured Editor for an active session.
121
77
  *
@@ -355,9 +311,6 @@ export class EditorLayoutController {
355
311
  }
356
312
  }
357
313
  this.clearEditorOwnership();
358
- ++this.accentRailLayoutPatchInstallSerial;
359
- this.accentRailLayoutPatchCleanup();
360
- this.accentRailLayoutPatchCleanup = () => {};
361
314
  this.activeTuiContext = undefined;
362
315
  }
363
316
 
@@ -516,8 +469,6 @@ export class EditorLayoutController {
516
469
  getConfig: this.context.getConfig,
517
470
  getState: this.context.getState,
518
471
  getThinkingLevel: this.context.getThinkingLevel,
519
- getContextWindow: this.context.getContextWindow,
520
- getContextPercent: this.context.getContextPercent,
521
472
  getAgentDurationMs: this.context.getAgentDurationMs,
522
473
  isAgentActive: this.context.isAgentActive,
523
474
  getProjectRoot: this.context.getProjectRoot,
@@ -526,11 +477,6 @@ export class EditorLayoutController {
526
477
  },
527
478
  onDecorationActive: (active) =>
528
479
  this.setMinimalistDecorationActive(active),
529
- isAccentRailActive: () =>
530
- this.context.sessionLifecycle.isCurrent() &&
531
- this.ownsActiveFactory(ctx) &&
532
- this.isEditorEnabled() &&
533
- this.context.getConfig().components.editor.style === "accent-rail",
534
480
  };
535
481
  }
536
482
 
@@ -1,14 +1,14 @@
1
1
  import type { Theme } from "@earendil-works/pi-coding-agent";
2
2
  import type { ZentuiConfig } from "../../app/config/shell.ts";
3
- import {
4
- type FormatToken,
5
- parseFooterFormat,
6
- } from "../footer/footer-format.ts";
7
3
  import {
8
4
  EDITOR_ACCENT_FALLBACK,
9
5
  renderStyleForSourceOrFallback,
10
6
  safeThemeFg,
11
7
  } from "../../shared/style.ts";
8
+ import {
9
+ type FormatToken,
10
+ parseFooterFormat,
11
+ } from "../footer/footer-format.ts";
12
12
 
13
13
  export type EditorMetadataValues = {
14
14
  model: string;
@@ -6,15 +6,14 @@ import type {
6
6
  } from "@earendil-works/pi-coding-agent";
7
7
  import type { EditorTheme, TUI } from "@earendil-works/pi-tui";
8
8
  import type { PolishedTuiConfig } from "../../app/config/shell.ts";
9
- import { modelLabelFor, type FooterState } from "../footer/index.ts";
10
- import { markAccentRailLayoutEditor } from "./accent-rail-layout-patch.ts";
11
- import { PolishedEditor, WrappedPolishedEditor } from "./ui.ts";
9
+ import { type FooterState, modelLabelFor } from "../footer/index.ts";
12
10
  import {
11
+ type EditorFactory,
13
12
  markEditorFactory,
14
13
  markWrappedEditorFactory,
15
- type EditorFactory,
16
14
  type ZentuiEditorFactory,
17
15
  } from "./ownership.ts";
16
+ import { PolishedEditor, WrappedPolishedEditor } from "./ui.ts";
18
17
 
19
18
  export type EditorFactoryRuntime = {
20
19
  readonly ownerToken: symbol;
@@ -22,14 +21,11 @@ export type EditorFactoryRuntime = {
22
21
  readonly getConfig: () => PolishedTuiConfig;
23
22
  readonly getState: () => FooterState;
24
23
  readonly getThinkingLevel: () => ReturnType<ExtensionAPI["getThinkingLevel"]>;
25
- readonly getContextWindow: (ctx: ExtensionContext) => number | undefined;
26
- readonly getContextPercent: (ctx: ExtensionContext) => number | undefined;
27
24
  readonly getAgentDurationMs: () => number;
28
25
  readonly isAgentActive: () => boolean;
29
26
  readonly getProjectRoot: () => string | undefined;
30
27
  readonly onRender: (requestRender: () => void) => void;
31
28
  readonly onDecorationActive: (active: boolean) => void;
32
- readonly isAccentRailActive: () => boolean;
33
29
  };
34
30
 
35
31
  /**
@@ -49,7 +45,7 @@ export function createEditorFactory(
49
45
  keybindings: KeybindingsManager,
50
46
  ) => {
51
47
  runtime.onRender(() => tui.requestRender());
52
- const editor = new PolishedEditor(
48
+ return new PolishedEditor(
53
49
  tui,
54
50
  theme,
55
51
  keybindings,
@@ -60,12 +56,6 @@ export function createEditorFactory(
60
56
  () => editorDecoration(ctx, runtime),
61
57
  runtime.onDecorationActive,
62
58
  );
63
- markAccentRailLayoutEditor(
64
- editor,
65
- runtime.ownerToken,
66
- runtime.isAccentRailActive,
67
- );
68
- return editor;
69
59
  }) as ZentuiEditorFactory;
70
60
  return markEditorFactory(factory, runtime.ownerToken);
71
61
  }
@@ -89,7 +79,7 @@ export function createWrappedEditorFactory(
89
79
  keybindings: KeybindingsManager,
90
80
  ) => {
91
81
  runtime.onRender(() => tui.requestRender());
92
- const editor = new WrappedPolishedEditor(
82
+ return new WrappedPolishedEditor(
93
83
  baseFactory(tui, theme, keybindings),
94
84
  runtime.sessionTheme,
95
85
  runtime.getConfig,
@@ -98,12 +88,6 @@ export function createWrappedEditorFactory(
98
88
  () => editorDecoration(ctx, runtime),
99
89
  runtime.onDecorationActive,
100
90
  );
101
- markAccentRailLayoutEditor(
102
- editor,
103
- runtime.ownerToken,
104
- runtime.isAccentRailActive,
105
- );
106
- return editor;
107
91
  }) as ZentuiEditorFactory;
108
92
  return markWrappedEditorFactory(factory, baseFactory, runtime.ownerToken);
109
93
  }
@@ -150,8 +134,6 @@ function editorDecoration(
150
134
  costLabel: state.costLabel,
151
135
  modelLabel: modelLabelFor(state, config.components.editor.modelLabel),
152
136
  thinkingLevel: runtime.getThinkingLevel(),
153
- contextPercent: runtime.getContextPercent(ctx),
154
- contextWindow: runtime.getContextWindow(ctx),
155
137
  sessionName: ctx.sessionManager.getSessionName() ?? "",
156
138
  agentDurationMs: runtime.getAgentDurationMs(),
157
139
  agentActive: runtime.isAgentActive(),
@@ -1,16 +1,10 @@
1
1
  export {
2
- EditorLayoutController,
3
2
  type EditorChangeResult,
3
+ EditorLayoutController,
4
4
  type EditorLayoutControllerContext,
5
5
  } from "./controller.ts";
6
6
  export {
7
- installHostAccentRailLayoutPatch,
8
- markAccentRailLayoutEditor,
9
- retainAccentRailLayoutPatchInstallation,
10
- type AccentRailLayoutPatchDiagnostic,
11
- } from "./accent-rail-layout-patch.ts";
12
- export {
13
- replaceEditorComponentWithExpandedText,
14
7
  type EditorTransferFailureReason,
8
+ replaceEditorComponentWithExpandedText,
15
9
  } from "./editor-transfer.ts";
16
10
  export { PolishedEditor, WrappedPolishedEditor } from "./ui.ts";
@@ -2,14 +2,8 @@ import { basename, isAbsolute, relative, sep } from "node:path";
2
2
  import type { Theme } from "@earendil-works/pi-coding-agent";
3
3
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import type { ZentuiConfig } from "../../app/config/shell.ts";
5
+ import { formatCwdLabel, formatElapsedDuration } from "../../shared/format.ts";
5
6
  import { sanitizeEditorMetadataText } from "./editor-metadata-format.ts";
6
- import {
7
- buildContextGauge,
8
- contextColorTier,
9
- formatCount,
10
- formatCwdLabel,
11
- formatElapsedDuration,
12
- } from "../../shared/format.ts";
13
7
 
14
8
  export { formatElapsedDuration } from "../../shared/format.ts";
15
9
 
@@ -53,8 +47,6 @@ export type MinimalistEditorMetadata = {
53
47
  costLabel?: string;
54
48
  modelLabel?: string;
55
49
  thinkingLevel?: string;
56
- contextPercent?: number;
57
- contextWindow?: number;
58
50
  sessionName?: string;
59
51
  agentDurationMs?: number;
60
52
  agentActive?: boolean;
@@ -201,7 +193,6 @@ function renderTopRight(
201
193
  metadata: MinimalistEditorMetadata,
202
194
  uiTheme: Theme,
203
195
  config: ZentuiConfig,
204
- availableWidth: number,
205
196
  renderBorder: (text: string) => string,
206
197
  renderThinking: (text: string) => string,
207
198
  ): string {
@@ -235,46 +226,6 @@ function renderTopRight(
235
226
  if (thinking && thinking.toLowerCase() !== "off") {
236
227
  parts.push(renderThinking(thinking));
237
228
  }
238
- if (
239
- metadata.contextPercent !== undefined &&
240
- Number.isFinite(metadata.contextPercent)
241
- ) {
242
- const percent = Math.round(
243
- Math.max(0, Math.min(999, metadata.contextPercent)),
244
- );
245
- const tier = contextColorTier(
246
- percent,
247
- config.components.editor.styles.minimalist.contextThresholds,
248
- );
249
- const style =
250
- tier === "error"
251
- ? config.colors.contextError
252
- : tier === "warning"
253
- ? config.colors.contextWarning
254
- : config.colors.contextNormal;
255
- const total =
256
- config.components.editor.styles.minimalist.contextFormat ===
257
- "percent-total" &&
258
- metadata.contextWindow !== undefined &&
259
- Number.isFinite(metadata.contextWindow) &&
260
- metadata.contextWindow > 0
261
- ? `/${formatCount(metadata.contextWindow)}`
262
- : "";
263
- const text = `${percent}%${total}`;
264
- let context = renderStyleForSource(uiTheme, source, style, text);
265
- if (config.components.editor.styles.minimalist.contextGauge) {
266
- for (const gaugeWidth of [5, 3]) {
267
- const gauge = `[${buildContextGauge(percent, gaugeWidth, config.icons.mode === "ascii")}] ${text}`;
268
- const styledGauge = renderStyleForSource(uiTheme, source, style, gauge);
269
- const candidate = joinParts([...parts, styledGauge]);
270
- if (visibleWidth(candidate) <= availableWidth) {
271
- context = styledGauge;
272
- break;
273
- }
274
- }
275
- }
276
- parts.push(context);
277
- }
278
229
  return joinParts(parts);
279
230
  }
280
231
 
@@ -500,7 +451,6 @@ export function renderMinimalistFrame({
500
451
  );
501
452
  const topViewport = viewportLabel("above", viewport?.above);
502
453
  const topLeft = joinStyled([topViewport, topMetadata], separator);
503
- const topRightBudget = Math.max(0, width - 8 - visibleWidth(topLeft));
504
454
  const topFallbacks = [
505
455
  joinStyled([topViewport, topOperational], separator),
506
456
  topOperational,
@@ -516,7 +466,6 @@ export function renderMinimalistFrame({
516
466
  metadata,
517
467
  uiTheme,
518
468
  config,
519
- topRightBudget,
520
469
  renderBorder,
521
470
  renderThinking,
522
471
  ),