Summary
Give the 3D highways (guitar / drums / keys) a per-panel camera under split-screen, driven by the Camera Director plugin, with named, shareable camera profiles and full multi-window (pop-out) support — so each panel can have its own camera and you can steer every panel, including popped-out ones on another monitor, from the main window.
This spans three repos and ships as three coordinated PRs (all open):
Camera Director's owner approved the cross-plugin work.
Architecture
A small, stable camera bridge decouples the pieces. Camera Director writes, the highways read:
window.__h3dCamCtl = { enabled, heightMul, distMul, yaw, pitch, panX, panY } — the global/focused camera.
window.__h3dCamCtlPanels = { 0: cam, 1: cam, … } | null — per-split-panel cameras by index.
Panels are identified by name (from split-screen's getPanels()), so a panel's camera follows it across tiling and pop-out, and profiles are shareable by name. Cross-window sync rides split-screen's existing BroadcastChannel('slopsmith-ss'); the main window is the single source of truth (sole store-writer) so there are no cross-window write races.
What each PR does
1. feedback #823 — highways read the per-panel camera
Each highway resolves its panel's camera via a shared _freeCamFor(canvas) helper → __h3dCamCtlPanels[panelIndexFor(canvas)], else the global __h3dCamCtl, else stock (NaN-safe). Guitar already read the global; keys and drums adopt the bridge for the first time. Renderers that don't read the map fall back to the focused camera, so behavior is unchanged when Camera Director isn't present.
2. camera_director #2 — the consumer (v3.4.0)
- Name-keyed store (v4): cameras, per-panel assignments and the edit target are keyed by panel name (migrates the old A–D slot model). Highways still read the per-index map.
- Shared named-profile library + per-panel assignment tracking (shows Custom after a manual tweak).
- Scrollable panel-name selector, editing follows the focused panel; Link all / Apply to all for across-the-board setups.
- Cross-window aggregation ("steer everything from main"): popped-out panels (which leave the main window) still appear in the selector — marked with a
⤢ — and are steerable from main. Follower windows are read-only agents that announce their panels and apply cameras by name.
- Follower-local free-camera drag: you can also drag directly on a popped-out window's canvas; the follower forwards the result to main (which clamps/persists/rebroadcasts).
3. feedBack-plugin-splitscreen #20 — panel identity
- Editable per-panel names (top-right of each panel), persisted and carried into pop-out windows.
- Panel API for panel-targeting plugins:
getPanels() -> [{index,name,canvas,focused,poppedOut}], panelName(i), setPanelName(i,name), plus a splitscreen:panels-changed event on the window.feedBack bus. A follower window exposes its own panels too, so a split follower's sub-panels are individually steerable.
- Pop/Dock button relocated to the panel's top-right next to the name (reachable even when the bottom bar is hidden).
Test matrix
- Single view + split (2–4): each panel renders its own camera across guitar/keys/drums; Link-all / Apply-to-all work.
- Pop a panel out → it still appears in main's selector (
⤢) and is steerable live; drag directly on the popped window too; close/redock drops it from the selector.
- Split a follower window → each sub-panel is steerable from main.
- Reload → cameras, assignments, names and the profile library persist.
Known caveats
- Follower sub-panels default to
P1/P2/…, which can collide with a main panel's name — since the store is globally name-keyed, same-named panels share a camera. Rename to disambiguate.
- Naming/env note: internal code still uses
slopsmith* names; splitscreen exposes both window.feedBackSplitscreen and the legacy window.slopsmithSplitscreen (consumers read feedBackSplitscreen || slopsmithSplitscreen).
Merge order
Land #823 and #20 first (or together) — they provide the read side and the panel identity that #2 consumes.
Summary
Give the 3D highways (guitar / drums / keys) a per-panel camera under split-screen, driven by the Camera Director plugin, with named, shareable camera profiles and full multi-window (pop-out) support — so each panel can have its own camera and you can steer every panel, including popped-out ones on another monitor, from the main window.
This spans three repos and ships as three coordinated PRs (all open):
Camera Director's owner approved the cross-plugin work.
Architecture
A small, stable camera bridge decouples the pieces. Camera Director writes, the highways read:
window.__h3dCamCtl = { enabled, heightMul, distMul, yaw, pitch, panX, panY }— the global/focused camera.window.__h3dCamCtlPanels = { 0: cam, 1: cam, … } | null— per-split-panel cameras by index.Panels are identified by name (from split-screen's
getPanels()), so a panel's camera follows it across tiling and pop-out, and profiles are shareable by name. Cross-window sync rides split-screen's existingBroadcastChannel('slopsmith-ss'); the main window is the single source of truth (sole store-writer) so there are no cross-window write races.What each PR does
1. feedback #823 — highways read the per-panel camera
Each highway resolves its panel's camera via a shared
_freeCamFor(canvas)helper →__h3dCamCtlPanels[panelIndexFor(canvas)], else the global__h3dCamCtl, else stock (NaN-safe). Guitar already read the global; keys and drums adopt the bridge for the first time. Renderers that don't read the map fall back to the focused camera, so behavior is unchanged when Camera Director isn't present.2. camera_director #2 — the consumer (v3.4.0)
⤢— and are steerable from main. Follower windows are read-only agents that announce their panels and apply cameras by name.3. feedBack-plugin-splitscreen #20 — panel identity
getPanels() -> [{index,name,canvas,focused,poppedOut}],panelName(i),setPanelName(i,name), plus asplitscreen:panels-changedevent on thewindow.feedBackbus. A follower window exposes its own panels too, so a split follower's sub-panels are individually steerable.Test matrix
⤢) and is steerable live; drag directly on the popped window too; close/redock drops it from the selector.Known caveats
P1/P2/…, which can collide with a main panel's name — since the store is globally name-keyed, same-named panels share a camera. Rename to disambiguate.slopsmith*names; splitscreen exposes bothwindow.feedBackSplitscreenand the legacywindow.slopsmithSplitscreen(consumers readfeedBackSplitscreen || slopsmithSplitscreen).Merge order
Land #823 and #20 first (or together) — they provide the read side and the panel identity that #2 consumes.