@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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
|
||||||
|
|
||||||
- Fixed idle follow-up submission to clear the editor like normal message submission ([#3926](https://github.com/badlogic/pi-mono/issues/3926)).
|
- 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.)
|
// Custom editor (vim mode, emacs mode, etc.)
|
||||||
ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
|
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
|
ctx.ui.setEditorComponent(undefined); // Restore default editor
|
||||||
|
|
||||||
// Theme management (see themes.md for creating themes)
|
// 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)
|
- 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
|
- Call `super.handleInput(data)` for keys you don't handle
|
||||||
- Factory receives `theme` and `keybindings` from the app
|
- 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)`
|
- 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.
|
See [tui.md](tui.md) Pattern 7 for a complete example with mode indicator.
|
||||||
|
|
||||||
### Message Rendering
|
### Message Rendering
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export type {
|
|||||||
ContextUsage,
|
ContextUsage,
|
||||||
CustomToolCallEvent,
|
CustomToolCallEvent,
|
||||||
CustomToolResultEvent,
|
CustomToolResultEvent,
|
||||||
|
EditorFactory,
|
||||||
EditToolCallEvent,
|
EditToolCallEvent,
|
||||||
EditToolResultEvent,
|
EditToolResultEvent,
|
||||||
ExecOptions,
|
ExecOptions,
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ const noOpUIContext: ExtensionUIContext = {
|
|||||||
editor: async () => undefined,
|
editor: async () => undefined,
|
||||||
addAutocompleteProvider: () => {},
|
addAutocompleteProvider: () => {},
|
||||||
setEditorComponent: () => {},
|
setEditorComponent: () => {},
|
||||||
|
getEditorComponent: () => undefined,
|
||||||
get theme() {
|
get theme() {
|
||||||
return theme;
|
return theme;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export interface WorkingIndicatorOptions {
|
|||||||
|
|
||||||
/** Wrap the current autocomplete provider with additional behavior. */
|
/** Wrap the current autocomplete provider with additional behavior. */
|
||||||
export type AutocompleteProviderFactory = (current: AutocompleteProvider) => AutocompleteProvider;
|
export type AutocompleteProviderFactory = (current: AutocompleteProvider) => AutocompleteProvider;
|
||||||
|
export type EditorFactory = (tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI context for extensions to request interactive UI.
|
* UI context for extensions to request interactive UI.
|
||||||
@@ -249,9 +250,10 @@ export interface ExtensionUIContext {
|
|||||||
* );
|
* );
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
setEditorComponent(
|
setEditorComponent(factory: EditorFactory | undefined): void;
|
||||||
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent) | 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. */
|
/** Get the current theme for styling. */
|
||||||
readonly theme: Theme;
|
readonly theme: Theme;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import type {
|
|||||||
AutocompleteItem,
|
AutocompleteItem,
|
||||||
AutocompleteProvider,
|
AutocompleteProvider,
|
||||||
EditorComponent,
|
EditorComponent,
|
||||||
EditorTheme,
|
|
||||||
Keybinding,
|
Keybinding,
|
||||||
KeyId,
|
KeyId,
|
||||||
MarkdownTheme,
|
MarkdownTheme,
|
||||||
@@ -60,6 +59,7 @@ import { type AgentSession, type AgentSessionEvent, parseSkillBlock } from "../.
|
|||||||
import { type AgentSessionRuntime, SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
import { type AgentSessionRuntime, SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
||||||
import type {
|
import type {
|
||||||
AutocompleteProviderFactory,
|
AutocompleteProviderFactory,
|
||||||
|
EditorFactory,
|
||||||
ExtensionCommandContext,
|
ExtensionCommandContext,
|
||||||
ExtensionContext,
|
ExtensionContext,
|
||||||
ExtensionRunner,
|
ExtensionRunner,
|
||||||
@@ -251,6 +251,7 @@ export class InteractiveMode {
|
|||||||
private statusContainer: Container;
|
private statusContainer: Container;
|
||||||
private defaultEditor: CustomEditor;
|
private defaultEditor: CustomEditor;
|
||||||
private editor: EditorComponent;
|
private editor: EditorComponent;
|
||||||
|
private editorComponentFactory: EditorFactory | undefined;
|
||||||
private autocompleteProvider: AutocompleteProvider | undefined;
|
private autocompleteProvider: AutocompleteProvider | undefined;
|
||||||
private autocompleteProviderWrappers: AutocompleteProviderFactory[] = [];
|
private autocompleteProviderWrappers: AutocompleteProviderFactory[] = [];
|
||||||
private fdPath: string | undefined;
|
private fdPath: string | undefined;
|
||||||
@@ -1985,6 +1986,7 @@ export class InteractiveMode {
|
|||||||
this.setupAutocompleteProvider();
|
this.setupAutocompleteProvider();
|
||||||
},
|
},
|
||||||
setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
|
setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
|
||||||
|
getEditorComponent: () => this.editorComponentFactory,
|
||||||
get theme() {
|
get theme() {
|
||||||
return theme;
|
return theme;
|
||||||
},
|
},
|
||||||
@@ -2182,9 +2184,9 @@ export class InteractiveMode {
|
|||||||
* Set a custom editor component from an extension.
|
* Set a custom editor component from an extension.
|
||||||
* Pass undefined to restore the default editor.
|
* Pass undefined to restore the default editor.
|
||||||
*/
|
*/
|
||||||
private setCustomEditorComponent(
|
private setCustomEditorComponent(factory: EditorFactory | undefined): void {
|
||||||
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent) | undefined,
|
this.editorComponentFactory = factory;
|
||||||
): void {
|
|
||||||
// Save text from current editor before switching
|
// Save text from current editor before switching
|
||||||
const currentText = this.editor.getText();
|
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
|
// Custom editor components not supported in RPC mode
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getEditorComponent() {
|
||||||
|
// Custom editor components not supported in RPC mode
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
get theme() {
|
get theme() {
|
||||||
return theme;
|
return theme;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user