standard-plugin-sdk 0.1.1

Write Standard Code plugins in Rust: wasm components against standard:plugin@2.0.0
Documentation
# Input

Keys, pastes and the pointer reach a UI plugin as events on its surfaces
(`event-types` in the SDK's `wit/plugin.wit`). The SDK types them:
`Event::Key(Key)`, `Event::Paste { surface, text }`,
`Event::Pointer(Pointer)`.

## Which surface hears what

- An open `stage`, `panel.popover` or `pane.overlay` takes every key but
  Escape, every paste, and the pointer over it. A `column` opens with
  focus and keeps it until Escape (which closes it) or a press elsewhere;
  a press on it gives it focus again. A `pane` has input focus while its
  workspace pane has focus: keys reach it as they reach a terminal pane
  (the viewer keeps its leader and pane navigation keys), and a press on it
  focuses its pane. Escape (or a click outside) closes it:
  the plugin sees `Event::Focus { focused: false }` and the surface's
  visibility go.
- Any surface receives the pointer over it. A press on a surface whose
  manifest sets `"input": true` gives it focus (`Event::Focus`); keys and
  pastes then go to it until Escape or a press elsewhere.
- A key, a paste, a pointer press and a command are *gestures*: while it
  handles one, the plugin may open its stage with `cx.open(id)` (the
  surface it handles the gesture on becomes the opener, which places a
  popover and colours a column; see
  [the manifest]manifest.md#surfaces), and while
  it handles one or within a second after, open one web page with
  `cx.open_url(url)` (grant `url.open:<host>`; see
  [grants]grants.md#opening-urls).
- A gesture reaches only the viewer the user acts in, so a call sent while
  handling one goes out once. A call sent from anything else (a value
  change, a plugin event, a resize, `frame()`) goes out once per open
  viewer: the [double-play]grants.md#double-play mistake. Send calls
  from gestures only, and move other effects to the companion
  ([working together]working-together.md#running-in-several-viewers).

## Keys

`Key { surface, code, text, modifiers, phase }`:

| Field | Values |
| --- | --- |
| `code` | `KeyCode::Char(c)` for a character key (the character it types: `a`, `A`, `1`, `?`); `Enter`, `Tab`, `BackTab`, `Backspace`, `Delete`, `Insert`, `Space`, `Left`, `Right`, `Up`, `Down`, `Home`, `End`, `PageUp`, `PageDown`, `F(1)`..`F(12)`. On the wire: the character, or `enter`, `tab`, `backtab`, `backspace`, `delete`, `insert`, `space`, `left`, `right`, `up`, `down`, `home`, `end`, `pageup`, `pagedown`, `f1`..`f12` |
| `text` | What the key types, when it types text (not with Ctrl or Super) |
| `modifiers` | `Modifiers`: shift 1, ctrl 2, alt 4, super 8 (`.shift()`, `.ctrl()`, ...) |
| `phase` | `Press`, `Repeat`, and `Release` where the terminal reports releases |

Escape never arrives. Shift+Enter arrives as `Enter` with `shift` in
every browser. A native viewer delivers it with `shift` while a text field
has the keys: the surface that takes keys has set a caret
([text fields](#text-fields-and-the-caret)) and the terminal speaks the
kitty keyboard protocol. Terminals report Shift on Enter only when they
send every key as an escape code, so the viewer asks for that mode while
such a caret has the keys and asks for its usual mode when the caret lets
them go. Typed text stays the same in that mode, Caps Lock and Option or
AltGr characters included. Without a caret, or on a terminal without the
protocol (Terminal.app among them), Shift+Enter arrives as a plain `Enter`
and Alt+Enter as `Enter` with `alt`, so a composer that starts a line on
Shift+Enter should accept Alt+Enter too.

## Text fields and the caret

A text field draws no cursor of its own: it tells the viewer where its
caret stands with `surface.set_caret(Some((col, row)))` (or
`cx.set_caret(id, ...)`), in cells of the surface, and `None` when no
field of the surface has the caret. While that surface takes keys (an open
stage, popover or pane overlay, or the surface with input focus) the
viewer draws its own cursor at that cell and its cursor trail moves there
from the pane, and back when the surface lets the keys go. The caret stays
until set again, so set it whenever the field repaints. In a native viewer
the caret is also what makes Shift+Enter arrive with `shift`
([keys](#keys)).

## The pointer

`Pointer { surface, x, y, col, row, button, kind, modifiers }`:

| Field | Values |
| --- | --- |
| `x`, `y` | The position in the surface's units: cells for a cells surface; surface pixels for a pixels surface. The browser reports the exact pixel, and motion inside a cell; a terminal reports cells, so a native viewer sends the cell's centre |
| `col`, `row` | The cell under the pointer |
| `kind` | `Down`, `Up`, `Move` (no button), `Drag` (a button held), `Wheel`; `Enter` when the pointer moves onto the surface (before its first `Move`) and `Leave` when it moves off, or the surface stops showing under it (at the last position on it), so a hover begins and ends exactly |
| `button` | `Left`, `Middle`, `Right`, `None`; for `Wheel`: `WheelUp`, `WheelDown`, `WheelLeft`, `WheelRight` (wire numbers 1, 2, 3, 0, 4 to 7) |

A sidebar surface (`sidebar.card`, `machine.after`, `project.after`,
`project.before`) receives no vertical `Wheel`: the sidebar scrolls instead,
so a surface row that scrolls under a resting pointer never stops the list.
No surface receives a sideways `Wheel`; it scrolls the workspace.

## Repainting after input

A commit made in `event` is not a frame request. Repaint and call
`cx.request_frame()`, or change the state `frame()` reads and request a
frame; see [rendering](rendering.md#cadence).