/** * Extension UI host. * * Extracted verbatim from interactive-mode.ts (god-file decomposition). Owns the interactive-mode * surface of the extension UI API: extension-provided dialogs (selector / confirm / input / multi-line * editor / custom component), widget mounting above and below the editor, extension status text, custom * footer and header swaps, the custom editor-component swap, keyboard-shortcut wiring, terminal-input * listeners, and the `ExtensionUIContext` handed to extensions. It owns exactly the state this cluster * mutates (the live dialog components, the widget maps, the terminal-input unsubscribers, the custom * footer/header, and the current custom-editor factory) and reaches everything else — the mutable * `editor`, the render-core containers, the working-indicator, autocomplete wiring, theme/tools state — * through a narrow deps/ui surface rather than the whole InteractiveMode instance. Extensions must see * identical behavior, so the render-core operations (working indicator, hidden-thinking label, * autocomplete rebuild) stay host-side and are invoked here as delegations. */ import type { AutocompleteProvider, Component, EditorComponent, LoaderIndicatorOptions } from "@caupulican/pi-tui"; import { Container, type TUI } from "@caupulican/pi-tui"; import type { AgentSession } from "../../core/agent-session.ts"; import type { AutocompleteProviderFactory, ExtensionRunner, ExtensionUIContext, ExtensionUIDialogOptions } from "../../core/extensions/index.ts"; import type { FooterDataProvider } from "../../core/footer-data-provider.ts"; import type { KeybindingsManager } from "../../core/keybindings.ts"; import type { SessionImageStore } from "../../core/session-image-store.ts"; import type { CustomEditor } from "./components/custom-editor.ts"; import type { FooterComponent } from "./components/footer.ts"; import type { EditorOverlayHost } from "./editor-overlay-host.ts"; /** * The narrow slice of InteractiveMode the extension UI host drives. Stable containers and the TUI are * passed directly; mutable render-core state (the active editor, built-in header, loading animation, * autocomplete wrappers, tools-expansion) is read/written through accessors so InteractiveMode keeps * ownership; and render-core / status operations stay host-side and are exposed here as callbacks. */ export interface ExtensionUiHostUi { readonly tui: TUI; readonly overlayHost: EditorOverlayHost; readonly keybindings: KeybindingsManager; readonly footer: FooterComponent; readonly footerDataProvider: FooterDataProvider; readonly headerContainer: Container; readonly chatContainer: Container; readonly defaultEditor: CustomEditor; readonly widgetContainerAbove: Container; readonly widgetContainerBelow: Container; getEditor(): EditorComponent; setEditor(editor: EditorComponent): void; getBuiltInHeader(): Component | undefined; getAutocompleteProvider(): AutocompleteProvider | undefined; getClipboardImageStore(): Pick | undefined; getToolsExpanded(): boolean; pushAutocompleteProviderWrapper(factory: AutocompleteProviderFactory): void; resetAutocompleteProviderWrappers(): void; setupAutocompleteProvider(): void; setWorkingVisible(visible: boolean): void; setWorkingIndicator(options?: LoaderIndicatorOptions): void; setHiddenThinkingLabel(label?: string): void; setWorkingMessage(message: string | undefined): void; resetWorkingIndicators(): void; setToolsExpanded(expanded: boolean): void; toggleToolsExpanded(): void; updateTerminalTitle(): void; markShutdownRequested(): void; abort(): void; reload(): Promise; showStatus(message: string): void; showWarning(message: string): void; showError(message: string): void; } export interface ExtensionUiHostDeps { getSession(): AgentSession; ui: ExtensionUiHostUi; } type DisposableComponent = Component & { dispose?(): void; }; type WidgetPlacement = "aboveEditor" | "belowEditor"; export declare class ExtensionUiHost { private readonly deps; private extensionSelector; private extensionInput; private extensionEditor; /** Settles the mounted extension dialog before another dialog can replace its overlay. */ private activeExtensionDialogCancel; private extensionTerminalInputUnsubscribers; private extensionWidgetsAbove; private extensionWidgetsBelow; private hostWidgetsAbove; private hostWidgetsBelow; private customFooter; private customHeader; private editorComponentFactory; constructor(deps: ExtensionUiHostDeps); private get session(); private get ui(); /** The active custom header, if an extension has installed one. */ getCustomHeader(): (Component & { dispose?(): void; }) | undefined; /** * Set up keyboard shortcuts registered by extensions. */ setupExtensionShortcuts(extensionRunner: ExtensionRunner): void; /** * Set extension status text in the footer. */ private setExtensionStatus; private static readonly MAX_WIDGET_LINES; private removeWidget; /** Mount a native host widget without coupling it to extension reload lifecycle. */ setHostWidget(key: string, component: DisposableComponent | undefined, placement: WidgetPlacement): void; clearHostWidgets(): void; /** * Set an extension widget (string array or custom component). */ private setExtensionWidget; private clearExtensionWidgets; resetExtensionUI(): void; /** * Render all extension widgets to the widget container. */ renderWidgets(): void; private renderWidgetContainer; /** * Set a custom footer component, or restore the built-in footer. */ private setExtensionFooter; /** * Set a custom header component, or restore the built-in header. */ private setExtensionHeader; private addExtensionTerminalInputListener; clearExtensionTerminalInputListeners(): void; /** * Create the ExtensionUIContext for extensions. */ createExtensionUIContext(): ExtensionUIContext; private showHumanInput; /** * Show a selector for extensions. */ showExtensionSelector(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise; /** * Hide the extension selector. */ private hideExtensionSelector; /** * Show a confirmation dialog for extensions. */ showExtensionConfirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise; /** * Show a text input for extensions. */ private showExtensionInput; private showExtensionDialog; /** * Hide the extension input. */ private hideExtensionInput; /** * Show a multi-line editor for extensions (with Ctrl+G support). */ showExtensionEditor(title: string, prefill?: string): Promise; /** * Hide the extension editor. */ private hideExtensionEditor; /** * Set a custom editor component from an extension. * Pass undefined to restore the default editor. */ private setCustomEditorComponent; /** * Show a notification for extensions. */ private showExtensionNotify; /** Show a custom component with keyboard focus. Overlay mode renders on top of existing content. */ private showExtensionCustom; /** * Show an extension error in the UI. */ showExtensionError(extensionPath: string, error: string, stack?: string): void; } export {}; //# sourceMappingURL=extension-ui-host.d.ts.map