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.
@@ -1,530 +0,0 @@
1
- import { readFileSync, realpathSync, statSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import { dirname, join, parse } from "node:path";
4
- import { pathToFileURL } from "node:url";
5
-
6
- export const ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR = Symbol.for(
7
- "pi-zentui.accent-rail-layout-editor",
8
- );
9
- const ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY = Symbol.for(
10
- "pi-zentui.accent-rail-layout-registry",
11
- );
12
- const LAYOUT_NODE = Symbol.for("@earendil-works/pi-tui/layout-node");
13
-
14
- const SUPPORTED_PI_TUI_VERSION = /^0\.84\.\d+$/;
15
-
16
- type AccentRailEditorMarker = {
17
- owner: symbol;
18
- active: () => boolean;
19
- };
20
-
21
- type MarkedEditor = {
22
- [ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR]?: AccentRailEditorMarker;
23
- };
24
-
25
- type StackEntry = {
26
- component?: unknown;
27
- minSize?: number;
28
- shrink?: number;
29
- [key: PropertyKey]: unknown;
30
- };
31
-
32
- type LayoutNode = {
33
- type?: unknown;
34
- entries?: unknown;
35
- [key: PropertyKey]: unknown;
36
- };
37
-
38
- type LayoutMethod = (this: unknown, ...args: unknown[]) => unknown;
39
-
40
- type LayoutPatchRegistry = {
41
- method: symbol;
42
- predecessor: LayoutMethod;
43
- predecessorDescriptor?: PropertyDescriptor;
44
- wrapper: LayoutMethod;
45
- owners: Map<symbol, number>;
46
- active: boolean;
47
- };
48
-
49
- type PatchPrototype = Record<PropertyKey, unknown>;
50
-
51
- type LayoutComponent = {
52
- render: (...args: unknown[]) => unknown;
53
- invalidate?: (...args: unknown[]) => unknown;
54
- };
55
-
56
- const forwardingComponents = new WeakMap<object, LayoutComponent>();
57
-
58
- function forwardingLayoutComponent(
59
- component: object,
60
- ): LayoutComponent | undefined {
61
- const cached = forwardingComponents.get(component);
62
- if (cached) return cached;
63
- const render = (component as Partial<LayoutComponent>).render;
64
- if (typeof render !== "function") return undefined;
65
- try {
66
- const wrapper: LayoutComponent = {
67
- render: (...args: unknown[]) => {
68
- const rows = Reflect.apply(render, component, args);
69
- return Array.isArray(rows) && rows.length === 1 ? ["", rows[0]] : rows;
70
- },
71
- invalidate: (...args: unknown[]) => {
72
- const invalidate = (component as Partial<LayoutComponent>).invalidate;
73
- return typeof invalidate === "function"
74
- ? Reflect.apply(invalidate, component, args)
75
- : undefined;
76
- },
77
- };
78
- forwardingComponents.set(component, wrapper);
79
- return wrapper;
80
- } catch {
81
- return undefined;
82
- }
83
- }
84
-
85
- export type AccentRailLayoutPatchTarget = {
86
- prototype: object;
87
- version: string;
88
- canonicalEntrypoint?: string;
89
- resolvedModulePath?: string;
90
- localModulePath?: string;
91
- };
92
-
93
- export type AccentRailLayoutPatchDiagnostic =
94
- | "installed"
95
- | "reused"
96
- | "displaced"
97
- | "unsupported-version"
98
- | "unsupported-shape"
99
- | "host-module-unavailable";
100
-
101
- export type AccentRailLayoutPatchInstallation = {
102
- cleanup: () => void;
103
- diagnostic: AccentRailLayoutPatchDiagnostic;
104
- version?: string;
105
- };
106
-
107
- export type AccentRailLayoutPatchRetention = "retained" | "stale" | "failed";
108
-
109
- export async function retainAccentRailLayoutPatchInstallation(
110
- install: () => Promise<AccentRailLayoutPatchInstallation>,
111
- isCurrent: () => boolean,
112
- retain: (installation: AccentRailLayoutPatchInstallation) => void,
113
- ): Promise<AccentRailLayoutPatchRetention> {
114
- let installation: AccentRailLayoutPatchInstallation;
115
- try {
116
- installation = await install();
117
- } catch {
118
- return "failed";
119
- }
120
- if (!isCurrent()) {
121
- installation.cleanup();
122
- return "stale";
123
- }
124
- try {
125
- retain(installation);
126
- } catch {
127
- installation.cleanup();
128
- return "failed";
129
- }
130
- return "retained";
131
- }
132
-
133
- function ownMarker(value: unknown): AccentRailEditorMarker | undefined {
134
- if (!value || typeof value !== "object") return undefined;
135
- if (!Object.hasOwn(value, ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR)) {
136
- return undefined;
137
- }
138
- const marker = (value as MarkedEditor)[ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR];
139
- return marker &&
140
- typeof marker.owner === "symbol" &&
141
- typeof marker.active === "function"
142
- ? marker
143
- : undefined;
144
- }
145
-
146
- function entryMarker(entry: StackEntry): AccentRailEditorMarker | undefined {
147
- const component = entry.component;
148
- if (!component || typeof component !== "object") return undefined;
149
- if (!Object.hasOwn(component, "children")) return undefined;
150
- const children = (component as { children?: unknown }).children;
151
- if (!Array.isArray(children) || children.length !== 1) return undefined;
152
- return ownMarker(children[0]);
153
- }
154
-
155
- export function adjustAccentRailLayoutNode(
156
- node: unknown,
157
- activeOwners: ReadonlySet<symbol>,
158
- ): unknown {
159
- if (!node || typeof node !== "object") return node;
160
- const candidate = node as LayoutNode;
161
- if (candidate.type !== "vstack" || !Array.isArray(candidate.entries))
162
- return node;
163
-
164
- const entries = candidate.entries as StackEntry[];
165
- const matches: number[] = [];
166
- for (let index = 0; index < entries.length; index += 1) {
167
- const entry = entries[index];
168
- if (entry?.minSize !== 3 || entry.shrink !== 1) continue;
169
- const marker = entryMarker(entry);
170
- if (!marker || !activeOwners.has(marker.owner)) continue;
171
- let active = false;
172
- try {
173
- active = marker.active() === true;
174
- } catch {
175
- return node;
176
- }
177
- if (active) matches.push(index);
178
- }
179
- if (matches.length !== 1) return node;
180
-
181
- const match = matches[0];
182
- const matchedEntry = entries[match];
183
- if (!matchedEntry?.component || typeof matchedEntry.component !== "object")
184
- return node;
185
- const component = forwardingLayoutComponent(matchedEntry.component);
186
- if (!component) return node;
187
- const nextEntries = entries.map((entry, index) =>
188
- index === match ? { ...entry, component } : entry,
189
- );
190
- return { ...candidate, entries: nextEntries };
191
- }
192
-
193
- export function markAccentRailLayoutEditor(
194
- editor: object,
195
- owner: symbol,
196
- active: () => boolean,
197
- ): boolean {
198
- try {
199
- Object.defineProperty(editor, ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR, {
200
- value: { owner, active } satisfies AccentRailEditorMarker,
201
- configurable: true,
202
- enumerable: false,
203
- writable: false,
204
- });
205
- return true;
206
- } catch {
207
- return false;
208
- }
209
- }
210
-
211
- function registryOn(
212
- prototype: PatchPrototype,
213
- ): LayoutPatchRegistry | undefined {
214
- const value = prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
215
- if (!value || typeof value !== "object") return undefined;
216
- const registry = value as Partial<LayoutPatchRegistry>;
217
- return registry.owners instanceof Map &&
218
- typeof registry.wrapper === "function"
219
- ? (value as LayoutPatchRegistry)
220
- : undefined;
221
- }
222
-
223
- function discoverLayoutMethod(prototype: PatchPrototype): symbol | undefined {
224
- const symbols = new Set<PropertyKey>([
225
- LAYOUT_NODE,
226
- ...Object.getOwnPropertySymbols(prototype),
227
- ]);
228
- for (const key of symbols) {
229
- const method = prototype[key];
230
- if (typeof method !== "function") continue;
231
- try {
232
- const receiver = Object.create(prototype) as Record<PropertyKey, unknown>;
233
- receiver.entries = [];
234
- receiver.layoutType = "vstack";
235
- receiver.gap = 0;
236
- receiver.align = "stretch";
237
- const node = Reflect.apply(method as LayoutMethod, receiver, []);
238
- if (
239
- node &&
240
- typeof node === "object" &&
241
- (node as LayoutNode).type === "vstack" &&
242
- Array.isArray((node as LayoutNode).entries)
243
- ) {
244
- return key as symbol;
245
- }
246
- } catch {
247
- // Probe candidates fail open.
248
- }
249
- }
250
- return undefined;
251
- }
252
-
253
- function restorePredecessor(
254
- prototype: PatchPrototype,
255
- registry: LayoutPatchRegistry,
256
- ): void {
257
- if (prototype[registry.method] !== registry.wrapper) return;
258
- if (registry.predecessorDescriptor) {
259
- Object.defineProperty(
260
- prototype,
261
- registry.method,
262
- registry.predecessorDescriptor,
263
- );
264
- } else {
265
- delete prototype[registry.method];
266
- }
267
- }
268
-
269
- function cleanupOwner(
270
- prototype: PatchPrototype,
271
- registry: LayoutPatchRegistry,
272
- owner: symbol,
273
- ): () => void {
274
- let cleaned = false;
275
- return () => {
276
- if (cleaned) return;
277
- cleaned = true;
278
- const count = registry.owners.get(owner) ?? 0;
279
- if (count <= 1) registry.owners.delete(owner);
280
- else registry.owners.set(owner, count - 1);
281
- if (registry.owners.size > 0) return;
282
-
283
- // Deactivate before restoration. If a successor patch captured and later
284
- // restores our wrapper, the ownerless wrapper remains an inert pass-through.
285
- registry.active = false;
286
- if (registryOn(prototype) !== registry) return;
287
- restorePredecessor(prototype, registry);
288
- if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === registry) {
289
- delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
290
- }
291
- };
292
- }
293
-
294
- export function installAccentRailLayoutPatchOnTarget(
295
- target: AccentRailLayoutPatchTarget,
296
- owner: symbol,
297
- ): AccentRailLayoutPatchInstallation {
298
- if (!SUPPORTED_PI_TUI_VERSION.test(target.version)) {
299
- return {
300
- cleanup: () => {},
301
- diagnostic: "unsupported-version",
302
- version: target.version,
303
- };
304
- }
305
- const prototype = target.prototype as PatchPrototype;
306
- const existing = registryOn(prototype);
307
- if (existing) {
308
- if (existing.active && prototype[existing.method] === existing.wrapper) {
309
- existing.owners.set(owner, (existing.owners.get(owner) ?? 0) + 1);
310
- return {
311
- cleanup: cleanupOwner(prototype, existing, owner),
312
- diagnostic: "reused",
313
- version: target.version,
314
- };
315
- }
316
-
317
- // A later extension displaced the method. Never wrap that successor and
318
- // never leave our old wrapper active if the successor restores it later.
319
- existing.active = false;
320
- if (prototype[existing.method] !== existing.wrapper) {
321
- if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === existing) {
322
- delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
323
- }
324
- return {
325
- cleanup: () => {},
326
- diagnostic: "displaced",
327
- version: target.version,
328
- };
329
- }
330
-
331
- // An inactive wrapper restored by a former successor can be safely reduced
332
- // to its original predecessor before a fresh installation.
333
- restorePredecessor(prototype, existing);
334
- if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === existing) {
335
- delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
336
- }
337
- }
338
-
339
- const method = discoverLayoutMethod(prototype);
340
- if (!method) {
341
- return {
342
- cleanup: () => {},
343
- diagnostic: "unsupported-shape",
344
- version: target.version,
345
- };
346
- }
347
- const predecessorDescriptor = Object.getOwnPropertyDescriptor(
348
- prototype,
349
- method,
350
- );
351
- const predecessor = prototype[method];
352
- if (
353
- typeof predecessor !== "function" ||
354
- predecessorDescriptor?.configurable === false
355
- ) {
356
- return {
357
- cleanup: () => {},
358
- diagnostic: "unsupported-shape",
359
- version: target.version,
360
- };
361
- }
362
-
363
- const registry: LayoutPatchRegistry = {
364
- method,
365
- predecessor: predecessor as LayoutMethod,
366
- predecessorDescriptor,
367
- wrapper: () => undefined,
368
- owners: new Map([[owner, 1]]),
369
- active: true,
370
- };
371
- registry.wrapper = function accentRailLayoutNodePatch(
372
- this: unknown,
373
- ...args: unknown[]
374
- ) {
375
- const node = Reflect.apply(registry.predecessor, this, args);
376
- if (!registry.active || registry.owners.size === 0) return node;
377
- try {
378
- return adjustAccentRailLayoutNode(node, new Set(registry.owners.keys()));
379
- } catch {
380
- return node;
381
- }
382
- };
383
- try {
384
- Object.defineProperty(prototype, method, {
385
- ...(predecessorDescriptor ?? {
386
- configurable: true,
387
- enumerable: false,
388
- writable: true,
389
- }),
390
- value: registry.wrapper,
391
- });
392
- Object.defineProperty(prototype, ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY, {
393
- value: registry,
394
- configurable: true,
395
- enumerable: false,
396
- });
397
- } catch {
398
- registry.active = false;
399
- restorePredecessor(prototype, registry);
400
- return {
401
- cleanup: () => {},
402
- diagnostic: "unsupported-shape",
403
- version: target.version,
404
- };
405
- }
406
- return {
407
- cleanup: cleanupOwner(prototype, registry, owner),
408
- diagnostic: "installed",
409
- version: target.version,
410
- };
411
- }
412
-
413
- type PackageLocation = {
414
- root: string;
415
- version: string;
416
- };
417
-
418
- function packageLocationFromEntry(
419
- entry: string,
420
- expectedName: string,
421
- ): PackageLocation | undefined {
422
- const root = parse(entry).root;
423
- let current = dirname(entry);
424
- while (current !== root) {
425
- try {
426
- const packageJson = JSON.parse(
427
- readFileSync(join(current, "package.json"), "utf8"),
428
- ) as {
429
- name?: unknown;
430
- version?: unknown;
431
- };
432
- if (
433
- packageJson.name === expectedName &&
434
- typeof packageJson.version === "string"
435
- ) {
436
- return { root: current, version: packageJson.version };
437
- }
438
- } catch {
439
- // Continue walking to the package root.
440
- }
441
- current = dirname(current);
442
- }
443
- return undefined;
444
- }
445
-
446
- function canonicalFile(path: string): string | undefined {
447
- try {
448
- const canonical = realpathSync(path);
449
- return statSync(canonical).isFile() ? canonical : undefined;
450
- } catch {
451
- return undefined;
452
- }
453
- }
454
-
455
- function localPiTuiPath(): string | undefined {
456
- try {
457
- return canonicalFile(
458
- createRequire(import.meta.url).resolve("@earendil-works/pi-tui"),
459
- );
460
- } catch {
461
- return undefined;
462
- }
463
- }
464
-
465
- export async function discoverAccentRailLayoutPatchTargetFromEntrypoint(
466
- entrypoint: string,
467
- ): Promise<AccentRailLayoutPatchTarget | undefined> {
468
- try {
469
- const canonicalEntrypoint = canonicalFile(entrypoint);
470
- if (!canonicalEntrypoint) return undefined;
471
- if (
472
- !packageLocationFromEntry(
473
- canonicalEntrypoint,
474
- "@earendil-works/pi-coding-agent",
475
- )
476
- ) {
477
- return undefined;
478
- }
479
-
480
- const hostRequire = createRequire(canonicalEntrypoint);
481
- const unresolved = hostRequire.resolve("@earendil-works/pi-tui");
482
- const resolvedModulePath = canonicalFile(unresolved);
483
- if (!resolvedModulePath) return undefined;
484
- const packageLocation = packageLocationFromEntry(
485
- resolvedModulePath,
486
- "@earendil-works/pi-tui",
487
- );
488
- if (
489
- !packageLocation ||
490
- !SUPPORTED_PI_TUI_VERSION.test(packageLocation.version)
491
- ) {
492
- return undefined;
493
- }
494
-
495
- const module = (await import(pathToFileURL(resolvedModulePath).href)) as {
496
- VStack?: { prototype?: object };
497
- };
498
- const prototype = module.VStack?.prototype;
499
- if (!prototype || !discoverLayoutMethod(prototype as PatchPrototype))
500
- return undefined;
501
-
502
- return {
503
- prototype,
504
- version: packageLocation.version,
505
- canonicalEntrypoint,
506
- resolvedModulePath,
507
- localModulePath: localPiTuiPath(),
508
- };
509
- } catch {
510
- return undefined;
511
- }
512
- }
513
-
514
- export async function discoverHostAccentRailLayoutPatchTarget(): Promise<
515
- AccentRailLayoutPatchTarget | undefined
516
- > {
517
- const entrypoint = process.argv[1];
518
- return entrypoint
519
- ? discoverAccentRailLayoutPatchTargetFromEntrypoint(entrypoint)
520
- : undefined;
521
- }
522
-
523
- export async function installHostAccentRailLayoutPatch(
524
- owner: symbol,
525
- ): Promise<AccentRailLayoutPatchInstallation> {
526
- const target = await discoverHostAccentRailLayoutPatchTarget();
527
- return target
528
- ? installAccentRailLayoutPatchOnTarget(target, owner)
529
- : { cleanup: () => {}, diagnostic: "host-module-unavailable" };
530
- }
@@ -1,183 +0,0 @@
1
- import type { Theme } from "@earendil-works/pi-coding-agent";
2
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
-
4
- export type CompletionRowsOptions = {
5
- lines: string[];
6
- width: number;
7
- theme: Theme;
8
- selectedPrefix: string;
9
- ownedBackground: boolean;
10
- };
11
-
12
- export type CompletionPaletteOptions = {
13
- lines: string[];
14
- width: number;
15
- theme: Theme;
16
- renderSeparator: (text: string) => string;
17
- ownedBackground: boolean;
18
- };
19
-
20
- const SGR_SEQUENCE = /(?:\x1b\[|\u009b)([0-9:;]*)m/g;
21
-
22
- function normalizeC1Csi(content: string): string {
23
- return content.replace(/\u009b([0-?]*[ -/]*[@-~])/g, "\x1b[$1");
24
- }
25
-
26
- function sgrBackgroundAction(parameters: string): "reset" | "set" | undefined {
27
- let action: "reset" | "set" | undefined;
28
- const values = (parameters || "0").split(";");
29
- for (let index = 0; index < values.length; index++) {
30
- const parameter = values[index] ?? "";
31
- const primary =
32
- parameter === ""
33
- ? 0
34
- : Number.parseInt(parameter.split(":", 1)[0] ?? "", 10);
35
- if (primary === 38 || primary === 48 || primary === 58) {
36
- if (primary === 48) action = "set";
37
- if (!parameter.includes(":")) {
38
- const mode = Number.parseInt(values[index + 1] ?? "", 10);
39
- if (mode === 5) index += 2;
40
- else if (mode === 2) index += 4;
41
- }
42
- continue;
43
- }
44
- if (primary === 0 || primary === 49) action = "reset";
45
- else if (
46
- (primary >= 40 && primary <= 47) ||
47
- (primary >= 100 && primary <= 107)
48
- )
49
- action = "set";
50
- }
51
- return action;
52
- }
53
-
54
- function restoresOwnedBackground(sequence: string): boolean {
55
- const parameters = sequence.slice(sequence.startsWith("\u009b") ? 1 : 2, -1);
56
- return sgrBackgroundAction(parameters) === "reset";
57
- }
58
-
59
- function truncateTerminalLine(content: string, width: number): string {
60
- const safeWidth = Math.max(0, width);
61
- const normalized = normalizeC1Csi(content);
62
- return visibleWidth(normalized) <= safeWidth
63
- ? content
64
- : truncateToWidth(normalized, safeWidth, "");
65
- }
66
-
67
- export function fillTerminalLine(content: string, width: number): string {
68
- const truncated = truncateTerminalLine(content, width);
69
- const renderedWidth = visibleWidth(normalizeC1Csi(truncated));
70
- return `${truncated}${" ".repeat(Math.max(0, width - renderedWidth))}`;
71
- }
72
-
73
- export function applyOwnedLayoutBackground(theme: Theme, text: string): string {
74
- try {
75
- const background = theme.getBgAnsi("userMessageBg");
76
- const restored = text.replace(SGR_SEQUENCE, (sequence) =>
77
- restoresOwnedBackground(sequence) ? `${sequence}${background}` : sequence,
78
- );
79
- return `${background}${restored}\x1b[49m`;
80
- } catch {
81
- try {
82
- let cursor = 0;
83
- let rendered = "";
84
- for (const match of text.matchAll(SGR_SEQUENCE)) {
85
- if (!restoresOwnedBackground(match[0])) continue;
86
- const before = text.slice(cursor, match.index);
87
- if (before) rendered += theme.bg("userMessageBg", before);
88
- rendered += match[0];
89
- cursor = (match.index ?? 0) + match[0].length;
90
- }
91
- const trailing = text.slice(cursor);
92
- if (trailing) rendered += theme.bg("userMessageBg", trailing);
93
- return rendered || theme.bg("userMessageBg", text);
94
- } catch {
95
- return text;
96
- }
97
- }
98
- }
99
-
100
- export function replaceNativeSelectedPrefix(
101
- line: string,
102
- replacement: string,
103
- ): string {
104
- const selectedPrefix = /^((?:(?:\x1b\[|\u009b)[0-9:;]*m)*)→ /;
105
- const match = line.match(selectedPrefix);
106
- if (!match) return line;
107
- return `${replacement}${match[1]}${line.slice(match[0].length)}`;
108
- }
109
-
110
- export function isNativeCompletionCountRow(line: string): boolean {
111
- const withoutSgr = line.replace(/(?:\x1b\[|\u009b)[0-?]*[ -/]*m/g, "");
112
- return /^\s*\(\d+\/\d+\)\s*$/.test(withoutSgr);
113
- }
114
-
115
- export function omitTrailingNativeCompletionCountRow(
116
- lines: string[],
117
- ): string[] {
118
- return lines.length > 0 && isNativeCompletionCountRow(lines.at(-1) ?? "")
119
- ? lines.slice(0, -1)
120
- : lines;
121
- }
122
-
123
- export function renderCompletionRows({
124
- lines,
125
- width,
126
- theme,
127
- selectedPrefix,
128
- ownedBackground,
129
- }: CompletionRowsOptions): string[] {
130
- return lines.map((line) => {
131
- const content = fillTerminalLine(
132
- replaceNativeSelectedPrefix(line, selectedPrefix),
133
- width,
134
- );
135
- const surfaced = ownedBackground
136
- ? applyOwnedLayoutBackground(theme, content)
137
- : content;
138
- return truncateTerminalLine(surfaced, width);
139
- });
140
- }
141
-
142
- function helpLabel(width: number): string {
143
- const labels = [
144
- " ↑↓ Navigate Enter Use Esc Close",
145
- " ↑↓ Nav Enter Use Esc",
146
- " ↑↓ Enter Esc",
147
- ];
148
- return (
149
- labels.find((label) => visibleWidth(label) <= width) ?? labels.at(-1) ?? ""
150
- );
151
- }
152
-
153
- /** Full-width, provenance-preserving completion shell for Opencode editor styles. */
154
- export function renderCompletionPalette({
155
- lines,
156
- width,
157
- theme,
158
- renderSeparator,
159
- ownedBackground,
160
- }: CompletionPaletteOptions): string[] {
161
- const resultLines = omitTrailingNativeCompletionCountRow(lines);
162
- if (resultLines.length === 0) return [];
163
- const safeWidth = Math.max(0, width);
164
- const rows = renderCompletionRows({
165
- lines: resultLines,
166
- width: safeWidth,
167
- theme,
168
- selectedPrefix: " ",
169
- ownedBackground,
170
- });
171
- const separator = renderSeparator("─".repeat(safeWidth));
172
- if (safeWidth < 3)
173
- return [...rows, truncateToWidth(separator, safeWidth, "")];
174
- const helpText = fillTerminalLine(helpLabel(safeWidth), safeWidth);
175
- const help = ownedBackground
176
- ? applyOwnedLayoutBackground(theme, helpText)
177
- : helpText;
178
- return [
179
- ...rows,
180
- truncateToWidth(separator, safeWidth, ""),
181
- truncateToWidth(help, safeWidth, ""),
182
- ];
183
- }