review: address PR #694 findings

- isVisible() forces a fresh DOM sample (was serving the rAF loop's
  throttled cache, contradicting its 'live DOM check' docstring).
- v3 chrome: reconcile the edge-driven overControls hover flag against
  matches(':hover') on the throttled ~6 Hz tick — covers a missed
  mouseleave (flag stuck true, transport never hides) and a re-created
  #player-controls node with lost listeners.
- highway_3d pre-warm now also covers teachFg/teachSd label textures
  and the technique sprite factories (mute X, hammer/pull triangles,
  bend chevrons, slide arrows) per active-palette string colour, plus
  a maintenance note tying new label styles to the warm list.
- Document that the visibility throttle's manual invalidations are
  latency-only (periodic resample self-heals within ~10 frames), and
  why highway_3d keeps its local lowerBoundT (downlevel hosts).

External-repo audit (finding 1): staffview, tabview, piano, drums,
keys_highway_3d, drum_highway_3d grepped — no cross-frame bundle
retention or bundle-identity checks found.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
OmikronApex
2026-07-02 00:35:24 +02:00
co-authored by Claude Fable 5
parent 77547af110
commit 1e9741043b
3 changed files with 57 additions and 0 deletions
+36
View File
@@ -1236,6 +1236,11 @@
// Binary lower-bound: returns the first index i in arr where arr[i].t >= t.
// Assumes arr is sorted ascending by .t (bundle.notes / bundle.chords always are).
// Byte-identical to core's bundle.lowerBoundT — kept as a local because this
// plugin must run on downlevel hosts whose bundles don't carry the helper
// (it's called from ~30 sites incl. top-level helpers that don't receive a
// bundle). New code that already holds a bundle should prefer
// bundle.lowerBoundT / bundle.lowerBoundTime.
function lowerBoundT(arr, t) {
let lo = 0, hi = arr.length;
while (lo < hi) {
@@ -9928,16 +9933,47 @@
if (mat && mat.map && ren) ren.initTexture(mat.map);
}
function _prewarmStatic() {
// MAINTENANCE NOTE: this list must cover every deterministic
// (chart-independent) material/texture the per-frame paths can
// request lazily. Adding a new label style or sprite factory to
// drawNote()/update() without warming it here silently
// reintroduces a first-appearance texSubImage2D/compile spike
// mid-song. Chart-dependent labels (chord names, section names)
// live in _prewarmChart.
try {
if (ren && scene && cam) ren.compile(scene, cam);
} catch (e) { console.warn('[3D-Hwy] prewarm compile:', e); }
try {
// Fret-number labels in the per-frame style/colour combos.
for (let f = 0; f <= NFRETS; f++) {
_prewarmTex(txtMat(f, FRET_LABEL_GOLD_HEX, false, 'noteFret'));
_prewarmTex(txtMat(f, FRET_LABEL_GOLD_HEX, false, 'fretRow'));
_prewarmTex(txtMat(f, FRET_LABEL_IDLE_HEX, false, 'fretRow'));
_prewarmTex(txtMat(f, '#ffffff', false, 'ghostFret'));
}
// Teaching marks (drawNote _drawTeachMark): finger hints
// T/1-4 (teachFg) and scale degrees 0-11 (teachSd).
_prewarmTex(txtMat('T', '#7fd1ff', false, 'teachFg'));
for (let i = 1; i <= 4; i++) _prewarmTex(txtMat(String(i), '#7fd1ff', false, 'teachFg'));
for (let i = 0; i <= 11; i++) _prewarmTex(txtMat(String(i), '#ffcc66', false, 'teachSd'));
// Technique sprite factories (own caches, keyed by packed
// number): PM/FH mute X, hammer/pull triangles, bend
// chevron stacks, slide direction arrows — per string
// colour of the active palette.
_prewarmTex(palmMuteXSpriteMat());
_prewarmTex(fretHandMuteXSpriteMat());
const _nWarm = Math.min(
Math.max(nStr, 6),
(activePalette && activePalette.length) || 0);
for (let s = 0; s < _nWarm; s++) {
const hex = activePalette[s] || 0xffffff;
_prewarmTex(triMat(true, hex));
_prewarmTex(triMat(false, hex));
for (let st = 1; st <= 4; st++) _prewarmTex(bendChevronMat(st, hex));
const arrowHex = darkenHex(hex, 0.55);
_prewarmTex(slideArrowMat(true, arrowHex));
_prewarmTex(slideArrowMat(false, arrowHex));
}
} catch (e) { console.warn('[3D-Hwy] prewarm labels:', e); }
}
function _prewarmChart(bundle) {