# Plugin Maker and Theme Studio

> Make a plugin or a theme and see it live in a sandbox, then package it for the plugin index.

Source: https://wolos.pages.dev/manual/plugin-maker

## What it is

Plugin Maker is the app for writing a shell plugin: an editor that checks as you type, a preview of the menu bar, Dock, desktop and Control Center, and Create Package and Share. Theme Studio is the same idea for themes, with colour sliders and live contrast checks.

## Why

A half-written plugin should not run on your real desktop with real permissions. The preview runs the same plugin host in a sandbox that cannot run commands or reach the web, so you see how it looks without risk.

## How

1. **Open it:** Settings > Appearance > *Make a theme*, Store > Plugins > *Create Plugin*, or run `wol-plugin-maker`.
   You see the start page with *Open Plugin* and *Create Plugin*.
2. **Create Plugin:** enter an id and switch on the kinds you want. A folder is written to `~/.local/share/wolos/plugins/<id>/`; an existing folder is never written into.
3. **Edit and Save.** The *Problems* list under the editor runs the same checks as `wol plugin check`.
   You see the preview redraw the menu bar, Dock, desktop and Control Center.
4. **Create Package** writes `<id>-<version>.wolplug` next to the folder; **Share** shows how to send it to the plugin index.
5. **Theme Studio:** *Make a theme* opens it; move the sliders, read the contrast badges, then *Save Theme* (refused if a rule fails).

## If something goes wrong

| What you see | What to do |
|---|---|
| Problems under the editor | The same checks as `wol plugin check`, run as you type. Fix them before Create Package. |
| Create Package asks you to save first | Save, so it does not pack old files. |
| Create Plugin refuses an existing folder | An existing folder is never written into; choose a new id. A write error leaves no half-written folder. |
| Theme Studio will not save | A theme that does not pass the contrast rules is refused. A saved theme never overwrites another with the same name. |
| The preview shows nothing happening | The preview runs no commands and reads nothing from the web; it renders on load, on each click and on each save. |

## Related

[Plugin API reference](/manual/plugins) · [wol-hue](/manual/hue) · [The Store](/manual/store)

## Reference

Plugin Maker (`wol-plugin-maker`) is the app where a person or an AI agent makes a shell plugin or a theme and sees it live, without turning anything on in their own desktop.

Decision: [ADR 0031](/manual/adr-0031), bullet "Plugin Maker / Theme Studio". Code: `crates/shell/src/ui/maker/` (`mod.rs` app and pages, `editor.rs`, `preview.rs`, `theme_studio.rs`, `publish.rs`), `crates/shell/src/bin/wol-plugin-maker.rs`, sentences in `crates/shell/src/copy_maker.rs`. Desktop entry: `desktop/overlay/usr/share/applications/org.wolos.plugin-maker.desktop` (icon `wolos-plugin-maker`, `design/icons/apps/`). The plugin API it edits is [plugins.md](/manual/plugins).

### Why

A plugin is a folder of text files, and `wol plugin new|check|run|pack` already covers making one from a terminal. What the terminal cannot show is how the plugin looks where it lands: an item in the menu bar, a card on the desktop, a Dock entry, a Control Center tile. Turning the plugin on to see it means a half-written plugin runs on the real desktop with real permissions. The app closes that gap: the same checks as the CLI, the same plugin host as the desktop, in a sandbox. The Theme Studio does the same for themes: sliders over the 13 tokens with the contrast contract measured while they move, so an unreadable theme is visible before it is saved.

### How

Launch: `wol-plugin-maker` (start page), `wol-plugin-maker <plugin folder>`, or `wol-plugin-maker --theme [theme.json]` (Theme Studio). It is one window per session: a second launch opens its argument in the first window (`gtk::Application` id `org.wolos.plugin-maker`). Settings > Appearance has a "Make a theme" row (`crates/shell/src/ui/settings/appearance.rs`, `--theme`) and Store > Plugins a Create Plugin button (`crates/shell/src/ui/store_plugins.rs`); both start the app through `ui::maker::launch`.

| Part | What it does | Code |
|---|---|---|
| Start page | Open Plugin (a folder chooser) or Create Plugin: an id and the kinds to add as switches (the ten kinds of `maker::KINDS`: menu bar, widget, Dock, tile, panel, search, toggle, hotkey, web app, theme; `[[look]]` and `[[hypr]]` are planned, not offered, see [ADR 0031](/manual/adr-0031)'s note on planned kinds), written by `wol_core::plugins::scaffold`, the function `wol plugin new` calls, into `~/.local/share/wolos/plugins/<id>/`. An existing folder is never written into. | `editor::create`, `Maker::start_page` |
| Editor | `plugin.toml` and every `.lua`, `.json`, `.toml` and `.conf` file of the folder in a monospace text view (a plain `GtkTextView`: the workspace has no GtkSourceView binding, see below). Save writes each changed file through a fresh file in the same folder, synced, then renamed over the old one. | `editor::Editor`, `editor::save_atomic` |
| Problems | Under the editor. The same function as `wol plugin check`: `wol_core::plugins::check_full` (the folder check: manifest, id, files named, symlinks, size, theme contrast; a `[[look]]` or `[[hypr]]` section is refused as planned, `shared::plugins::Kind::planned_sentence`) with the Lua step `wol_lua::plugin::lua_problems` (every script loaded with the host's deadlines and run once). The app's services act on nothing; the CLI's run commands. Unsaved text is checked in a scratch copy of the folder, so the list follows the typing; it reruns 400 ms after the last keystroke (`editor::CHECK_AFTER`), off the GTK thread, and only the newest result is shown (`editor::Turns`). | `editor::check_folder`, `editor::check_draft` |
| Preview | The plugin loaded through the shell's own plugin host (`ui::plugins::Host`) and drawn on four surfaces: a menu bar strip with its items in the slots the default layout gives them, a Dock strip, its desktop cards, its Control Center tiles. Deadlines and the three-strike rule are the desktop's: a failing entry shows its sentence where it sits, and three failures in a row turn the plugin off in the preview. Reloads after every save. | `preview::Preview`, `preview::surfaces` |
| Sandbox | The preview runs no command and reads nothing from the web (`wol.run` and `wol.http_get` answer with a sentence); state, toggles and the enable list live in `$XDG_RUNTIME_DIR/wol-plugin-maker-<pid>/`, removed with the window. Nothing in the preview touches the person's `plugins.json`, toggles or plugin state. | `preview::PreviewServices`, `preview::sandbox` |
| Check | The folder as saved, now. | `Editor::check` |
| Create Package | Save first: with unsaved edits it only says so (`maker::pack_gate`, `copy_maker::SAVE_FIRST`), so the package holds what the editor shows. Then `wol plugin pack <folder> --yes --json`: the CLI's own pack (`.wolplug`, the format in `crates/core/src/package.rs`), written next to the folder; then the SHA-256 and size of the file. | `Maker::pack`, `publish::pack` |
| Share | A sheet with the plugin index request as text: where to put the file, the index entry (format 1, `wopkg::index::plugins::Entry`) with the file's SHA-256 and size, and the `gh pr create` command for the `wolos-plugins` index repository. Copy Steps puts it on the clipboard. The app sends nothing; the index CI runs `wol plugin check` again and signs the package. | `publish::steps`, `copy::maker::publish_steps` |
| Theme Studio | Starts on the theme in use, or a `theme.json` opened (a theme that extends another is resolved against the installed catalogue). Two sliders per token group (Surfaces, Text, Accent, Status Colors): lightness moves OKLab L by up to 0.3 either way, color scales OKLab chroma from grey to twice as vivid; hue and alpha are kept. A translucent token is written as `#rrggbbaa`. | `theme_studio::shift`, `theme_studio::apply` |
| Contrast | One badge per rule of `shared::theme::CONTRAST_RULES`, measured the way `shared::theme::validate` measures it (a translucent background over the surface, the surface over white); the line under them is `validate`'s verdict. Save Theme refuses a theme `validate` does not pass. | `theme_studio::badges`, `theme_studio::problems` |
| Previews of a theme | The theme gallery card (`kit::widgets::theme_preview`, as Settings > Appearance and the picker draw themes) and the terminal palette (`wol_core::theme::terminal::palette`, the 16 colours every terminal gets). | `theme_studio::Studio` |
| Use Wallpaper Colors | The desktop picture decoded small off the GTK thread, `wol_core::theme::generate::from_samples` with the same variant; the sliders go back to the middle. | `theme_studio::samples_of` |
| Save Theme | `$XDG_CONFIG_HOME/wolos/themes/<id>/theme.json` (id from the name, never `wol-*`), written atomically, so Settings > Appearance and `wol theme list` show it. | `theme_studio::save_user_theme` |

Rules it keeps: every read, check, save and pack runs off the GTK thread (`ui::maker::off_thread`); every sentence is in `copy_maker.rs` and passes `copy::lint`; widgets are Kit parts except the text view, the share text's monospace label and the terminal strip's drawing area.

Not in v0.12: syntax colouring (the editor stays a plain text view; GtkSourceView would be a new C dependency with its own theme work), and refresh timers in the preview (an entry renders on load, on each click and on each save, not every `refresh` seconds).

The Theme Studio and [hue.md](/manual/hue) overlap on purpose for now: `wol-hue` is the command-line palette engine and the Studio is the window; in a later release the Studio will call `wol-hue` for palette extraction instead of its own wallpaper sampling.

### Tests

- `crates/shell/src/ui/maker/editor.rs` tests: Create Plugin writes exactly the CLI scaffold and it checks clean; drafts give the folder check's own sentences and leave the folder and the temp folder clean; atomic save; only the newest edit is current; 400 ms.
- `crates/shell/src/ui/maker/preview.rs` tests: a scaffolded plugin with a menu bar item, a card, a Dock entry and a tile renders on all four surfaces without a display; a failing entry shows its sentence and the third failure turns the plugin off, in the sandbox only; a slow entry stops at the host deadline; the sandbox runs nothing.
- `crates/shell/src/ui/maker/theme_studio.rs` tests: OKLab round trip and a known value, shifts keep hue and alpha, a slider moves only its group, the badges fail exactly the rules `validate` fails, a saved theme loads from the catalogue and an unreadable one is not saved.
- `crates/shell/src/ui/maker/publish.rs` tests: the pack is the CLI verb; the index entry pins the file hash and reads back as an index entry.
- Goldens `maker-editor`, `maker-preview`, `maker-theme`, `maker-start`, `maker-share` (light and dark): `crates/shell/tests/golden_cases/maker.rs`.
