Skip to main content

Module previews

Module previews 

Source
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 resize and visibility events.
  • It runs each command whose opens names 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 an opens.
  • 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:

  1. 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).
  2. Make no calls and start no requests: they answer unavailable.
  3. 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.