Expand description
§Store previews and icons
The plugin store shows each plugin’s page with a live preview: your plugin’s real UI half, run by the viewer in a sandbox, drawing every surface it declares on a strip of tiles. A large surface (a stage, a pane, a column, a popover) can be expanded to the size of the store’s window. The person browsing can click a tile and use it with the keyboard and pointer before installing.
What the store does without any work from you:
- It shows each surface your manifest declares, at the size the store gives
it, and sends the plugin the usual
resizeandvisibilityevents. - It runs each command whose
opensnames a surface that must be opened (a stage, a popover, a pane or a column), so the surface starts as it would when a person opens it. Give every such command anopens. - For a surface the viewer makes one of per pane, machine or project
(
pane.footer,machine.after,project.after, …), it shows one instance, for the first pane, machine or project of a sample account.
What the sandbox gives your plugin: a sample account of two machines, two
projects and three panes (one agent working, one waiting); values,
secrets, claims and configuration that start empty and vanish with the
preview; calls that answer unavailable (your daemon half never runs);
and no URL opener. When your manifest asks for events.on:system.agents.*,
an agent pane finishes or starts a turn every few seconds.
§Your plugin’s part: its own preview state
A plugin whose surfaces show data from its daemon, a service or the account would show only “Loading…” or “No data” in that sandbox. Each plugin is responsible for a preview that shows what it really looks like, and for keeping it current as the plugin changes.
The store sets the value <your id>.standard-preview to true before your
plugin activates. It is in your own namespace, so reading it needs no
grant. When it is true:
- Build your state from sample data instead of reading it: realistic values that show every part of every surface (a full card, a warning state, a long name that truncates).
- Make no calls and start no requests: they answer
unavailable. - Keep animating as usual, so the preview moves as the plugin does.
const PREVIEW: &str = "my-plugin.standard-preview";
fn activate(&mut self, cx: &mut Context) {
let preview = cx.values().get::<bool>(PREVIEW).ok().flatten() == Some(true);
if preview {
self.state = State::sample();
return;
}
// ... the real start: read values, call the daemon, watch the account
}Keep State::sample() beside your real state and update it in the same
change as any new field or surface, so the preview never falls behind. A
unit test that activates the plugin with the preview value set and checks
that it paints without a call is the cheapest way to keep it honest:
let mut host = MockHost::new("my-plugin");
host.values.insert("my-plugin.standard-preview".into(), "true".into());The preview is still your plugin’s real code drawing: only the data is yours to choose. Show what a person will see, not a better version of it.
§Your plugin’s icon
The store shows an icon beside your plugin’s name, drawn by terminals that
support Kitty graphics (others show a tile with your plugin’s initials).
For a first-party plugin, put a square 256×256 PNG of at most 64 KiB in the
plugin’s directory as icon.png: the release carries it in the package’s
metadata and the registry serves it to the store. Make it show what your
plugin draws, readable at 32 px, with no text.