From b212314f45ba8d1e46e9f7bfbaec5f46f5c313d5 Mon Sep 17 00:00:00 2001 From: Colin Mason Date: Mon, 26 Jan 2026 20:02:24 -0500 Subject: [PATCH] feat: add autocompleteMaxVisible setting for configurable dropdown height --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/docs/settings.md | 1 + .../coding-agent/src/core/settings-manager.ts | 10 +++++++++ .../components/settings-selector.ts | 15 +++++++++++++ .../src/modes/interactive/interactive-mode.ts | 14 +++++++++++- packages/tui/CHANGELOG.md | 1 + packages/tui/src/components/editor.ts | 22 ++++++++++++++++--- packages/tui/src/editor-component.ts | 3 +++ 8 files changed, 63 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 4e27979e..334df74c 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -9,6 +9,7 @@ - Fixed custom header not displaying correctly with `quietStartup` enabled ([#1039](https://github.com/badlogic/pi-mono/pull/1039) by [@tudoroancea](https://github.com/tudoroancea)) ### Added +- Added `autocompleteMaxVisible` setting for configurable autocomplete dropdown height (3-20 items, default 5) ([#972](https://github.com/badlogic/pi-mono/pull/972) by [@masonc15](https://github.com/masonc15)) - Added shell-style keybindings: `alt+b`/`alt+f` for word navigation, `ctrl+d` for delete character forward (when editor has text) ([#1043](https://github.com/badlogic/pi-mono/issues/1043) by [@jasonish](https://github.com/jasonish)) ### Fixed diff --git a/packages/coding-agent/docs/settings.md b/packages/coding-agent/docs/settings.md index 7798cc7f..1690dec6 100644 --- a/packages/coding-agent/docs/settings.md +++ b/packages/coding-agent/docs/settings.md @@ -43,6 +43,7 @@ Edit directly or use `/settings` for common options. | `collapseChangelog` | boolean | `false` | Show condensed changelog after updates | | `doubleEscapeAction` | string | `"tree"` | Action for double-escape: `"tree"` or `"fork"` | | `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) | +| `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) | | `showHardwareCursor` | boolean | `false` | Show terminal cursor | ### Compaction diff --git a/packages/coding-agent/src/core/settings-manager.ts b/packages/coding-agent/src/core/settings-manager.ts index d68533a0..1d13dbe4 100644 --- a/packages/coding-agent/src/core/settings-manager.ts +++ b/packages/coding-agent/src/core/settings-manager.ts @@ -81,6 +81,7 @@ export interface Settings { doubleEscapeAction?: "fork" | "tree"; // Action for double-escape with empty editor (default: "tree") thinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels editorPaddingX?: number; // Horizontal padding for input editor (default: 0) + autocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5) showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME markdown?: MarkdownSettings; } @@ -673,6 +674,15 @@ export class SettingsManager { this.save(); } + getAutocompleteMaxVisible(): number { + return this.settings.autocompleteMaxVisible ?? 5; + } + + setAutocompleteMaxVisible(maxVisible: number): void { + this.globalSettings.autocompleteMaxVisible = Math.max(3, Math.min(20, Math.floor(maxVisible))); + this.save(); + } + getCodeBlockIndent(): string { return this.settings.markdown?.codeBlockIndent ?? " "; } diff --git a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts index 4b67e4a2..a79a7d48 100644 --- a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts @@ -38,6 +38,7 @@ export interface SettingsConfig { doubleEscapeAction: "fork" | "tree"; showHardwareCursor: boolean; editorPaddingX: number; + autocompleteMaxVisible: number; quietStartup: boolean; } @@ -57,6 +58,7 @@ export interface SettingsCallbacks { onDoubleEscapeActionChange: (action: "fork" | "tree") => void; onShowHardwareCursorChange: (enabled: boolean) => void; onEditorPaddingXChange: (padding: number) => void; + onAutocompleteMaxVisibleChange: (maxVisible: number) => void; onQuietStartupChange: (enabled: boolean) => void; onCancel: () => void; } @@ -300,6 +302,16 @@ export class SettingsSelectorComponent extends Container { values: ["0", "1", "2", "3"], }); + // Autocomplete max visible toggle (insert after editor-padding) + const editorPaddingIndex = items.findIndex((item) => item.id === "editor-padding"); + items.splice(editorPaddingIndex + 1, 0, { + id: "autocomplete-max-visible", + label: "Autocomplete max items", + description: "Max visible items in autocomplete dropdown (3-20)", + currentValue: String(config.autocompleteMaxVisible), + values: ["3", "5", "7", "10", "15", "20"], + }); + // Add borders this.addChild(new DynamicBorder()); @@ -348,6 +360,9 @@ export class SettingsSelectorComponent extends Container { case "editor-padding": callbacks.onEditorPaddingXChange(parseInt(newValue, 10)); break; + case "autocomplete-max-visible": + callbacks.onAutocompleteMaxVisibleChange(parseInt(newValue, 10)); + break; } }, callbacks.onCancel, diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 227c8036..f8a5f885 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -265,7 +265,11 @@ export class InteractiveMode { this.widgetContainerBelow = new Container(); this.keybindings = KeybindingsManager.create(); const editorPaddingX = this.settingsManager.getEditorPaddingX(); - this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, { paddingX: editorPaddingX }); + const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible(); + this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, { + paddingX: editorPaddingX, + autocompleteMaxVisible, + }); this.editor = this.defaultEditor; this.editorContainer = new Container(); this.editorContainer.addChild(this.editor as Component); @@ -2978,6 +2982,7 @@ export class InteractiveMode { doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(), showHardwareCursor: this.settingsManager.getShowHardwareCursor(), editorPaddingX: this.settingsManager.getEditorPaddingX(), + autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(), quietStartup: this.settingsManager.getQuietStartup(), }, { @@ -3060,6 +3065,13 @@ export class InteractiveMode { this.editor.setPaddingX(padding); } }, + onAutocompleteMaxVisibleChange: (maxVisible) => { + this.settingsManager.setAutocompleteMaxVisible(maxVisible); + this.defaultEditor.setAutocompleteMaxVisible(maxVisible); + if (this.editor !== this.defaultEditor && this.editor.setAutocompleteMaxVisible !== undefined) { + this.editor.setAutocompleteMaxVisible(maxVisible); + } + }, onCancel: () => { done(); this.ui.requestRender(); diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index 09133237..34008fca 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added +- Added `autocompleteMaxVisible` option to `EditorOptions` with getter/setter methods for configurable autocomplete dropdown height ([#972](https://github.com/badlogic/pi-mono/pull/972) by [@masonc15](https://github.com/masonc15)) - Added `alt+b` and `alt+f` as alternative keybindings for word navigation (`cursorWordLeft`, `cursorWordRight`) and `ctrl+d` for `deleteCharForward` ([#1043](https://github.com/badlogic/pi-mono/issues/1043) by [@jasonish](https://github.com/jasonish)) ## [0.50.1] - 2026-01-26 diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 8d61b12e..60f0a229 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -146,6 +146,7 @@ export interface EditorTheme { export interface EditorOptions { paddingX?: number; + autocompleteMaxVisible?: number; } export class Editor implements Component, Focusable { @@ -176,6 +177,7 @@ export class Editor implements Component, Focusable { private autocompleteList?: SelectList; private autocompleteState: "regular" | "force" | null = null; private autocompletePrefix: string = ""; + private autocompleteMaxVisible: number = 5; // Paste tracking for large pastes private pastes: Map = new Map(); @@ -207,6 +209,8 @@ export class Editor implements Component, Focusable { this.borderColor = theme.borderColor; const paddingX = options.paddingX ?? 0; this.paddingX = Number.isFinite(paddingX) ? Math.max(0, Math.floor(paddingX)) : 0; + const maxVisible = options.autocompleteMaxVisible ?? 5; + this.autocompleteMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5; } getPaddingX(): number { @@ -221,6 +225,18 @@ export class Editor implements Component, Focusable { } } + getAutocompleteMaxVisible(): number { + return this.autocompleteMaxVisible; + } + + setAutocompleteMaxVisible(maxVisible: number): void { + const newMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5; + if (this.autocompleteMaxVisible !== newMaxVisible) { + this.autocompleteMaxVisible = newMaxVisible; + this.tui.requestRender(); + } + } + setAutocompleteProvider(provider: AutocompleteProvider): void { this.autocompleteProvider = provider; } @@ -1734,7 +1750,7 @@ export class Editor implements Component, Focusable { if (suggestions && suggestions.items.length > 0) { this.autocompletePrefix = suggestions.prefix; - this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList); + this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList); this.autocompleteState = "regular"; } else { this.cancelAutocomplete(); @@ -1803,7 +1819,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/ } this.autocompletePrefix = suggestions.prefix; - this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList); + this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList); this.autocompleteState = "force"; } else { this.cancelAutocomplete(); @@ -1836,7 +1852,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/ if (suggestions && suggestions.items.length > 0) { this.autocompletePrefix = suggestions.prefix; // Always create new SelectList to ensure update - this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList); + this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList); } else { this.cancelAutocomplete(); } diff --git a/packages/tui/src/editor-component.ts b/packages/tui/src/editor-component.ts index 9116de09..87ec9581 100644 --- a/packages/tui/src/editor-component.ts +++ b/packages/tui/src/editor-component.ts @@ -65,4 +65,7 @@ export interface EditorComponent extends Component { /** Set horizontal padding */ setPaddingX?(padding: number): void; + + /** Set max visible items in autocomplete dropdown */ + setAutocompleteMaxVisible?(maxVisible: number): void; }