{"version":3,"file":"extension-dialogs.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/extension-dialogs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extension dialog primitives: the selector, options pane, confirm, text\n * input, multi-line editor, and custom-component dialogs that swap into the\n * editor slot (or an overlay) and restore the prompt editor when done.\n * Extracted from interactive-mode.ts; consumed by the ExtensionUIContext.\n */\n\nimport type { Component, Container, OverlayHandle, OverlayOptions, TUI } from \"@kolisachint/hoocode-tui\";\nimport type { AskQuestion, ExtensionUIDialogOptions } from \"../../core/extensions/index.js\";\nimport type { KeybindingsManager } from \"../../core/keybindings.js\";\nimport { AskOptionsComponent } from \"./components/ask-options.js\";\nimport { ExtensionEditorComponent } from \"./components/extension-editor.js\";\nimport { ExtensionInputComponent } from \"./components/extension-input.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { type Theme, theme } from \"./theme/theme.js\";\n\n/** The slice of the interactive mode the dialogs render into. */\nexport interface ExtensionDialogsDeps {\n\tui: TUI;\n\teditorContainer: Container;\n\tkeybindings: KeybindingsManager;\n\t/** The prompt editor (read at call time; the editor can be swapped). */\n\tgetEditor(): Component & { getText(): string; setText(text: string): void };\n}\n\nexport class ExtensionDialogs {\n\tprivate selector: ExtensionSelectorComponent | undefined = undefined;\n\tprivate input: ExtensionInputComponent | undefined = undefined;\n\tprivate askOptionsPane: AskOptionsComponent | undefined = undefined;\n\tprivate editorDialog: ExtensionEditorComponent | undefined = undefined;\n\n\tconstructor(private readonly deps: ExtensionDialogsDeps) {}\n\n\t/** True while the options pane is on screen (used to queue team approvals). */\n\tisAskOptionsOpen(): boolean {\n\t\treturn this.askOptionsPane !== undefined;\n\t}\n\n\t/** Hide whatever dialog is open and restore the prompt editor. */\n\treset(): void {\n\t\tif (this.selector) {\n\t\t\tthis.hideSelector();\n\t\t}\n\t\tif (this.input) {\n\t\t\tthis.hideInput();\n\t\t}\n\t\tif (this.askOptionsPane) {\n\t\t\tthis.hideAskOptions();\n\t\t}\n\t\tif (this.editorDialog) {\n\t\t\tthis.hideEditor();\n\t\t}\n\t}\n\n\tprivate restoreEditor(): void {\n\t\tconst editor = this.deps.getEditor();\n\t\tthis.deps.editorContainer.clear();\n\t\tthis.deps.editorContainer.addChild(editor);\n\t\tthis.deps.ui.setFocus(editor);\n\t\tthis.deps.ui.requestRender();\n\t}\n\n\t/**\n\t * Show a selector for extensions.\n\t */\n\tshowSelector(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (opts?.signal?.aborted) {\n\t\t\t\tresolve(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tthis.hideSelector();\n\t\t\t\tresolve(undefined);\n\t\t\t};\n\t\t\topts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\tthis.selector = new ExtensionSelectorComponent(\n\t\t\t\ttitle,\n\t\t\t\toptions,\n\t\t\t\t(option) => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideSelector();\n\t\t\t\t\tresolve(option);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideSelector();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ tui: this.deps.ui, timeout: opts?.timeout },\n\t\t\t);\n\n\t\t\tthis.deps.editorContainer.clear();\n\t\t\tthis.deps.editorContainer.addChild(this.selector);\n\t\t\tthis.deps.ui.setFocus(this.selector);\n\t\t\tthis.deps.ui.requestRender();\n\t\t});\n\t}\n\n\t/**\n\t * Hide the extension selector.\n\t */\n\thideSelector(): void {\n\t\tthis.selector?.dispose();\n\t\tthis.selector = undefined;\n\t\tthis.restoreEditor();\n\t}\n\n\t/**\n\t * Show the options pane — the agent asking the user one or more questions.\n\t * Resolves with one answer per question, or undefined if skipped/aborted.\n\t */\n\tshowAskOptions(questions: AskQuestion[], opts?: ExtensionUIDialogOptions): Promise<string[] | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (!questions.length || opts?.signal?.aborted) {\n\t\t\t\tresolve(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tthis.hideAskOptions();\n\t\t\t\tresolve(undefined);\n\t\t\t};\n\t\t\topts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\tthis.askOptionsPane = new AskOptionsComponent(\n\t\t\t\tquestions,\n\t\t\t\t(answers) => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideAskOptions();\n\t\t\t\t\tresolve(answers);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideAskOptions();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tthis.deps.editorContainer.clear();\n\t\t\tthis.deps.editorContainer.addChild(this.askOptionsPane);\n\t\t\tthis.deps.ui.setFocus(this.askOptionsPane);\n\t\t\tthis.deps.ui.requestRender();\n\t\t});\n\t}\n\n\t/**\n\t * Hide the options pane and restore the editor.\n\t */\n\thideAskOptions(): void {\n\t\tif (!this.askOptionsPane) return;\n\t\tthis.askOptionsPane = undefined;\n\t\tthis.restoreEditor();\n\t}\n\n\t/**\n\t * Show a confirmation dialog for extensions.\n\t */\n\tasync confirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise<boolean> {\n\t\tconst result = await this.showSelector(`${title}\\n${message}`, [\"Yes\", \"No\"], opts);\n\t\treturn result === \"Yes\";\n\t}\n\n\t/**\n\t * Show a text input for extensions.\n\t */\n\tshowInput(title: string, placeholder?: string, opts?: ExtensionUIDialogOptions): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (opts?.signal?.aborted) {\n\t\t\t\tresolve(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tthis.hideInput();\n\t\t\t\tresolve(undefined);\n\t\t\t};\n\t\t\topts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\tthis.input = new ExtensionInputComponent(\n\t\t\t\ttitle,\n\t\t\t\tplaceholder,\n\t\t\t\t(value) => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideInput();\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tthis.hideInput();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ tui: this.deps.ui, timeout: opts?.timeout },\n\t\t\t);\n\n\t\t\tthis.deps.editorContainer.clear();\n\t\t\tthis.deps.editorContainer.addChild(this.input);\n\t\t\tthis.deps.ui.setFocus(this.input);\n\t\t\tthis.deps.ui.requestRender();\n\t\t});\n\t}\n\n\t/**\n\t * Hide the extension input.\n\t */\n\thideInput(): void {\n\t\tthis.input?.dispose();\n\t\tthis.input = undefined;\n\t\tthis.restoreEditor();\n\t}\n\n\t/**\n\t * Show a multi-line editor for extensions (with Ctrl+G support).\n\t */\n\tshowEditor(title: string, prefill?: string): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.editorDialog = new ExtensionEditorComponent(\n\t\t\t\tthis.deps.ui,\n\t\t\t\tthis.deps.keybindings,\n\t\t\t\ttitle,\n\t\t\t\tprefill,\n\t\t\t\t(value) => {\n\t\t\t\t\tthis.hideEditor();\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tthis.hideEditor();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tthis.deps.editorContainer.clear();\n\t\t\tthis.deps.editorContainer.addChild(this.editorDialog);\n\t\t\tthis.deps.ui.setFocus(this.editorDialog);\n\t\t\tthis.deps.ui.requestRender();\n\t\t});\n\t}\n\n\t/**\n\t * Hide the extension editor.\n\t */\n\thideEditor(): void {\n\t\tthis.editorDialog = undefined;\n\t\tthis.restoreEditor();\n\t}\n\n\t/** Show a custom component with keyboard focus. Overlay mode renders on top of existing content. */\n\tasync showCustom<T>(\n\t\tfactory: (\n\t\t\ttui: TUI,\n\t\t\ttheme: Theme,\n\t\t\tkeybindings: KeybindingsManager,\n\t\t\tdone: (result: T) => void,\n\t\t) => (Component & { dispose?(): void }) | Promise<Component & { dispose?(): void }>,\n\t\toptions?: {\n\t\t\toverlay?: boolean;\n\t\t\toverlayOptions?: OverlayOptions | (() => OverlayOptions);\n\t\t\tonHandle?: (handle: OverlayHandle) => void;\n\t\t},\n\t): Promise<T> {\n\t\tconst savedText = this.deps.getEditor().getText();\n\t\tconst isOverlay = options?.overlay ?? false;\n\n\t\tconst restoreEditorWithText = () => {\n\t\t\tthis.restoreEditor();\n\t\t\tthis.deps.getEditor().setText(savedText);\n\t\t};\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet component: Component & { dispose?(): void };\n\t\t\tlet closed = false;\n\n\t\t\tconst close = (result: T) => {\n\t\t\t\tif (closed) return;\n\t\t\t\tclosed = true;\n\t\t\t\tif (isOverlay) this.deps.ui.hideOverlay();\n\t\t\t\telse restoreEditorWithText();\n\t\t\t\t// Note: both branches above already call requestRender\n\t\t\t\tresolve(result);\n\t\t\t\ttry {\n\t\t\t\t\tcomponent?.dispose?.();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore dispose errors */\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tPromise.resolve(factory(this.deps.ui, theme, this.deps.keybindings, close))\n\t\t\t\t.then((c) => {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tcomponent = c;\n\t\t\t\t\tif (isOverlay) {\n\t\t\t\t\t\t// Resolve overlay options - can be static or dynamic function\n\t\t\t\t\t\tconst resolveOptions = (): OverlayOptions | undefined => {\n\t\t\t\t\t\t\tif (options?.overlayOptions) {\n\t\t\t\t\t\t\t\tconst opts =\n\t\t\t\t\t\t\t\t\ttypeof options.overlayOptions === \"function\"\n\t\t\t\t\t\t\t\t\t\t? options.overlayOptions()\n\t\t\t\t\t\t\t\t\t\t: options.overlayOptions;\n\t\t\t\t\t\t\t\treturn opts;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Fallback: use component's width property if available\n\t\t\t\t\t\t\tconst w = (component as { width?: number }).width;\n\t\t\t\t\t\t\treturn w ? { width: w } : undefined;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst handle = this.deps.ui.showOverlay(component, resolveOptions());\n\t\t\t\t\t\t// Expose handle to caller for visibility control\n\t\t\t\t\t\toptions?.onHandle?.(handle);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.deps.editorContainer.clear();\n\t\t\t\t\t\tthis.deps.editorContainer.addChild(component);\n\t\t\t\t\t\tthis.deps.ui.setFocus(component);\n\t\t\t\t\t\tthis.deps.ui.requestRender();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tif (!isOverlay) restoreEditorWithText();\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n}\n"]}