mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-10 02:15:02 +00:00
feat(plugins): let a plugin put the cursor in any agent's composer
A pill could insert text but left the cursor elsewhere, so the user had to click the composer before typing. Plugins can address any agent's composer, and an author cannot know where the user is, so focus() owns revealing the agent as well as focusing it. A running agent's composer holds one pending request and navigates to the agent, completing when an input that can take the keyboard attaches. Drafts and the New workspace composer have nothing to navigate to and must not pull the user back, so they reject when off screen.
This commit is contained in:
@@ -1953,6 +1953,7 @@ export default function contribute(client: PluginClientContext) {
|
||||
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `context` | `"agent"` for a running agent, `"draft"` for a new agent in a workspace, `"new-workspace"` for the New workspace screen. |
|
||||
| `prompt`, `setPrompt(text)`, `insertText(text)` | The prompt. `insertText` replaces the selection or inserts at the cursor. |
|
||||
| `focus(options?)` | Puts the cursor in this composer, ready to type, and resolves when it is there. `selection: { start, end }` places the cursor or selects text, as offsets into `prompt` clamped to its length; omit it to keep the cursor where it is. See [focus a composer](#focus-a-composer). |
|
||||
| `attachments`, `addAttachment`, `removeAttachment` | Items from [your own attachment sources](#add-a-composer-attachment-source). Attachments from other plugins and Paseo's own are not listed. |
|
||||
| `agent.provider`, `model`, `mode`, `thinking` | What the agent runs with, or what the draft will create. |
|
||||
| `agent.options` | The provider options in effect, defaults included. In a draft: the options the agent will be created with, including your changes. |
|
||||
@@ -1992,11 +1993,39 @@ The [composer controls example](https://github.com/getpaseo/paseo/tree/main/plug
|
||||
registers a `Select`, two `Toggle`s (one writes a provider option) and an `Action`, plus an
|
||||
Explorer panel and a Command Center item that reach a composer by reference.
|
||||
|
||||
### Focus a composer
|
||||
|
||||
`insertText` and `setPrompt` do not move the cursor. Call `focus()` after them so the user can keep
|
||||
typing:
|
||||
|
||||
```tsx
|
||||
composer.insertText("- [ ] Tests\n");
|
||||
await composer.focus();
|
||||
```
|
||||
|
||||
`await composer.focus({ selection: { start: 0, end: composer.prompt.length } })` selects the prompt.
|
||||
|
||||
You do not need to know where the user is. On a running agent's composer, Paseo navigates to the
|
||||
agent's tab or workspace when it is not on screen, closes a phone's agent list or Explorer panel
|
||||
that covers it, and moves focus to the agent's pane, then focuses the input. When the input is
|
||||
already on screen and focused in its pane, `focus()` only puts the cursor there. A phone opens its
|
||||
keyboard.
|
||||
|
||||
`focus()` rejects when the composer cannot take focus: the agent was archived, its composer did not
|
||||
come on screen within 15 seconds, or the input refused focus. A rejected promise returned from a
|
||||
button's `onPress` or a Command Center item's `onSelect` shows the user an error toast.
|
||||
|
||||
Drafts and the New workspace composer are only reachable from an item inside them, so `focus()` does
|
||||
not navigate to them. It works while the composer is on screen, and rejects with "The composer is
|
||||
not on screen." when the user has already moved on. A locked composer accepts focus.
|
||||
|
||||
### Reach a composer by reference
|
||||
|
||||
A running agent has a composer whether or not its tab is open. `useComposers(selector)` in any plugin
|
||||
component, and `composers(selector)` on every command context, return the same `PluginComposer` that
|
||||
`useComposer()` gives an item, for an agent you name or for the focused one:
|
||||
component, `composers(selector)` on every command context, and `client.composers(selector)` on the
|
||||
client context return the same `PluginComposer` that `useComposer()` gives an item, for an agent you
|
||||
name or for the focused one. Use `client.composers` in a callback that receives no context, such as a
|
||||
[pill's `onPress`](#composer-pills):
|
||||
|
||||
```tsx
|
||||
import { useComposers, type PluginClientContext } from "@getpaseo/plugin/client";
|
||||
@@ -2054,6 +2083,7 @@ you reach from outside:
|
||||
rejects with the provider's message. `model` and `thinking` read what the model picker
|
||||
shows at that moment: until the provider's catalog loads that is the id the agent reports or was
|
||||
created with, which may be an alias, and once it loads the catalog's own id.
|
||||
- `focus()` reveals the composer first, as [described above](#focus-a-composer).
|
||||
- Drafts and the New workspace screen are not addressable by reference. Only an item inside one
|
||||
reaches its composer, through `useComposer()`.
|
||||
|
||||
@@ -2158,6 +2188,31 @@ const pill = client.addComposerPill({
|
||||
});
|
||||
```
|
||||
|
||||
A pill's `onPress` receives no arguments. Reach its agent's composer with
|
||||
[`client.composers`](#reach-a-composer-by-reference):
|
||||
|
||||
```tsx
|
||||
const pill = client.addComposerPill({
|
||||
id: "hello",
|
||||
workspaceId,
|
||||
agentId,
|
||||
button: {
|
||||
title: "Add HELLO to the composer",
|
||||
icon: "Hand",
|
||||
label: "Hello",
|
||||
behavior: {
|
||||
kind: "action",
|
||||
async onPress() {
|
||||
for (const composer of client.composers({ agentId })) {
|
||||
composer.insertText("HELLO");
|
||||
await composer.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For pills that follow the agent directory, use an explicit [owned list subscription](/docs/sdk/events#follow-one-agents-status).
|
||||
The [local plugin example](https://github.com/getpaseo/paseo/blob/main/plugin-examples/local-plugin/client/main.tsx)
|
||||
replaces registrations on each snapshot and aborts the observation during entry cleanup, including pending bootstrap.
|
||||
|
||||
Reference in New Issue
Block a user