pi-one-ui 0.4.0 → 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 +27 -1
- package/README.en.md +5 -4
- package/README.md +7 -5
- package/extensions/app/commands/settings-previews.ts +23 -44
- package/extensions/app/config/shell.ts +19 -58
- package/extensions/app/panel.ts +42 -19
- package/extensions/app/presets.ts +2 -2
- 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 -8
- package/extensions/layouts/editor/minimalist-editor.ts +10 -5
- package/extensions/layouts/editor/ui.ts +52 -365
- package/extensions/layouts/footer/footer.ts +7 -3
- package/extensions/shared/style.ts +48 -0
- package/extensions/tools/patch-keys.ts +5 -0
- package/package.json +1 -1
- package/themes/cc-dark.json +4 -1
- package/themes/cc-light.json +4 -1
- package/extensions/layouts/editor/completion-menu.ts +0 -183
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
renderStyleForSourceOrFallback,
|
|
20
20
|
safeThemeFg,
|
|
21
21
|
} from "../../shared/style.ts";
|
|
22
|
-
import { renderCompletionPalette } from "./completion-menu.ts";
|
|
23
22
|
import { renderEditorMetadataFormat } from "./editor-metadata-format.ts";
|
|
24
23
|
import {
|
|
25
24
|
type MinimalistEditorMetadata,
|
|
@@ -91,34 +90,6 @@ export type EditorMeta = {
|
|
|
91
90
|
sessionName?: string;
|
|
92
91
|
};
|
|
93
92
|
|
|
94
|
-
export type PolishedEditorFrameOptions = {
|
|
95
|
-
width: number;
|
|
96
|
-
editorLines: string[];
|
|
97
|
-
autocompleteLines?: string[];
|
|
98
|
-
viewport?: ViewportCounts;
|
|
99
|
-
uiTheme: Theme;
|
|
100
|
-
config: ZentuiConfig;
|
|
101
|
-
modelMeta: EditorMeta;
|
|
102
|
-
thinkingLevel?: string;
|
|
103
|
-
rightStatus?: string;
|
|
104
|
-
borderColor?: (text: string) => string;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
type PolishedFrameOptions = {
|
|
108
|
-
width: number;
|
|
109
|
-
baseRendered: string[];
|
|
110
|
-
autocompleteSource: AutocompleteEditorInternals;
|
|
111
|
-
autocompleteCapture?: AutocompleteCapture;
|
|
112
|
-
uiTheme: Theme;
|
|
113
|
-
config: ZentuiConfig;
|
|
114
|
-
modelMeta: EditorMeta;
|
|
115
|
-
thinkingLevel: string | undefined;
|
|
116
|
-
rightStatus?: string;
|
|
117
|
-
ownedFrame?: PolishedFrameSplit;
|
|
118
|
-
trustedBaseFrame?: boolean;
|
|
119
|
-
borderColor?: (text: string) => string;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
93
|
type PolishedFrameResult = {
|
|
123
94
|
lines: string[];
|
|
124
95
|
decorated: boolean;
|
|
@@ -316,6 +287,29 @@ export function renderWithAutocompleteCapture<T>(
|
|
|
316
287
|
}
|
|
317
288
|
}
|
|
318
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
|
+
|
|
319
313
|
function clampRenderedLines(lines: string[], width: number): string[] {
|
|
320
314
|
const maxWidth = Math.max(0, width);
|
|
321
315
|
return lines.map((line) => truncateToWidth(line, maxWidth, ""));
|
|
@@ -342,24 +336,6 @@ function getEditorChromeWidths(
|
|
|
342
336
|
return { rail, railWidth: visibleWidth(rail) };
|
|
343
337
|
}
|
|
344
338
|
|
|
345
|
-
function composeMetadataLine(
|
|
346
|
-
left: string,
|
|
347
|
-
right: string | undefined,
|
|
348
|
-
width: number,
|
|
349
|
-
): string {
|
|
350
|
-
if (!right) return left;
|
|
351
|
-
const maxWidth = Math.max(0, width);
|
|
352
|
-
const rightWidth = visibleWidth(right);
|
|
353
|
-
if (rightWidth >= maxWidth) return truncateToWidth(right, maxWidth, "");
|
|
354
|
-
|
|
355
|
-
const leftWidth = Math.max(0, maxWidth - rightWidth - 1);
|
|
356
|
-
const leftText = truncateToWidth(left, leftWidth, "");
|
|
357
|
-
const gap = " ".repeat(
|
|
358
|
-
Math.max(1, maxWidth - visibleWidth(leftText) - rightWidth),
|
|
359
|
-
);
|
|
360
|
-
return `${leftText}${gap}${right}`;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
339
|
function ansiStrippedText(line: string): string {
|
|
364
340
|
return line
|
|
365
341
|
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
@@ -382,17 +358,6 @@ function parseEditorBorder(
|
|
|
382
358
|
return match?.[1] ? { count: match[1] } : undefined;
|
|
383
359
|
}
|
|
384
360
|
|
|
385
|
-
function renderEditorBorder(
|
|
386
|
-
width: number,
|
|
387
|
-
direction: keyof ViewportCounts,
|
|
388
|
-
count: string | undefined,
|
|
389
|
-
): string {
|
|
390
|
-
if (!count) return "─".repeat(width);
|
|
391
|
-
const arrow = direction === "above" ? "↑" : "↓";
|
|
392
|
-
const indicator = `─── ${arrow} ${count} more `;
|
|
393
|
-
return `${indicator}${"─".repeat(Math.max(0, width - visibleWidth(indicator)))}`;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
361
|
function unwrapPolishedFrameOnly(
|
|
397
362
|
lines: string[],
|
|
398
363
|
config: ZentuiConfig,
|
|
@@ -458,35 +423,6 @@ function inspectPolishedFrameProvenance(
|
|
|
458
423
|
return { safe: !unsafe, ownedFrame };
|
|
459
424
|
}
|
|
460
425
|
|
|
461
|
-
function vimModeColor(mode: string): string {
|
|
462
|
-
switch (mode.toLowerCase()) {
|
|
463
|
-
case "insert":
|
|
464
|
-
return "success";
|
|
465
|
-
case "normal":
|
|
466
|
-
return "accent";
|
|
467
|
-
case "ex":
|
|
468
|
-
return "warning";
|
|
469
|
-
case "replace":
|
|
470
|
-
return "error";
|
|
471
|
-
case "visual":
|
|
472
|
-
return "syntaxKeyword";
|
|
473
|
-
default:
|
|
474
|
-
return "muted";
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
function readVimStatus(
|
|
479
|
-
editor: WrappedEditor,
|
|
480
|
-
uiTheme: Theme,
|
|
481
|
-
): string | undefined {
|
|
482
|
-
const mode = editor.getMode?.();
|
|
483
|
-
if (typeof mode !== "string") return undefined;
|
|
484
|
-
const normalized = mode.trim();
|
|
485
|
-
if (!normalized) return undefined;
|
|
486
|
-
const label = `${normalized.toUpperCase()} `;
|
|
487
|
-
return safeThemeFg(uiTheme, vimModeColor(normalized), label);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
426
|
function renderMinimalistFrameFromBase({
|
|
491
427
|
width,
|
|
492
428
|
baseRendered,
|
|
@@ -551,172 +487,6 @@ function renderMinimalistFrameFromBase({
|
|
|
551
487
|
};
|
|
552
488
|
}
|
|
553
489
|
|
|
554
|
-
function renderPolishedFrame({
|
|
555
|
-
width,
|
|
556
|
-
baseRendered,
|
|
557
|
-
autocompleteSource,
|
|
558
|
-
autocompleteCapture,
|
|
559
|
-
uiTheme,
|
|
560
|
-
config,
|
|
561
|
-
modelMeta,
|
|
562
|
-
thinkingLevel,
|
|
563
|
-
rightStatus,
|
|
564
|
-
ownedFrame,
|
|
565
|
-
trustedBaseFrame = false,
|
|
566
|
-
borderColor,
|
|
567
|
-
}: PolishedFrameOptions): PolishedFrameResult {
|
|
568
|
-
if (width <= 2)
|
|
569
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
570
|
-
|
|
571
|
-
if (baseRendered.length < 2) {
|
|
572
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
573
|
-
}
|
|
574
|
-
if (ownedFrame && !isPolishedFrameSplit(ownedFrame, baseRendered.length)) {
|
|
575
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const autocomplete = ownedFrame
|
|
579
|
-
? { known: true, count: ownedFrame.trailingLines.length }
|
|
580
|
-
: autocompleteCount(autocompleteSource, autocompleteCapture, baseRendered);
|
|
581
|
-
if (!autocomplete.known) {
|
|
582
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
583
|
-
}
|
|
584
|
-
const editorFrame =
|
|
585
|
-
!ownedFrame && autocomplete.count > 0
|
|
586
|
-
? baseRendered.slice(0, -autocomplete.count)
|
|
587
|
-
: baseRendered;
|
|
588
|
-
const autocompleteLines = ownedFrame
|
|
589
|
-
? ownedFrame.trailingLines
|
|
590
|
-
: autocomplete.count > 0
|
|
591
|
-
? baseRendered.slice(-autocomplete.count)
|
|
592
|
-
: [];
|
|
593
|
-
if (editorFrame.length < 2) {
|
|
594
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const parsedTop = parseEditorBorder(editorFrame[0] ?? "", "above");
|
|
598
|
-
const parsedBottom = parseEditorBorder(editorFrame.at(-1) ?? "", "below");
|
|
599
|
-
if (!ownedFrame && !trustedBaseFrame && (!parsedTop || !parsedBottom)) {
|
|
600
|
-
return { lines: clampRenderedLines(baseRendered, width), decorated: false };
|
|
601
|
-
}
|
|
602
|
-
const editorLines = ownedFrame?.editorLines ?? editorFrame.slice(1, -1);
|
|
603
|
-
const viewport = ownedFrame?.viewport ?? {
|
|
604
|
-
above: parsedTop?.count,
|
|
605
|
-
below: parsedBottom?.count,
|
|
606
|
-
};
|
|
607
|
-
const lines = renderPolishedEditorFrame({
|
|
608
|
-
width,
|
|
609
|
-
editorLines,
|
|
610
|
-
autocompleteLines,
|
|
611
|
-
viewport,
|
|
612
|
-
uiTheme,
|
|
613
|
-
config,
|
|
614
|
-
modelMeta,
|
|
615
|
-
thinkingLevel,
|
|
616
|
-
rightStatus,
|
|
617
|
-
borderColor,
|
|
618
|
-
});
|
|
619
|
-
POLISHED_FRAME_SPLITS.set(lines, {
|
|
620
|
-
rows: Object.freeze([...lines]),
|
|
621
|
-
split: {
|
|
622
|
-
editorLines,
|
|
623
|
-
trailingLines: autocompleteLines,
|
|
624
|
-
viewport,
|
|
625
|
-
},
|
|
626
|
-
});
|
|
627
|
-
return { lines, decorated: true };
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/** Pure Opencode frame composition shared by the live editor and settings preview. */
|
|
631
|
-
export function renderPolishedEditorFrame({
|
|
632
|
-
width,
|
|
633
|
-
editorLines,
|
|
634
|
-
autocompleteLines = [],
|
|
635
|
-
viewport = {},
|
|
636
|
-
uiTheme,
|
|
637
|
-
config,
|
|
638
|
-
modelMeta,
|
|
639
|
-
thinkingLevel,
|
|
640
|
-
rightStatus,
|
|
641
|
-
borderColor,
|
|
642
|
-
}: PolishedEditorFrameOptions): string[] {
|
|
643
|
-
if (width <= 2) return clampRenderedLines(editorLines, width);
|
|
644
|
-
const reset = "\x1b[0m";
|
|
645
|
-
const colorSource = config.components.editor.colorSource;
|
|
646
|
-
const { rail, railWidth } = getEditorChromeWidths(config, uiTheme, reset);
|
|
647
|
-
const innerWidth = Math.max(0, width - railWidth);
|
|
648
|
-
const meta = renderEditorMetadataFormat(
|
|
649
|
-
config.components.editor.styles.opencode.metadataFormat,
|
|
650
|
-
{
|
|
651
|
-
model: modelMeta.modelLabel,
|
|
652
|
-
modelId: modelMeta.modelId ?? "",
|
|
653
|
-
modelName: modelMeta.modelName ?? "",
|
|
654
|
-
provider: modelMeta.providerLabel,
|
|
655
|
-
thinking: thinkingLevel ?? "",
|
|
656
|
-
sessionName: modelMeta.sessionName ?? "",
|
|
657
|
-
},
|
|
658
|
-
uiTheme,
|
|
659
|
-
config,
|
|
660
|
-
);
|
|
661
|
-
const railedMeta = composeMetadataLine(meta, rightStatus, innerWidth);
|
|
662
|
-
|
|
663
|
-
const renderStaticBorder = (text: string) =>
|
|
664
|
-
renderStyleForSourceOrFallback(
|
|
665
|
-
uiTheme,
|
|
666
|
-
colorSource,
|
|
667
|
-
config.colors.editorBorder,
|
|
668
|
-
EDITOR_BORDER_FALLBACK,
|
|
669
|
-
text,
|
|
670
|
-
);
|
|
671
|
-
const renderBorder = (text: string) => {
|
|
672
|
-
if (
|
|
673
|
-
config.components.editor.borderColorMode !== "adaptive" ||
|
|
674
|
-
typeof borderColor !== "function"
|
|
675
|
-
) {
|
|
676
|
-
return renderStaticBorder(text);
|
|
677
|
-
}
|
|
678
|
-
try {
|
|
679
|
-
const rendered = borderColor(text);
|
|
680
|
-
return typeof rendered === "string" ? rendered : renderStaticBorder(text);
|
|
681
|
-
} catch {
|
|
682
|
-
return renderStaticBorder(text);
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
const top = renderBorder(
|
|
686
|
-
renderEditorBorder(
|
|
687
|
-
width,
|
|
688
|
-
"above",
|
|
689
|
-
config.components.editor.viewportIndicators ? viewport.above : undefined,
|
|
690
|
-
),
|
|
691
|
-
);
|
|
692
|
-
const bottom = renderBorder(
|
|
693
|
-
renderEditorBorder(
|
|
694
|
-
width,
|
|
695
|
-
"below",
|
|
696
|
-
config.components.editor.viewportIndicators ? viewport.below : undefined,
|
|
697
|
-
),
|
|
698
|
-
);
|
|
699
|
-
const completionLines =
|
|
700
|
-
config.components.editor.styles.opencode.completionMenu === "palette"
|
|
701
|
-
? renderCompletionPalette({
|
|
702
|
-
lines: autocompleteLines,
|
|
703
|
-
width,
|
|
704
|
-
theme: uiTheme,
|
|
705
|
-
renderSeparator: renderBorder,
|
|
706
|
-
ownedBackground: false,
|
|
707
|
-
})
|
|
708
|
-
: autocompleteLines;
|
|
709
|
-
const lines = ["", ...editorLines, "", railedMeta];
|
|
710
|
-
const renderedLines = [
|
|
711
|
-
top,
|
|
712
|
-
...lines.map((line) => `${rail}${fillLine(line, innerWidth)}`),
|
|
713
|
-
bottom,
|
|
714
|
-
...completionLines,
|
|
715
|
-
];
|
|
716
|
-
|
|
717
|
-
return clampRenderedLines(renderedLines, width);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
490
|
export class PolishedEditor extends CustomEditor {
|
|
721
491
|
private readonly getModelMeta: () => EditorMeta;
|
|
722
492
|
private readonly getThinkingLevel: () => string | undefined;
|
|
@@ -752,83 +522,45 @@ export class PolishedEditor extends CustomEditor {
|
|
|
752
522
|
|
|
753
523
|
render(width: number): string[] {
|
|
754
524
|
const config = this.getConfig();
|
|
755
|
-
if (
|
|
525
|
+
if (config.components.editor.style === "off") {
|
|
526
|
+
const offBorder = offEditorBorderColor(config, this.uiTheme);
|
|
527
|
+
if (offBorder) this.borderColor = offBorder;
|
|
756
528
|
this.reportMinimalistDecoration(false);
|
|
757
529
|
return clampRenderedLines(super.render(width), width);
|
|
758
530
|
}
|
|
759
|
-
if (
|
|
760
|
-
|
|
761
|
-
this.reportMinimalistDecoration(false);
|
|
762
|
-
return clampRenderedLines(super.render(width), width);
|
|
763
|
-
}
|
|
764
|
-
let captured: { value: string[]; capture?: AutocompleteCapture };
|
|
765
|
-
try {
|
|
766
|
-
captured = renderWithAutocompleteCapture(
|
|
767
|
-
this as unknown as AutocompleteEditorInternals,
|
|
768
|
-
() => super.render(Math.max(0, width - 4)),
|
|
769
|
-
);
|
|
770
|
-
} catch {
|
|
771
|
-
this.reportMinimalistDecoration(false);
|
|
772
|
-
return clampRenderedLines(super.render(width), width);
|
|
773
|
-
}
|
|
774
|
-
try {
|
|
775
|
-
const result = renderMinimalistFrameFromBase({
|
|
776
|
-
width,
|
|
777
|
-
baseRendered: captured.value,
|
|
778
|
-
autocompleteSource: this as unknown as AutocompleteEditorInternals,
|
|
779
|
-
autocompleteCapture: captured.capture,
|
|
780
|
-
uiTheme: this.uiTheme,
|
|
781
|
-
config,
|
|
782
|
-
inputText: this.getText(),
|
|
783
|
-
metadata: this.getMinimalistMetadata(),
|
|
784
|
-
trustedBaseFrame: true,
|
|
785
|
-
borderColor: this.borderColor,
|
|
786
|
-
});
|
|
787
|
-
this.reportMinimalistDecoration(result.decorated);
|
|
788
|
-
return result.lines;
|
|
789
|
-
} catch {
|
|
790
|
-
this.reportMinimalistDecoration(false);
|
|
791
|
-
return clampRenderedLines(captured.value, width);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
this.reportMinimalistDecoration(false);
|
|
795
|
-
if (width <= 2) {
|
|
531
|
+
if (width <= 4) {
|
|
532
|
+
this.reportMinimalistDecoration(false);
|
|
796
533
|
return clampRenderedLines(super.render(width), width);
|
|
797
534
|
}
|
|
798
|
-
|
|
799
|
-
const { railWidth } = getEditorChromeWidths(
|
|
800
|
-
config,
|
|
801
|
-
this.uiTheme,
|
|
802
|
-
"\x1b[0m",
|
|
803
|
-
);
|
|
804
|
-
const innerWidth = Math.max(0, width - railWidth);
|
|
805
535
|
let captured: { value: string[]; capture?: AutocompleteCapture };
|
|
806
536
|
try {
|
|
807
537
|
captured = renderWithAutocompleteCapture(
|
|
808
538
|
this as unknown as AutocompleteEditorInternals,
|
|
809
|
-
() => super.render(
|
|
539
|
+
() => super.render(Math.max(0, width - 4)),
|
|
810
540
|
);
|
|
811
541
|
} catch {
|
|
542
|
+
this.reportMinimalistDecoration(false);
|
|
812
543
|
return clampRenderedLines(super.render(width), width);
|
|
813
544
|
}
|
|
814
545
|
try {
|
|
815
|
-
const result =
|
|
546
|
+
const result = renderMinimalistFrameFromBase({
|
|
816
547
|
width,
|
|
817
548
|
baseRendered: captured.value,
|
|
818
549
|
autocompleteSource: this as unknown as AutocompleteEditorInternals,
|
|
819
550
|
autocompleteCapture: captured.capture,
|
|
820
551
|
uiTheme: this.uiTheme,
|
|
821
552
|
config,
|
|
822
|
-
|
|
823
|
-
|
|
553
|
+
inputText: this.getText(),
|
|
554
|
+
metadata: this.getMinimalistMetadata(),
|
|
824
555
|
trustedBaseFrame: true,
|
|
825
556
|
borderColor: this.borderColor,
|
|
826
557
|
});
|
|
827
|
-
|
|
558
|
+
this.reportMinimalistDecoration(result.decorated);
|
|
559
|
+
return result.lines;
|
|
828
560
|
} catch {
|
|
829
|
-
|
|
561
|
+
this.reportMinimalistDecoration(false);
|
|
562
|
+
return clampRenderedLines(captured.value, width);
|
|
830
563
|
}
|
|
831
|
-
return clampRenderedLines(captured.value, width);
|
|
832
564
|
}
|
|
833
565
|
}
|
|
834
566
|
|
|
@@ -956,73 +688,25 @@ export class WrappedPolishedEditor implements EditorComponent {
|
|
|
956
688
|
|
|
957
689
|
render(width: number): string[] {
|
|
958
690
|
const config = this.getConfig();
|
|
959
|
-
if (
|
|
691
|
+
if (config.components.editor.style === "off") {
|
|
692
|
+
const offBorder = offEditorBorderColor(config, this.uiTheme);
|
|
693
|
+
if (offBorder) this.borderColor = offBorder;
|
|
960
694
|
this.reportMinimalistDecoration(false);
|
|
961
695
|
return clampRenderedLines(this.base.render(width), width);
|
|
962
696
|
}
|
|
963
|
-
if (
|
|
964
|
-
if (width <= 4) {
|
|
965
|
-
this.reportMinimalistDecoration(false);
|
|
966
|
-
return clampRenderedLines(this.base.render(width), width);
|
|
967
|
-
}
|
|
968
|
-
let captured: { value: string[]; capture?: AutocompleteCapture };
|
|
969
|
-
try {
|
|
970
|
-
captured = renderWithAutocompleteCapture(this.base, () =>
|
|
971
|
-
this.base.render(Math.max(0, width - 4)),
|
|
972
|
-
);
|
|
973
|
-
} catch {
|
|
974
|
-
this.reportMinimalistDecoration(false);
|
|
975
|
-
return clampRenderedLines(this.base.render(width), width);
|
|
976
|
-
}
|
|
977
|
-
try {
|
|
978
|
-
const provenance = inspectPolishedFrameProvenance(
|
|
979
|
-
this.base,
|
|
980
|
-
captured.value,
|
|
981
|
-
config,
|
|
982
|
-
this.uiTheme,
|
|
983
|
-
);
|
|
984
|
-
if (provenance.safe) {
|
|
985
|
-
const result = renderMinimalistFrameFromBase({
|
|
986
|
-
width,
|
|
987
|
-
baseRendered: captured.value,
|
|
988
|
-
autocompleteSource: this.base,
|
|
989
|
-
autocompleteCapture: captured.capture,
|
|
990
|
-
uiTheme: this.uiTheme,
|
|
991
|
-
config,
|
|
992
|
-
inputText: this.base.getText(),
|
|
993
|
-
metadata: this.getMinimalistMetadata(),
|
|
994
|
-
ownedFrame: provenance.ownedFrame,
|
|
995
|
-
borderColor: this.borderColor,
|
|
996
|
-
});
|
|
997
|
-
if (result.decorated) {
|
|
998
|
-
this.reportMinimalistDecoration(true);
|
|
999
|
-
return result.lines;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
} catch {
|
|
1003
|
-
// Decoration is optional; preserve the completed same-render rows below.
|
|
1004
|
-
}
|
|
697
|
+
if (width <= 4) {
|
|
1005
698
|
this.reportMinimalistDecoration(false);
|
|
1006
|
-
return clampRenderedLines(
|
|
699
|
+
return clampRenderedLines(this.base.render(width), width);
|
|
1007
700
|
}
|
|
1008
|
-
this.reportMinimalistDecoration(false);
|
|
1009
|
-
if (width <= 2) return clampRenderedLines(this.base.render(width), width);
|
|
1010
|
-
|
|
1011
|
-
const { railWidth } = getEditorChromeWidths(
|
|
1012
|
-
config,
|
|
1013
|
-
this.uiTheme,
|
|
1014
|
-
"\x1b[0m",
|
|
1015
|
-
);
|
|
1016
|
-
const innerWidth = Math.max(0, width - railWidth);
|
|
1017
701
|
let captured: { value: string[]; capture?: AutocompleteCapture };
|
|
1018
702
|
try {
|
|
1019
703
|
captured = renderWithAutocompleteCapture(this.base, () =>
|
|
1020
|
-
this.base.render(
|
|
704
|
+
this.base.render(Math.max(0, width - 4)),
|
|
1021
705
|
);
|
|
1022
706
|
} catch {
|
|
707
|
+
this.reportMinimalistDecoration(false);
|
|
1023
708
|
return clampRenderedLines(this.base.render(width), width);
|
|
1024
709
|
}
|
|
1025
|
-
let result: PolishedFrameResult | undefined;
|
|
1026
710
|
try {
|
|
1027
711
|
const provenance = inspectPolishedFrameProvenance(
|
|
1028
712
|
this.base,
|
|
@@ -1031,24 +715,27 @@ export class WrappedPolishedEditor implements EditorComponent {
|
|
|
1031
715
|
this.uiTheme,
|
|
1032
716
|
);
|
|
1033
717
|
if (provenance.safe) {
|
|
1034
|
-
result =
|
|
718
|
+
const result = renderMinimalistFrameFromBase({
|
|
1035
719
|
width,
|
|
1036
720
|
baseRendered: captured.value,
|
|
1037
721
|
autocompleteSource: this.base,
|
|
1038
722
|
autocompleteCapture: captured.capture,
|
|
1039
723
|
uiTheme: this.uiTheme,
|
|
1040
724
|
config,
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
rightStatus: readVimStatus(this.base, this.uiTheme),
|
|
725
|
+
inputText: this.base.getText(),
|
|
726
|
+
metadata: this.getMinimalistMetadata(),
|
|
1044
727
|
ownedFrame: provenance.ownedFrame,
|
|
1045
728
|
borderColor: this.borderColor,
|
|
1046
729
|
});
|
|
730
|
+
if (result.decorated) {
|
|
731
|
+
this.reportMinimalistDecoration(true);
|
|
732
|
+
return result.lines;
|
|
733
|
+
}
|
|
1047
734
|
}
|
|
1048
735
|
} catch {
|
|
1049
736
|
// Decoration is optional; preserve the completed same-render rows below.
|
|
1050
737
|
}
|
|
1051
|
-
|
|
738
|
+
this.reportMinimalistDecoration(false);
|
|
1052
739
|
return clampRenderedLines(captured.value, width);
|
|
1053
740
|
}
|
|
1054
741
|
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
formatUsernameHostLabel,
|
|
23
23
|
} from "../../shared/format.ts";
|
|
24
24
|
import { resolveRuntimeSymbol } from "../../shared/icons.ts";
|
|
25
|
-
import { renderStyleForSource } from "../../shared/style.ts";
|
|
25
|
+
import { renderSourceColor, renderStyleForSource } from "../../shared/style.ts";
|
|
26
26
|
import { sanitizeEditorMetadataText } from "../editor/editor-metadata-format.ts";
|
|
27
27
|
import {
|
|
28
28
|
collectExtensionStatusSegments,
|
|
@@ -322,10 +322,12 @@ export function installFooter(
|
|
|
322
322
|
separatorText[config.components.footer.styles.starship.separator],
|
|
323
323
|
);
|
|
324
324
|
const innerWidth = Math.max(1, width - 2);
|
|
325
|
-
const cwdLabel =
|
|
325
|
+
const cwdLabel = renderSourceColor(
|
|
326
326
|
theme,
|
|
327
327
|
colorSource,
|
|
328
328
|
config.colors.cwd,
|
|
329
|
+
"cwd",
|
|
330
|
+
"bold cyan",
|
|
329
331
|
formattedCwd,
|
|
330
332
|
);
|
|
331
333
|
const needsSessionName =
|
|
@@ -932,10 +934,12 @@ export function installFooter(
|
|
|
932
934
|
|
|
933
935
|
const chunkBudget = compactChunkBudget(innerWidth);
|
|
934
936
|
const compactCwdLabel = truncateToWidth(
|
|
935
|
-
|
|
937
|
+
renderSourceColor(
|
|
936
938
|
theme,
|
|
937
939
|
colorSource,
|
|
938
940
|
config.colors.cwd,
|
|
941
|
+
"cwd",
|
|
942
|
+
"bold cyan",
|
|
939
943
|
sanitizeEditorMetadataText(
|
|
940
944
|
formatCwdLabel(ctx.cwd, config.icons.cwd, {
|
|
941
945
|
mode: "basename",
|
|
@@ -427,6 +427,54 @@ export function renderStyleForSourceOrFallback(
|
|
|
427
427
|
return renderStyleForSource(theme, source, style ?? fallbackStyle, text);
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
/**
|
|
431
|
+
* 主题画板存在性探测:pi Theme 暴露 fgColors Map;测试或其他实现可
|
|
432
|
+
* 提供 hasThemeToken 回调,缺失时保守按“无该 token”处理。
|
|
433
|
+
*/
|
|
434
|
+
function themeHasToken(theme: ThemeLike, token: string): boolean {
|
|
435
|
+
if (
|
|
436
|
+
typeof (theme as { hasThemeToken?: (t: string) => boolean })
|
|
437
|
+
.hasThemeToken === "function"
|
|
438
|
+
) {
|
|
439
|
+
return (
|
|
440
|
+
theme as unknown as { hasThemeToken(t: string): boolean }
|
|
441
|
+
).hasThemeToken(token);
|
|
442
|
+
}
|
|
443
|
+
const fgColors = (theme as unknown as { fgColors?: Map<string, string> })
|
|
444
|
+
.fgColors;
|
|
445
|
+
return fgColors instanceof Map && fgColors.has(token);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* 颜色统一渲染入口(配置优先,其次主题 token,最后原生默认):
|
|
450
|
+
* - 配置了 spec:按 colorSource 现有语义解释(theme=token/hex,terminal=终端色);
|
|
451
|
+
* - 未配置且 theme 源:主题定义了 themeToken 时用主题色(可指向 vars 变量),
|
|
452
|
+
* 否则回落原生默认 themeDefault;
|
|
453
|
+
* - 未配置且 terminal 源:使用固定终端默认 terminalDefault。
|
|
454
|
+
*/
|
|
455
|
+
export function renderSourceColor(
|
|
456
|
+
theme: ThemeLike,
|
|
457
|
+
source: ColorSource,
|
|
458
|
+
spec: ColorSpec | undefined,
|
|
459
|
+
themeToken: string,
|
|
460
|
+
themeDefault: ColorSpec,
|
|
461
|
+
text: string,
|
|
462
|
+
): string {
|
|
463
|
+
if (typeof spec === "string" && spec.trim() !== "") {
|
|
464
|
+
return renderStyleForSource(theme, source, spec, text);
|
|
465
|
+
}
|
|
466
|
+
// 未配置时:theme 源优先主题专有 token,terminal 源与主题缺 token 时
|
|
467
|
+
// 都回落 Pi 原生默认(主题语义色),保证两种 colorSource 默认观感一致。
|
|
468
|
+
if (source !== "terminal" && themeHasToken(theme, themeToken)) {
|
|
469
|
+
try {
|
|
470
|
+
return theme.fg(themeToken, text);
|
|
471
|
+
} catch {
|
|
472
|
+
// 主题色解析失败时继续回退原生默认。
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return renderThemeStyle(theme, themeDefault, text);
|
|
476
|
+
}
|
|
477
|
+
|
|
430
478
|
export function renderStyleForSourceOrFallbackStrict(
|
|
431
479
|
theme: ThemeLike,
|
|
432
480
|
source: ColorSource,
|
|
@@ -52,6 +52,11 @@ export const TOOL_EXPANDED_BACKGROUND_PATCH = Symbol.for(
|
|
|
52
52
|
"pi.ccstyle.tool-expanded-background-patch",
|
|
53
53
|
);
|
|
54
54
|
|
|
55
|
+
// ── 工具 updateDisplay 跨 toggle 重建缓存 ──
|
|
56
|
+
export const TOGGLE_RENDER_CACHE_PATCH = Symbol.for(
|
|
57
|
+
"pi.ccstyle.toggle-render-cache-patch",
|
|
58
|
+
);
|
|
59
|
+
|
|
55
60
|
// ── 消息组件补丁 ──
|
|
56
61
|
export const MESSAGE_DISPLAY_PATCH = Symbol.for(
|
|
57
62
|
"pi.ccstyle.message-display-patch",
|
package/package.json
CHANGED
package/themes/cc-dark.json
CHANGED
package/themes/cc-light.json
CHANGED