UNPKG

@earendil-works/pi-coding-agent

Version:

Coding agent CLI with read, bash, edit, write tools and session management

753 lines • 36.2 kB
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai"; import { Container, getCapabilities, SettingsList, Spacer, Text, } from "@earendil-works/pi-tui"; import { formatHttpIdleTimeoutMs, HTTP_IDLE_TIMEOUT_CHOICES } from "../../../core/http-dispatcher.js"; import { SETTINGS_DEFAULTS } from "../../../core/settings-defaults.js"; import { CACHE_WARMING_MODES, } from "../../../core/settings-manager.js"; import { getSettingsListTheme, parseAutoThemeSetting, SYSTEM_THEME_NAME, theme, } from "../theme/theme.js"; import { DynamicBorder } from "./dynamic-border.js"; import { keyDisplayText } from "./keybinding-hints.js"; import { SelectSubmenu, SteppedSubmenu } from "./settings-submenu.js"; const MODEL_PICKER_LAYOUT = { minPrimaryColumnWidth: 12, maxPrimaryColumnWidth: 46 }; const THINKING_DESCRIPTIONS = { off: "No reasoning", minimal: "Very brief reasoning (~1k tokens)", low: "Light reasoning (~2k tokens)", medium: "Moderate reasoning (~8k tokens)", high: "Deep reasoning (~16k tokens)", xhigh: "Extra-high reasoning (~32k tokens)", max: "Maximum reasoning", }; const DEFAULT_PROJECT_TRUST_LABELS = { ask: "Ask", always: "Always trust", never: "Never trust", }; const DEFAULT_PROJECT_TRUST_BY_LABEL = new Map(Object.entries(DEFAULT_PROJECT_TRUST_LABELS).map(([value, label]) => [label, value])); /** * A submenu component for selecting from a list of options. */ class WarningSettingsSubmenu extends Container { settingsList; state; constructor(warnings, onChange, onCancel) { super(); this.state = { ...warnings }; const items = [ { id: "anthropic-extra-usage", label: "Anthropic extra usage", description: "Warn when Anthropic subscription auth may use paid extra usage", currentValue: (this.state.anthropicExtraUsage ?? SETTINGS_DEFAULTS.warnings.anthropicExtraUsage) ? "true" : "false", values: ["true", "false"], }, ]; this.settingsList = new SettingsList(items, Math.min(items.length, 10), getSettingsListTheme(), (id, newValue) => { switch (id) { case "anthropic-extra-usage": this.state = { ...this.state, anthropicExtraUsage: newValue === "true" }; onChange({ ...this.state }); break; } }, onCancel); this.addChild(this.settingsList); } handleInput(data) { this.settingsList.handleInput(data); } } const CLEAR_OVERRIDE_VALUE = "__clear__"; function modelSettingKey(model) { return `${model.provider}/${model.id}`; } function modelDisplayLabel(model) { return `${model.id} [${model.provider}]`; } function modelThinkingOverridesSummary(overrides) { const count = Object.keys(overrides).length; if (count === 0) return "none"; return `${count} configured`; } function modelItemLabel(model) { return `${model.id} ${theme.fg("muted", `[${model.provider}]`)}`; } function themeItems(availableThemes, currentTheme) { return availableThemes.map((name) => ({ value: name, label: `${name === currentTheme ? "✓ " : " "}${name}`, ...(name === SYSTEM_THEME_NAME ? { description: "Theme created from your terminal's colors" } : {}), })); } const AUTOMATIC_THEME_VALUE = "/"; /** The system theme comes first, then automatic mode, then the remaining themes. */ function singleModeThemeItems(availableThemes, currentTheme) { const items = themeItems(availableThemes, currentTheme); const systemIndex = items.findIndex((item) => item.value === SYSTEM_THEME_NAME); const system = systemIndex === -1 ? [] : items.splice(systemIndex, 1); return [ ...system, { value: AUTOMATIC_THEME_VALUE, label: " automatic", description: "Use separate themes for light and dark terminal appearance", }, ...items, ]; } function preferredTheme(availableThemes, preferred, fallback) { if (preferred && availableThemes.includes(preferred)) return preferred; if (availableThemes.includes(fallback)) return fallback; return availableThemes[0] ?? fallback; } function defaultAutomaticThemes(currentThemeSetting, availableThemes) { const autoTheme = parseAutoThemeSetting(currentThemeSetting); if (autoTheme) return autoTheme; const currentFixedTheme = currentThemeSetting.includes("/") ? undefined : currentThemeSetting; const themeName = preferredTheme(availableThemes, currentFixedTheme, SYSTEM_THEME_NAME); return { lightTheme: themeName, darkTheme: themeName }; } class ThemeSubmenu extends Container { inputComponent; callbacks; availableThemes; terminalTheme; onDone; originalThemeSetting; mode; singleTheme; lightTheme; darkTheme; constructor(currentThemeSetting, terminalTheme, availableThemes, callbacks, onDone) { super(); this.callbacks = callbacks; this.availableThemes = availableThemes; this.terminalTheme = terminalTheme; this.onDone = onDone; this.originalThemeSetting = currentThemeSetting; const autoTheme = parseAutoThemeSetting(currentThemeSetting); const automaticThemes = defaultAutomaticThemes(currentThemeSetting, availableThemes); const fixedTheme = autoTheme || currentThemeSetting.includes("/") ? undefined : currentThemeSetting; this.mode = autoTheme ? "automatic" : "single"; this.lightTheme = automaticThemes.lightTheme; this.darkTheme = automaticThemes.darkTheme; this.singleTheme = preferredTheme(availableThemes, fixedTheme ?? (autoTheme ? this.getActiveAutomaticTheme() : undefined), SYSTEM_THEME_NAME); if (this.mode === "automatic") { this.showAutomaticMenu(); } else { this.showSingleMenu(); } } handleInput(data) { this.inputComponent?.handleInput?.(data); } setContent(renderComponent, inputComponent = renderComponent) { this.clear(); this.addChild(renderComponent); this.inputComponent = inputComponent; } showSingleMenu() { this.mode = "single"; const menu = new SelectSubmenu("Theme", "Select a theme, or choose automatic to follow terminal appearance.", singleModeThemeItems(this.availableThemes, this.singleTheme), this.singleTheme, (value) => { if (value === AUTOMATIC_THEME_VALUE) { this.mode = "automatic"; this.callbacks.onThemePreview?.(this.getThemeSetting()); this.showAutomaticMenu(); return; } this.singleTheme = value; this.apply(value); }, () => this.cancel(), (value) => { this.callbacks.onThemePreview?.(value === AUTOMATIC_THEME_VALUE ? this.getAutomaticThemeSetting() : value); }); this.setContent(menu); } showAutomaticMenu() { this.mode = "automatic"; const content = new Container(); content.addChild(new Text(theme.bold(theme.fg("accent", "Automatic Theme")), 0, 0)); content.addChild(new Spacer(1)); content.addChild(new Text(theme.fg("muted", "Choose themes for terminal light and dark appearance."), 0, 0)); content.addChild(new Text(theme.fg("muted", "Light/dark detection requires terminal support."), 0, 0)); content.addChild(new Spacer(1)); const items = [ { id: "light-theme", label: "Light theme", description: "Theme to use in automatic mode when the terminal is light", currentValue: this.lightTheme, submenu: (currentValue, done) => this.createThemeSelect("Light Theme", "Select the theme to use for light terminal appearance", currentValue, done, (value) => { this.lightTheme = value; this.callbacks.onThemePreview?.(this.getThemeSetting()); done(value); }), }, { id: "dark-theme", label: "Dark theme", description: "Theme to use in automatic mode when the terminal is dark", currentValue: this.darkTheme, submenu: (currentValue, done) => this.createThemeSelect("Dark Theme", "Select the theme to use for dark terminal appearance", currentValue, done, (value) => { this.darkTheme = value; this.callbacks.onThemePreview?.(this.getThemeSetting()); done(value); }), }, { id: "apply", label: "Apply", description: "Save and go back", currentValue: "save and go back", values: ["save and go back"], }, { id: "single-mode", label: "Change mode", description: "Switch to one theme for light and dark", currentValue: "switch to single theme", values: ["switch to single theme"], }, ]; const settingsList = new SettingsList(items, Math.min(items.length, 10), getSettingsListTheme(), (id) => { switch (id) { case "single-mode": this.mode = "single"; this.singleTheme = this.getActiveAutomaticTheme(); this.callbacks.onThemePreview?.(this.singleTheme); this.showSingleMenu(); break; case "apply": this.apply(this.getAutomaticThemeSetting()); break; } }, () => this.cancel()); content.addChild(settingsList); this.setContent(content, settingsList); } createThemeSelect(title, description, currentValue, done, onSelect) { return new SelectSubmenu(title, description, themeItems(this.availableThemes, currentValue), currentValue, onSelect, () => { this.callbacks.onThemePreview?.(this.getThemeSetting()); done(); }, (value) => this.callbacks.onThemePreview?.(value)); } getThemeSetting() { return this.mode === "automatic" ? this.getAutomaticThemeSetting() : this.singleTheme; } getActiveAutomaticTheme() { return this.terminalTheme === "light" ? this.lightTheme : this.darkTheme; } getAutomaticThemeSetting() { return `${this.lightTheme}/${this.darkTheme}`; } apply(themeSetting) { this.onDone(themeSetting); } cancel() { this.callbacks.onThemePreview?.(this.originalThemeSetting); this.onDone(); } } /** * Main settings selector component. */ export class SettingsSelectorComponent extends Container { settingsList; constructor(config, callbacks) { super(); const supportsImages = getCapabilities().images; const followUpKey = keyDisplayText("app.message.followUp"); const cycleThinkingKey = keyDisplayText("app.thinking.cycle"); let currentWarnings = { ...config.warnings }; const currentModelThinkingLevels = { ...config.modelThinkingLevels }; const defaultModelByValue = new Map(config.availableDefaultModels.map((model) => [modelSettingKey(model), model])); const currentDefaultModelKey = defaultModelByValue.has(config.defaultModel) ? config.defaultModel : undefined; const currentModelKey = config.currentModel ? modelSettingKey(config.currentModel) : undefined; const items = [ { id: "autocompact", label: "Auto-compact", description: "Automatically compact context when it gets too large", currentValue: config.autoCompact ? "true" : "false", values: ["true", "false"], }, { id: "steering-mode", label: "Steering mode", description: "Enter while streaming queues steering messages. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.", currentValue: config.steeringMode, values: ["one-at-a-time", "all"], }, { id: "follow-up-mode", label: "Follow-up mode", description: `${followUpKey} queues follow-up messages until agent stops. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.`, currentValue: config.followUpMode, values: ["one-at-a-time", "all"], }, { id: "transport", label: "Transport", description: "Preferred transport for providers that support multiple transports", currentValue: config.transport, values: ["sse", "websocket", "websocket-cached", "auto"], }, { id: "share-destination", label: "Session share destination", description: "Where to upload sessions with /share (auto prefers Radius when authenticated, otherwise GitHub Gist)", currentValue: config.sessionShareDestination, values: ["auto", "gist", "radius"], }, { id: "http-idle-timeout", label: "HTTP idle timeout", description: "Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.", currentValue: formatHttpIdleTimeoutMs(config.httpIdleTimeoutMs), values: HTTP_IDLE_TIMEOUT_CHOICES.map((choice) => choice.label), }, { id: "cache-warming-mode", label: "Cache warming", description: "off; streaming while the agent runs; idle also between runs while continuation stays profitable", currentValue: config.cacheWarmingMode, values: [...CACHE_WARMING_MODES], }, { id: "hide-thinking", label: "Hide thinking", description: "Hide thinking blocks in assistant responses", currentValue: config.hideThinkingBlock ? "true" : "false", values: ["true", "false"], }, { id: "mermaid-rendering", label: "Mermaid diagrams", description: "Render Mermaid code blocks as Unicode diagrams", currentValue: config.mermaidRenderingMode, values: ["off", "final", "streaming"], }, { id: "cache-miss-notices", label: "Cache miss notices", description: "Show transcript notices for cache costs and provider recovery diagnostics", currentValue: config.showCacheMissNotices ? "true" : "false", values: ["true", "false"], }, { id: "collapse-changelog", label: "Collapse changelog", description: "Show condensed changelog after updates", currentValue: config.collapseChangelog ? "true" : "false", values: ["true", "false"], }, { id: "quiet-startup", label: "Quiet startup", description: "Disable verbose printing at startup (header: keep only the startup header)", currentValue: String(config.quietStartup), values: ["true", "header", "false"], }, { id: "install-telemetry", label: "Install telemetry", description: "Send an anonymous version/update ping after changelog-detected updates", currentValue: config.enableInstallTelemetry ? "true" : "false", values: ["true", "false"], }, { id: "default-project-trust", label: "Default project trust", description: "Fallback behavior when no extension or saved trust decision decides project trust", currentValue: DEFAULT_PROJECT_TRUST_LABELS[config.defaultProjectTrust], values: Object.values(DEFAULT_PROJECT_TRUST_LABELS), }, { id: "double-escape-action", label: "Double-escape action", description: "Action when pressing Escape twice with empty editor", currentValue: config.doubleEscapeAction, values: ["tree", "fork", "none"], }, { id: "tree-filter-mode", label: "Tree filter mode", description: "Default filter when opening /tree", currentValue: config.treeFilterMode, values: ["default", "no-tools", "user-only", "labeled-only", "all"], }, { id: "warnings", label: "Warnings", description: "Enable or disable individual warnings", currentValue: "configure", submenu: (_currentValue, done) => new WarningSettingsSubmenu(currentWarnings, (warnings) => { currentWarnings = warnings; callbacks.onWarningsChange(warnings); }, () => done()), }, { id: "model-thinking", label: "Default thinking level per model", description: `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.`, currentValue: modelThinkingOverridesSummary(currentModelThinkingLevels), submenu: (_currentValue, done) => { const steps = [ { key: "model", title: "Per-Model Thinking Level", description: "Select a model to configure", options: () => { const sorted = [...config.availableDefaultModels].sort((a, b) => { const aKey = modelSettingKey(a); const bKey = modelSettingKey(b); if (aKey === currentModelKey) return -1; if (bKey === currentModelKey) return 1; if (aKey === currentDefaultModelKey) return -1; if (bKey === currentDefaultModelKey) return 1; return a.provider.localeCompare(b.provider); }); const items = sorted.map((model) => { const key = modelSettingKey(model); const override = currentModelThinkingLevels[key]; return { value: key, label: modelItemLabel(model), description: override ?? undefined, }; }); if (items.length === 0) { items.push({ value: "__none__", label: "No models available", description: "Log in to a provider or configure an API key first", }); } return items; }, preselect: () => currentModelKey ?? currentDefaultModelKey, searchable: true, layout: MODEL_PICKER_LAYOUT, }, { key: "level", title: (ctx) => { const m = defaultModelByValue.get(ctx.model); return `Thinking Level for ${m ? modelDisplayLabel(m) : ctx.model}`; }, description: "Select default thinking level for this model", options: (ctx) => { const model = defaultModelByValue.get(ctx.model); if (!model) return []; const levels = (model.reasoning ? getSupportedThinkingLevels(model) : ["off"]); const activeLevel = currentModelThinkingLevels[ctx.model]; const items = levels.map((level) => ({ value: level, label: `${level === activeLevel ? "✓ " : " "}${level}`, description: THINKING_DESCRIPTIONS[level], })); if (currentModelThinkingLevels[ctx.model] !== undefined) { items.push({ value: CLEAR_OVERRIDE_VALUE, label: " (clear override)", description: `Revert to global default (${config.thinkingLevel})`, }); } return items; }, preselect: (ctx) => currentModelThinkingLevels[ctx.model], }, ]; const summary = () => modelThinkingOverridesSummary(currentModelThinkingLevels); return new SteppedSubmenu(steps, (selections) => { const model = defaultModelByValue.get(selections.model); if (!model) return; if (selections.level === CLEAR_OVERRIDE_VALUE) { callbacks.onModelThinkingLevelRemove(model.provider, model.id); delete currentModelThinkingLevels[selections.model]; } else { callbacks.onModelThinkingLevelChange(model.provider, model.id, selections.level); currentModelThinkingLevels[selections.model] = selections.level; } }, () => { done(summary()); }, { loop: true }); }, }, { id: "tui-mode", label: "TUI mode", description: "Interface layout; regular mode uses the terminal's normal scrollback", currentValue: config.tuiMode, values: ["regular", "fullscreen"], }, { id: "fullscreen-exit-output", label: "Fullscreen exit output", description: "Print the transcript or only a session resume hint when exiting fullscreen mode", currentValue: config.fullscreenExitOutput, values: ["transcript", "resume-hint"], }, { id: "fullscreen-scrollbar", label: "Fullscreen scrollbar", description: "Scrollbar behavior in fullscreen mode; has no effect in regular mode", currentValue: config.fullscreenScrollbar, values: ["auto", "always", "hidden"], }, { id: "fullscreen-copy-on-select", label: "Fullscreen copy on select", description: "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X", currentValue: config.fullscreenCopyOnSelect ? "true" : "false", values: ["true", "false"], }, { id: "fullscreen-wheel-scroll-lines", label: "Fullscreen wheel scrolling", description: "Lines per mouse-wheel event in fullscreen mode; 'auto' speeds up fast wheel spins where the terminal does not", currentValue: String(config.fullscreenWheelScrollLines), values: [ "auto", ...[...new Set([1, 2, 3, 5, 10, config.fullscreenWheelScrollLines])] .filter((lines) => lines !== "auto") .sort((a, b) => a - b) .map(String), ], }, { id: "theme", label: "Theme", description: "Color theme for the interface", currentValue: config.currentTheme, submenu: (currentValue, done) => new ThemeSubmenu(currentValue, config.terminalTheme, config.availableThemes, callbacks, done), }, ]; // Only show image toggle if terminal supports it if (supportsImages) { // Insert after autocompact items.splice(1, 0, { id: "show-images", label: "Show images", description: "Render images inline in terminal", currentValue: config.showImages ? "true" : "false", values: ["true", "false"], }); items.splice(2, 0, { id: "image-width-cells", label: "Image width", description: "Preferred inline image width in terminal cells", currentValue: String(config.imageWidthCells), values: ["60", "80", "120"], }); } // Image auto-resize toggle (always available, affects both attached and read images) items.splice(supportsImages ? 3 : 1, 0, { id: "auto-resize-images", label: "Auto-resize images", description: "Resize large images to 2000x2000 max for better model compatibility", currentValue: config.autoResizeImages ? "true" : "false", values: ["true", "false"], }); // Block images toggle (always available, insert after auto-resize-images) const autoResizeIndex = items.findIndex((item) => item.id === "auto-resize-images"); items.splice(autoResizeIndex + 1, 0, { id: "block-images", label: "Block images", description: "Prevent images from being sent to LLM providers", currentValue: config.blockImages ? "true" : "false", values: ["true", "false"], }); // Skill commands toggle (insert after block-images) const blockImagesIndex = items.findIndex((item) => item.id === "block-images"); items.splice(blockImagesIndex + 1, 0, { id: "skill-commands", label: "Skill commands", description: "Register skills as /skill:name commands", currentValue: config.enableSkillCommands ? "true" : "false", values: ["true", "false"], }); // Hardware cursor toggle (insert after skill-commands) const skillCommandsIndex = items.findIndex((item) => item.id === "skill-commands"); items.splice(skillCommandsIndex + 1, 0, { id: "show-hardware-cursor", label: "Show hardware cursor", description: "Use the terminal cursor instead of Pi's drawn cursor", currentValue: config.showHardwareCursor ? "true" : "false", values: ["true", "false"], }); // Editor padding toggle (insert after show-hardware-cursor) const hardwareCursorIndex = items.findIndex((item) => item.id === "show-hardware-cursor"); items.splice(hardwareCursorIndex + 1, 0, { id: "editor-padding", label: "Editor padding", description: "Horizontal padding for input editor (0-3)", currentValue: String(config.editorPaddingX), values: ["0", "1", "2", "3"], }); // Output padding toggle (insert after editor-padding) const editorPaddingIndex = items.findIndex((item) => item.id === "editor-padding"); items.splice(editorPaddingIndex + 1, 0, { id: "output-padding", label: "Output padding", description: "Horizontal padding for messages, tool output, and command output", currentValue: String(config.outputPad), values: ["0", "1"], }); // Autocomplete max visible toggle (insert after output-padding) const outputPaddingIndex = items.findIndex((item) => item.id === "output-padding"); items.splice(outputPaddingIndex + 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"], }); // Clear on shrink toggle (insert after autocomplete-max-visible) const autocompleteIndex = items.findIndex((item) => item.id === "autocomplete-max-visible"); items.splice(autocompleteIndex + 1, 0, { id: "clear-on-shrink", label: "Clear on shrink", description: "Clear empty rows when content shrinks (may cause flicker)", currentValue: config.clearOnShrink ? "true" : "false", values: ["true", "false"], }); // Terminal progress toggle (insert after clear-on-shrink) const clearOnShrinkIndex = items.findIndex((item) => item.id === "clear-on-shrink"); items.splice(clearOnShrinkIndex + 1, 0, { id: "terminal-progress", label: "Terminal progress", description: "Show OSC 9;4 progress indicators in the terminal tab bar", currentValue: config.showTerminalProgress ? "true" : "false", values: ["true", "false"], }); // Add borders this.addChild(new DynamicBorder()); this.settingsList = new SettingsList(items, 10, getSettingsListTheme(), (id, newValue) => { switch (id) { case "autocompact": callbacks.onAutoCompactChange(newValue === "true"); break; case "show-images": callbacks.onShowImagesChange(newValue === "true"); break; case "image-width-cells": callbacks.onImageWidthCellsChange(parseInt(newValue, 10)); break; case "auto-resize-images": callbacks.onAutoResizeImagesChange(newValue === "true"); break; case "block-images": callbacks.onBlockImagesChange(newValue === "true"); break; case "skill-commands": callbacks.onEnableSkillCommandsChange(newValue === "true"); break; case "steering-mode": callbacks.onSteeringModeChange(newValue); break; case "follow-up-mode": callbacks.onFollowUpModeChange(newValue); break; case "transport": callbacks.onTransportChange(newValue); break; case "share-destination": callbacks.onSessionShareDestinationChange(newValue); break; case "http-idle-timeout": { const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.label === newValue); if (choice) { callbacks.onHttpIdleTimeoutMsChange(choice.timeoutMs); } break; } case "cache-warming-mode": callbacks.onCacheWarmingModeChange(newValue); break; case "hide-thinking": callbacks.onHideThinkingBlockChange(newValue === "true"); break; case "mermaid-rendering": callbacks.onMermaidRenderingModeChange(newValue); break; case "cache-miss-notices": callbacks.onShowCacheMissNoticesChange(newValue === "true"); break; case "collapse-changelog": callbacks.onCollapseChangelogChange(newValue === "true"); break; case "quiet-startup": callbacks.onQuietStartupChange(newValue === "header" ? "header" : newValue === "true"); break; case "install-telemetry": callbacks.onEnableInstallTelemetryChange(newValue === "true"); break; case "default-project-trust": { const defaultProjectTrust = DEFAULT_PROJECT_TRUST_BY_LABEL.get(newValue); if (defaultProjectTrust) { callbacks.onDefaultProjectTrustChange(defaultProjectTrust); } break; } case "double-escape-action": callbacks.onDoubleEscapeActionChange(newValue); break; case "tree-filter-mode": callbacks.onTreeFilterModeChange(newValue); break; case "show-hardware-cursor": callbacks.onShowHardwareCursorChange(newValue === "true"); break; case "editor-padding": callbacks.onEditorPaddingXChange(parseInt(newValue, 10)); break; case "output-padding": callbacks.onOutputPadChange(newValue === "0" ? 0 : 1); break; case "autocomplete-max-visible": callbacks.onAutocompleteMaxVisibleChange(parseInt(newValue, 10)); break; case "clear-on-shrink": callbacks.onClearOnShrinkChange(newValue === "true"); break; case "terminal-progress": callbacks.onShowTerminalProgressChange(newValue === "true"); break; case "tui-mode": callbacks.onTuiModeChange(newValue); break; case "fullscreen-exit-output": callbacks.onFullscreenExitOutputChange(newValue); break; case "fullscreen-scrollbar": callbacks.onFullscreenScrollbarChange(newValue); break; case "fullscreen-copy-on-select": callbacks.onFullscreenCopyOnSelectChange(newValue === "true"); break; case "fullscreen-wheel-scroll-lines": callbacks.onFullscreenWheelScrollLinesChange(newValue === "auto" ? "auto" : parseInt(newValue, 10)); break; case "theme": callbacks.onThemeChange(newValue); break; } }, callbacks.onCancel, { enableSearch: true }); this.addChild(this.settingsList); this.addChild(new DynamicBorder()); } getSettingsList() { return this.settingsList; } } //# sourceMappingURL=settings-selector.js.map