pi-one-ui 0.3.1 → 0.5.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.
@@ -12,18 +12,13 @@ import {
12
12
  truncateToWidth,
13
13
  visibleWidth,
14
14
  } from "@earendil-works/pi-tui";
15
- import type { EditorStyle, ZentuiConfig } from "../../app/config/shell.ts";
15
+ import type { ZentuiConfig } from "../../app/config/shell.ts";
16
16
  import {
17
17
  EDITOR_ACCENT_FALLBACK,
18
18
  EDITOR_BORDER_FALLBACK,
19
19
  renderStyleForSourceOrFallback,
20
20
  safeThemeFg,
21
21
  } from "../../shared/style.ts";
22
- import {
23
- ACCENT_RAIL_CHROME_WIDTH,
24
- renderAccentRailEditorFrame,
25
- } from "./accent-rail-editor.ts";
26
- import { renderCompletionPalette } from "./completion-menu.ts";
27
22
  import { renderEditorMetadataFormat } from "./editor-metadata-format.ts";
28
23
  import {
29
24
  type MinimalistEditorMetadata,
@@ -95,50 +90,11 @@ export type EditorMeta = {
95
90
  sessionName?: string;
96
91
  };
97
92
 
98
- export type PolishedEditorFrameOptions = {
99
- width: number;
100
- editorLines: string[];
101
- autocompleteLines?: string[];
102
- viewport?: ViewportCounts;
103
- uiTheme: Theme;
104
- config: ZentuiConfig;
105
- modelMeta: EditorMeta;
106
- thinkingLevel?: string;
107
- rightStatus?: string;
108
- borderColor?: (text: string) => string;
109
- };
110
-
111
- type PolishedFrameOptions = {
112
- width: number;
113
- baseRendered: string[];
114
- autocompleteSource: AutocompleteEditorInternals;
115
- autocompleteCapture?: AutocompleteCapture;
116
- uiTheme: Theme;
117
- config: ZentuiConfig;
118
- modelMeta: EditorMeta;
119
- thinkingLevel: string | undefined;
120
- rightStatus?: string;
121
- ownedFrame?: PolishedFrameSplit;
122
- trustedBaseFrame?: boolean;
123
- borderColor?: (text: string) => string;
124
- };
125
-
126
93
  type PolishedFrameResult = {
127
94
  lines: string[];
128
95
  decorated: boolean;
129
96
  };
130
97
 
131
- type AccentRailFrameAdapterOptions = {
132
- width: number;
133
- baseRendered: string[];
134
- autocompleteSource: AutocompleteEditorInternals;
135
- autocompleteCapture?: AutocompleteCapture;
136
- uiTheme: Theme;
137
- config: ZentuiConfig;
138
- ownedFrame?: PolishedFrameSplit;
139
- trustedBaseFrame?: boolean;
140
- };
141
-
142
98
  type MinimalistFrameAdapterOptions = {
143
99
  width: number;
144
100
  baseRendered: string[];
@@ -331,6 +287,29 @@ export function renderWithAutocompleteCapture<T>(
331
287
  }
332
288
  }
333
289
 
290
+ /**
291
+ * off 模式的原生边框覆盖:仅在 style=off 且用户显式配置了 colors.editorBorder
292
+ * 时返回按 colorSource 解释的边框渲染函数;未配置或 on 模式返回 undefined
293
+ * (保持 Pi 原生 effort/主题变色不动)。
294
+ */
295
+ export function offEditorBorderColor(
296
+ config: ZentuiConfig,
297
+ uiTheme: Theme,
298
+ ): ((text: string) => string) | undefined {
299
+ const editor = config.components.editor;
300
+ if (editor.style !== "off") return undefined;
301
+ const spec = config.colors.editorBorder;
302
+ if (typeof spec !== "string" || spec.trim() === "") return undefined;
303
+ return (text) =>
304
+ renderStyleForSourceOrFallback(
305
+ uiTheme,
306
+ editor.colorSource,
307
+ spec,
308
+ EDITOR_BORDER_FALLBACK,
309
+ text,
310
+ );
311
+ }
312
+
334
313
  function clampRenderedLines(lines: string[], width: number): string[] {
335
314
  const maxWidth = Math.max(0, width);
336
315
  return lines.map((line) => truncateToWidth(line, maxWidth, ""));
@@ -342,79 +321,19 @@ function fillLine(content: string, width: number): string {
342
321
  return `${truncated}${pad}`;
343
322
  }
344
323
 
345
- function isLowRailPolishedStyle(style: EditorStyle): boolean {
346
- return style === "opencode-copy-friendly";
347
- }
348
-
349
- function selectedPolishedConfig(config: ZentuiConfig) {
350
- switch (config.components.editor.style) {
351
- case "opencode":
352
- return config.components.editor.styles.opencode;
353
- case "opencode-copy-friendly":
354
- return config.components.editor.styles["opencode-copy-friendly"];
355
- case "accent-rail":
356
- case "minimalist":
357
- return undefined;
358
- }
359
- }
360
-
361
- function lowRailPrompt(
362
- config: ZentuiConfig,
363
- uiTheme: Theme,
364
- reset: string,
365
- ): string {
366
- const promptIcon = config.icons.editorPrompt;
367
- return promptIcon
368
- ? `${renderStyleForSourceOrFallback(
369
- uiTheme,
370
- config.components.editor.colorSource,
371
- config.colors.editorPrompt ?? config.colors.editorAccent,
372
- EDITOR_ACCENT_FALLBACK,
373
- promptIcon,
374
- )}${reset} `
375
- : "";
376
- }
377
-
378
324
  function getEditorChromeWidths(
379
325
  config: ZentuiConfig,
380
326
  uiTheme: Theme,
381
327
  reset: string,
382
328
  ) {
383
- const lowRail = isLowRailPolishedStyle(config.components.editor.style);
384
- const prompt = lowRailPrompt(config, uiTheme, reset);
385
- const rail = lowRail
386
- ? ""
387
- : `${renderStyleForSourceOrFallback(
388
- uiTheme,
389
- config.components.editor.colorSource,
390
- config.colors.editorAccent,
391
- EDITOR_ACCENT_FALLBACK,
392
- config.icons.rail,
393
- )}${reset} `;
394
- return {
395
- prompt,
396
- promptWidth: visibleWidth(prompt),
397
- rail,
398
- railWidth: lowRail ? visibleWidth(prompt) : visibleWidth(rail),
399
- };
400
- }
401
-
402
- function composeMetadataLine(
403
- left: string,
404
- right: string | undefined,
405
- width: number,
406
- ): string {
407
- if (!right) return left;
408
- const maxWidth = Math.max(0, width);
409
- const rightWidth = visibleWidth(right);
410
- if (rightWidth >= maxWidth) return truncateToWidth(right, maxWidth, "");
411
-
412
- const leftWidth = Math.max(0, maxWidth - rightWidth - 1);
413
- const leftText = truncateToWidth(left, leftWidth, "");
414
- const gap = " ".repeat(
415
- Math.max(1, maxWidth - visibleWidth(leftText) - rightWidth),
416
- );
417
- return `${leftText}${gap}${right}`;
329
+ const rail = `${renderStyleForSourceOrFallback(
330
+ uiTheme,
331
+ config.components.editor.colorSource,
332
+ config.colors.editorAccent,
333
+ EDITOR_ACCENT_FALLBACK,
334
+ config.icons.rail,
335
+ )}${reset} `;
336
+ return { rail, railWidth: visibleWidth(rail) };
418
337
  }
419
338
 
420
339
  function ansiStrippedText(line: string): string {
@@ -439,17 +358,6 @@ function parseEditorBorder(
439
358
  return match?.[1] ? { count: match[1] } : undefined;
440
359
  }
441
360
 
442
- function renderEditorBorder(
443
- width: number,
444
- direction: keyof ViewportCounts,
445
- count: string | undefined,
446
- ): string {
447
- if (!count) return "─".repeat(width);
448
- const arrow = direction === "above" ? "↑" : "↓";
449
- const indicator = `─── ${arrow} ${count} more `;
450
- return `${indicator}${"─".repeat(Math.max(0, width - visibleWidth(indicator)))}`;
451
- }
452
-
453
361
  function unwrapPolishedFrameOnly(
454
362
  lines: string[],
455
363
  config: ZentuiConfig,
@@ -464,31 +372,6 @@ function unwrapPolishedFrameOnly(
464
372
  const interior = lines.slice(1, -1);
465
373
  if (interior.length < 3) return undefined;
466
374
 
467
- if (isLowRailPolishedStyle(config.components.editor.style)) {
468
- if (
469
- plainRenderedText(interior[0] ?? "").trim() !== "" ||
470
- plainRenderedText(interior.at(-2) ?? "").trim() !== "" ||
471
- !(interior.at(-1) ?? "").startsWith(" ")
472
- )
473
- return undefined;
474
-
475
- const { prompt, promptWidth } = getEditorChromeWidths(
476
- config,
477
- uiTheme,
478
- "\x1b[0m",
479
- );
480
- const continuation = " ".repeat(promptWidth);
481
- const content = interior.slice(1, -2);
482
- const unwrapped: string[] = [];
483
- for (let index = 0; index < content.length; index++) {
484
- const prefix = index === 0 ? prompt : continuation;
485
- const line = content[index] ?? "";
486
- if (prefix && !line.startsWith(prefix)) return undefined;
487
- unwrapped.push(prefix ? line.slice(prefix.length) : line);
488
- }
489
- return { editorLines: unwrapped, viewport };
490
- }
491
-
492
375
  const { rail } = getEditorChromeWidths(config, uiTheme, "\x1b[0m");
493
376
  if (!rail || interior.some((line) => !line.startsWith(rail)))
494
377
  return undefined;
@@ -540,94 +423,6 @@ function inspectPolishedFrameProvenance(
540
423
  return { safe: !unsafe, ownedFrame };
541
424
  }
542
425
 
543
- function vimModeColor(mode: string): string {
544
- switch (mode.toLowerCase()) {
545
- case "insert":
546
- return "success";
547
- case "normal":
548
- return "accent";
549
- case "ex":
550
- return "warning";
551
- case "replace":
552
- return "error";
553
- case "visual":
554
- return "syntaxKeyword";
555
- default:
556
- return "muted";
557
- }
558
- }
559
-
560
- function readVimStatus(
561
- editor: WrappedEditor,
562
- uiTheme: Theme,
563
- ): string | undefined {
564
- const mode = editor.getMode?.();
565
- if (typeof mode !== "string") return undefined;
566
- const normalized = mode.trim();
567
- if (!normalized) return undefined;
568
- const label = `${normalized.toUpperCase()} `;
569
- return safeThemeFg(uiTheme, vimModeColor(normalized), label);
570
- }
571
-
572
- function renderAccentRailFrameFromBase({
573
- width,
574
- baseRendered,
575
- autocompleteSource,
576
- autocompleteCapture,
577
- uiTheme,
578
- config,
579
- ownedFrame,
580
- trustedBaseFrame = false,
581
- }: AccentRailFrameAdapterOptions): PolishedFrameResult {
582
- if (width < ACCENT_RAIL_CHROME_WIDTH + 1 || baseRendered.length < 2) {
583
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
584
- }
585
- if (ownedFrame && !isPolishedFrameSplit(ownedFrame, baseRendered.length)) {
586
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
587
- }
588
- const autocomplete = ownedFrame
589
- ? { known: true as const, count: ownedFrame.trailingLines.length }
590
- : autocompleteCount(autocompleteSource, autocompleteCapture, baseRendered);
591
- if (!autocomplete.known) {
592
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
593
- }
594
- const editorFrame =
595
- !ownedFrame && autocomplete.count > 0
596
- ? baseRendered.slice(0, -autocomplete.count)
597
- : baseRendered;
598
- const autocompleteLines = ownedFrame
599
- ? ownedFrame.trailingLines
600
- : autocomplete.count > 0
601
- ? baseRendered.slice(-autocomplete.count)
602
- : [];
603
- if (editorFrame.length < 2) {
604
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
605
- }
606
- const parsedTop = parseEditorBorder(editorFrame[0] ?? "", "above");
607
- const parsedBottom = parseEditorBorder(editorFrame.at(-1) ?? "", "below");
608
- if (!ownedFrame && !trustedBaseFrame && (!parsedTop || !parsedBottom)) {
609
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
610
- }
611
- const editorLines = ownedFrame?.editorLines ?? editorFrame.slice(1, -1);
612
- const viewport = ownedFrame?.viewport ?? {
613
- above: parsedTop?.count,
614
- below: parsedBottom?.count,
615
- };
616
- const lines = renderAccentRailEditorFrame({
617
- width,
618
- editorLines,
619
- autocompleteLines,
620
- viewport,
621
- uiTheme,
622
- config,
623
- });
624
- POLISHED_FRAME_SPLITS.set(lines, {
625
- rows: Object.freeze([...lines]),
626
- split: { editorLines, trailingLines: autocompleteLines, viewport },
627
- });
628
- return { lines, decorated: true };
629
- }
630
-
631
426
  function renderMinimalistFrameFromBase({
632
427
  width,
633
428
  baseRendered,
@@ -692,196 +487,6 @@ function renderMinimalistFrameFromBase({
692
487
  };
693
488
  }
694
489
 
695
- function renderPolishedFrame({
696
- width,
697
- baseRendered,
698
- autocompleteSource,
699
- autocompleteCapture,
700
- uiTheme,
701
- config,
702
- modelMeta,
703
- thinkingLevel,
704
- rightStatus,
705
- ownedFrame,
706
- trustedBaseFrame = false,
707
- borderColor,
708
- }: PolishedFrameOptions): PolishedFrameResult {
709
- if (width <= 2)
710
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
711
-
712
- if (baseRendered.length < 2) {
713
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
714
- }
715
- if (ownedFrame && !isPolishedFrameSplit(ownedFrame, baseRendered.length)) {
716
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
717
- }
718
-
719
- const autocomplete = ownedFrame
720
- ? { known: true, count: ownedFrame.trailingLines.length }
721
- : autocompleteCount(autocompleteSource, autocompleteCapture, baseRendered);
722
- if (!autocomplete.known) {
723
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
724
- }
725
- const editorFrame =
726
- !ownedFrame && autocomplete.count > 0
727
- ? baseRendered.slice(0, -autocomplete.count)
728
- : baseRendered;
729
- const autocompleteLines = ownedFrame
730
- ? ownedFrame.trailingLines
731
- : autocomplete.count > 0
732
- ? baseRendered.slice(-autocomplete.count)
733
- : [];
734
- if (editorFrame.length < 2) {
735
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
736
- }
737
-
738
- const parsedTop = parseEditorBorder(editorFrame[0] ?? "", "above");
739
- const parsedBottom = parseEditorBorder(editorFrame.at(-1) ?? "", "below");
740
- if (!ownedFrame && !trustedBaseFrame && (!parsedTop || !parsedBottom)) {
741
- return { lines: clampRenderedLines(baseRendered, width), decorated: false };
742
- }
743
- const editorLines = ownedFrame?.editorLines ?? editorFrame.slice(1, -1);
744
- const viewport = ownedFrame?.viewport ?? {
745
- above: parsedTop?.count,
746
- below: parsedBottom?.count,
747
- };
748
- const lines = renderPolishedEditorFrame({
749
- width,
750
- editorLines,
751
- autocompleteLines,
752
- viewport,
753
- uiTheme,
754
- config,
755
- modelMeta,
756
- thinkingLevel,
757
- rightStatus,
758
- borderColor,
759
- });
760
- POLISHED_FRAME_SPLITS.set(lines, {
761
- rows: Object.freeze([...lines]),
762
- split: {
763
- editorLines,
764
- trailingLines: autocompleteLines,
765
- viewport,
766
- },
767
- });
768
- return { lines, decorated: true };
769
- }
770
-
771
- /** Pure Opencode frame composition shared by the live editor and settings preview. */
772
- export function renderPolishedEditorFrame({
773
- width,
774
- editorLines,
775
- autocompleteLines = [],
776
- viewport = {},
777
- uiTheme,
778
- config,
779
- modelMeta,
780
- thinkingLevel,
781
- rightStatus,
782
- borderColor,
783
- }: PolishedEditorFrameOptions): string[] {
784
- if (width <= 2) return clampRenderedLines(editorLines, width);
785
- const reset = "\x1b[0m";
786
- const colorSource = config.components.editor.colorSource;
787
- const { prompt, promptWidth, rail, railWidth } = getEditorChromeWidths(
788
- config,
789
- uiTheme,
790
- reset,
791
- );
792
- const innerWidth = Math.max(0, width - railWidth);
793
- const lowRailContinuation = " ".repeat(promptWidth);
794
- const meta = renderEditorMetadataFormat(
795
- selectedPolishedConfig(config)?.metadataFormat ??
796
- config.components.editor.styles.opencode.metadataFormat,
797
- {
798
- model: modelMeta.modelLabel,
799
- modelId: modelMeta.modelId ?? "",
800
- modelName: modelMeta.modelName ?? "",
801
- provider: modelMeta.providerLabel,
802
- thinking: thinkingLevel ?? "",
803
- sessionName: modelMeta.sessionName ?? "",
804
- },
805
- uiTheme,
806
- config,
807
- );
808
- const lowRailMeta = composeMetadataLine(
809
- meta,
810
- rightStatus,
811
- Math.max(0, width - 1),
812
- );
813
- const railedMeta = composeMetadataLine(meta, rightStatus, innerWidth);
814
-
815
- const renderStaticBorder = (text: string) =>
816
- renderStyleForSourceOrFallback(
817
- uiTheme,
818
- colorSource,
819
- config.colors.editorBorder,
820
- EDITOR_BORDER_FALLBACK,
821
- text,
822
- );
823
- const renderBorder = (text: string) => {
824
- if (
825
- config.components.editor.borderColorMode !== "adaptive" ||
826
- typeof borderColor !== "function"
827
- ) {
828
- return renderStaticBorder(text);
829
- }
830
- try {
831
- const rendered = borderColor(text);
832
- return typeof rendered === "string" ? rendered : renderStaticBorder(text);
833
- } catch {
834
- return renderStaticBorder(text);
835
- }
836
- };
837
- const top = renderBorder(
838
- renderEditorBorder(
839
- width,
840
- "above",
841
- config.components.editor.viewportIndicators ? viewport.above : undefined,
842
- ),
843
- );
844
- const bottom = renderBorder(
845
- renderEditorBorder(
846
- width,
847
- "below",
848
- config.components.editor.viewportIndicators ? viewport.below : undefined,
849
- ),
850
- );
851
- const completionLines =
852
- selectedPolishedConfig(config)?.completionMenu === "palette"
853
- ? renderCompletionPalette({
854
- lines: autocompleteLines,
855
- width,
856
- theme: uiTheme,
857
- renderSeparator: renderBorder,
858
- ownedBackground: false,
859
- })
860
- : autocompleteLines;
861
- const lines = ["", ...editorLines, "", railedMeta];
862
- const renderedLines = isLowRailPolishedStyle(config.components.editor.style)
863
- ? [
864
- top,
865
- "",
866
- ...editorLines.map(
867
- (line, index) =>
868
- `${index === 0 ? prompt : lowRailContinuation}${fillLine(line, innerWidth)}`,
869
- ),
870
- "",
871
- ` ${truncateToWidth(lowRailMeta, Math.max(0, width - 1), "")}`,
872
- bottom,
873
- ...completionLines,
874
- ]
875
- : [
876
- top,
877
- ...lines.map((line) => `${rail}${fillLine(line, innerWidth)}`),
878
- bottom,
879
- ...completionLines,
880
- ];
881
-
882
- return clampRenderedLines(renderedLines, width);
883
- }
884
-
885
490
  export class PolishedEditor extends CustomEditor {
886
491
  private readonly getModelMeta: () => EditorMeta;
887
492
  private readonly getThinkingLevel: () => string | undefined;
@@ -917,113 +522,45 @@ export class PolishedEditor extends CustomEditor {
917
522
 
918
523
  render(width: number): string[] {
919
524
  const config = this.getConfig();
920
- if (!config.components.editor.enabled) {
525
+ if (config.components.editor.style === "off") {
526
+ const offBorder = offEditorBorderColor(config, this.uiTheme);
527
+ if (offBorder) this.borderColor = offBorder;
921
528
  this.reportMinimalistDecoration(false);
922
529
  return clampRenderedLines(super.render(width), width);
923
530
  }
924
- if (config.components.editor.style === "accent-rail") {
531
+ if (width <= 4) {
925
532
  this.reportMinimalistDecoration(false);
926
- if (width < ACCENT_RAIL_CHROME_WIDTH + 1) {
927
- return clampRenderedLines(super.render(width), width);
928
- }
929
- let captured: { value: string[]; capture?: AutocompleteCapture };
930
- try {
931
- captured = renderWithAutocompleteCapture(
932
- this as unknown as AutocompleteEditorInternals,
933
- () => super.render(width - ACCENT_RAIL_CHROME_WIDTH),
934
- );
935
- } catch {
936
- return clampRenderedLines(super.render(width), width);
937
- }
938
- try {
939
- const result = renderAccentRailFrameFromBase({
940
- width,
941
- baseRendered: captured.value,
942
- autocompleteSource: this as unknown as AutocompleteEditorInternals,
943
- autocompleteCapture: captured.capture,
944
- uiTheme: this.uiTheme,
945
- config,
946
- trustedBaseFrame: true,
947
- });
948
- if (result.decorated) return result.lines;
949
- } catch {
950
- // Decoration is optional; preserve the completed same-render rows below.
951
- }
952
- return clampRenderedLines(captured.value, width);
953
- }
954
- if (config.components.editor.style === "minimalist") {
955
- if (width <= 4) {
956
- this.reportMinimalistDecoration(false);
957
- return clampRenderedLines(super.render(width), width);
958
- }
959
- let captured: { value: string[]; capture?: AutocompleteCapture };
960
- try {
961
- captured = renderWithAutocompleteCapture(
962
- this as unknown as AutocompleteEditorInternals,
963
- () => super.render(Math.max(0, width - 4)),
964
- );
965
- } catch {
966
- this.reportMinimalistDecoration(false);
967
- return clampRenderedLines(super.render(width), width);
968
- }
969
- try {
970
- const result = renderMinimalistFrameFromBase({
971
- width,
972
- baseRendered: captured.value,
973
- autocompleteSource: this as unknown as AutocompleteEditorInternals,
974
- autocompleteCapture: captured.capture,
975
- uiTheme: this.uiTheme,
976
- config,
977
- inputText: this.getText(),
978
- metadata: this.getMinimalistMetadata(),
979
- trustedBaseFrame: true,
980
- borderColor: this.borderColor,
981
- });
982
- this.reportMinimalistDecoration(result.decorated);
983
- return result.lines;
984
- } catch {
985
- this.reportMinimalistDecoration(false);
986
- return clampRenderedLines(captured.value, width);
987
- }
988
- }
989
- this.reportMinimalistDecoration(false);
990
- if (width <= 2) {
991
533
  return clampRenderedLines(super.render(width), width);
992
534
  }
993
-
994
- const { railWidth } = getEditorChromeWidths(
995
- config,
996
- this.uiTheme,
997
- "\x1b[0m",
998
- );
999
- const innerWidth = Math.max(0, width - railWidth);
1000
535
  let captured: { value: string[]; capture?: AutocompleteCapture };
1001
536
  try {
1002
537
  captured = renderWithAutocompleteCapture(
1003
538
  this as unknown as AutocompleteEditorInternals,
1004
- () => super.render(innerWidth),
539
+ () => super.render(Math.max(0, width - 4)),
1005
540
  );
1006
541
  } catch {
542
+ this.reportMinimalistDecoration(false);
1007
543
  return clampRenderedLines(super.render(width), width);
1008
544
  }
1009
545
  try {
1010
- const result = renderPolishedFrame({
546
+ const result = renderMinimalistFrameFromBase({
1011
547
  width,
1012
548
  baseRendered: captured.value,
1013
549
  autocompleteSource: this as unknown as AutocompleteEditorInternals,
1014
550
  autocompleteCapture: captured.capture,
1015
551
  uiTheme: this.uiTheme,
1016
552
  config,
1017
- modelMeta: this.getModelMeta(),
1018
- thinkingLevel: this.getThinkingLevel(),
553
+ inputText: this.getText(),
554
+ metadata: this.getMinimalistMetadata(),
1019
555
  trustedBaseFrame: true,
1020
556
  borderColor: this.borderColor,
1021
557
  });
1022
- if (result.decorated) return result.lines;
558
+ this.reportMinimalistDecoration(result.decorated);
559
+ return result.lines;
1023
560
  } catch {
1024
- // Decoration is optional; preserve the completed same-render rows below.
561
+ this.reportMinimalistDecoration(false);
562
+ return clampRenderedLines(captured.value, width);
1025
563
  }
1026
- return clampRenderedLines(captured.value, width);
1027
564
  }
1028
565
  }
1029
566
 
@@ -1151,110 +688,25 @@ export class WrappedPolishedEditor implements EditorComponent {
1151
688
 
1152
689
  render(width: number): string[] {
1153
690
  const config = this.getConfig();
1154
- if (!config.components.editor.enabled) {
691
+ if (config.components.editor.style === "off") {
692
+ const offBorder = offEditorBorderColor(config, this.uiTheme);
693
+ if (offBorder) this.borderColor = offBorder;
1155
694
  this.reportMinimalistDecoration(false);
1156
695
  return clampRenderedLines(this.base.render(width), width);
1157
696
  }
1158
- if (config.components.editor.style === "accent-rail") {
1159
- this.reportMinimalistDecoration(false);
1160
- if (width < ACCENT_RAIL_CHROME_WIDTH + 1) {
1161
- return clampRenderedLines(this.base.render(width), width);
1162
- }
1163
- let captured: { value: string[]; capture?: AutocompleteCapture };
1164
- try {
1165
- captured = renderWithAutocompleteCapture(this.base, () =>
1166
- this.base.render(width - ACCENT_RAIL_CHROME_WIDTH),
1167
- );
1168
- } catch {
1169
- return clampRenderedLines(this.base.render(width), width);
1170
- }
1171
- try {
1172
- const provenance = inspectPolishedFrameProvenance(
1173
- this.base,
1174
- captured.value,
1175
- config,
1176
- this.uiTheme,
1177
- );
1178
- if (provenance.safe) {
1179
- const result = renderAccentRailFrameFromBase({
1180
- width,
1181
- baseRendered: captured.value,
1182
- autocompleteSource: this.base,
1183
- autocompleteCapture: captured.capture,
1184
- uiTheme: this.uiTheme,
1185
- config,
1186
- ownedFrame: provenance.ownedFrame,
1187
- });
1188
- if (result.decorated) return result.lines;
1189
- }
1190
- } catch {
1191
- // Decoration is optional; preserve the completed same-render rows below.
1192
- }
1193
- return clampRenderedLines(captured.value, width);
1194
- }
1195
- if (config.components.editor.style === "minimalist") {
1196
- if (width <= 4) {
1197
- this.reportMinimalistDecoration(false);
1198
- return clampRenderedLines(this.base.render(width), width);
1199
- }
1200
- let captured: { value: string[]; capture?: AutocompleteCapture };
1201
- try {
1202
- captured = renderWithAutocompleteCapture(this.base, () =>
1203
- this.base.render(Math.max(0, width - 4)),
1204
- );
1205
- } catch {
1206
- this.reportMinimalistDecoration(false);
1207
- return clampRenderedLines(this.base.render(width), width);
1208
- }
1209
- try {
1210
- const provenance = inspectPolishedFrameProvenance(
1211
- this.base,
1212
- captured.value,
1213
- config,
1214
- this.uiTheme,
1215
- );
1216
- if (provenance.safe) {
1217
- const result = renderMinimalistFrameFromBase({
1218
- width,
1219
- baseRendered: captured.value,
1220
- autocompleteSource: this.base,
1221
- autocompleteCapture: captured.capture,
1222
- uiTheme: this.uiTheme,
1223
- config,
1224
- inputText: this.base.getText(),
1225
- metadata: this.getMinimalistMetadata(),
1226
- ownedFrame: provenance.ownedFrame,
1227
- borderColor: this.borderColor,
1228
- });
1229
- if (result.decorated) {
1230
- this.reportMinimalistDecoration(true);
1231
- return result.lines;
1232
- }
1233
- }
1234
- } catch {
1235
- // Decoration is optional; preserve the completed same-render rows below.
1236
- }
697
+ if (width <= 4) {
1237
698
  this.reportMinimalistDecoration(false);
1238
- return clampRenderedLines(captured.value, width);
699
+ return clampRenderedLines(this.base.render(width), width);
1239
700
  }
1240
- this.reportMinimalistDecoration(false);
1241
- if (width <= 2) return clampRenderedLines(this.base.render(width), width);
1242
-
1243
- const { railWidth } = getEditorChromeWidths(
1244
- config,
1245
- this.uiTheme,
1246
- "\x1b[0m",
1247
- );
1248
- const innerWidth = Math.max(0, width - railWidth);
1249
701
  let captured: { value: string[]; capture?: AutocompleteCapture };
1250
702
  try {
1251
703
  captured = renderWithAutocompleteCapture(this.base, () =>
1252
- this.base.render(innerWidth),
704
+ this.base.render(Math.max(0, width - 4)),
1253
705
  );
1254
706
  } catch {
707
+ this.reportMinimalistDecoration(false);
1255
708
  return clampRenderedLines(this.base.render(width), width);
1256
709
  }
1257
- let result: PolishedFrameResult | undefined;
1258
710
  try {
1259
711
  const provenance = inspectPolishedFrameProvenance(
1260
712
  this.base,
@@ -1263,24 +715,27 @@ export class WrappedPolishedEditor implements EditorComponent {
1263
715
  this.uiTheme,
1264
716
  );
1265
717
  if (provenance.safe) {
1266
- result = renderPolishedFrame({
718
+ const result = renderMinimalistFrameFromBase({
1267
719
  width,
1268
720
  baseRendered: captured.value,
1269
721
  autocompleteSource: this.base,
1270
722
  autocompleteCapture: captured.capture,
1271
723
  uiTheme: this.uiTheme,
1272
724
  config,
1273
- modelMeta: this.getModelMeta(),
1274
- thinkingLevel: this.getThinkingLevel(),
1275
- rightStatus: readVimStatus(this.base, this.uiTheme),
725
+ inputText: this.base.getText(),
726
+ metadata: this.getMinimalistMetadata(),
1276
727
  ownedFrame: provenance.ownedFrame,
1277
728
  borderColor: this.borderColor,
1278
729
  });
730
+ if (result.decorated) {
731
+ this.reportMinimalistDecoration(true);
732
+ return result.lines;
733
+ }
1279
734
  }
1280
735
  } catch {
1281
736
  // Decoration is optional; preserve the completed same-render rows below.
1282
737
  }
1283
- if (result?.decorated) return result.lines;
738
+ this.reportMinimalistDecoration(false);
1284
739
  return clampRenderedLines(captured.value, width);
1285
740
  }
1286
741