# Buffer API

## Buffer Queries

### `getThemeSchema`

Get the theme JSON Schema for the theme editor
Returns the raw JSON Schema generated by schemars for ThemeFile.
The schema uses standard JSON Schema format with $ref for type references.
Plugins are responsible for parsing the schema and resolving $ref references.

```typescript
getThemeSchema(): unknown
```

### `getBuiltinThemes`

```typescript
getBuiltinThemes(): unknown
```

### `getConfig`

Get the current editor configuration
Returns the merged configuration (user config file + compiled-in defaults).
This is the runtime config that the editor is actually using, including
all default values for LSP servers, languages, keybindings, etc.

```typescript
getConfig(): unknown
```

### `getUserConfig`

Get the user's configuration (only explicitly set values)
Returns only the configuration from the user's config file.
Fields not present here are using default values.
Use this with getConfig() to determine which values are defaults.

```typescript
getUserConfig(): unknown
```

### `getConfigDir`

Returns the absolute path to the user config directory (e.g., `~/.config/fresh/` on Linux).

```typescript
getConfigDir(): string
```

### `getThemesDir`

Get the user themes directory path
Returns the absolute path to the directory where user themes are stored.
e.g. ~/.config/fresh/themes/

```typescript
getThemesDir(): string
```

### `getActiveBufferId`

Get the buffer ID of the focused editor pane
Returns 0 if no buffer is active (rare edge case).
Use this ID with other buffer operations like insertText.

```typescript
getActiveBufferId(): number
```

### `getCursorPosition`

Returns 0 if no cursor. For multi-cursor, use `getAllCursors`. Note: byte offset, not character index.

```typescript
getCursorPosition(): number
```

### `getBufferPath`

Get the absolute file path for a buffer
Returns empty string for unsaved buffers or virtual buffers.
The path is always absolute. Use this to determine file type,
construct related paths, or display to the user.

```typescript
getBufferPath(buffer_id: number): string
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Target buffer ID |

### `getBufferLength`

Get the total byte length of a buffer's content
Returns 0 if buffer doesn't exist.

```typescript
getBufferLength(buffer_id: number): number
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Target buffer ID |

### `isBufferModified`

Check if a buffer has been modified since last save
Returns false if buffer doesn't exist or has never been saved.
Virtual buffers are never considered modified.

```typescript
isBufferModified(buffer_id: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Target buffer ID |

### `getCurrentLocale`

Get the currently active locale

```typescript
getCurrentLocale(): string
```

### `getActiveSplitId`

Get the ID of the focused split pane
Use with focusSplit, setSplitBuffer, or createVirtualBufferInExistingSplit
to manage split layouts.

```typescript
getActiveSplitId(): number
```

### `getCursorLine`

Get the line number of the primary cursor (1-indexed)
Line numbers start at 1. Returns 1 if no cursor exists.
For byte offset use getCursorPosition instead.

```typescript
getCursorLine(): number
```

### `getAllCursorPositions`

Get byte offsets of all cursors (multi-cursor support)
Returns array of positions; empty if no cursors. Primary cursor
is typically first. For selection info use getAllCursors instead.

```typescript
getAllCursorPositions(): number[]
```

### `isProcessRunning`

Check if a background process is still running

```typescript
isProcessRunning(process_id: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `process_id` | `number` | ID returned from spawnBackgroundProcess |

### `getHighlights`

Compute syntax highlighting for a buffer range

```typescript
getHighlights(buffer_id: number, start: number, end: number): Promise<TsHighlightSpan[]>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | - |
| `start` | `number` | - |
| `end` | `number` | - |

### `getBufferSavedDiff`

Get diff vs last saved snapshot for a buffer

```typescript
getBufferSavedDiff(buffer_id: number): TsBufferSavedDiff | null
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | - |

### `getAllDiagnostics`

Get all LSP diagnostics across all files

```typescript
getAllDiagnostics(): TsDiagnostic[]
```

### `getBufferText`

Get text from a buffer range
Used by vi mode plugin for yank operations - reads text without deleting.

```typescript
getBufferText(buffer_id: number, start: number, end: number): Promise<string>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Buffer ID |
| `start` | `number` | Start byte offset |
| `end` | `number` | End byte offset |

### `getEditorMode`

Get the current global editor mode

```typescript
getEditorMode(): string
```

## Buffer Info Queries

### `getBufferInfo`

Get full information about a buffer

```typescript
getBufferInfo(buffer_id: number): BufferInfo | null
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Buffer ID |

### `listBuffers`

List all open buffers

```typescript
listBuffers(): BufferInfo[]
```

### `listGrammars`

List all available grammars with source information. Returns grammars from all sources: built-in, user-installed, language packs, bundles, and plugin-registered.

```typescript
listGrammars(): GrammarInfoSnapshot[]
```

**Returns:** Array of objects with:

| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Grammar name (use this in config `grammar` field) |
| `source` | `string` | Where the grammar is from (e.g. "built-in", "plugin (myplugin)") |
| `file_extensions` | `string[]` | File extensions associated with this grammar |

### `getPrimaryCursor`

Get primary cursor with selection info

```typescript
getPrimaryCursor(): CursorInfo | null
```

### `getAllCursors`

Get all cursors (for multi-cursor support)

```typescript
getAllCursors(): CursorInfo[]
```

### `getViewport`

Get viewport information

```typescript
getViewport(): ViewportInfo | null
```

## Prompt Operations

### `startPrompt`

Start an interactive prompt

```typescript
startPrompt(label: string, prompt_type: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `label` | `string` | Label to display (e.g., "Git grep: ") |
| `prompt_type` | `string` | Type identifier (e.g., "git-grep") |

### `setPromptSuggestions`

Set suggestions for the current prompt

```typescript
setPromptSuggestions(suggestions: PromptSuggestion[]): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `suggestions` | `PromptSuggestion[]` | Array of suggestions to display |

### `setPromptTitle`

Set the title shown in a floating-overlay prompt's frame header
as styled segments. An empty array clears it and falls back to
the prompt-type default.

```typescript
setPromptTitle(title: StyledText[]): boolean
```

| Name | Type | Description |
|------|------|-------------|
| `title` | `StyledText[]` | Styled segments rendered along the overlay's top toolbar row |

### `setPromptFooter`

Set the footer chrome row of the floating-overlay prompt's
results pane. Plugins use this for hotkey-hint banners — for
example, the Orchestrator plugin renders
`↑↓ preview  Enter dive  Esc close`. Empty array clears the
footer. Has no visible effect on non-overlay prompts.

```typescript
setPromptFooter(footer: StyledText[]): boolean
```

| Name | Type | Description |
|------|------|-------------|
| `footer` | `StyledText[]` | Styled segments rendered along the overlay's bottom row |

## Buffer Mutations

### `applyTheme`

Apply a theme by name
Loads and applies the specified theme immediately. The theme can be a built-in
theme name or a custom theme from the themes directory.

```typescript
applyTheme(theme_name: string): void
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `theme_name` | `string` | Name of the theme to apply (e.g., "dark", "light", "my-custom-theme") |

### `reloadConfig`

Reload configuration from file
After a plugin saves config changes to the config file, call this to reload
the editor's in-memory configuration. This ensures the editor and plugins
stay in sync with the saved config.

```typescript
reloadConfig(): void
```

### `error`

Log an error message from a plugin
Messages appear in log file when running with RUST_LOG=error.
Use for critical errors that need attention.

```typescript
error(message: string): void
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `message` | `string` | Error message |

### `warn`

Log a warning message from a plugin
Messages appear in log file when running with RUST_LOG=warn.
Use for warnings that don't prevent operation but indicate issues.

```typescript
warn(message: string): void
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `message` | `string` | Warning message |

### `info`

Log an info message from a plugin
Messages appear in log file when running with RUST_LOG=info.
Use for important operational messages.

```typescript
info(message: string): void
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `message` | `string` | Info message |

### `setClipboard`

Copy text to the system clipboard
Copies the provided text to both the internal and system clipboard.
Uses OSC 52 and arboard for cross-platform compatibility.

```typescript
setClipboard(text: string): void
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `text` | `string` | Text to copy to clipboard |

### `insertText`

Insert text at a byte position in a buffer
Text is inserted before the byte at position. Position must be valid
(0 to buffer length). Insertion shifts all text after position.
Operation is asynchronous; returns true if command was sent successfully.

```typescript
insertText(buffer_id: number, position: number, text: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Target buffer ID |
| `position` | `number` | Byte offset where text will be inserted (must be at char boundary) |
| `text` | `string` | UTF-8 text to insert |

### `deleteRange`

Delete a byte range from a buffer
Deletes bytes from start (inclusive) to end (exclusive).
Both positions must be at valid UTF-8 char boundaries.
Operation is asynchronous; returns true if command was sent successfully.

```typescript
deleteRange(buffer_id: number, start: number, end: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Target buffer ID |
| `start` | `number` | Start byte offset (inclusive) |
| `end` | `number` | End byte offset (exclusive) |

#### `clearNamespace`

Clear all overlays in a namespace

```typescript
clearNamespace(buffer_id: number, namespace: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The buffer ID |
| `namespace` | `string` | The namespace to clear |

#### `setLineNumbers`

Enable/disable line numbers for a buffer

```typescript
setLineNumbers(buffer_id: number, enabled: boolean): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The buffer ID |
| `enabled` | `boolean` | Whether to show line numbers |

#### `addVirtualLine`

Add a virtual line above or below a source line

```typescript
addVirtualLine(buffer_id: number, position: number, text: string, fg_r: number, fg_g: number, fg_b: number, bg_r: number, bg_g: number, bg_b: number, above: boolean, namespace: string, priority: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The buffer ID |
| `position` | `number` | Byte position to anchor the virtual line to |
| `text` | `string` | The text content of the virtual line |
| `fg_r` | `number` | Foreground red color component (0-255) |
| `fg_g` | `number` | Foreground green color component (0-255) |
| `fg_b` | `number` | Foreground blue color component (0-255) |
| `bg_r` | `number` | Background red color component (0-255), -1 for transparent |
| `bg_g` | `number` | Background green color component (0-255), -1 for transparent |
| `bg_b` | `number` | Background blue color component (0-255), -1 for transparent |
| `above` | `boolean` | Whether to insert above (true) or below (false) the line |
| `namespace` | `string` | Namespace for bulk removal (e.g., "git-blame") |
| `priority` | `number` | Priority for ordering multiple lines at same position |

#### `setLineIndicator`

Set a line indicator in the gutter's indicator column

```typescript
setLineIndicator(buffer_id: number, line: number, namespace: string, symbol: string, r: number, g: number, b: number, priority: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The buffer ID |
| `line` | `number` | Line number (0-indexed) |
| `namespace` | `string` | Namespace for grouping (e.g., "git-gutter", "breakpoints") |
| `symbol` | `string` | Symbol to display (e.g., "│", "●", "★") |
| `r` | `number` | Red color component (0-255) |
| `g` | `number` | Green color component (0-255) |
| `b` | `number` | Blue color component (0-255) |
| `priority` | `number` | Priority for display when multiple indicators exist (higher wins) |

#### `clearLineIndicators`

Clear all line indicators for a specific namespace

```typescript
clearLineIndicators(buffer_id: number, namespace: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The buffer ID |
| `namespace` | `string` | Namespace to clear (e.g., "git-gutter") |

#### `setFileExplorerDecorations`

Set file explorer decorations for a namespace. Namespaces are isolated per
plugin at runtime, so different plugins may safely reuse the same namespace
label without clearing each other's explorer state.

```typescript
setFileExplorerDecorations(namespace: string, decorations: FileExplorerDecoration[]): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `namespace` | `string` | Namespace for grouping (e.g., "git-status") |
| `decorations` | `FileExplorerDecoration[]` | Decoration entries |

#### `clearFileExplorerDecorations`

Clear file explorer decorations for a namespace

```typescript
clearFileExplorerDecorations(namespace: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `namespace` | `string` | Namespace to clear (e.g., "git-status") |

#### `setFileExplorerSlots`

Set file explorer slot overrides for a namespace. Each entry can override the
leading icon, trailing badge, and/or name color for a path. Unset fields fall
back to the editor default (no icon, no badge, default name color). Use
`suppressLeading`, `suppressTrailing`, or `suppressNameColor` to explicitly
clear a slot instead of replacing it.

```typescript
setFileExplorerSlots(namespace: string, slots: FileExplorerSlotEntry[]): boolean
```

Example (git-style name coloring):

```typescript
editor.setFileExplorerSlots("git-status", [{
  path: "/project/src/main.rs",
  nameColor: { color: "ui.syntax.string" },
  priority: 10,
}]);
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `namespace` | `string` | Namespace for grouping (e.g., "git-status") |
| `slots` | `FileExplorerSlotEntry[]` | Slot override entries |

#### `clearFileExplorerSlots`

Clear file explorer slot overrides for a namespace

```typescript
clearFileExplorerSlots(namespace: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `namespace` | `string` | Namespace to clear (e.g., "git-status") |

#### `submitViewTransform`

Submit a transformed view stream for a viewport

```typescript
submitViewTransform(buffer_id: number, split_id?: number | null, start: number, end: number, tokens: ViewTokenWire[], layout_hints?: LayoutHints | null): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Buffer to apply the transform to |
| `split_id` | `number | null` (optional) | - |
| `start` | `number` | Viewport start byte |
| `end` | `number` | Viewport end byte |
| `tokens` | `ViewTokenWire[]` | Array of tokens with source offsets |
| `layout_hints` | `LayoutHints | null` (optional) | Optional layout hints (compose width, column guides) |

#### `clearViewTransform`

Clear view transform for a buffer/split (returns to normal rendering)

```typescript
clearViewTransform(buffer_id: number, split_id?: number | null): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | Buffer ID |
| `split_id` | `number | null` (optional) | Optional split ID (uses active split if not specified) |

#### `insertAtCursor`

Insert text at the current cursor position in the active buffer

```typescript
insertAtCursor(text: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `text` | `string` | The text to insert |

#### `pluginTranslate`


Translate a string for a plugin using the current locale

```typescript
pluginTranslate(plugin_name: string, key: string, args: Record<string, unknown>): string
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `plugin_name` | `string` | - |
| `key` | `string` | - |
| `args` | `Record<string, unknown>` | - |

#### `registerCommand`

Register a command in the command palette (Ctrl+P).

Usually you should omit `context` so the command is always visible.
If provided, the command is hidden unless your plugin has activated that context with `editor.setContext(name, true)` or the focused buffer's virtual mode (from `defineMode()`) matches.

```typescript
registerCommand(name: string, description: string, handlerName: string, context?: string | null): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `name` | `string` | Display name shown in the command palette |
| `description` | `string` | Description shown alongside the command |
| `handlerName` | `string` | Name of the `globalThis` function to call |
| `context` | `string \| null` | Optional custom context for visibility filtering |

#### `unregisterCommand`

Unregister a custom command by name

```typescript
unregisterCommand(name: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `name` | `string` | The name of the command to unregister |

#### `setContext`

Set or unset a custom context for command visibility
Custom contexts allow plugins to control when their commands are available.
For example, setting "config-editor" context makes config editor commands visible.

```typescript
setContext(name: string, active: boolean): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `name` | `string` | Context name (e.g., "config-editor") |
| `active` | `boolean` | Whether the context is active (true = set, false = unset) |

#### `openFile`

Open a file in the editor, optionally at a specific location

```typescript
openFile(path: string, line: number, column: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `path` | `string` | File path to open |
| `line` | `number` | Line number to jump to (0 for no jump) |
| `column` | `number` | Column number to jump to (0 for no jump) |

#### `openFileInSplit`

Open a file in a specific split pane

```typescript
openFileInSplit(split_id: number, path: string, line: number, column: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `split_id` | `number` | The split ID to open the file in |
| `path` | `string` | File path to open |
| `line` | `number` | Line number to jump to (0 for no jump) |
| `column` | `number` | Column number to jump to (0 for no jump) |

#### `spawnBackgroundProcess`

Spawn a long-running background process
Unlike spawnProcess which waits for completion, this starts a process
in the background and returns immediately with a process ID.
Use killProcess(id) to terminate the process later.
Use isProcessRunning(id) to check if it's still running.

```typescript
spawnBackgroundProcess(command: string, args: string[], cwd?: string | null): Promise<BackgroundProcessResult>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `command` | `string` | Program name (searched in PATH) or absolute path |
| `args` | `string[]` | Command arguments (each array element is one argument) |
| `cwd` | `string | null` (optional) | Working directory; null uses editor's cwd |

**Example:**

```typescript
const proc = await editor.spawnBackgroundProcess("asciinema", ["rec", "output.cast"]);
// Later...
await editor.killProcess(proc.process_id);
```

#### `killProcess`

Kill a background or cancellable process by ID
Sends SIGTERM to gracefully terminate the process.
Returns true if the process was found and killed, false if not found.

```typescript
killProcess(process_id: number): Promise<boolean>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `process_id` | `number` | ID returned from spawnBackgroundProcess or spawnProcessStart |

#### `spawnProcessWait`

Wait for a cancellable process to complete and get its result

```typescript
spawnProcessWait(process_id: number): Promise<SpawnResult>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `process_id` | `number` | ID returned from spawnProcessStart |

#### `delay`

Delay execution for a specified number of milliseconds
Useful for debouncing user input or adding delays between operations.
await editor.delay(100);  // Wait 100ms

```typescript
delay(ms: number): Promise<void>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `ms` | `number` | Number of milliseconds to delay |

**Example:**

```typescript
await editor.delay(100);  // Wait 100ms
```

#### `findBufferByPath`

Find a buffer ID by its file path

```typescript
findBufferByPath(path: string): number
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `path` | `string` | - |

#### `startPromptWithInitial`

Start a prompt with pre-filled initial value

```typescript
startPromptWithInitial(label: string, prompt_type: string, initial_value: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `label` | `string` | Label to display (e.g., "Git grep: ") |
| `prompt_type` | `string` | Type identifier (e.g., "git-grep") |
| `initial_value` | `string` | Initial text to pre-fill in the prompt |

#### `deleteTheme`

Delete a theme file by name
Only deletes files from the user's themes directory.
This is a safe operation that prevents plugins from deleting arbitrary files.

```typescript
deleteTheme(name: string): Promise<void>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `name` | `string` | Theme name (without .json extension) |

#### `createCompositeBuffer`

Create a composite buffer that displays multiple source buffers
Composite buffers allow displaying multiple underlying buffers in a single
tab/view area with custom layouts (side-by-side, stacked, unified).
This is useful for diff views, merge conflict resolution, etc.

```typescript
createCompositeBuffer(options: TsCreateCompositeBufferOptions): Promise<number>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `options` | `TsCreateCompositeBufferOptions` | Configuration for the composite buffer |

#### `updateCompositeAlignment`

Update line alignment for a composite buffer

```typescript
updateCompositeAlignment(buffer_id: number, hunks: TsCompositeHunk[]): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The composite buffer ID |
| `hunks` | `TsCompositeHunk[]` | New diff hunks for alignment |

#### `closeCompositeBuffer`

Close a composite buffer

```typescript
closeCompositeBuffer(buffer_id: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | The composite buffer ID to close |

#### `sendLspRequest`

Send an arbitrary LSP request and receive the raw JSON response

```typescript
sendLspRequest(language: string, method: string, params?: unknown | null): Promise<unknown>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `language` | `string` | Language ID (e.g., "cpp") |
| `method` | `string` | Full LSP method (e.g., "textDocument/switchSourceHeader") |
| `params` | `unknown | null` (optional) | Optional request payload |

#### `setSplitScroll`

Set the scroll position of a specific split

```typescript
setSplitScroll(split_id: number, top_byte: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `split_id` | `number` | The split ID |
| `top_byte` | `number` | The byte offset of the top visible line |

#### `setSplitRatio`

Set the ratio of a split container

```typescript
setSplitRatio(split_id: number, ratio: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `split_id` | `number` | ID of the split |
| `ratio` | `number` | Ratio between 0.0 and 1.0 (0.5 = equal split) |

#### `distributeSplitsEvenly`

Distribute all visible splits evenly
This adjusts the ratios of all container splits so each leaf split gets equal space

```typescript
distributeSplitsEvenly(): boolean
```

#### `setBufferCursor`

Set cursor position in a buffer (also scrolls viewport to show cursor)

```typescript
setBufferCursor(buffer_id: number, position: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `buffer_id` | `number` | ID of the buffer |
| `position` | `number` | Byte offset position for the cursor |

#### `executeAction`

Execute a built-in editor action by name
This is used by vi mode plugin to run motions and then check cursor position.
For example, to implement "dw" (delete word), the plugin:
1. Saves current cursor position
2. Calls executeAction("move_word_right") - cursor moves
3. Gets new cursor position
4. Deletes from old to new position

```typescript
executeAction(action_name: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `action_name` | `string` | Action name (e.g., "move_word_right", "move_line_end") |

#### `executeActions`

Execute multiple actions in sequence, each with an optional repeat count
Used by vi mode for count prefix (e.g., "3dw" = delete 3 words).
All actions execute atomically with no plugin roundtrips between them.

```typescript
executeActions(actions: ActionSpecJs[]): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `actions` | `ActionSpecJs[]` | Array of {action: string, count?: number} objects |

#### `setEditorMode`

Set the global editor mode (for modal editing like vi mode)
When a mode is set, its keybindings take precedence over normal key handling.
Pass null/undefined to clear the mode and return to normal editing.

```typescript
setEditorMode(mode?: string | null): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `mode` | `string | null` (optional) | Mode name (e.g., "vi-normal") or null to clear |

#### `showActionPopup`

Show an action popup with buttons for user interaction
When the user selects an action, the ActionPopupResult hook is fired.

```typescript
showActionPopup(options: TsActionPopupOptions): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `options` | `TsActionPopupOptions` | Popup configuration with id, title, message, and actions |

#### `disableLspForLanguage`

Disable LSP for a specific language and persist to config
This is used by LSP helper plugins to let users disable LSP for languages
where the server is not available or not working.

```typescript
disableLspForLanguage(language: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `language` | `string` | The language to disable LSP for (e.g., "python", "rust") |

#### `createScrollSyncGroup`

Create a scroll sync group for anchor-based synchronized scrolling
Used for side-by-side diff views where two panes need to scroll together.
The plugin provides the group ID (must be unique per plugin).

```typescript
createScrollSyncGroup(group_id: number, left_split: number, right_split: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `group_id` | `number` | - |
| `left_split` | `number` | - |
| `right_split` | `number` | - |

#### `setScrollSyncAnchors`

Set sync anchors for a scroll sync group
Anchors map corresponding line numbers between left and right buffers.
Each anchor is a tuple of (left_line, right_line).

```typescript
setScrollSyncAnchors(group_id: number, anchors: [number, number][]): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `group_id` | `number` | - |
| `anchors` | `[number, number][]` | - |

#### `removeScrollSyncGroup`

Remove a scroll sync group

```typescript
removeScrollSyncGroup(group_id: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `group_id` | `number` | - |

## Windows / Orchestrator API

A *window* (modelled on a VS Code window) is a project-rooted
bundle of editor state — file explorer, LSP set, file watchers,
split layout, and buffer membership — that can be swapped in
and out as a unit. The "base" window at startup is
`WindowId(1)`. Subsequent windows are created by plugins
(typically the first-party Orchestrator plugin, which uses windows
to drive parallel-agent worktrees).

> **Naming note.** Internally the editor calls these "windows"
> to disambiguate from Fresh's pre-existing workspace-recovery
> and config-layer "session" concepts. Orchestrator's UX still
> presents them as "agent sessions" because that's the
> parallel-agents domain language users see. Plugin API names
> all use `Window` / `windowId`.

See `docs/internal/orchestrator-sessions-design.md` for the full
architecture rationale.

### `createWindow`

Allocate a new session rooted at `root` with the given label.
Does not switch to it — call `setActiveWindow` to dive.
Returns the new session's id.

```typescript
createWindow(root: string, label: string): Promise<number>
```

| Name | Type | Description |
|------|------|-------------|
| `root` | `string` | Absolute path to the session root |
| `label` | `string` | Display label (empty string defaults to root basename) |

### `setActiveWindow`

Make `id` the active session. The previous active session's
state (file explorer, LSP set, splits, view states) is stashed
on its `Session` and the incoming session's stash is swapped
into the editor — O(1), no buffer recreation, no LSP restart.

```typescript
setActiveWindow(id: number): boolean
```

### `closeWindow`

Close a session. Buffers attached only to this session are
dropped; shared buffers stay open. Closing the active session
falls back to the base session.

```typescript
closeWindow(id: number): boolean
```

### `listWindows`

Snapshot of every session with its id, label, and root.

```typescript
listWindows(): WindowInfo[]
```

### `activeWindow`

Return the currently active session id.

```typescript
activeWindow(): number
```

### `prewarmWindow`

Spin up the session's LSP set + split layout in the
background, without diving. The first dive into a prewarmed
session is then instant. Useful when a plugin knows the user
will likely visit a session soon.

```typescript
prewarmWindow(id: number): boolean
```

### `previewWindowInRect`

Render the entire stashed UI of session `id` (splits,
terminals, syntax-highlighted buffers, decorations) into the
floating-overlay prompt's preview pane on the next frame.
Cleared automatically when the prompt closes; call
`clearWindowPreview` to clear earlier.

This is *Primitive #1* of the Orchestrator design and is the
mechanism the Orchestrator plugin uses to show a live preview of
the highlighted session as the user moves the selection in
the session list.

```typescript
previewWindowInRect(id: number): boolean
```

### `clearWindowPreview`

Clear an earlier `previewWindowInRect` so the preview pane
falls back to the prompt's default behaviour.

```typescript
clearWindowPreview(): boolean
```

### `setWindowState` / `getWindowState`

Per-session, per-plugin state map. Like `setGlobalState` but
scoped to a single session — useful for plugin state that
should follow a session across saves/restores rather than
applying globally. Persists to `.fresh/sessions.json`.

```typescript
setWindowState(key: string, value: unknown): boolean
getWindowState(key: string): unknown | null
```

### `openFileInBackground`

Open a file without switching to it. With `opts.windowId`,
the buffer is attached to that session's stashed tab strip
instead of the active session's. Pairs with `createTerminal`'s
`windowId` for setting up an inactive session's contents
without diving.

```typescript
openFileInBackground(path: string, opts?: { windowId?: number }): Promise<number>
```

### `watchPath` / `unwatchPath`

Subscribe to filesystem changes under `path`. Returns a
handle. Each change fires a `path_changed` hook with the
handle id, the changed path, and the change kind. Releases via
`unwatchPath(handle)`.

```typescript
watchPath(path: string): Promise<number>
unwatchPath(handle: number): boolean
```

## Terminal API

### `createTerminal`

Create a new terminal in a split. Returns a `TerminalResult`
with buffer, terminal, and split IDs.

When `opts.windowId` is set the terminal attaches to that
session's stashed split tree without diving — the user's
current view stays put and the terminal becomes visible only
when the user dives into the named session. This is how
Orchestrator spawns agents into background worktrees without
disturbing the foreground session.

```typescript
createTerminal(opts?: CreateTerminalOptions): Promise<TerminalResult>
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `opts` | `CreateTerminalOptions` | Optional terminal configuration |

### `sendTerminalInput`

Send input data to a terminal by its terminal ID.

```typescript
sendTerminalInput(terminalId: number, data: string): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `terminalId` | `number` | The terminal ID (from `TerminalResult`) |
| `data` | `string` | Data to write to the terminal PTY (UTF-8 string, may include escape sequences) |

### `closeTerminal`

Close a terminal by its terminal ID.

```typescript
closeTerminal(terminalId: number): boolean
```

**Parameters:**

| Name | Type | Description |
|------|------|-------------|
| `terminalId` | `number` | The terminal ID to close |
