@rosetears/aili-pi 0.1.0 → 0.1.1

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.
Files changed (46) hide show
  1. package/README.md +7 -1
  2. package/THIRD_PARTY_NOTICES.md +11 -0
  3. package/extensions/header/index.ts +92 -0
  4. package/extensions/matrix/index.ts +375 -0
  5. package/extensions/zentui/config.ts +1014 -0
  6. package/extensions/zentui/extension-status.ts +96 -0
  7. package/extensions/zentui/fixed-editor/cluster.ts +98 -0
  8. package/extensions/zentui/fixed-editor/compositor.ts +719 -0
  9. package/extensions/zentui/fixed-editor/index.ts +223 -0
  10. package/extensions/zentui/fixed-editor/input.ts +85 -0
  11. package/extensions/zentui/fixed-editor/pi-compat.ts +296 -0
  12. package/extensions/zentui/fixed-editor/selection.ts +217 -0
  13. package/extensions/zentui/fixed-editor/terminal-modes.ts +75 -0
  14. package/extensions/zentui/fixed-editor/types.ts +37 -0
  15. package/extensions/zentui/footer-format.ts +279 -0
  16. package/extensions/zentui/footer.ts +595 -0
  17. package/extensions/zentui/format.ts +434 -0
  18. package/extensions/zentui/git.ts +384 -0
  19. package/extensions/zentui/gradient.ts +70 -0
  20. package/extensions/zentui/icons.ts +252 -0
  21. package/extensions/zentui/index.ts +577 -0
  22. package/extensions/zentui/live-context.ts +75 -0
  23. package/extensions/zentui/package-version.ts +650 -0
  24. package/extensions/zentui/project-refresh.ts +104 -0
  25. package/extensions/zentui/project-state.ts +59 -0
  26. package/extensions/zentui/prototype-patch-registry.ts +111 -0
  27. package/extensions/zentui/runtime.ts +841 -0
  28. package/extensions/zentui/selector-border.ts +77 -0
  29. package/extensions/zentui/session-lifecycle.ts +60 -0
  30. package/extensions/zentui/settings-command.ts +897 -0
  31. package/extensions/zentui/state.ts +55 -0
  32. package/extensions/zentui/style.ts +332 -0
  33. package/extensions/zentui/thinking-message.ts +159 -0
  34. package/extensions/zentui/tool-execution.ts +189 -0
  35. package/extensions/zentui/ui.ts +618 -0
  36. package/extensions/zentui/user-message.ts +252 -0
  37. package/licenses/pi-sakura-cyberdeck-MIT.txt +21 -0
  38. package/licenses/pi-zentui-MIT.txt +21 -0
  39. package/manifests/provenance.json +11 -0
  40. package/manifests/sbom.json +17 -1
  41. package/notices/pi-sakura-cyberdeck-NOTICE.txt +6 -0
  42. package/package.json +11 -2
  43. package/src/runtime/doctor.ts +1 -1
  44. package/src/runtime/registry.ts +1 -1
  45. package/src/runtime/rem-head.txt +38 -0
  46. package/themes/rem-cyberdeck.json +32 -0
@@ -0,0 +1,618 @@
1
+ import { CustomEditor, type KeybindingsManager, type Theme } from "@earendil-works/pi-coding-agent";
2
+ import {
3
+ type AutocompleteProvider,
4
+ type Component,
5
+ type EditorComponent,
6
+ type EditorTheme,
7
+ type TUI,
8
+ truncateToWidth,
9
+ visibleWidth,
10
+ } from "@earendil-works/pi-tui";
11
+ import type { PolishedTuiConfig } from "./config.js";
12
+ import { formatTimeLabel } from "./format.js";
13
+ import { renderSakuraGradient, SAKURA_MACARON_GRADIENT } from "./gradient.js";
14
+ import {
15
+ EDITOR_ACCENT_FALLBACK,
16
+ EDITOR_BORDER_FALLBACK,
17
+ renderStyleForSourceOrFallback,
18
+ safeThemeFg,
19
+ } from "./style.js";
20
+
21
+ type AutocompleteEditorInternals = {
22
+ autocompleteList?: Pick<Component, "render">;
23
+ isShowingAutocomplete?: () => boolean;
24
+ };
25
+
26
+ type WrappedEditor = EditorComponent &
27
+ AutocompleteEditorInternals & {
28
+ focused?: boolean;
29
+ onEscape?: () => void;
30
+ onCtrlD?: () => void;
31
+ onPasteImage?: () => void;
32
+ onExtensionShortcut?: (data: string) => boolean;
33
+ actionHandlers?: Map<unknown, () => void>;
34
+ wantsKeyRelease?: boolean;
35
+ disableSubmit?: boolean;
36
+ getLines?: () => string[];
37
+ getCursor?: () => unknown;
38
+ getMode?: () => unknown;
39
+ getPaddingX?: () => number;
40
+ getAutocompleteMaxVisible?: () => number;
41
+ addToHistory?: (text: string) => void;
42
+ getExpandedText?: () => string;
43
+ insertTextAtCursor?: (text: string) => void;
44
+ setAutocompleteProvider?: (provider: AutocompleteProvider) => void;
45
+ setPaddingX?: (padding: number) => void;
46
+ setAutocompleteMaxVisible?: (maxVisible: number) => void;
47
+ };
48
+
49
+ type EditorMeta = {
50
+ modelLabel: string;
51
+ providerLabel: string;
52
+ contextLabel: string;
53
+ contextUsedLabel: string;
54
+ tokenLabel: string;
55
+ };
56
+
57
+ type PolishedFrameOptions = {
58
+ width: number;
59
+ baseRendered: string[];
60
+ autocompleteSource: AutocompleteEditorInternals;
61
+ uiTheme: Theme;
62
+ config: PolishedTuiConfig;
63
+ modelMeta: EditorMeta;
64
+ previousModelMeta?: EditorMeta;
65
+ thinkingLevel: string | undefined;
66
+ rightStatus?: string;
67
+ };
68
+
69
+ function clampRenderedLines(lines: string[], width: number): string[] {
70
+ const maxWidth = Math.max(0, width);
71
+ return lines.map((line) => truncateToWidth(line, maxWidth, ""));
72
+ }
73
+
74
+ function fillLine(content: string, width: number): string {
75
+ const truncated = truncateToWidth(content, Math.max(0, width), "");
76
+ const pad = " ".repeat(Math.max(0, width - visibleWidth(truncated)));
77
+ return `${truncated}${pad}`;
78
+ }
79
+
80
+ export function renderEditorFrameBorder(
81
+ text: string,
82
+ config: PolishedTuiConfig,
83
+ uiTheme: Theme,
84
+ colorSource: PolishedTuiConfig["colorSources"]["editor"],
85
+ ): string {
86
+ if (config.colors.editorBorder === SAKURA_MACARON_GRADIENT) {
87
+ return renderSakuraGradient(text);
88
+ }
89
+ return renderStyleForSourceOrFallback(
90
+ uiTheme,
91
+ colorSource,
92
+ config.colors.editorBorder,
93
+ EDITOR_BORDER_FALLBACK,
94
+ text,
95
+ );
96
+ }
97
+
98
+ function editorThinkingStyle(config: PolishedTuiConfig, level: string): string | undefined {
99
+ switch (level.toLowerCase()) {
100
+ case "minimal":
101
+ return config.colors.editorThinkingMinimal ?? config.colors.editorThinking;
102
+ case "low":
103
+ return config.colors.editorThinkingLow ?? config.colors.editorThinking;
104
+ case "medium":
105
+ return config.colors.editorThinkingMedium ?? config.colors.editorThinking;
106
+ case "high":
107
+ return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
108
+ case "xhigh":
109
+ return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
110
+ default:
111
+ return config.colors.editorThinking;
112
+ }
113
+ }
114
+
115
+ function copyFriendlyPrompt(config: PolishedTuiConfig, uiTheme: Theme, reset: string): string {
116
+ const promptIcon = config.icons.editorPrompt;
117
+ return promptIcon
118
+ ? `${renderStyleForSourceOrFallback(
119
+ uiTheme,
120
+ config.colorSources.editor,
121
+ config.colors.editorPrompt ?? config.colors.editorAccent,
122
+ EDITOR_ACCENT_FALLBACK,
123
+ promptIcon,
124
+ )}${reset} `
125
+ : "";
126
+ }
127
+
128
+ function getEditorChromeWidths(config: PolishedTuiConfig, uiTheme: Theme, reset: string) {
129
+ const prompt = copyFriendlyPrompt(config, uiTheme, reset);
130
+ const rail = config.features.copyFriendly
131
+ ? ""
132
+ : `${renderStyleForSourceOrFallback(
133
+ uiTheme,
134
+ config.colorSources.editor,
135
+ config.colors.editorAccent,
136
+ EDITOR_ACCENT_FALLBACK,
137
+ config.icons.rail,
138
+ )}${reset} `;
139
+ return {
140
+ prompt,
141
+ promptWidth: visibleWidth(prompt),
142
+ rail,
143
+ railWidth: config.features.copyFriendly ? visibleWidth(prompt) : visibleWidth(rail),
144
+ };
145
+ }
146
+
147
+ function composeMetadataLine(left: string, right: string | undefined, width: number): string {
148
+ if (!right) return left;
149
+ const maxWidth = Math.max(0, width);
150
+ const rightWidth = visibleWidth(right);
151
+ if (rightWidth >= maxWidth) return truncateToWidth(right, maxWidth, "");
152
+
153
+ const leftWidth = Math.max(0, maxWidth - rightWidth - 1);
154
+ const leftText = truncateToWidth(left, leftWidth, "");
155
+ const gap = " ".repeat(Math.max(1, maxWidth - visibleWidth(leftText) - rightWidth));
156
+ return `${leftText}${gap}${right}`;
157
+ }
158
+
159
+ function plainRenderedText(line: string): string {
160
+ return line
161
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
162
+ .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "")
163
+ .replace(/\[[/?][^\]]+\]/g, "");
164
+ }
165
+
166
+ function isHorizontalBorder(line: string): boolean {
167
+ const plain = plainRenderedText(line).trim();
168
+ return plain.length > 0 && /^─+$/.test(plain);
169
+ }
170
+
171
+ function isRenderedModelMetaLine(line: string, modelMeta: EditorMeta): boolean {
172
+ const plain = plainRenderedText(line);
173
+ return plain.includes(modelMeta.modelLabel) && plain.includes(modelMeta.providerLabel);
174
+ }
175
+
176
+ function matchesAnyModelMeta(
177
+ line: string,
178
+ modelMeta: EditorMeta,
179
+ previousMeta?: EditorMeta,
180
+ ): boolean {
181
+ if (isRenderedModelMetaLine(line, modelMeta)) return true;
182
+ if (previousMeta && isRenderedModelMetaLine(line, previousMeta)) return true;
183
+ return false;
184
+ }
185
+
186
+ function hasRenderedModelMetaLine(
187
+ lines: string[],
188
+ modelMeta: EditorMeta,
189
+ previousMeta?: EditorMeta,
190
+ ): boolean {
191
+ return lines.some((line) => matchesAnyModelMeta(line, modelMeta, previousMeta));
192
+ }
193
+
194
+ function isAlreadyPolishedFrame(
195
+ lines: string[],
196
+ modelMeta: EditorMeta,
197
+ previousMeta?: EditorMeta,
198
+ ): boolean {
199
+ return (
200
+ lines.length >= 3 &&
201
+ isHorizontalBorder(lines[0] ?? "") &&
202
+ isHorizontalBorder(lines.at(-1) ?? "") &&
203
+ hasRenderedModelMetaLine(lines.slice(1, -1), modelMeta, previousMeta)
204
+ );
205
+ }
206
+
207
+ function removeRenderedModelMetaLines(
208
+ lines: string[],
209
+ modelMeta: EditorMeta,
210
+ previousMeta?: EditorMeta,
211
+ ): string[] {
212
+ const result: string[] = [];
213
+ for (let index = 0; index < lines.length; index++) {
214
+ const line = lines[index] ?? "";
215
+ if (matchesAnyModelMeta(line, modelMeta, previousMeta)) continue;
216
+
217
+ const plain = plainRenderedText(line).trim();
218
+ const previousWasMeta =
219
+ index > 0 && matchesAnyModelMeta(lines[index - 1] ?? "", modelMeta, previousMeta);
220
+ const nextIsMeta =
221
+ index < lines.length - 1 &&
222
+ matchesAnyModelMeta(lines[index + 1] ?? "", modelMeta, previousMeta);
223
+ if (!plain && (previousWasMeta || nextIsMeta)) continue;
224
+
225
+ result.push(line);
226
+ }
227
+ return result;
228
+ }
229
+
230
+ function removeStalePolishedLeadingSpacer(lines: string[], shouldRemove: boolean): string[] {
231
+ if (!shouldRemove || lines.length === 0) return lines;
232
+ const firstLine = lines[0] ?? "";
233
+ if (plainRenderedText(firstLine).trim()) return lines;
234
+ return lines.slice(1);
235
+ }
236
+
237
+ function vimModeColor(mode: string): string {
238
+ switch (mode.toLowerCase()) {
239
+ case "insert":
240
+ return "success";
241
+ case "normal":
242
+ return "accent";
243
+ case "ex":
244
+ return "warning";
245
+ case "replace":
246
+ return "error";
247
+ case "visual":
248
+ return "syntaxKeyword";
249
+ default:
250
+ return "muted";
251
+ }
252
+ }
253
+
254
+ function readVimStatus(editor: WrappedEditor, uiTheme: Theme): string | undefined {
255
+ const mode = editor.getMode?.();
256
+ if (typeof mode !== "string") return undefined;
257
+ const normalized = mode.trim();
258
+ if (!normalized) return undefined;
259
+ const label = `${normalized.toUpperCase()} `;
260
+ return safeThemeFg(uiTheme, vimModeColor(normalized), label);
261
+ }
262
+
263
+ function renderPolishedFrame({
264
+ width,
265
+ baseRendered,
266
+ autocompleteSource,
267
+ uiTheme,
268
+ config,
269
+ modelMeta,
270
+ previousModelMeta,
271
+ thinkingLevel,
272
+ rightStatus,
273
+ }: PolishedFrameOptions): string[] {
274
+ if (width <= 2) return clampRenderedLines(baseRendered, width);
275
+
276
+ const reset = "\x1b[0m";
277
+ const colorSource = config.colorSources.editor;
278
+ const { prompt, promptWidth, rail, railWidth } = getEditorChromeWidths(config, uiTheme, reset);
279
+ const innerWidth = Math.max(0, width - railWidth);
280
+ const copyFriendlyContinuation = " ".repeat(promptWidth);
281
+ const isShowingAutocomplete =
282
+ typeof autocompleteSource.isShowingAutocomplete === "function"
283
+ ? Boolean(autocompleteSource.isShowingAutocomplete())
284
+ : false;
285
+
286
+ if (baseRendered.length < 2) return clampRenderedLines(baseRendered, width);
287
+
288
+ const { autocompleteList } = autocompleteSource;
289
+ const autocompleteCount =
290
+ isShowingAutocomplete && typeof autocompleteList?.render === "function"
291
+ ? autocompleteList.render(innerWidth).length
292
+ : 0;
293
+ const editorFrame =
294
+ autocompleteCount > 0 && autocompleteCount < baseRendered.length
295
+ ? baseRendered.slice(0, -autocompleteCount)
296
+ : baseRendered;
297
+ const autocompleteLines =
298
+ autocompleteCount > 0 && autocompleteCount < baseRendered.length
299
+ ? baseRendered.slice(-autocompleteCount)
300
+ : [];
301
+ if (editorFrame.length < 2) return clampRenderedLines(baseRendered, width);
302
+
303
+ const stalePolishedFrame = isAlreadyPolishedFrame(editorFrame, modelMeta, previousModelMeta);
304
+ const editorLines = removeStalePolishedLeadingSpacer(
305
+ removeRenderedModelMetaLines(editorFrame.slice(1, -1), modelMeta, previousModelMeta),
306
+ stalePolishedFrame,
307
+ );
308
+ const model = renderStyleForSourceOrFallback(
309
+ uiTheme,
310
+ colorSource,
311
+ config.colors.editorModel,
312
+ EDITOR_ACCENT_FALLBACK,
313
+ modelMeta.modelLabel,
314
+ );
315
+ const provider = renderStyleForSourceOrFallback(
316
+ uiTheme,
317
+ colorSource,
318
+ config.colors.editorProvider,
319
+ "text",
320
+ modelMeta.providerLabel,
321
+ );
322
+ const renderedModelMeta = [model, provider]
323
+ .filter(Boolean)
324
+ .join(safeThemeFg(uiTheme, "borderMuted", " "));
325
+ const context = renderStyleForSourceOrFallback(
326
+ uiTheme,
327
+ colorSource,
328
+ config.colors.contextNormal,
329
+ "muted",
330
+ modelMeta.contextLabel,
331
+ );
332
+ const tokens = renderStyleForSourceOrFallback(
333
+ uiTheme,
334
+ colorSource,
335
+ config.colors.tokens,
336
+ "muted",
337
+ modelMeta.tokenLabel,
338
+ );
339
+ const time = renderStyleForSourceOrFallback(
340
+ uiTheme,
341
+ colorSource,
342
+ config.colors.editorProvider,
343
+ "muted",
344
+ formatTimeLabel(config.icons.time),
345
+ );
346
+ const metaParts = [renderedModelMeta];
347
+ if (thinkingLevel && thinkingLevel !== "off") {
348
+ metaParts.push(
349
+ renderStyleForSourceOrFallback(
350
+ uiTheme,
351
+ colorSource,
352
+ editorThinkingStyle(config, thinkingLevel),
353
+ "muted",
354
+ thinkingLevel,
355
+ ),
356
+ );
357
+ }
358
+ const meta = metaParts.filter(Boolean).join(safeThemeFg(uiTheme, "border", " "));
359
+ const contextUsed = renderStyleForSourceOrFallback(
360
+ uiTheme,
361
+ colorSource,
362
+ config.colors.contextNormal,
363
+ "muted",
364
+ modelMeta.contextUsedLabel,
365
+ );
366
+ const topRightMeta = [context, contextUsed, tokens, time, rightStatus]
367
+ .filter(Boolean)
368
+ .join(safeThemeFg(uiTheme, "border", " "));
369
+ const copyFriendlyMeta = composeMetadataLine(meta, topRightMeta, Math.max(0, width - 1));
370
+ const railedMeta = composeMetadataLine(meta, topRightMeta, innerWidth);
371
+
372
+ const top = renderEditorFrameBorder("─".repeat(width), config, uiTheme, colorSource);
373
+ const bottom = renderEditorFrameBorder("─".repeat(width), config, uiTheme, colorSource);
374
+ const lines = [railedMeta, "", ...editorLines, ""];
375
+ const renderedLines = config.features.copyFriendly
376
+ ? [
377
+ top,
378
+ "",
379
+ ...editorLines.map(
380
+ (line, index) =>
381
+ `${index === 0 ? prompt : copyFriendlyContinuation}${fillLine(line, innerWidth)}`,
382
+ ),
383
+ "",
384
+ ` ${truncateToWidth(copyFriendlyMeta, Math.max(0, width - 1), "")}`,
385
+ bottom,
386
+ ...autocompleteLines,
387
+ ]
388
+ : [
389
+ top,
390
+ ...lines.map((line) => `${rail}${fillLine(line, innerWidth)}`),
391
+ bottom,
392
+ ...autocompleteLines,
393
+ ];
394
+
395
+ return clampRenderedLines(renderedLines, width);
396
+ }
397
+
398
+ export class PolishedEditor extends CustomEditor {
399
+ private readonly getModelMeta: () => EditorMeta;
400
+ private readonly getThinkingLevel: () => string | undefined;
401
+ private readonly getConfig: () => PolishedTuiConfig;
402
+ private readonly uiTheme: Theme;
403
+ private previousModelMeta?: EditorMeta;
404
+
405
+ constructor(
406
+ tui: TUI,
407
+ theme: EditorTheme,
408
+ keybindings: KeybindingsManager,
409
+ uiTheme: Theme,
410
+ getConfig: () => PolishedTuiConfig,
411
+ getModelMeta: () => EditorMeta,
412
+ getThinkingLevel: () => string | undefined,
413
+ ) {
414
+ super(tui, theme, keybindings, { paddingX: 0 });
415
+ this.borderColor = (text: string) => safeThemeFg(uiTheme, "border", text);
416
+ this.uiTheme = uiTheme;
417
+ this.getConfig = getConfig;
418
+ this.getModelMeta = getModelMeta;
419
+ this.getThinkingLevel = getThinkingLevel;
420
+ }
421
+
422
+ render(width: number): string[] {
423
+ if (width <= 2) {
424
+ return clampRenderedLines(super.render(width), width);
425
+ }
426
+
427
+ const config = this.getConfig();
428
+ const { railWidth } = getEditorChromeWidths(config, this.uiTheme, "\x1b[0m");
429
+ const innerWidth = Math.max(0, width - railWidth);
430
+ const rendered = super.render(innerWidth);
431
+ const modelMeta = this.getModelMeta();
432
+ const result = renderPolishedFrame({
433
+ width,
434
+ baseRendered: rendered,
435
+ autocompleteSource: this as unknown as AutocompleteEditorInternals,
436
+ uiTheme: this.uiTheme,
437
+ config,
438
+ modelMeta,
439
+ previousModelMeta: this.previousModelMeta,
440
+ thinkingLevel: this.getThinkingLevel(),
441
+ });
442
+ this.previousModelMeta = modelMeta;
443
+ return result;
444
+ }
445
+ }
446
+
447
+ export class WrappedPolishedEditor implements EditorComponent {
448
+ private previousModelMeta?: EditorMeta;
449
+
450
+ constructor(
451
+ private readonly base: WrappedEditor,
452
+ private readonly uiTheme: Theme,
453
+ private readonly getConfig: () => PolishedTuiConfig,
454
+ private readonly getModelMeta: () => EditorMeta,
455
+ private readonly getThinkingLevel: () => string | undefined,
456
+ ) {}
457
+
458
+ get focused(): boolean {
459
+ return Boolean(this.base.focused);
460
+ }
461
+ set focused(value: boolean) {
462
+ this.base.focused = value;
463
+ }
464
+
465
+ get borderColor(): ((str: string) => string) | undefined {
466
+ return this.base.borderColor;
467
+ }
468
+ set borderColor(value: ((str: string) => string) | undefined) {
469
+ this.base.borderColor = value;
470
+ }
471
+
472
+ get onSubmit(): ((text: string) => void) | undefined {
473
+ return this.base.onSubmit;
474
+ }
475
+ set onSubmit(value: ((text: string) => void) | undefined) {
476
+ this.base.onSubmit = value;
477
+ }
478
+
479
+ get onChange(): ((text: string) => void) | undefined {
480
+ return this.base.onChange;
481
+ }
482
+ set onChange(value: ((text: string) => void) | undefined) {
483
+ this.base.onChange = value;
484
+ }
485
+
486
+ get onEscape(): (() => void) | undefined {
487
+ return this.base.onEscape;
488
+ }
489
+ set onEscape(value: (() => void) | undefined) {
490
+ this.base.onEscape = value;
491
+ }
492
+
493
+ get onCtrlD(): (() => void) | undefined {
494
+ return this.base.onCtrlD;
495
+ }
496
+ set onCtrlD(value: (() => void) | undefined) {
497
+ this.base.onCtrlD = value;
498
+ }
499
+
500
+ get onPasteImage(): (() => void) | undefined {
501
+ return this.base.onPasteImage;
502
+ }
503
+ set onPasteImage(value: (() => void) | undefined) {
504
+ this.base.onPasteImage = value;
505
+ }
506
+
507
+ get onExtensionShortcut(): ((data: string) => boolean) | undefined {
508
+ return this.base.onExtensionShortcut;
509
+ }
510
+ set onExtensionShortcut(value: ((data: string) => boolean) | undefined) {
511
+ this.base.onExtensionShortcut = value;
512
+ }
513
+
514
+ get actionHandlers(): Map<unknown, () => void> | undefined {
515
+ return this.base.actionHandlers;
516
+ }
517
+ set actionHandlers(value: Map<unknown, () => void> | undefined) {
518
+ this.base.actionHandlers = value;
519
+ }
520
+
521
+ get wantsKeyRelease(): boolean | undefined {
522
+ return this.base.wantsKeyRelease;
523
+ }
524
+ set wantsKeyRelease(value: boolean | undefined) {
525
+ this.base.wantsKeyRelease = value;
526
+ }
527
+
528
+ get disableSubmit(): boolean | undefined {
529
+ return this.base.disableSubmit;
530
+ }
531
+ set disableSubmit(value: boolean | undefined) {
532
+ this.base.disableSubmit = value;
533
+ }
534
+
535
+ render(width: number): string[] {
536
+ if (width <= 2) return clampRenderedLines(this.base.render(width), width);
537
+
538
+ const config = this.getConfig();
539
+ const { railWidth } = getEditorChromeWidths(config, this.uiTheme, "\x1b[0m");
540
+ const innerWidth = Math.max(0, width - railWidth);
541
+ const rendered = this.base.render(innerWidth);
542
+ const vimStatus = readVimStatus(this.base, this.uiTheme);
543
+ const modelMeta = this.getModelMeta();
544
+ const result = renderPolishedFrame({
545
+ width,
546
+ baseRendered: rendered,
547
+ autocompleteSource: this.base,
548
+ uiTheme: this.uiTheme,
549
+ config,
550
+ modelMeta,
551
+ previousModelMeta: this.previousModelMeta,
552
+ thinkingLevel: this.getThinkingLevel(),
553
+ rightStatus: vimStatus,
554
+ });
555
+ this.previousModelMeta = modelMeta;
556
+ return result;
557
+ }
558
+
559
+ invalidate(): void {
560
+ this.base.invalidate?.();
561
+ }
562
+
563
+ handleInput(data: string): void {
564
+ this.base.handleInput(data);
565
+ }
566
+
567
+ getText(): string {
568
+ return this.base.getText();
569
+ }
570
+
571
+ setText(text: string): void {
572
+ this.base.setText(text);
573
+ }
574
+
575
+ addToHistory(text: string): void {
576
+ this.base.addToHistory?.(text);
577
+ }
578
+
579
+ insertTextAtCursor(text: string): void {
580
+ this.base.insertTextAtCursor?.(text);
581
+ }
582
+
583
+ getExpandedText(): string {
584
+ return this.base.getExpandedText?.() ?? this.base.getText();
585
+ }
586
+
587
+ setAutocompleteProvider(provider: AutocompleteProvider): void {
588
+ this.base.setAutocompleteProvider?.(provider);
589
+ }
590
+
591
+ setPaddingX(padding: number): void {
592
+ this.base.setPaddingX?.(padding);
593
+ }
594
+
595
+ setAutocompleteMaxVisible(maxVisible: number): void {
596
+ this.base.setAutocompleteMaxVisible?.(maxVisible);
597
+ }
598
+
599
+ getLines(): string[] {
600
+ return this.base.getLines?.() ?? this.base.getText().split("\n");
601
+ }
602
+
603
+ getCursor(): unknown {
604
+ return this.base.getCursor?.();
605
+ }
606
+
607
+ getMode(): unknown {
608
+ return this.base.getMode?.();
609
+ }
610
+
611
+ getPaddingX(): number | undefined {
612
+ return this.base.getPaddingX?.();
613
+ }
614
+
615
+ getAutocompleteMaxVisible(): number | undefined {
616
+ return this.base.getAutocompleteMaxVisible?.();
617
+ }
618
+ }