Clickable previews: exact app deep-links everywhere

Every rendered still links to the app frame that produced it (song + seed + frame + venue/viz pins + probe warm-up, new window), via shared src/ui/preview.js. Supporting work, each measured: app honors ?frame/?venueSel/?viz/?warm= with pre-build pins (Show.applyPins) and primes deep-link loads; shared pinViz/pinVenueShell paths for gallery, filmstrip and app; probes prime and carry their warm-up; bank probes analyse the bank PCM and file decode is fixed-rate with bit-exact WAV parsing so app and probes see identical samples; materials built mid-update compile synchronously (needsCompile). Round trip verified bit-identical gallery-versus-app.
This commit is contained in:
Dejvino
2026-09-20 18:42:53 +02:00
parent cc8888077f
commit 4d05250294
18 changed files with 377 additions and 96 deletions
+47
View File
@@ -0,0 +1,47 @@
// Pin one viz as the subject of every section's stacks.
//
// The same override in two places: the venue gallery's viz picker and the
// app's `?viz=`. One code path, so a gallery card and its app deep-link
// render the same pixels — the venue ground (and overlays) stay untouched,
// only the subject slot is re-cast. Seeds derive from (viz, section, look
// seed), so the pin is stable across rebuilds and identical everywhere.
import { Rng, hashString } from '../engine/rng.js';
import { sampleValues } from '../params/schema.js';
import { subjectIndexOf } from './stack.js';
import { scenes } from '../scenes/registry.js';
/**
* @param {object} look LookSpec from generateLook (mutated in place)
* @param {string} name scene name, e.g. 'Synthwave Corridor'
* @returns {object} the pinned scene module
*/
export function pinViz(look, name) {
const viz = scenes.find((s) => s.name === name);
if (!viz) throw new Error(`unknown viz "${name}"`);
const seedBase = look.seed >>> 0;
const temper = look.personality && look.personality.temperament;
for (const sec of look.sections) {
const rng = new Rng(hashString(`${viz.name}:sec:${sec.index}:${seedBase}`));
for (let v = 0; v < (sec.variants || []).length; v++) {
const stack = sec.variants[v];
if (!stack) continue;
const at = subjectIndexOf(stack);
const cur = stack[at];
if (!cur) continue;
stack[at] = {
module: viz,
params: sampleValues(viz, rng.fork(`variant:${v}`), sec.bias, temper),
seed: rng.int(0, 0x7fffffff),
// A model viz composites by alpha; a fragment viz needs the
// key/screen path over the venue ground. Reuse the old blend
// when it already fits, else fall back to the compositor-safe one.
blend: viz.kind === 'model' ? 'normal'
: (cur.blend === 'normal' ? 'lumakey' : cur.blend),
opacity: cur.opacity ?? 1,
blaze: cur.blaze ?? false,
};
}
}
return viz;
}