@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `ctx.ui.getEditorComponent()` so extensions can wrap the currently configured custom editor factory ([#3935](https://github.com/badlogic/pi-mono/issues/3935)).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed idle follow-up submission to clear the editor like normal message submission ([#3926](https://github.com/badlogic/pi-mono/issues/3926)).
|
||||
|
||||
@@ -2225,6 +2225,10 @@ ctx.ui.setToolsExpanded(wasExpanded);
|
||||
|
||||
// Custom editor (vim mode, emacs mode, etc.)
|
||||
ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
|
||||
const currentEditor = ctx.ui.getEditorComponent();
|
||||
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
|
||||
new WrappedEditor(tui, theme, keybindings, currentEditor?.(tui, theme, keybindings))
|
||||
);
|
||||
ctx.ui.setEditorComponent(undefined); // Restore default editor
|
||||
|
||||
// Theme management (see themes.md for creating themes)
|
||||
@@ -2379,8 +2383,18 @@ export default function (pi: ExtensionAPI) {
|
||||
- Extend `CustomEditor` (not base `Editor`) to get app keybindings (escape to abort, ctrl+d, model switching)
|
||||
- Call `super.handleInput(data)` for keys you don't handle
|
||||
- Factory receives `theme` and `keybindings` from the app
|
||||
- Use `ctx.ui.getEditorComponent()` before `setEditorComponent()` to wrap the previously configured custom editor
|
||||
- Pass `undefined` to restore default: `ctx.ui.setEditorComponent(undefined)`
|
||||
|
||||
To compose with another extension that already replaced the editor, capture the previous factory before setting yours:
|
||||
|
||||
```typescript
|
||||
const previous = ctx.ui.getEditorComponent();
|
||||
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
|
||||
new MyEditor(tui, theme, keybindings, { base: previous?.(tui, theme, keybindings) })
|
||||
);
|
||||
```
|
||||
|
||||
See [tui.md](tui.md) Pattern 7 for a complete example with mode indicator.
|
||||
|
||||
### Message Rendering
|
||||
|
||||
@@ -47,6 +47,7 @@ export type {
|
||||
ContextUsage,
|
||||
CustomToolCallEvent,
|
||||
CustomToolResultEvent,
|
||||
EditorFactory,
|
||||
EditToolCallEvent,
|
||||
EditToolResultEvent,
|
||||
ExecOptions,
|
||||
|
||||
@@ -207,6 +207,7 @@ const noOpUIContext: ExtensionUIContext = {
|
||||
editor: async () => undefined,
|
||||
addAutocompleteProvider: () => {},
|
||||
setEditorComponent: () => {},
|
||||
getEditorComponent: () => undefined,
|
||||
get theme() {
|
||||
return theme;
|
||||
},
|
||||
|
||||
@@ -115,6 +115,7 @@ export interface WorkingIndicatorOptions {
|
||||
|
||||
/** Wrap the current autocomplete provider with additional behavior. */
|
||||
export type AutocompleteProviderFactory = (current: AutocompleteProvider) => AutocompleteProvider;
|
||||
export type EditorFactory = (tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent;
|
||||
|
||||
/**
|
||||
* UI context for extensions to request interactive UI.
|
||||
@@ -249,9 +250,10 @@ export interface ExtensionUIContext {
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
setEditorComponent(
|
||||
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent) | undefined,
|
||||
): void;
|
||||
setEditorComponent(factory: EditorFactory | undefined): void;
|
||||
|
||||
/** Get the currently configured custom editor factory, or undefined when using the default editor. */
|
||||
getEditorComponent(): EditorFactory | undefined;
|
||||
|
||||
/** Get the current theme for styling. */
|
||||
readonly theme: Theme;
|
||||
|
||||
@@ -20,7 +20,6 @@ import type {
|
||||
AutocompleteItem,
|
||||
AutocompleteProvider,
|
||||
EditorComponent,
|
||||
EditorTheme,
|
||||
Keybinding,
|
||||
KeyId,
|
||||
MarkdownTheme,
|
||||
@@ -60,6 +59,7 @@ import { type AgentSession, type AgentSessionEvent, parseSkillBlock } from "../.
|
||||
import { type AgentSessionRuntime, SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
||||
import type {
|
||||
AutocompleteProviderFactory,
|
||||
EditorFactory,
|
||||
ExtensionCommandContext,
|
||||
ExtensionContext,
|
||||
ExtensionRunner,
|
||||
@@ -251,6 +251,7 @@ export class InteractiveMode {
|
||||
private statusContainer: Container;
|
||||
private defaultEditor: CustomEditor;
|
||||
private editor: EditorComponent;
|
||||
private editorComponentFactory: EditorFactory | undefined;
|
||||
private autocompleteProvider: AutocompleteProvider | undefined;
|
||||
private autocompleteProviderWrappers: AutocompleteProviderFactory[] = [];
|
||||
private fdPath: string | undefined;
|
||||
@@ -1985,6 +1986,7 @@ export class InteractiveMode {
|
||||
this.setupAutocompleteProvider();
|
||||
},
|
||||
setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
|
||||
getEditorComponent: () => this.editorComponentFactory,
|
||||
get theme() {
|
||||
return theme;
|
||||
},
|
||||
@@ -2182,9 +2184,9 @@ export class InteractiveMode {
|
||||
* Set a custom editor component from an extension.
|
||||
* Pass undefined to restore the default editor.
|
||||
*/
|
||||
private setCustomEditorComponent(
|
||||
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent) | undefined,
|
||||
): void {
|
||||
private setCustomEditorComponent(factory: EditorFactory | undefined): void {
|
||||
this.editorComponentFactory = factory;
|
||||
|
||||
// Save text from current editor before switching
|
||||
const currentText = this.editor.getText();
|
||||
|
||||
|
||||
@@ -271,6 +271,11 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<neve
|
||||
// Custom editor components not supported in RPC mode
|
||||
},
|
||||
|
||||
getEditorComponent() {
|
||||
// Custom editor components not supported in RPC mode
|
||||
return undefined;
|
||||
},
|
||||
|
||||
get theme() {
|
||||
return theme;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user