PluginsEarly
Events
What a plugin can listen for with on(...), what each one carries and what its handler may return.
From the plugin interface of gr 0.2.16 (API 0.1, 0.2, 0.3). gr plugin api events prints it on your machine.
A plugin listens with on(kind, handler). The handler gets the payload and, for events marked "with context", the app's context. Its time is held to the budget named.
load
The script was loaded for a root.
- Payload
- none
- Returns
void- Sent by
- daemon
- Budget
- load
unload
The instance is going away.
- Payload
{ reason: "idle" | "disabled" | "removed" | "reload" }- Returns
void- Sent by
- daemon
- Budget
- unload
command
One of the plugin's commands was run from a key, the palette, the command line or a menu. From a menu a right click opened (since 0.2), menu is its place (one of [[menus]]' places, or row for a row's own menu) and args is [target], a MenuTarget, or a row menu entry's args.
- Payload
{ command: string; args: Json[]; menu?: string }- Returns
Cmd[] | void- Sent by
- app, with context
- Budget
- handler
view
A panel or float wants its tree.
- Payload
{ view: string }- Returns
Element- Sent by
- app, daemon, with context
- Budget
- view
rows
A windowed list, or (0.2) table, wants items [start, end); list is its key; rows for another rev are dropped.
- Payload
{ view: string; rev: number; list: string; start: number; end: number }- Returns
{ rev: number; items: Element[] }- Sent by
- app
- Budget
- rows
act
The person acted on an element of a view; value is an input's text, a select's chosen value, or a toggle's true or false (0.2); column is the column a table's sort asks for (0.2)
- Payload
{ view: string; element: string; act: "activate" | "select" | "press" | "submit" | "change" | "open" | "close" | "sort"; item?: string; value?: string | boolean; column?: string }- Returns
Cmd[] | void- Sent by
- app, with context
- Budget
- handler
picker
A picker wants its entries.
- Payload
{ picker: string; query: string }- Returns
Entry[]- Sent by
- app, with context
- Budget
- rows
pick
The person chose an entry.
- Payload
{ picker: string; entry: string; how: "open" | "split" | "vsplit" | "tab" }- Returns
Cmd[] | void- Sent by
- app, with context
- Budget
- handler
focus
Focus moved; at most 10 a second.
- Payload
- none
- Returns
void- Sent by
- app, with context
- Budget
- background
file.opened
A file was opened.
- Payload
{ path: string }- Returns
void- Sent by
- app, with context
- Budget
- background
file.saved
A file was saved.
- Payload
{ path: string }- Returns
void- Sent by
- app, with context
- Budget
- background
file.closed
A file was closed.
- Payload
{ path: string }- Returns
void- Sent by
- app
- Budget
- background
mode
The mode changed.
- Payload
{ mode: Context["mode"] }- Returns
void- Sent by
- app, with context
- Budget
- background
theme
The theme changed.
- Payload
{ name: string; dark: boolean }- Returns
void- Sent by
- app
- Budget
- background
agent.started
An agent started.
- Payload
{ agent: string }- Returns
void- Sent by
- daemon
- Budget
- background
agent.stopped
An agent is no longer running (stopped, asleep or archived); not that its turn ended, which is agent.attention with attention idle.
- Payload
{ agent: string }- Returns
void- Sent by
- daemon
- Budget
- background
agent.attention
An agent asks for the person: attention (0.2) says what for; 0.1 named it kind, which the event's own kind hides.
- Payload
{ agent: string; attention: "permission" | "question" | "plan" | "idle" }- Returns
void- Sent by
- daemon
- Budget
- background
graph.changed
Nodes changed (at most 1,000), or all; coalesced to one a 250 ms.
- Payload
{ nodes?: string[]; all?: true }- Returns
void- Sent by
- daemon
- Budget
- background
settings
The plugin's settings changed.
- Payload
{ settings: Record<string, Json> }- Returns
void- Sent by
- daemon
- Budget
- background
files.changed
Files of the root changed on disk (at most 1,000 paths, slash-separated and within the root, .git's own files left out), or all; coalesced to one a 250 ms.
- Payload
{ paths?: string[]; all?: true }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
agent.state
An agent's state changed (Agent.state's words), with what it waits for when it waits.
- Payload
{ agent: string; state: string; attention?: string }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
shown
One of the plugin's views began to show in some window.
- Payload
{ view: string }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
hidden
One of the plugin's views shows in no window any more.
- Payload
{ view: string }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.opened
A document of a language the plugin's [documents] names (every language when the table names none) was opened in an editor of the root.
- Payload
{ path: string; version: number; language: string; dirty: boolean }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.changed
A document's text changed; one a document every 50 ms at most; changes, in order, only with fs:read:workspace and under 64 KiB (absent: read it again)
- Payload
{ path: string; version: number; changes?: TextEdit[] }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.saved
A document was saved at version.
- Payload
{ path: string; version: number }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.closed
A document was closed, or its window went away.
- Payload
{ path: string }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.viewport
The lines of a document on screen changed (top to bottom, from 0)
- Payload
{ path: string; version: number; top: number; bottom: number }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
document.edited
The answer to the plugin's own edit at version: applied, or refused and why; now is the version after it, or the document's current one.
- Payload
{ path: string; version: number; applied: boolean; reason?: "moved" | "closed" | "readonly" | "shape"; now?: number }- Returns
void- Sent by
- app
- Budget
- background
- Since
- API 0.2
tool
An agent called one of the plugin's [[tools]], through Graphide's MCP server, where it is plugin_<plugin>_<verb>: tool is its name (<plugin>.<verb>), input what the agent gave, already checked against its [tools.input]. A tool is declared in plugin.toml with [allow] agents = ["tools"]: [[tools]] name, description (at most 1,000 characters: what the agent reads), read_only (it changes nothing), and [tools.input] required = [...] with [tools.input.properties.<name>] type (string, number, integer, boolean or array), description, enum (strings) and items (an array's, not an array). The handler computes within a command's budget and may wait on $ calls; the agent waits at most 30 s in all for the answer (tool.timeout). Example: on("tool", { tool: "todos.list" }, async (e) => ({ text: await todosOf(e.input.path) }))
- Payload
{ tool: string; input: Record<string, Json> }- Returns
ToolAnswer- Sent by
- daemon
- Budget
- handler
- Since
- API 0.2
mention
The person is typing an @ in an agent's composer: source is one of the plugin's [[mentions]] by its id, query what follows the @ (or the @<prefix>:, which asks this source alone). Asked once typing rests (80 ms), on the editor's threads, never holding a key: an answer that comes after the next question is dropped, and one that never comes shows nothing. At most 50 entries (mention_entries), the first kept. A source is declared in plugin.toml as [[mentions]] id = "<plugin>.<name>" (in the plugin's one set of ids), title (the heading its entries show under, at most 40 characters) and prefix (1 to 16 lowercase letters, digits and hyphens, beginning with a letter: @<prefix>: narrows the list to it). Example: on("mention", { source: "issues.open" }, async (e) => (await open(e.query)).map((i) => ({ key: i.id, label: i.title, attach: { text: i.body } })))
- Payload
{ source: string; query: string }- Returns
MentionEntry[]- Sent by
- app
- Budget
- rows
- Since
- API 0.2
mention.pick
The person chose an entry of a mention source (entry is its key); the editor has attached what the entry said already, and tells the plugin after.
- Payload
{ source: string; entry: string }- Returns
Cmd[] | void- Sent by
- app
- Budget
- handler
- Since
- API 0.2
layer.left
One of the plugin's own key layers was left: a key it does not bind (which then went on where it was going), Escape, its timeout, a change of the keys' mode, or the plugin's own layer.exit; told to that plugin alone.
- Payload
{ layer: string; why: "key" | "escape" | "timeout" | "mode" | "exit" }- Returns
void- Sent by
- app
- Budget
- background
- Since
- API 0.2
complete
The person is typing a word in a file of a language one of the plugin's [[completions]] lists (or any file, for one that lists none): path is the file within the root, version its version in the editor, at the caret (a Pos), prefix the word before the caret, trigger the character just typed where it is one the file's language server completes after. Asked 30 ms after a word's first keys rest, after a trigger character and when the person asks for completions (Ctrl+Space), at most once a pause, on the editor's threads and never holding a key: the menu shows at once and the items join it when they come; an answer after the next question, or later than 250 ms after the question, is dropped. The editor narrows the answer itself as the word goes on, without asking again. Items list after the language server's, in the order given, the first 100 kept: label is what the menu shows and what is typed over the prefix unless insert says other text (plain text), detail a few words after it, kind one of LSP's kinds in lower case (function, method, variable, field, class, struct, interface, module, keyword, snippet, constant, ...) marking it with that kind's colour. Needs fs:read:workspace: the prefix is the file's text, and a plugin not granted it is asked nothing (gr plugin check warns of [[completions]] without it). Example: on("complete", async (e) => ({ items: (await words(e.path)).filter((w) => w.startsWith(e.prefix)).map((w) => ({ label: w, kind: "text" })) }))
- Payload
{ path: string; version: number; at: Pos; trigger?: string; prefix: string }- Returns
{ items: { label: string; detail?: string; insert?: string; kind?: string }[] }- Sent by
- app
- Budget
- rows
- Since
- API 0.2
lsp.diagnostics
What the language servers say of these files of the root changed (at most 1,000 paths, slash-separated and within the root): ask lsp.diagnostics for the ones shown. No path means more changed than an event names: ask for them all. Coalesced to one a 250 ms.
- Payload
{ paths: string[] }- Returns
void- Sent by
- daemon
- Budget
- background
- Since
- API 0.2
These docs describe Graphide 0.2.16, the latest release. Something wrong or missing? Tell us onDiscord or at hello@graphide.com.