mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-22 12:52:29 +00:00
Address review: resolve cache, Date.now fallback, prune-on-open, rename
- Memoize _resolveTuneFor per pane, invalidated by a revision bumped on every tune mutation (all writes funnel through _aspectPersist). Panes with an override no longer rebuild the merged object every frame; panes without one still return the base directly. - _aspectNowMs falls back to Date.now() when the Performance API is absent, so pane/readout pruning still works in older/borrowed contexts. - _setAspectPanelVisible prunes stale panes before the first dropdown build, so panes from a prior song/split don't flash until the first RAF tick. - Rename _abShortcutRegistered/_registerAspectAbShortcut to _tunerShortcutRegistered/_registerTunerShortcut — the shortcut opens/closes the tuner now, it isn't an A/B toggle. - Fix a stale 'pane1' example in a comment (keys are 'arr:<name>'/'pane:<uid>'). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com>
This commit is contained in:
parent
58047e6ad6
commit
24d24ef2cf
@ -1614,11 +1614,11 @@
|
||||
// itself out); it's a harmless debug control, so it is never unregistered.
|
||||
// No-ops where the core shortcut API isn't present (older core / borrowed
|
||||
// contexts).
|
||||
let _abShortcutRegistered = false;
|
||||
function _registerAspectAbShortcut() {
|
||||
if (_abShortcutRegistered) return;
|
||||
let _tunerShortcutRegistered = false;
|
||||
function _registerTunerShortcut() {
|
||||
if (_tunerShortcutRegistered) return;
|
||||
if (typeof window.registerShortcut !== 'function') return;
|
||||
_abShortcutRegistered = true;
|
||||
_tunerShortcutRegistered = true;
|
||||
try {
|
||||
window.registerShortcut({
|
||||
key: 'A', // uppercase e.key → produced with Shift held (Shift+A)
|
||||
@ -1632,7 +1632,7 @@
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
_abShortcutRegistered = false; // allow a later retry if it threw
|
||||
_tunerShortcutRegistered = false; // allow a later retry if it threw
|
||||
}
|
||||
}
|
||||
|
||||
@ -1685,8 +1685,8 @@
|
||||
let _aspectHfovCb = null; // hfov-override checkbox (synced explicitly)
|
||||
let _aspectHfovSl = null; // hfov-override slider
|
||||
// Which pane the panel edits. '' = all panes (writes the shared base object);
|
||||
// a pane key (e.g. 'pane1') writes that pane's sparse override, so one split
|
||||
// pane can be framed independently of the others.
|
||||
// a pane key ('arr:<name>' or the fallback 'pane:<uid>') writes that pane's
|
||||
// sparse override, so one split pane can be framed independently.
|
||||
let _aspectEditTarget = '';
|
||||
// Bumped when the SET of live panes changes (add/prune) so the panel rebuilds
|
||||
// the Target dropdown — never on a per-frame label re-report, which would
|
||||
@ -1696,7 +1696,8 @@
|
||||
// arrangement name to key by).
|
||||
let _aspectPaneCounter = 0;
|
||||
function _aspectNowMs() {
|
||||
try { return (performance && performance.now) ? performance.now() : 0; } catch (e) { return 0; }
|
||||
try { if (performance && performance.now) return performance.now(); } catch (e) {}
|
||||
try { return Date.now(); } catch (e) { return 0; } // keep pruning functional
|
||||
}
|
||||
// Pane key: prefer the arrangement name ('arr:Bass') so a pane's framing is
|
||||
// stable across songs AND distinct between split panes, with no dependency on
|
||||
@ -1727,7 +1728,11 @@
|
||||
}
|
||||
return t;
|
||||
}
|
||||
// Bumped on every tune mutation (all writes funnel through _aspectPersist) so
|
||||
// the per-pane resolve cache below can invalidate cheaply.
|
||||
let _aspectRev = 0;
|
||||
function _aspectPersist() {
|
||||
_aspectRev++;
|
||||
try {
|
||||
const t = _aspectTune(), out = {};
|
||||
Object.keys(_ASPECT_DEFAULTS).forEach((k) => { out[k] = t[k]; });
|
||||
@ -1747,13 +1752,20 @@
|
||||
}
|
||||
|
||||
// Resolve the effective tune for a pane: the shared base, with that pane's
|
||||
// override keys (if any) laid on top. Called every frame per renderer.
|
||||
// override keys (if any) laid on top. Called every frame per renderer, so the
|
||||
// merged object is memoized per pane and only rebuilt when the tune mutates
|
||||
// (_aspectRev changes). Panes with no override return the base directly (no
|
||||
// allocation).
|
||||
const _aspectResolveCache = new Map(); // paneKey -> { rev, obj }
|
||||
function _resolveTuneFor(paneKey) {
|
||||
const base = _aspectTune();
|
||||
const ov = base.__panels && base.__panels[paneKey];
|
||||
if (!ov) return base;
|
||||
const c = _aspectResolveCache.get(paneKey);
|
||||
if (c && c.rev === _aspectRev) return c.obj;
|
||||
const out = {};
|
||||
Object.keys(_ASPECT_DEFAULTS).forEach((k) => { out[k] = (k in ov) ? ov[k] : base[k]; });
|
||||
_aspectResolveCache.set(paneKey, { rev: _aspectRev, obj: out });
|
||||
return out;
|
||||
}
|
||||
// Record a live pane so the Target dropdown can list it. Called every frame
|
||||
@ -2037,7 +2049,9 @@
|
||||
if (!_aspectPanelEl) return;
|
||||
_aspectPanelEl.style.display = on ? 'block' : 'none';
|
||||
window.__h3dAspectPanelOpen = !!on; // gates the per-frame readout publish
|
||||
if (on) { _aspectBuildTargets(); }
|
||||
// Prune before the first build so panes from a prior song/split don't
|
||||
// flash in the dropdown until the first RAF tick.
|
||||
if (on) { _aspectPrunePanes(); _aspectBuildTargets(); }
|
||||
if (on && !_aspectPanelRAF) {
|
||||
const tick = () => {
|
||||
if (!window.__h3dAspectPanelOpen) { _aspectPanelRAF = 0; return; }
|
||||
@ -14898,7 +14912,7 @@
|
||||
_destroyed = _isReady = false;
|
||||
_isFocused = true;
|
||||
if (!_paneUid) _paneUid = ++_aspectPaneCounter; // fallback pane id (no-arrangement panes)
|
||||
_registerAspectAbShortcut(); // session-global tuner shortcut (self-guarded)
|
||||
_registerTunerShortcut(); // session-global tuner shortcut (self-guarded)
|
||||
const myToken = ++_initToken;
|
||||
highwayCanvas = canvas;
|
||||
_invertedCached = !!(bundle && bundle.inverted);
|
||||
|
||||
@ -243,6 +243,31 @@ test('single-pane forces the edit target back to All (no hidden pane edits)', ()
|
||||
);
|
||||
});
|
||||
|
||||
test('resolved per-pane tune is memoized and invalidated by a revision', () => {
|
||||
assert.match(src, /_aspectRev\s*\+\+/, 'a mutation revision must be bumped on persist');
|
||||
assert.match(
|
||||
src,
|
||||
/_aspectResolveCache\.get\(\s*paneKey\s*\)[\s\S]*?c\.rev\s*===\s*_aspectRev[\s\S]*?return\s+c\.obj/,
|
||||
'_resolveTuneFor must return a cached object when the revision is unchanged',
|
||||
);
|
||||
});
|
||||
|
||||
test('the pane clock falls back to Date.now so pruning keeps working', () => {
|
||||
assert.match(
|
||||
src,
|
||||
/function\s+_aspectNowMs\s*\(\)[\s\S]*?performance\.now\(\)[\s\S]*?return\s+Date\.now\(\)/,
|
||||
'_aspectNowMs must fall back to Date.now() when the Performance API is absent',
|
||||
);
|
||||
});
|
||||
|
||||
test('opening the panel prunes before the first dropdown build', () => {
|
||||
assert.match(
|
||||
src,
|
||||
/if\s*\(\s*on\s*\)\s*\{\s*_aspectPrunePanes\(\)\s*;\s*_aspectBuildTargets\(\)/,
|
||||
'_setAspectPanelVisible must prune stale panes before building the dropdown',
|
||||
);
|
||||
});
|
||||
|
||||
test('the panel has a dismiss (close) control', () => {
|
||||
assert.match(
|
||||
src,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user