Editors¶
Product::with_editor(Editor { html, width, height, resizable }) gives a product
a web editor: one HTML page of up to 1 MiB, between 64 and 4096 pixels in each
direction. Resizable editors may grow to four times their size.
The editor is built only with the ravet-plugin feature editor (umbrella
plugin-editor), which adds ravet-webview:
ravet-plugin = { version = "…", features = ["clap", "native", "editor"] }
Without it, clap and native build no web view: the CLAP export offers no
GUI extension and the native table reports a 0 × 0 editor, so adapters show
none. Products without an editor leave the feature off and link no WebKit or
Cocoa bindings. The delay and gain examples turn it on.
The editor opens in every format that has an embedded editor on the platform (decision 0293):
| Platform | Web view | CLAP | VST3 | LV2 | AAX | AUv2 |
|---|---|---|---|---|---|---|
| macOS | Cocoa/WebKit | cocoa |
NSView |
CocoaUI |
NSView |
Cocoa view |
| Windows | Win32/WebView2 | win32 |
HWND |
WindowsUI |
HWND |
— |
| Linux | GTK 3/WebKitGTK 4.1 on X11 or XWayland | x11 |
X11EmbedWindowID |
X11UI |
— | — |
Windows needs the WebView2 Runtime installed (Windows 11 ships it); editor
sizes there are the host's physical pixels, and WebView2 scales page content by
the monitor's DPI, so above 100 % display scaling a fixed-size page has fewer
CSS pixels than its declared size. Keep layouts responsive; content-scale
negotiation is open work. On Linux the parent is an X11 window, which need not
be an XEmbed socket: the editor's GtkPlug is mapped inside plain host windows
such as REAPER's. GTK work is pumped by editor_idle (VST3 IRunLoop timer,
LV2 idle interface) or a CLAP host timer; hosts already running GTK on Wayland,
or built on GTK 2/GTK 4, are refused. Keyboard input in Windows and Linux
editors is not validated yet. Evidence and hosts are listed in the
support matrix and work status.
Editor assets¶
Images, fonts, scripts and styles need not be inlined into the 1 MiB page or
sent through 64 KiB messages. Declare them as EditorAssets and refer to them
by relative URL:
```rust,ignore use ravet_plugin::{Editor, EditorAsset};
const ASSETS: &[EditorAsset] = &[ EditorAsset::new("app.js", include_bytes!("../editor/app.js")), EditorAsset::new("img/knob.png", include_bytes!("../editor/img/knob.png")), // An explicit content type when the extension does not give one. EditorAsset { path: "data/curve.bin", mime: "application/octet-stream", data: include_bytes!("../editor/curve.bin") }, ]; // Product::new(…).with_editor(Editor { … }).with_editor_assets(ASSETS) ```
The page then refers to each asset by its relative path, exactly as declared:
a stylesheet link to style.css, a script element for app.js and an image
whose source is img/knob.png.
With assets, the page itself is served as index.html from a local asset
origin that also serves them: ravet://editor/ on macOS,
ravet-<id>://editor/ on Linux and https://editor.ravet.invalid/ on Windows.
Requests are answered from the binary's memory (GET only, 404 for unknown
paths); nothing is read from disk and nothing reaches the network. Products
without assets keep the inline page. Limits, checked by Product::resolve
(ProductError::InvalidEditorAsset): at most 4,096 assets, 64 MiB each and
256 MiB in total; paths of at most 512 bytes from ASCII letters, digits and
._-/, with no ., .. or empty segment; index.html is reserved. An empty
mime infers the type from the extension (HTML, CSS, JavaScript, JSON, SVG,
PNG, JPEG, GIF, WebP, ICO, WOFF/WOFF2, TTF/OTF, WASM, WAV, MP3, text), else
application/octet-stream. Each request copies the asset once. The Gain
example ships asset.css this way.
RAVET injects its editor client into the page's <head>. The client speaks the
editor protocol and exposes window.ravetPlugin:
| Member | Purpose |
|---|---|
info |
Product and parameter metadata, or null until it arrives |
value(key), normalized(key) |
Current physical or 0–1 value |
on("info" \| "values" \| "product", listener) |
Metadata, value updates ({values, changed}) and product messages |
set(key, value), setNormalized(key, n) |
One complete edit |
begin(key), update(key, value), updateNormalized(key, n), end(key) |
A continuous gesture |
send(data) |
A product-channel message; PluginExport::editor_message may reply |
On macOS, focusing a text field in the page takes keyboard focus from hosts that keep it for their own shortcuts, such as Pro Tools, and gives it back when the field loses focus; see keyboard focus. The editor protocol is unchanged.
examples/gain-plugin/editor/index.html is a minimal example;
examples/delay-plugin/editor/index.html draws knobs and a division selector.
Editor edits update the parameter cache immediately, then travel through a bounded lock-free queue (256 edits) to whichever thread delivers parameter events. That thread sends the host gesture-begin, value and gesture-end events, and gives the processor the values at the start of the block; host automation later in the block still wins. A complete edit is queued all at once or not at all. If the queue is full, the edit is dropped and the page keeps showing the host's value. Closing the editor ends every gesture the page left open.
The web view asks the plug-in for its next message 20 times a second.
Queued messages (info, product replies) go first, then a snapshot, which is
rebuilt only when a value changes. Messages are limited to 64 KiB. A product with
so many parameters that info exceeds this receives no info. Static data
belongs in editor assets; larger dynamic data still has to be
split by the product.