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.
- package/CHANGELOG.md +45 -1
- package/README.en.md +33 -28
- package/README.md +11 -5
- package/extensions/app/commands/settings-previews.ts +23 -56
- package/extensions/app/config/shell.ts +26 -187
- package/extensions/app/panel.ts +42 -24
- package/extensions/app/presets.ts +2 -2
- package/extensions/app/runtime/tui-runtime.ts +1 -26
- package/extensions/layouts/context/renderer/index.ts +15 -0
- package/extensions/layouts/context/renderer/tool/toggle-render-cache.ts +333 -0
- package/extensions/layouts/editor/controller.ts +16 -62
- package/extensions/layouts/editor/factory.ts +2 -20
- package/extensions/layouts/editor/index.ts +0 -6
- package/extensions/layouts/editor/minimalist-editor.ts +11 -57
- package/extensions/layouts/editor/ui.ts +60 -605
- package/extensions/layouts/footer/footer.ts +7 -3
- package/extensions/layouts/working-line/interaction-summary.ts +47 -7
- package/extensions/layouts/working-line/working-line.ts +24 -3
- package/extensions/shared/icons.ts +0 -4
- package/extensions/shared/style.ts +48 -0
- package/extensions/tools/patch-keys.ts +5 -0
- package/package.json +2 -3
- package/themes/cc-dark.json +4 -1
- package/themes/cc-light.json +4 -1
- package/extensions/layouts/editor/accent-rail-editor.ts +0 -112
- package/extensions/layouts/editor/accent-rail-layout-patch.ts +0 -530
- package/extensions/layouts/editor/completion-menu.ts +0 -183
|
@@ -12,18 +12,13 @@ import {
|
|
|
12
12
|
truncateToWidth,
|
|
13
13
|
visibleWidth,
|
|
14
14
|
} from "@earendil-works/pi-tui";
|
|
15
|
-
import type {
|
|
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
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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 (
|
|
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 (
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
1018
|
-
|
|
553
|
+
inputText: this.getText(),
|
|
554
|
+
metadata: this.getMinimalistMetadata(),
|
|
1019
555
|
trustedBaseFrame: true,
|
|
1020
556
|
borderColor: this.borderColor,
|
|
1021
557
|
});
|
|
1022
|
-
|
|
558
|
+
this.reportMinimalistDecoration(result.decorated);
|
|
559
|
+
return result.lines;
|
|
1023
560
|
} catch {
|
|
1024
|
-
|
|
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 (
|
|
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 (
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
1274
|
-
|
|
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
|
-
|
|
738
|
+
this.reportMinimalistDecoration(false);
|
|
1284
739
|
return clampRenderedLines(captured.value, width);
|
|
1285
740
|
}
|
|
1286
741
|
|