diff --git a/docs/plugin-panes.md b/docs/plugin-panes.md new file mode 100644 index 0000000..56085c3 --- /dev/null +++ b/docs/plugin-panes.md @@ -0,0 +1,213 @@ +# Detachable panes (`window.feedBack.panes`) + +A **pane** is live UI that stays put: a mixer, a camera rig, a readout, a settings +board. You author it once, and the host decides where it lives — docked beside the +player, or popped out into its own OS window that remembers where you put it and +minimizes to the system tray. + +Panes exist because the player's rail popovers are **exclusive**: opening one closes +the last. You cannot watch the mixer while riding the camera, and both vanish the +moment you want to look at the highway. Panes are non-exclusive, and they survive +song switches. + +--- + +## The two-line version + +If your plugin already has a dialog, give it a pop-out chip: + +```js +feedBack.panes.register({ + id: 'camera_director', + title: 'Camera Director', + icon: '🎥', + mount(root, ctx) { root.appendChild(buildCameraUI(ctx)); }, // your existing builder + unmount(root) { root.replaceChildren(); }, +}); + +feedBack.panes.attachChip(myDialogEl, 'camera_director'); +``` + +`attachChip()` injects **the** standard ⇱ button — same glyph, same place, same +behaviour everywhere. Clicking it opens the pane in its host and **hides your +dialog**, leaving a "⇲ … is popped out" stub in its place. Closing the pane +un-hides your dialog and restores the chip. + +**You write no show/hide logic.** Core owns it, so that every plugin's pop-out +behaves identically — which is the entire point. + +--- + +## The one rule + +> **`mount(root, ctx)` runs in a realm that may not have the app in it.** + +Docked, your pane runs in the main window with everything present. Popped out, it +runs in a **separate JS realm** — a different window, with no `window.highway`, no +`window.feedBack.capabilities`, no `