Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ screen.js
| `splitscreenAlwaysSplit` | `'true'`/`'false'` |
| `splitscreenPanelPrefs` | JSON array of per-panel pref objects (see below) |
| `splitscreenControlsHidden` | `'true'`/`'false'` — whether bottom controls bar was hidden |
| `splitscreenRoomKey` | Persistent 6-char LAN room key (splitscreen#21) — survives sessions so viewer bookmarks stay valid; rotated only via settings Regenerate |
| `splitscreenLanShareActive` | `'true'` while a LAN share is live — read on load to auto-resume the share after a crash/reload |
| `splitscreenLanShareCfg` | JSON of the shared panel's follower config (detect fields stripped) — what `config` replies carry |

### Panel pref object shape (in `splitscreenPanelPrefs`)

Expand Down Expand Up @@ -262,6 +265,18 @@ A panel can be detached into its own browser window (`⇱ Pop`) for multi-monito

**Caveats.** localStorage is shared between the windows; per-panel viz keys (e.g. `h3d_bg_panel<N>_*`) written by both are last-write-wins (acceptable). The `ss-follower` chrome-hiding CSS keys on hardcoded element ids — brittle if core renames them (deliberate tradeoff vs. chasing every id). Re-popping the same panel isn't possible (it's removed from the layout on the first pop); multiple *different* panels can be popped at once (tracked in the `popups` Map by `popupId`).

## LAN share / remote followers (splitscreen#21)

A second follower transport: the server's `/ws/sync/{session_id}` relay (feedBack#1030, core ≥ the #1032 merge) carries the SAME message protocol as the BroadcastChannel to browsers on **other machines**. `_followerBusHandler(msg)` is the shared dispatch for both transports; popups wire it to `ch.onmessage` in `buildFollowerLayout`, remote viewers wire it to the relay socket.

**Main side.** `📡 LAN` (per-panel, next to `⇱ Pop`; `panel.lanBtn`, main-window only) starts a share: `startLanShare(panel)` captures the panel's follower config **with detect fields stripped** into `_lanShare.cfg`, connects `_lanConnect()` to `/ws/sync/<room key>`, and opens the share dialog (`_showLanShareModal` — key big, join URLs from the desktop preload's `network.getLanAccess().urls` when available else `location.origin`, Copy, Stop). `_lanSend(msg)` mirrors every broadcast-channel post over the relay — `time` frames throttled to ~20 Hz (`LAN_TIME_MIN_INTERVAL_MS`); the local BC leg stays ≤60 Hz. The broadcaster now runs when `popups.size > 0 || _lanShare`. Relay-specific messages: viewers send `{type:'hello', popupId}`; the host answers each with `{type:'config', popupId, filename, cfg, t, playing}` (only once `currentFilename` exists — viewers hello-poll until then); `{type:'share-ended'}` is sent by `stopLanShare()` and is the ONLY terminal signal for viewers. Sharing is persistent: `_maybeResumeLanShare()` re-arms an active share at plugin load (crash/reload recovery), and the playSong wrapper re-arms the broadcaster.

**Viewer side.** `?ss=<key>` (and no `ssFollower`) parses into `REMOTE_JOIN` (via `normalizeRoomKey` — 6 chars, `ROOM_KEY_ALPHABET`, case-insensitive). `bootRemoteJoin()` shows the waiting overlay and connects; on the first `config` it builds `FOLLOWER = makeRemoteFollowerCfg(msg, popupId)` (**`FOLLOWER` is `let` now**; `remote: true`; detect stripped) and runs the normal `bootFollowerMode()` — chart data still streams per-panel over `/ws/highway` directly, only clock/session messages ride the relay. Reconnect: infinite backoff (1s→10s cap), re-`hello` on every open; `main-closed` puts a REMOTE viewer into the recoverable waiting overlay (host reload/relaunch auto-resumes), NOT the orphan overlay — only `share-ended` orphans it. Remote viewers hide the Dock button, never post `docked`/`closed`, and never bind input devices.

**Room key.** `ensureRoomKey()` — persistent per-install (`splitscreenRoomKey`), settings section displays it with Regenerate (rotating stops any live share). Alphabet `ABCDEFGHJKMNPQRSTVWXYZ23456789` (no 0/O/1/I/L/U) makes keys read-aloud-safe; `buildShareUrl(origin, key)` → `<origin>/?ss=<KEY>`.

**Test-env note.** The end-of-IIFE boot gates remote-join/share-resume behind `_nodeTestEnv` (modern node has a global `WebSocket`, so a capability check alone would open real sockets under `node tests/screen.test.js`).

## `playSong` wrapper and the `_onReady` race

The plugin wraps `window.playSong` to:
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ Every popup has a small toolbar pinned to its bottom edge with a **Layout** pick

> Pop-out uses standard `window.open` and `BroadcastChannel`. The popup must be triggered by your click (a user gesture) so popup blockers should leave it alone — but if your browser does block it (or doesn't support `BroadcastChannel`), the panel stays put and you get a brief notice instead. If you close the main window while popups are open, each popup detects it and shows a "main window closed" notice so you know to close it (best-effort — if the browser doesn't deliver that last message the popup just freezes, same as before).

### Sharing a panel to other devices on your network (📡 LAN)

Where **⇱ Pop** opens a follower window on *this* machine, **📡 LAN** (next to it, top-right of every panel) lets a browser on **another machine** — a laptop, a tablet, a TV browser — watch that panel live, synced to this machine's playback. The room hears one audio source (yours); each viewer gets their own screen-sized highway.

Click **📡 LAN** and you get a dialog with a six-character **room key** (e.g. `K7TR4M`) and a join URL like `http://192.168.1.20:8000/?ss=K7TR4M`. Read the key across the room or have viewers type the URL once and **bookmark it — the key is permanent** for your install (see below), so next session they just tap the bookmark. Viewers are *mirrors*, not moves: the panel stays in your layout, and the viewer window can re-split itself (Single/Top-Bottom/Left-Right/Quad) without affecting you. Note-detection is never forwarded to viewers, and viewers have no Dock button.

The **room key** is generated once and saved (Settings → Split Screen shows it, with a **Regenerate** button that rotates it — invalidating old bookmarks). Sharing survives crashes and restarts: if the app (or just the tab) goes down mid-song, viewers show "Reconnecting…" and recover automatically when you're back — the share re-arms itself on launch. Ending it is explicit: **Stop sharing** in the LAN dialog, which tells every viewer the session is over.

Requirements & caveats:

- The server must be reachable on your LAN. On the desktop app, enable **LAN access** (Plugin Manager → Network); Docker/standalone servers just need the port reachable. Your OS firewall may prompt once.
- Needs a server with the `/ws/sync` relay endpoint (feedBack ≥ the #1030 relay). On an older server, LAN sharing simply won't connect; everything else is unaffected.
Comment thread
topkoa marked this conversation as resolved.
- Viewer devices need WebGL2 for the 3D highway (any recent tablet/laptop is fine; the renderer auto-scales on weak GPUs).
- Browsers only keep screens awake on secure pages, so a tablet viewing over plain `http://` may sleep mid-song — tap it awake, or raise the device's screen-timeout for the session.
- If your machine's LAN IP changes (DHCP), bookmarked URLs go stale — give it a DHCP reservation in your router if that bites.

## Settings

Open **Settings → Split Screen** to pick the default layout (Top/Bottom, Left/Right, Tri 1+2, Tri 2+1, or Quad). The choice is stored in `localStorage` as `splitscreenLayout` and applies the next time you toggle split screen on.
Expand Down
4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "splitscreen",
"name": "Split Screen",
"version": "1.10.8",
"version": "1.11.0",
"private": false,
"settings": {
"html": "settings.html",
Expand All @@ -10,6 +10,6 @@
"script": "screen.js",
"tour": "tour.json",
"category": "creation",
"description": "Practice multiple arrangements at once — split into 2–4 synced panels, each with its own view, or pop one out to another monitor.",
"description": "Practice multiple arrangements at once — split into 2–4 synced panels, each with its own view, pop one out to another monitor, or share one to other devices on your network.",
"icon": "assets/thumb.svg"
}
Loading
Loading