fix(highway_3d): _bgPanelKey rejects non-integer panel index; JSDoc bridge fns

- _bgPanelKey() treated any non-null panelIndexFor() return as a valid panel id,
  so a NaN/non-finite index minted a bogus "panelNaN" localStorage key instead
  of falling back to "main". Gate on Number.isInteger(idx) && idx >= 0. (The
  camera path is already NaN-safe — panelsMap[NaN] misses and falls through.)
- Add a NaN/negative-index case to the resolver tests (drum 22, keys 57, pass).
- Convert the camera-bridge helpers' comments to JSDoc (_bgPanelKey, _freeCamFor,
  _resolveFreeCam, _ssApi across the three plugins) to lift docstring coverage on
  the changed surface. Comment/robustness only; no behavior change beyond the
  NaN guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
This commit is contained in:
Kris Anderson 2026-07-09 16:27:56 -04:00
parent a6a5186180
commit bcee2e8610
5 changed files with 68 additions and 24 deletions

View File

@ -1365,15 +1365,24 @@
* Camera Director bridge resolver (pure exported via createFactory.__test)
* ====================================================================== */
// Resolve the active splitscreen API, defensive on the global-name rename in
// flight (feedBackSplitscreen is canonical; slopsmithSplitscreen is the legacy
// alias). Returns null when splitscreen isn't present.
/**
* The active splitscreen API, defensive on the global-name rename in flight
* (feedBackSplitscreen is canonical; slopsmithSplitscreen is the legacy alias).
* @returns {object|null} the splitscreen API, or null when not present
*/
function _ssApi() { return window.feedBackSplitscreen || window.slopsmithSplitscreen || null; }
// Given the splitscreen API, THIS window's per-panel camera map, and the
// global camera, return this panel's camera under splitscreen, else the
// global, else null (Camera Director absent → stock framing). Throw-safe on
// panelIndexFor so a misbehaving splitscreen build can't break framing.
/**
* Resolve the Camera Director camera for a canvas: this panel's camera under
* splitscreen, else the global, else null (Camera Director absent stock
* framing). Throw-safe on panelIndexFor so a misbehaving splitscreen build
* can't break framing.
* @param {HTMLCanvasElement} canvas this renderer's highway canvas
* @param {object|null} ss the splitscreen API (see _ssApi)
* @param {object|null} panelsMap window.__h3dCamCtlPanels (per-panel cameras by index)
* @param {object|null} globalCam window.__h3dCamCtl (single global camera)
* @returns {object|null} the resolved free-camera bridge, or null
*/
function _resolveFreeCam(canvas, ss, panelsMap, globalCam) {
if (panelsMap && ss && typeof ss.panelIndexFor === 'function') {
try {

View File

@ -56,6 +56,13 @@ test('_resolveFreeCam: throw-safe on panelIndexFor → falls back to global', ()
assert.equal(__test._resolveFreeCam({}, ss, { 0: {} }, g), g);
});
test('_resolveFreeCam: NaN/invalid panel index → falls back to global', () => {
const { __test } = load();
const g = { id: 'global' };
assert.equal(__test._resolveFreeCam({}, { panelIndexFor: () => NaN }, { 0: {} }, g), g);
assert.equal(__test._resolveFreeCam({}, { panelIndexFor: () => -1 }, { 0: {} }, g), g);
});
test('_ssApi: null when neither global set; slopsmith alias; feedBack canonical wins', () => {
const { window, __test } = load();
assert.equal(__test._ssApi(), null);

View File

@ -2595,24 +2595,36 @@
}
const FRET_NUMBER_GHOST_SCOPE_IDS = ['chords', 'all'];
/**
* localStorage panel key for per-panel background settings ('main' or
* 'panel<index>'). Defensive on the splitscreen global-name rename in flight,
* and throw-safe on panelIndexFor same as _freeCamFor so a misbehaving
* splitscreen build can't take down background-settings resolution. Only a
* non-negative integer index yields a 'panel<N>' key; anything else (null,
* NaN, negative, non-integer) falls back to 'main' so a bad index can never
* mint a bogus "panelNaN"-style key.
* @param {HTMLCanvasElement} canvas this renderer's highway canvas
* @returns {string} 'main' or 'panel<index>'
*/
function _bgPanelKey(canvas) {
// Defensive on the splitscreen global name (rename in flight) AND throw-safe
// on panelIndexFor — same as _freeCamFor — so a misbehaving splitscreen
// build can't take down background-settings resolution. Falls back to 'main'.
const ss = window.feedBackSplitscreen || window.slopsmithSplitscreen;
let idx = null;
if (ss && typeof ss.panelIndexFor === 'function') {
try { idx = ss.panelIndexFor(canvas); } catch (e) { idx = null; }
}
return (idx == null) ? 'main' : 'panel' + idx;
return (Number.isInteger(idx) && idx >= 0) ? 'panel' + idx : 'main';
}
// Camera Director bridge resolver. Prefers THIS panel's per-panel camera
// under splitscreen (window.__h3dCamCtlPanels[panelIndex]) and falls back to
// the single global (window.__h3dCamCtl); returns null when Camera Director
// is absent → 100% stock framing. Defensive on the splitscreen global name
// (rename in flight: feedBackSplitscreen vs slopsmithSplitscreen). Mirrors
// the panel resolution in _bgPanelKey.
/**
* Camera Director bridge resolver. Prefers THIS panel's per-panel camera under
* splitscreen (window.__h3dCamCtlPanels[panelIndex]) and falls back to the
* single global (window.__h3dCamCtl); returns null when Camera Director is
* absent 100% stock framing. Defensive on the splitscreen global-name rename
* in flight (feedBackSplitscreen vs slopsmithSplitscreen); throw-safe on
* panelIndexFor. Mirrors the panel resolution in _bgPanelKey.
* @param {HTMLCanvasElement} canvas this renderer's highway canvas
* @returns {object|null} the resolved free-camera bridge, or null
*/
function _freeCamFor(canvas) {
const map = window.__h3dCamCtlPanels;
if (map) {

View File

@ -1604,15 +1604,24 @@
* Camera Director bridge resolver (pure exported via createFactory.__test)
* ====================================================================== */
// Resolve the active splitscreen API, defensive on the global-name rename in
// flight (feedBackSplitscreen is canonical; slopsmithSplitscreen is the legacy
// alias). Returns null when splitscreen isn't present.
/**
* The active splitscreen API, defensive on the global-name rename in flight
* (feedBackSplitscreen is canonical; slopsmithSplitscreen is the legacy alias).
* @returns {object|null} the splitscreen API, or null when not present
*/
function _ssApi() { return window.feedBackSplitscreen || window.slopsmithSplitscreen || null; }
// Given the splitscreen API, THIS window's per-panel camera map, and the
// global camera, return this panel's camera under splitscreen, else the
// global, else null (Camera Director absent → 100% stock framing). Throw-safe
// on panelIndexFor so a misbehaving splitscreen build can't break framing.
/**
* Resolve the Camera Director camera for a canvas: this panel's camera under
* splitscreen, else the global, else null (Camera Director absent 100% stock
* framing). Throw-safe on panelIndexFor so a misbehaving splitscreen build
* can't break framing.
* @param {HTMLCanvasElement} canvas this renderer's highway canvas
* @param {object|null} ss the splitscreen API (see _ssApi)
* @param {object|null} panelsMap window.__h3dCamCtlPanels (per-panel cameras by index)
* @param {object|null} globalCam window.__h3dCamCtl (single global camera)
* @returns {object|null} the resolved free-camera bridge, or null
*/
function _resolveFreeCam(canvas, ss, panelsMap, globalCam) {
if (panelsMap && ss && typeof ss.panelIndexFor === 'function') {
try {

View File

@ -56,6 +56,13 @@ test('_resolveFreeCam: throw-safe on panelIndexFor → falls back to global', ()
assert.equal(__test._resolveFreeCam({}, ss, { 0: {} }, g), g);
});
test('_resolveFreeCam: NaN/invalid panel index → falls back to global', () => {
const { __test } = load();
const g = { id: 'global' };
assert.equal(__test._resolveFreeCam({}, { panelIndexFor: () => NaN }, { 0: {} }, g), g);
assert.equal(__test._resolveFreeCam({}, { panelIndexFor: () => -1 }, { 0: {} }, g), g);
});
test('_ssApi: null when neither global set; slopsmith alias; feedBack canonical wins', () => {
const { window, __test } = load();
assert.equal(__test._ssApi(), null);