pi-zentui 0.1.15 → 0.2.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/extensions/zentui/index.ts +67 -6
- package/extensions/zentui/ui.ts +415 -145
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ import { readRuntimeInfo } from "./runtime";
|
|
|
23
23
|
import { installSelectorBorderStyle } from "./selector-border";
|
|
24
24
|
import { registerZentuiSettingsCommand } from "./settings-command";
|
|
25
25
|
import { type FooterState, createInitialState, syncState } from "./state";
|
|
26
|
-
import { PolishedEditor } from "./ui";
|
|
26
|
+
import { PolishedEditor, WrappedPolishedEditor } from "./ui";
|
|
27
27
|
import { installUserMessageStyle } from "./user-message";
|
|
28
28
|
|
|
29
29
|
const ZENTUI_EDITOR_FACTORY = Symbol.for("pi-zentui.editor-factory");
|
|
@@ -38,6 +38,8 @@ type ApplyUiResult = {
|
|
|
38
38
|
editorBlocked: boolean;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
type EditorInstallMode = "none" | "standalone" | "wrapper";
|
|
42
|
+
|
|
41
43
|
function isZentuiEditorFactory(factory: EditorFactory | undefined): boolean {
|
|
42
44
|
return Boolean((factory as ZentuiEditorFactory | undefined)?.[ZENTUI_EDITOR_FACTORY]);
|
|
43
45
|
}
|
|
@@ -53,6 +55,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
53
55
|
let cleanupPrototypePatches: () => void = () => {};
|
|
54
56
|
let footerInstalled = false;
|
|
55
57
|
let editorInstalled = false;
|
|
58
|
+
let editorInstallMode: EditorInstallMode = "none";
|
|
59
|
+
let wrappedEditorFactory: EditorFactory | undefined;
|
|
56
60
|
let prototypePatchesInstalled = false;
|
|
57
61
|
let projectRefreshInFlight = false;
|
|
58
62
|
let projectRefreshPending = false;
|
|
@@ -139,12 +143,42 @@ export default function (pi: ExtensionAPI) {
|
|
|
139
143
|
return factory;
|
|
140
144
|
};
|
|
141
145
|
|
|
146
|
+
const makeWrappedEditorFactory = (
|
|
147
|
+
ctx: ExtensionContext,
|
|
148
|
+
baseFactory: EditorFactory,
|
|
149
|
+
): ZentuiEditorFactory => {
|
|
150
|
+
const factory = ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) =>
|
|
151
|
+
new WrappedPolishedEditor(
|
|
152
|
+
baseFactory(tui, theme, keybindings),
|
|
153
|
+
ctx.ui.theme,
|
|
154
|
+
getCurrentConfig,
|
|
155
|
+
() => ({
|
|
156
|
+
modelLabel: state.modelLabel,
|
|
157
|
+
providerLabel: state.providerLabel,
|
|
158
|
+
}),
|
|
159
|
+
getThinkingLevel,
|
|
160
|
+
)) as ZentuiEditorFactory;
|
|
161
|
+
factory[ZENTUI_EDITOR_FACTORY] = true;
|
|
162
|
+
return factory;
|
|
163
|
+
};
|
|
164
|
+
|
|
142
165
|
const installEditor = (ctx: ExtensionContext): boolean => {
|
|
143
166
|
const currentFactory = ctx.ui.getEditorComponent();
|
|
144
|
-
if (currentFactory &&
|
|
167
|
+
if (currentFactory && isZentuiEditorFactory(currentFactory)) {
|
|
168
|
+
editorInstalled = true;
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
145
171
|
|
|
146
172
|
installPrototypePatches();
|
|
147
|
-
|
|
173
|
+
if (currentFactory) {
|
|
174
|
+
wrappedEditorFactory = currentFactory;
|
|
175
|
+
ctx.ui.setEditorComponent(makeWrappedEditorFactory(ctx, currentFactory));
|
|
176
|
+
editorInstallMode = "wrapper";
|
|
177
|
+
} else {
|
|
178
|
+
wrappedEditorFactory = undefined;
|
|
179
|
+
ctx.ui.setEditorComponent(makeEditorFactory(ctx));
|
|
180
|
+
editorInstallMode = "standalone";
|
|
181
|
+
}
|
|
148
182
|
editorInstalled = true;
|
|
149
183
|
return true;
|
|
150
184
|
};
|
|
@@ -154,7 +188,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
154
188
|
if (currentFactory && !isZentuiEditorFactory(currentFactory)) return false;
|
|
155
189
|
|
|
156
190
|
uninstallPrototypePatches();
|
|
157
|
-
ctx.ui.setEditorComponent(
|
|
191
|
+
ctx.ui.setEditorComponent(
|
|
192
|
+
editorInstallMode === "wrapper" && wrappedEditorFactory ? wrappedEditorFactory : undefined,
|
|
193
|
+
);
|
|
194
|
+
wrappedEditorFactory = undefined;
|
|
195
|
+
editorInstallMode = "none";
|
|
158
196
|
editorInstalled = false;
|
|
159
197
|
return true;
|
|
160
198
|
};
|
|
@@ -192,7 +230,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
192
230
|
if (!ctx.hasUI) return result;
|
|
193
231
|
activeTheme = ctx.ui.theme;
|
|
194
232
|
if (currentConfig.features.editor) {
|
|
195
|
-
|
|
233
|
+
const currentFactory = ctx.ui.getEditorComponent();
|
|
234
|
+
const editorMissingOrReplaced = !editorInstalled || !isZentuiEditorFactory(currentFactory);
|
|
235
|
+
if (editorMissingOrReplaced) result.editorBlocked = !installEditor(ctx);
|
|
196
236
|
} else if (editorInstalled || prototypePatchesInstalled) {
|
|
197
237
|
result.editorBlocked = !uninstallEditor(ctx);
|
|
198
238
|
}
|
|
@@ -219,6 +259,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
219
259
|
refresh();
|
|
220
260
|
};
|
|
221
261
|
|
|
262
|
+
const scheduleEditorReconciliation = (ctx: ExtensionContext) => {
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
if (!ctx.hasUI || !currentConfig.features.editor) return;
|
|
265
|
+
const currentFactory = ctx.ui.getEditorComponent();
|
|
266
|
+
if (currentFactory && !isZentuiEditorFactory(currentFactory)) {
|
|
267
|
+
applyConfiguredUi(ctx);
|
|
268
|
+
refresh();
|
|
269
|
+
}
|
|
270
|
+
}, 0);
|
|
271
|
+
};
|
|
272
|
+
|
|
222
273
|
const cleanupUi = (ctx?: ExtensionContext) => {
|
|
223
274
|
uninstallPrototypePatches();
|
|
224
275
|
stopProjectRefresh();
|
|
@@ -226,8 +277,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
226
277
|
getActiveExtensionStatuses = () => new Map();
|
|
227
278
|
if (ctx?.hasUI) {
|
|
228
279
|
ctx.ui.setFooter(undefined);
|
|
229
|
-
ctx.ui.
|
|
280
|
+
const currentFactory = ctx.ui.getEditorComponent();
|
|
281
|
+
if (!currentFactory || isZentuiEditorFactory(currentFactory)) {
|
|
282
|
+
ctx.ui.setEditorComponent(
|
|
283
|
+
editorInstallMode === "wrapper" && wrappedEditorFactory
|
|
284
|
+
? wrappedEditorFactory
|
|
285
|
+
: undefined,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
230
288
|
}
|
|
289
|
+
wrappedEditorFactory = undefined;
|
|
290
|
+
editorInstallMode = "none";
|
|
231
291
|
footerInstalled = false;
|
|
232
292
|
editorInstalled = false;
|
|
233
293
|
activeTheme = undefined;
|
|
@@ -242,6 +302,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
242
302
|
|
|
243
303
|
pi.on("session_start", async (_event, ctx) => {
|
|
244
304
|
installUi(ctx);
|
|
305
|
+
scheduleEditorReconciliation(ctx);
|
|
245
306
|
});
|
|
246
307
|
|
|
247
308
|
registerZentuiSettingsCommand(pi, {
|
package/extensions/zentui/ui.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CustomEditor, type KeybindingsManager, type Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import {
|
|
3
|
+
type AutocompleteProvider,
|
|
3
4
|
type Component,
|
|
5
|
+
type EditorComponent,
|
|
4
6
|
type EditorTheme,
|
|
5
7
|
type TUI,
|
|
6
8
|
truncateToWidth,
|
|
@@ -19,22 +21,260 @@ type AutocompleteEditorInternals = {
|
|
|
19
21
|
isShowingAutocomplete?: () => boolean;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
type WrappedEditor = EditorComponent &
|
|
25
|
+
AutocompleteEditorInternals & {
|
|
26
|
+
focused?: boolean;
|
|
27
|
+
onEscape?: () => void;
|
|
28
|
+
onCtrlD?: () => void;
|
|
29
|
+
onPasteImage?: () => void;
|
|
30
|
+
onExtensionShortcut?: (data: string) => boolean;
|
|
31
|
+
actionHandlers?: Map<unknown, () => void>;
|
|
32
|
+
wantsKeyRelease?: boolean;
|
|
33
|
+
disableSubmit?: boolean;
|
|
34
|
+
getLines?: () => string[];
|
|
35
|
+
getCursor?: () => unknown;
|
|
36
|
+
getMode?: () => unknown;
|
|
37
|
+
getPaddingX?: () => number;
|
|
38
|
+
getAutocompleteMaxVisible?: () => number;
|
|
39
|
+
addToHistory?: (text: string) => void;
|
|
40
|
+
getExpandedText?: () => string;
|
|
41
|
+
insertTextAtCursor?: (text: string) => void;
|
|
42
|
+
setAutocompleteProvider?: (provider: AutocompleteProvider) => void;
|
|
43
|
+
setPaddingX?: (padding: number) => void;
|
|
44
|
+
setAutocompleteMaxVisible?: (maxVisible: number) => void;
|
|
45
|
+
};
|
|
46
|
+
|
|
22
47
|
type EditorMeta = {
|
|
23
48
|
modelLabel: string;
|
|
24
49
|
providerLabel: string;
|
|
25
50
|
};
|
|
26
51
|
|
|
52
|
+
type PolishedFrameOptions = {
|
|
53
|
+
width: number;
|
|
54
|
+
baseRendered: string[];
|
|
55
|
+
autocompleteSource: AutocompleteEditorInternals;
|
|
56
|
+
uiTheme: Theme;
|
|
57
|
+
config: PolishedTuiConfig;
|
|
58
|
+
modelMeta: EditorMeta;
|
|
59
|
+
thinkingLevel: string | undefined;
|
|
60
|
+
rightStatus?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
27
63
|
function clampRenderedLines(lines: string[], width: number): string[] {
|
|
28
64
|
const maxWidth = Math.max(0, width);
|
|
29
65
|
return lines.map((line) => truncateToWidth(line, maxWidth, ""));
|
|
30
66
|
}
|
|
31
67
|
|
|
68
|
+
function fillLine(content: string, width: number): string {
|
|
69
|
+
const truncated = truncateToWidth(content, Math.max(0, width), "");
|
|
70
|
+
const pad = " ".repeat(Math.max(0, width - visibleWidth(truncated)));
|
|
71
|
+
return `${truncated}${pad}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function editorThinkingStyle(config: PolishedTuiConfig, level: string): string | undefined {
|
|
75
|
+
switch (level.toLowerCase()) {
|
|
76
|
+
case "minimal":
|
|
77
|
+
return config.colors.editorThinkingMinimal ?? config.colors.editorThinking;
|
|
78
|
+
case "low":
|
|
79
|
+
return config.colors.editorThinkingLow ?? config.colors.editorThinking;
|
|
80
|
+
case "medium":
|
|
81
|
+
return config.colors.editorThinkingMedium ?? config.colors.editorThinking;
|
|
82
|
+
case "high":
|
|
83
|
+
return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
|
|
84
|
+
case "xhigh":
|
|
85
|
+
return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
|
|
86
|
+
default:
|
|
87
|
+
return config.colors.editorThinking;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function copyFriendlyPrompt(config: PolishedTuiConfig, uiTheme: Theme, reset: string): string {
|
|
92
|
+
const promptIcon = config.icons.editorPrompt;
|
|
93
|
+
return promptIcon
|
|
94
|
+
? `${renderStyleForSourceOrFallback(
|
|
95
|
+
uiTheme,
|
|
96
|
+
config.colorSources.editor,
|
|
97
|
+
config.colors.editorPrompt ?? config.colors.editorAccent,
|
|
98
|
+
EDITOR_ACCENT_FALLBACK,
|
|
99
|
+
promptIcon,
|
|
100
|
+
)}${reset} `
|
|
101
|
+
: "";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getEditorChromeWidths(config: PolishedTuiConfig, uiTheme: Theme, reset: string) {
|
|
105
|
+
const prompt = copyFriendlyPrompt(config, uiTheme, reset);
|
|
106
|
+
const rail = config.features.copyFriendly
|
|
107
|
+
? ""
|
|
108
|
+
: `${renderStyleForSourceOrFallback(
|
|
109
|
+
uiTheme,
|
|
110
|
+
config.colorSources.editor,
|
|
111
|
+
config.colors.editorAccent,
|
|
112
|
+
EDITOR_ACCENT_FALLBACK,
|
|
113
|
+
"│",
|
|
114
|
+
)}${reset} `;
|
|
115
|
+
return {
|
|
116
|
+
prompt,
|
|
117
|
+
promptWidth: visibleWidth(prompt),
|
|
118
|
+
rail,
|
|
119
|
+
railWidth: config.features.copyFriendly ? visibleWidth(prompt) : visibleWidth(rail),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function composeMetadataLine(left: string, right: string | undefined, width: number): string {
|
|
124
|
+
if (!right) return left;
|
|
125
|
+
const maxWidth = Math.max(0, width);
|
|
126
|
+
const rightWidth = visibleWidth(right);
|
|
127
|
+
if (rightWidth >= maxWidth) return truncateToWidth(right, maxWidth, "");
|
|
128
|
+
|
|
129
|
+
const leftWidth = Math.max(0, maxWidth - rightWidth - 1);
|
|
130
|
+
const leftText = truncateToWidth(left, leftWidth, "");
|
|
131
|
+
const gap = " ".repeat(Math.max(1, maxWidth - visibleWidth(leftText) - rightWidth));
|
|
132
|
+
return `${leftText}${gap}${right}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function vimModeColor(mode: string): string {
|
|
136
|
+
switch (mode.toLowerCase()) {
|
|
137
|
+
case "insert":
|
|
138
|
+
return "success";
|
|
139
|
+
case "normal":
|
|
140
|
+
return "accent";
|
|
141
|
+
case "ex":
|
|
142
|
+
return "warning";
|
|
143
|
+
case "replace":
|
|
144
|
+
return "error";
|
|
145
|
+
case "visual":
|
|
146
|
+
return "syntaxKeyword";
|
|
147
|
+
default:
|
|
148
|
+
return "muted";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readVimStatus(editor: WrappedEditor, uiTheme: Theme): string | undefined {
|
|
153
|
+
const mode = editor.getMode?.();
|
|
154
|
+
if (typeof mode !== "string") return undefined;
|
|
155
|
+
const normalized = mode.trim();
|
|
156
|
+
if (!normalized) return undefined;
|
|
157
|
+
const label = `${normalized.toUpperCase()} `;
|
|
158
|
+
return safeThemeFg(uiTheme, vimModeColor(normalized), label);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function renderPolishedFrame({
|
|
162
|
+
width,
|
|
163
|
+
baseRendered,
|
|
164
|
+
autocompleteSource,
|
|
165
|
+
uiTheme,
|
|
166
|
+
config,
|
|
167
|
+
modelMeta,
|
|
168
|
+
thinkingLevel,
|
|
169
|
+
rightStatus,
|
|
170
|
+
}: PolishedFrameOptions): string[] {
|
|
171
|
+
if (width <= 2) return clampRenderedLines(baseRendered, width);
|
|
172
|
+
|
|
173
|
+
const reset = "\x1b[0m";
|
|
174
|
+
const colorSource = config.colorSources.editor;
|
|
175
|
+
const { prompt, promptWidth, rail, railWidth } = getEditorChromeWidths(config, uiTheme, reset);
|
|
176
|
+
const innerWidth = Math.max(0, width - railWidth);
|
|
177
|
+
const copyFriendlyContinuation = " ".repeat(promptWidth);
|
|
178
|
+
const isShowingAutocomplete =
|
|
179
|
+
typeof autocompleteSource.isShowingAutocomplete === "function"
|
|
180
|
+
? Boolean(autocompleteSource.isShowingAutocomplete())
|
|
181
|
+
: false;
|
|
182
|
+
|
|
183
|
+
if (baseRendered.length < 2) return clampRenderedLines(baseRendered, width);
|
|
184
|
+
|
|
185
|
+
const { autocompleteList } = autocompleteSource;
|
|
186
|
+
const autocompleteCount =
|
|
187
|
+
isShowingAutocomplete && typeof autocompleteList?.render === "function"
|
|
188
|
+
? autocompleteList.render(innerWidth).length
|
|
189
|
+
: 0;
|
|
190
|
+
const editorFrame =
|
|
191
|
+
autocompleteCount > 0 && autocompleteCount < baseRendered.length
|
|
192
|
+
? baseRendered.slice(0, -autocompleteCount)
|
|
193
|
+
: baseRendered;
|
|
194
|
+
const autocompleteLines =
|
|
195
|
+
autocompleteCount > 0 && autocompleteCount < baseRendered.length
|
|
196
|
+
? baseRendered.slice(-autocompleteCount)
|
|
197
|
+
: [];
|
|
198
|
+
|
|
199
|
+
if (editorFrame.length < 2) return clampRenderedLines(baseRendered, width);
|
|
200
|
+
|
|
201
|
+
const editorLines = editorFrame.slice(1, -1);
|
|
202
|
+
const model = renderStyleForSourceOrFallback(
|
|
203
|
+
uiTheme,
|
|
204
|
+
colorSource,
|
|
205
|
+
config.colors.editorModel,
|
|
206
|
+
EDITOR_ACCENT_FALLBACK,
|
|
207
|
+
modelMeta.modelLabel,
|
|
208
|
+
);
|
|
209
|
+
const provider = renderStyleForSourceOrFallback(
|
|
210
|
+
uiTheme,
|
|
211
|
+
colorSource,
|
|
212
|
+
config.colors.editorProvider,
|
|
213
|
+
"text",
|
|
214
|
+
modelMeta.providerLabel,
|
|
215
|
+
);
|
|
216
|
+
const renderedModelMeta = [model, provider]
|
|
217
|
+
.filter(Boolean)
|
|
218
|
+
.join(safeThemeFg(uiTheme, "borderMuted", " "));
|
|
219
|
+
const metaParts = [renderedModelMeta];
|
|
220
|
+
if (thinkingLevel && thinkingLevel !== "off") {
|
|
221
|
+
metaParts.push(
|
|
222
|
+
renderStyleForSourceOrFallback(
|
|
223
|
+
uiTheme,
|
|
224
|
+
colorSource,
|
|
225
|
+
editorThinkingStyle(config, thinkingLevel),
|
|
226
|
+
"muted",
|
|
227
|
+
thinkingLevel,
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const meta = metaParts.filter(Boolean).join(safeThemeFg(uiTheme, "border", " "));
|
|
232
|
+
const copyFriendlyMeta = composeMetadataLine(meta, rightStatus, Math.max(0, width - 1));
|
|
233
|
+
const railedMeta = composeMetadataLine(meta, rightStatus, innerWidth);
|
|
234
|
+
|
|
235
|
+
const top = renderStyleForSourceOrFallback(
|
|
236
|
+
uiTheme,
|
|
237
|
+
colorSource,
|
|
238
|
+
config.colors.editorBorder,
|
|
239
|
+
EDITOR_BORDER_FALLBACK,
|
|
240
|
+
"─".repeat(width),
|
|
241
|
+
);
|
|
242
|
+
const bottom = renderStyleForSourceOrFallback(
|
|
243
|
+
uiTheme,
|
|
244
|
+
colorSource,
|
|
245
|
+
config.colors.editorBorder,
|
|
246
|
+
EDITOR_BORDER_FALLBACK,
|
|
247
|
+
"─".repeat(width),
|
|
248
|
+
);
|
|
249
|
+
const lines = ["", ...editorLines, "", railedMeta];
|
|
250
|
+
const renderedLines = config.features.copyFriendly
|
|
251
|
+
? [
|
|
252
|
+
top,
|
|
253
|
+
"",
|
|
254
|
+
...editorLines.map(
|
|
255
|
+
(line, index) =>
|
|
256
|
+
`${index === 0 ? prompt : copyFriendlyContinuation}${fillLine(line, innerWidth)}`,
|
|
257
|
+
),
|
|
258
|
+
"",
|
|
259
|
+
` ${truncateToWidth(copyFriendlyMeta, Math.max(0, width - 1), "")}`,
|
|
260
|
+
bottom,
|
|
261
|
+
...autocompleteLines,
|
|
262
|
+
]
|
|
263
|
+
: [
|
|
264
|
+
top,
|
|
265
|
+
...lines.map((line) => `${rail}${fillLine(line, innerWidth)}`),
|
|
266
|
+
bottom,
|
|
267
|
+
...autocompleteLines,
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
return clampRenderedLines(renderedLines, width);
|
|
271
|
+
}
|
|
272
|
+
|
|
32
273
|
export class PolishedEditor extends CustomEditor {
|
|
33
274
|
private readonly getModelMeta: () => EditorMeta;
|
|
34
275
|
private readonly getThinkingLevel: () => string | undefined;
|
|
35
276
|
private readonly getConfig: () => PolishedTuiConfig;
|
|
36
277
|
private readonly uiTheme: Theme;
|
|
37
|
-
private readonly reset = "\x1b[0m";
|
|
38
278
|
|
|
39
279
|
constructor(
|
|
40
280
|
tui: TUI,
|
|
@@ -53,160 +293,190 @@ export class PolishedEditor extends CustomEditor {
|
|
|
53
293
|
this.getThinkingLevel = getThinkingLevel;
|
|
54
294
|
}
|
|
55
295
|
|
|
56
|
-
private fillLine(content: string, width: number): string {
|
|
57
|
-
const truncated = truncateToWidth(content, Math.max(0, width), "");
|
|
58
|
-
const pad = " ".repeat(Math.max(0, width - visibleWidth(truncated)));
|
|
59
|
-
return `${truncated}${pad}`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private editorThinkingStyle(config: PolishedTuiConfig, level: string): string | undefined {
|
|
63
|
-
switch (level.toLowerCase()) {
|
|
64
|
-
case "minimal":
|
|
65
|
-
return config.colors.editorThinkingMinimal ?? config.colors.editorThinking;
|
|
66
|
-
case "low":
|
|
67
|
-
return config.colors.editorThinkingLow ?? config.colors.editorThinking;
|
|
68
|
-
case "medium":
|
|
69
|
-
return config.colors.editorThinkingMedium ?? config.colors.editorThinking;
|
|
70
|
-
case "high":
|
|
71
|
-
return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
|
|
72
|
-
case "xhigh":
|
|
73
|
-
return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
|
|
74
|
-
default:
|
|
75
|
-
return config.colors.editorThinking;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
296
|
render(width: number): string[] {
|
|
80
297
|
if (width <= 2) {
|
|
81
298
|
return clampRenderedLines(super.render(width), width);
|
|
82
299
|
}
|
|
83
300
|
|
|
84
301
|
const config = this.getConfig();
|
|
85
|
-
const
|
|
86
|
-
const promptIcon = config.icons.editorPrompt;
|
|
87
|
-
const copyFriendlyPrompt = promptIcon
|
|
88
|
-
? `${renderStyleForSourceOrFallback(
|
|
89
|
-
this.uiTheme,
|
|
90
|
-
colorSource,
|
|
91
|
-
config.colors.editorPrompt ?? config.colors.editorAccent,
|
|
92
|
-
EDITOR_ACCENT_FALLBACK,
|
|
93
|
-
promptIcon,
|
|
94
|
-
)}${this.reset} `
|
|
95
|
-
: "";
|
|
96
|
-
const copyFriendlyPromptWidth = visibleWidth(copyFriendlyPrompt);
|
|
97
|
-
const copyFriendlyContinuation = " ".repeat(copyFriendlyPromptWidth);
|
|
98
|
-
const rail = config.features.copyFriendly
|
|
99
|
-
? ""
|
|
100
|
-
: `${renderStyleForSourceOrFallback(
|
|
101
|
-
this.uiTheme,
|
|
102
|
-
colorSource,
|
|
103
|
-
config.colors.editorAccent,
|
|
104
|
-
EDITOR_ACCENT_FALLBACK,
|
|
105
|
-
"│",
|
|
106
|
-
)}${this.reset} `;
|
|
107
|
-
const railWidth = config.features.copyFriendly ? copyFriendlyPromptWidth : visibleWidth(rail);
|
|
302
|
+
const { railWidth } = getEditorChromeWidths(config, this.uiTheme, "\x1b[0m");
|
|
108
303
|
const innerWidth = Math.max(0, width - railWidth);
|
|
109
304
|
const rendered = super.render(innerWidth);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
305
|
+
return renderPolishedFrame({
|
|
306
|
+
width,
|
|
307
|
+
baseRendered: rendered,
|
|
308
|
+
autocompleteSource: this as unknown as AutocompleteEditorInternals,
|
|
309
|
+
uiTheme: this.uiTheme,
|
|
310
|
+
config,
|
|
311
|
+
modelMeta: this.getModelMeta(),
|
|
312
|
+
thinkingLevel: this.getThinkingLevel(),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
115
316
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
317
|
+
export class WrappedPolishedEditor implements EditorComponent {
|
|
318
|
+
constructor(
|
|
319
|
+
private readonly base: WrappedEditor,
|
|
320
|
+
private readonly uiTheme: Theme,
|
|
321
|
+
private readonly getConfig: () => PolishedTuiConfig,
|
|
322
|
+
private readonly getModelMeta: () => EditorMeta,
|
|
323
|
+
private readonly getThinkingLevel: () => string | undefined,
|
|
324
|
+
) {}
|
|
119
325
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
autocompleteCount > 0 && autocompleteCount < rendered.length
|
|
127
|
-
? rendered.slice(0, -autocompleteCount)
|
|
128
|
-
: rendered;
|
|
129
|
-
const autocompleteLines =
|
|
130
|
-
autocompleteCount > 0 && autocompleteCount < rendered.length
|
|
131
|
-
? rendered.slice(-autocompleteCount)
|
|
132
|
-
: [];
|
|
133
|
-
|
|
134
|
-
if (editorFrame.length < 2) {
|
|
135
|
-
return clampRenderedLines(rendered, width);
|
|
136
|
-
}
|
|
326
|
+
get focused(): boolean {
|
|
327
|
+
return Boolean(this.base.focused);
|
|
328
|
+
}
|
|
329
|
+
set focused(value: boolean) {
|
|
330
|
+
this.base.focused = value;
|
|
331
|
+
}
|
|
137
332
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
333
|
+
get borderColor(): ((str: string) => string) | undefined {
|
|
334
|
+
return this.base.borderColor;
|
|
335
|
+
}
|
|
336
|
+
set borderColor(value: ((str: string) => string) | undefined) {
|
|
337
|
+
this.base.borderColor = value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
get onSubmit(): ((text: string) => void) | undefined {
|
|
341
|
+
return this.base.onSubmit;
|
|
342
|
+
}
|
|
343
|
+
set onSubmit(value: ((text: string) => void) | undefined) {
|
|
344
|
+
this.base.onSubmit = value;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
get onChange(): ((text: string) => void) | undefined {
|
|
348
|
+
return this.base.onChange;
|
|
349
|
+
}
|
|
350
|
+
set onChange(value: ((text: string) => void) | undefined) {
|
|
351
|
+
this.base.onChange = value;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
get onEscape(): (() => void) | undefined {
|
|
355
|
+
return this.base.onEscape;
|
|
356
|
+
}
|
|
357
|
+
set onEscape(value: (() => void) | undefined) {
|
|
358
|
+
this.base.onEscape = value;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
get onCtrlD(): (() => void) | undefined {
|
|
362
|
+
return this.base.onCtrlD;
|
|
363
|
+
}
|
|
364
|
+
set onCtrlD(value: (() => void) | undefined) {
|
|
365
|
+
this.base.onCtrlD = value;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
get onPasteImage(): (() => void) | undefined {
|
|
369
|
+
return this.base.onPasteImage;
|
|
370
|
+
}
|
|
371
|
+
set onPasteImage(value: (() => void) | undefined) {
|
|
372
|
+
this.base.onPasteImage = value;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get onExtensionShortcut(): ((data: string) => boolean) | undefined {
|
|
376
|
+
return this.base.onExtensionShortcut;
|
|
377
|
+
}
|
|
378
|
+
set onExtensionShortcut(value: ((data: string) => boolean) | undefined) {
|
|
379
|
+
this.base.onExtensionShortcut = value;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
get actionHandlers(): Map<unknown, () => void> | undefined {
|
|
383
|
+
return this.base.actionHandlers;
|
|
384
|
+
}
|
|
385
|
+
set actionHandlers(value: Map<unknown, () => void> | undefined) {
|
|
386
|
+
this.base.actionHandlers = value;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
get wantsKeyRelease(): boolean | undefined {
|
|
390
|
+
return this.base.wantsKeyRelease;
|
|
391
|
+
}
|
|
392
|
+
set wantsKeyRelease(value: boolean | undefined) {
|
|
393
|
+
this.base.wantsKeyRelease = value;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
get disableSubmit(): boolean | undefined {
|
|
397
|
+
return this.base.disableSubmit;
|
|
398
|
+
}
|
|
399
|
+
set disableSubmit(value: boolean | undefined) {
|
|
400
|
+
this.base.disableSubmit = value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
render(width: number): string[] {
|
|
404
|
+
if (width <= 2) return clampRenderedLines(this.base.render(width), width);
|
|
405
|
+
|
|
406
|
+
const config = this.getConfig();
|
|
407
|
+
const { railWidth } = getEditorChromeWidths(config, this.uiTheme, "\x1b[0m");
|
|
408
|
+
const innerWidth = Math.max(0, width - railWidth);
|
|
409
|
+
const rendered = this.base.render(innerWidth);
|
|
410
|
+
const vimStatus = readVimStatus(this.base, this.uiTheme);
|
|
411
|
+
return renderPolishedFrame({
|
|
412
|
+
width,
|
|
413
|
+
baseRendered: rendered,
|
|
414
|
+
autocompleteSource: this.base,
|
|
415
|
+
uiTheme: this.uiTheme,
|
|
416
|
+
config,
|
|
417
|
+
modelMeta: this.getModelMeta(),
|
|
418
|
+
thinkingLevel: this.getThinkingLevel(),
|
|
419
|
+
rightStatus: vimStatus,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
invalidate(): void {
|
|
424
|
+
this.base.invalidate?.();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
handleInput(data: string): void {
|
|
428
|
+
this.base.handleInput(data);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
getText(): string {
|
|
432
|
+
return this.base.getText();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
setText(text: string): void {
|
|
436
|
+
this.base.setText(text);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
addToHistory(text: string): void {
|
|
440
|
+
this.base.addToHistory?.(text);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
insertTextAtCursor(text: string): void {
|
|
444
|
+
this.base.insertTextAtCursor?.(text);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
getExpandedText(): string {
|
|
448
|
+
return this.base.getExpandedText?.() ?? this.base.getText();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
setAutocompleteProvider(provider: AutocompleteProvider): void {
|
|
452
|
+
this.base.setAutocompleteProvider?.(provider);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
setPaddingX(padding: number): void {
|
|
456
|
+
this.base.setPaddingX?.(padding);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
setAutocompleteMaxVisible(maxVisible: number): void {
|
|
460
|
+
this.base.setAutocompleteMaxVisible?.(maxVisible);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
getLines(): string[] {
|
|
464
|
+
return this.base.getLines?.() ?? this.base.getText().split("\n");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
getCursor(): unknown {
|
|
468
|
+
return this.base.getCursor?.();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
getMode(): unknown {
|
|
472
|
+
return this.base.getMode?.();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
getPaddingX(): number | undefined {
|
|
476
|
+
return this.base.getPaddingX?.();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
getAutocompleteMaxVisible(): number | undefined {
|
|
480
|
+
return this.base.getAutocompleteMaxVisible?.();
|
|
211
481
|
}
|
|
212
482
|
}
|