import { ModelSelectorComponent, SettingsSelectorComponent, type Theme, } from "@earendil-works/pi-coding-agent"; import type { ZentuiConfig } from "../../app/config/shell.ts"; import { installPrototypePatch, removePrototypePatch, } from "../../app/runtime/prototype-patch-registry.ts"; import { EDITOR_BORDER_FALLBACK, renderEditorBorder, renderThemeStyleOrFallback, } from "../../shared/style.ts"; type PatchableSelectorPrototype = { render: (width: number) => string[]; }; type Cleanup = () => void; function stripAnsi(text: string): string { return text.replaceAll(/\x1b\[[0-9;]*m/g, ""); } function isHorizontalBorderLine(line: string): boolean { return /^─+$/.test(stripAnsi(line)); } function renderBorderLine( width: number, theme: Theme | undefined, config: ZentuiConfig | undefined, ): string { const text = "─".repeat(Math.max(1, width)); if (theme && config) { // 选择器边框跟随主题 borderMuted 语义色。 return renderThemeStyleOrFallback( theme, undefined, EDITOR_BORDER_FALLBACK, text, ); } return renderEditorBorder(text); } export function patchSelectorBorderStyle( prototype: PatchableSelectorPrototype, getTheme?: () => Theme | undefined, getConfig?: () => ZentuiConfig, ): Cleanup { return installPrototypePatch( prototype, "render", "selector-border-render", ({ predecessor, receiver, args }) => { const lines = Reflect.apply(predecessor, receiver, args) as string[]; const width = args[0]; if (lines.length === 0 || typeof width !== "number" || width <= 0) return lines; return lines.map((line, index) => { if (index !== 0 && index !== lines.length - 1) return line; if (!isHorizontalBorderLine(line)) return line; return renderBorderLine(width, getTheme?.(), getConfig?.()); }); }, ); } export function removeSelectorBorderStyle(): void { removePrototypePatch( ModelSelectorComponent.prototype, "render", "selector-border-render", ); removePrototypePatch( SettingsSelectorComponent.prototype, "render", "selector-border-render", ); } export function installSelectorBorderStyle( getTheme?: () => Theme | undefined, getConfig?: () => ZentuiConfig, ): Cleanup { const cleanupModel = patchSelectorBorderStyle( ModelSelectorComponent.prototype as unknown as PatchableSelectorPrototype, getTheme, getConfig, ); let cleanupSettings: Cleanup; try { cleanupSettings = patchSelectorBorderStyle( SettingsSelectorComponent.prototype as unknown as PatchableSelectorPrototype, getTheme, getConfig, ); } catch (error) { cleanupModel(); throw error; } return () => { cleanupModel(); cleanupSettings(); }; }