diff --git a/plugins/highway_3d/screen.js b/plugins/highway_3d/screen.js index beeaf12..cad20cc 100644 --- a/plugins/highway_3d/screen.js +++ b/plugins/highway_3d/screen.js @@ -1681,15 +1681,26 @@ let _aspectPanelRO = null; // readout
let _aspectPanelRAF = 0; // readout poll handle let _aspectTargetSel = null; // the "Target" . let _aspectPanesDirty = true; + // Monotonic counter handing each renderer instance a stable pane id. + let _aspectPaneCounter = 0; + function _aspectNowMs() { + try { return (performance && performance.now) ? performance.now() : 0; } catch (e) { return 0; } + } + function _aspectPaneLabel(arrangement, uid) { + const a = (typeof arrangement === 'string') ? arrangement.trim() : ''; + return a || ('Pane ' + uid); + } // Get-or-create the shared bridge object, seeded from defaults + localStorage. // May carry a sparse `__panels` map of per-pane overrides. @@ -1708,8 +1719,10 @@ function _aspectPersist() { try { const t = _aspectTune(), out = {}; + // Persist only the shared base. Per-pane overrides (__panels) are + // keyed by ephemeral instance ids, so they're intentionally + // session-only — persisting them would leak stale keys forever. Object.keys(_ASPECT_DEFAULTS).forEach((k) => { out[k] = t[k]; }); - if (t.__panels) out.__panels = t.__panels; localStorage.setItem(_ASPECT_LS, JSON.stringify(out)); } catch (e) {} } @@ -1724,18 +1737,27 @@ Object.keys(_ASPECT_DEFAULTS).forEach((k) => { out[k] = (k in ov) ? ov[k] : base[k]; }); return out; } - // Record a live pane so the Target dropdown can list it. Label prefers the - // arrangement name (e.g. "Panel 1 — Rhythm") so panes are easy to tell apart. - function _aspectRegisterPane(paneKey, arrangement) { + // Record a live pane so the Target dropdown can list it. Called every frame + // by each renderer with its stable pane key + arrangement. `seen` is + // refreshed each call for pruning; the dropdown is only marked dirty when a + // pane is newly added or its label first resolves — not on every re-report, + // which would flicker the