1→// OVE — app identity + version stamp. 2→// 3→// Single source of truth for the product name and version shown in the UI 4→// (header, footer, document title). Bump APP_VERSION whenever we ship a 5→// large change so the running build can always be identified at a glance — 6→// useful both for the z.ai Version History and for naming local backups 7→// (v6.0 = rename to Open Visualization Engine + version indicator). 8→// 9→// STANDING RULE (since v7.6): every APP_VERSION bump MUST come with a 10→// matching entry at the top of RELEASE_NOTES in src/lib/release-notes.ts — 11→// that list powers the header's "What's New!" dialog. A dev-mode guard 12→// warns in the console when the two drift apart. 13→// 14→// History: 15→// 7.13.1 — Beat Matrix endpoint fix (user report): the rightmost column 16→// of dots sat one row short -- its bottom dot missing -- while a 17→// phantom dot spilled one column past the wall's right edge at 18→// the top. Root cause: the engine's point position i spans 0..1 19→// INCLUSIVE (classic AVS Superscope convention, engine.ts: i = 20→// k/(n-1)), but Beat Matrix's grid math assumed exclusive i = k/n: 21→// floor(i*cols) turned the last point into col 16 (one past the 22→// 16-column wall) at row 0, and column 15 was left with nine 23→// dots (rows 0-8, no bottom). Fix: the script now maps through 24→// cell centers -- cell = i*(cols*rows-1)+0.5, col = 25→// floor(cell/rows), row = floor(cell-col*rows) -- which puts 26→// every floor() half a cell away from an integer boundary, so 27→// the endpoint can never spill and float dust can never shuffle 28→// a dot. Full 16x10 bijection verified headlessly (all 160 29→// points in 160 distinct cells, col<=15, row<=9, bottom-right 30→// cell present). Sweep of the other 100 presets: every other 31→// floor(i*M) use is benign -- angular presets close their loop 32→// (bar=bars lands exactly on bar 0's angle), Aurora Veil's 33→// phantom lands off-screen (x=1.19), the megabuf particle 34→// presets draw their phantom slot at alpha 0, and the converted 35→// Winamp classics rendered identically under original AVS 36→// (which also uses inclusive i). EMBEDDED_SEED_VERSION 7.12 -> 37→// 7.13.1 re-seeds the library so every visitor gets the fixed 38→// Beat Matrix. 39→// 7.13 — one build, every feature: the parallel development lines are 40→// merged. This tree now carries BOTH the community OVE-Directory 41→// (v7.11 here) AND the parallel line's additions — the spectrum 42→// 16 kHz cap (its 7.10.1), the screen wake lock + respectful 43→// Demo button + one-click Save (its 7.11, folded into this 44→// history as 7.10.2 to keep one clean timeline), and the 45→// AVS-style background movement family (bgrot / bgx / bgy / 46→// bgzoom) with the eleventh Zai-Magic preset "osc-rotations" 47→// (its 7.12). Git three-way merge against the shared v7.10 48→// ancestor (identical tree hashes verified on both sides); the 49→// only hand-merged surface was the Script tab toolbar, which 50→// now carries the one-click Save and "Upload to OVE-Directory" 51→// side by side, and the two changelogs (here + release-notes). 52→// 7.12 — background movement + osc-rotations. Scripts can now move the 53→// whole framebuffer every frame, the way the classic Winamp AVS 54→// plugins did: bgrot (radians/frame, + clockwise on screen), 55→// bgx / bgy (per-frame drift in the -1..1 screen convention, y 56→// up), bgzoom (< 1 draw-in, > 1 blow-out) -- read after [frame]/ 57→// [beat] so this frame's values apply this frame. The engine 58→// snapshots the canvas into an offscreen buffer, wipes, and 59→// draws the snapshot back through the transform, so the 60→// persistence fade blends the moving trails: "speed accelerates 61→// to the beat, a hard hit flips the spin, fade to black stays 62→// slow" is pure script (a beatage envelope drives the rate; a 63→// [beat] section flips a dir variable on hard hits via bass vs 64→// bass_att). Neutral readings (0/0/0/1) skip the pass entirely 65→// (zero cost for every preset that doesn't move); non-finite 66→// values fall back to neutral (a NaN in a canvas transform 67→// matrix would black the screen with no error); per-frame 68→// clamps: rot ±0.6 rad, drift ±0.25, zoom 0.5..2. New 11th 69→// Zai-Magic preset "osc-rotations" (user-requested): a 70→// stationary centered oscilloscope (time-domain v) whose color 71→// is mirror-symmetric -- points equidistant left/right of 72→// center shift together, the center points hold their own hue 73→// -- over a background rotating cw/ccw to the music that 74→// temporarily accelerates on the beat; hard hits 75→// (bass > 1.25 × bass_att) flip the spin; a whisper of draw-in 76→// (bgzoom ~0.999) spirals the trails inward; persist 0.965 77→// keeps the fade slow so colors blend. Help panel: bg* variable 78→// rows + three new example formulas. EMBEDDED_SEED_VERSION 79→// 7.10 -> 7.12 (library re-seed adds the preset; the share 80→// codec's dictionary stays frozen per its compatibility 81→// contract -- bg* identifiers ride the pair packing like any 82→// custom variable). Headless validation of the preset's 83→// compiled programs: quiet 0.004 rad/frame, hard beat 0.026 84→// (6.5×) + direction flip, soft beat no flip, mirrored points 85→// share hue / center distinct 86→// 7.11 — the community OVE-Directory. A sixth tab (Directory) lists every 87→// script published to the community directory — a separate VPS at 88→// s3374dir.openvisengine.com, reached ONLY through this server's 89→// backend proxy (/api/ove-directory/*), which holds the API key in 90→// env and re-validates every upload (the share string must decode 91→// AND compile). Publishing: the Script tab gains "Upload to 92→// OVE-Directory" (enabled only while the editor shows ✔ Compiled OK 93→// with no runtime error); the script travels as its Script⇢Hash 94→// string, so the directory stores a fraction of the source. The 95→// listing carries each entry's name, type and date, a 1–5 star 96→// widget (the voter id is an HttpOnly cookie minted by the proxy; 97→// one vote per script per visitor, enforced upstream), the live 98→// "Average: #.#" to the right of the stars, and sorting by Name / 99→// Type / Date / Rating (done upstream, both directions). "Get" 100→// fetches the full record, decodes the hash back into the exact 101→// script, files it in this browser's preset tree under 102→// root/user created/OVE-Directory (content-deduped, names numbered 103→// "(1) (2) (3)…" on collision) and offers "Open in editor". 104→// Duplicate uploads resolve as success (409 DUPLICATE_HASH carries 105→// the existing row); duplicate votes show the stored aggregates. 106→// 7.10.2 — wake lock, a respectful Demo button, one-click save. While any 107→// audio source is live the site holds a Screen Wake Lock so phone 108→// and tablet screens stay on (released the moment the engine goes 109→// idle so the normal inactivity dimming returns; browsers silently 110→// drop the sentinel on hide, so a visibilitychange watcher 111→// re-acquires it on return while music is still going; 112→// unsupported browsers degrade silently). The Demo Track button no 113→// longer force-engages the Sequence checkbox: a visitor who loaded 114→// a preset (tree double-click / Load / Copy to my presets / created 115→// one), brought their own script material into the editor (typed, 116→// pasted, Hash⇢Script, file), saved their own default startup 117→// script, or runs their own Random/Sequence mix keeps their 118→// selection -- Demo just plays the track (the showcase's tree 119→// re-scope, 20 s interval and retire-on-end all skip too; a new 120→// per-visit manualChoice flag + a boot-script snapshot decide, and 121→// ensureDefaultPreset gained the same guard so mic/file/link never 122→// clobber a custom draft either; the showcase now only starts for 123→// a visit that never picked or typed anything). Script tab: a valid 124→// script shows the Save button with NO save target needed -- one 125→// click creates a preset under "user created", auto-named from the 126→// script's name: header (a hash import of "Spectrum Bars:[▼…]" 127→// saves as "Spectrum Bars"), "Empty Name" when nameless, " (1)"/ 128→// " (2)"… suffixes on conflicts, and whenever the name had to be 129→// invented a "Give This Preset a Name" dialog opens to rename the 130→// just-saved preset in one step (the preset is always saved FIRST, 131→// renamed second, so nothing is ever lost to a naming problem). 132→// Hash⇢Script imports clear the stale save target (the editor now 133→// holds foreign content) and pass the decoded label through as the 134→// save-name fallback when the script has no name: header 135→// 7.10.1 — the frozen right edge of spectrum visuals. getspec()/sv mapped 136→// the bar field across the whole FFT (bin 2 ... Nyquist, ~12 Hz to 137→// 24 kHz), but music carries almost nothing above ~16 kHz and every 138→// lossy source low-passes even lower (192 kbps MP3 at ~18.6 kHz, 139→// YouTube AAC at ~15.5 kHz; measured across the demo collection: 140→// 0.000% of total magnitude above 18.6 kHz), so the rightmost ~20% 141→// of every spectrum visual read a permanent zero -- bars that never 142→// moved. getSpectrum() now tops the field out at 16 kHz 143→// (SPEC_TOP_HZ, matching the spectral-flux cap in getBands()), so 144→// the right edge lands on the 12-16 kHz "air" band where cymbals, 145→// hats and synth sparkle live. rust-agent's spectrum_at() mirrors 146→// the change, and the help panel's sv entry now states the span. 147→// 7.10 — ten demo tracks + first-person changelog + declutter. The demo 148→// source is now a COLLECTION: ten mastered tracks in 149→// public/demo-tracks/ (demo-01.mp3 … demo-10.mp3, filename order 150→// 1→10, replacing the single Neural Mecha Pulse file). Each Demo 151→// click plays the visitor's PLACE in the collection once through; 152→// the place advances when a pass truly ends — natural finish or 153→// the Stop button ('ended'/'stop' teardown reasons, a new 154→// onDemoEnded signature) — and wraps 10→1; switching to another 155→// source keeps the place. The place persists per visitor in 156→// localStorage (ove.demoTrack.next), so returning picks up where 157→// they left off. The engine caches one decoded buffer at a time, 158→// keyed by track index (~50–60 MB PCM each — all ten would be half 159→// a gigabyte). The header chip + footer SRC label show the current 160→// track's name (Demo Track N). RELEASE_NOTES rewritten in the 161→// operator's first-person voice (no more third-person marketing 162→// copy or addressing the visitor), and every em dash in the 163→// interface replaced with a plain double minus ( -- ) — including 164→// the embedded preset library's descriptions and script comments 165→// (EMBEDDED_SEED_VERSION 7.01 → 7.10 re-seed). Declutter: the 166→// landing menu's source list line, the presets panel's two 167→// read-only paragraphs, the Random/Sequence hint line, the empty 168→// user-folder note reworded, the Settings theme/highlight/Native 169→// paragraphs, the link dialog's privacy footnote and the footer's 170→// engine-ok dot are all gone — compile errors still toast, so the 171→// always-green badge was noise 172→// 7.9 — footer credit + What's New polish. The footer's blank middle now 173→// carries the maker's signature: "© {year} · Site Created & 174→// Deployed by Sum'ting White" (year generated from the clock, 175→// hydration-guarded) with a "View my portfolio at zcreations.dev" 176→// link that opens zcreations.dev in a new tab — centered in the 177→// blank area from xl (mx-auto between the stats and the version 178→// stamp), dropping to its own centered row on narrower screens so 179→// nothing ever crowds. The What's New dialog got a typographic 180→// tidy-up: a hairline divider above every release box 181→// (border-zinc-800 — a ramp step, so it matches every base theme 182→// through the globals.css remaps), and each change row became an 183→// invisible two-column grid — the kind tag top-right justified 184→// against the column boundary, the description top-left with word 185→// wrap — so all descriptions start on the same pixel column 186→// 7.8 — share strings 2.0: ~72% shorter than the script (was ~49%). The 187→// base-62 idea, cranked to 11,172… 32,164: after the symbol 188→// dictionary (which grew ~170 data-mined entries — every function 189→// WITH its open paren, every engine variable, the AVS idioms 190→// "hue = hue + dt * " and "t = t + dt * ", " * 0.", ") * ", 191→// blank-line+section skeletons, "linewidth: " / "blend: additive" 192→// headers, ~70 number literals, and the library's common 193→// variable names incl. chroma) the whole body is read as ONE BIG 194→// NUMBER and rewritten in base 32,164 — one digit per character 195→// from the CJK + Hangul blocks, ~15 bits a character vs base-62's 196→// 5.95 (so "1400" costs ~0.85 of a character). The decoder gets 197→// the body's alphabet back from a ~32-character bitmap header 198→// instead of ~80 literal symbols. Short scripts where the header 199→// wouldn't pay keep the v7.7 symbol form (encoder picks whichever 200→// is shorter); ▲ strings from v7.7 still decode; still lossless 201→// for ANY text, still copy-safe, still self-checking. Corpus 202→// measure: median share string is 27% of the script's length 203→// (best 22%), e.g. Bass Bloom 1341 → 326 characters 204→// 7.7 — script sharing + settings declutter. Script⇢Hash / Hash⇢Script: 205→// two buttons in the Script tab turn the editor's script into ONE 206→// short shareable Unicode string ("Bass Bloom:[▲…①①①①①①①①]") and 207→// paste one back — the script returns exactly as written (lossless 208→// for ANY text, compiling NOT required, so WIP scripts can be 209→// shared for debugging). The string is built from built-in symbol 210→// references (♤ = line return, ♡ = beats, ♠ = beatage, ♦ = space, 211→// ♩♪♫♬ = the four sections, one symbol per function/variable, plus 212→// a pair-packing layer that folds leftover identifier/number text 213→// two chars per CJK code point) — ~50% shorter than the script on 214→// real presets. Copy-safe by construction (no raw whitespace or 215→// control chars; platform-injected variation selectors / zero- 216→// widths stripped on paste) and self-checking: 8 check digits (an 217→// FNV-1a digest rendered as ①–⑯) reject strings that were cut off 218→// by a character limit or altered in transit, instead of decoding 219→// into garbage. Also: the Music Services log in / "log out of all" 220→// section in Settings is parked (MUSIC_SERVICES_UI flag) — the 221→// built-in house account already powers Stream-a-Link for 222→// everyone; the section returns reworked in a later update 223→// 7.6.1 — header declutter: the Set Beat button is gone from the header 224→// (tap-tempo lives in the Beat tab, and the Space shortcut works 225→// anywhere — both unchanged). The beat indicator dot stays. 226→// 7.6 — "What's New!" dialog: a Sparkles button in the header (left of 227→// the theme switch) opens the visitor-facing changelog — every 228→// release from v4.0 on, newest first, each with one-line Added / 229→// Improved / Fixed / Removed notes (src/lib/release-notes.ts, 230→// color-coded tags, the running version badged "current", 231→// scrollable with the thin scrollbar). A small emerald dot on the 232→// button marks updates the visitor hasn't seen yet — per visitor 233→// in localStorage, cleared on open (mount-guarded, no hydration 234→// mismatch). Standing rule added: version bumps ship with their 235→// release note; a dev-mode guard warns when they drift 236→// 7.5.1 — Link dialog width fix: text no longer escapes the dialog box. 237→// Root cause: Radix dialogs lay their children in an implicit CSS 238→// grid whose single column sizes to the widest child's min-content; 239→// the RECENT LINKS rows carry truncate spans (white-space: nowrap), 240→// and Chromium measures a nowrap flex item's full text width for 241→// that measurement — so a long saved title (e.g. "Bobby Foxx — This 242→// Is Why We Can't Have Nice Things (Official Music Video)") blew 243→// the column ~40 px past the 448 px dialog, and EVERY child (title, 244→// description, input + Load, rows, footer) laid out beyond the 245→// right border. Fix: the shared Dialog/AlertDialog content now pins 246→// the column to minmax(0,1fr) — capped at the available width, so 247→// no nowrap content can ever stretch a dialog again (covers every 248→// dialog app-wide) — the Link dialog widened to max-w-lg for 249→// comfier wrapping, and min-w-0 + break-words added on the 250→// recents/playlist containers and error text as belt-and-braces 251→// 7.5 — yt-dlp compatibility: single tracks resolve by DOWNLOAD + CONVERT 252→// instead of proxying the extractor's stream URL. The old stream 253→// URLs were IP-locked (googlevideo bakes in the resolver's IP), 254→// expired within hours, and arrived as webm/opus — a container 255→// Safari & co. refuse ("The media resource … was not suitable"). 256→// The server now runs the operator's pipeline per track: 257→// yt-dlp -f bestaudio/best -x --audio-format mp3 258→// --audio-quality 0 -o "/%(title)s.%(ext)s" URL 259→// — audio-only download, universal MP3 at VBR-0 (~245 kbps), the 260→// video's real title preserved for the Playing: label. The 261→// finished file streams same-origin with full Range support from 262→// a per-track workspace dir (30-min sliding TTL + orphan sweeper); 263→// /api/audio/stream gained a file-serving mode. Hardening around 264→// it: shared failure classifier for both yt-dlp passes (metadata + 265→// download), 180 s download budget, 300 MB cap, bot-wall/stale- 266→// extractor answers unchanged, ffmpeg-missing degrades to the old 267→// proxy path (logged once), queue look-ahead lead 15 s → 60 s so 268→// playlist transitions don't stall behind conversions 269→// 7.4 — Sequence mode: a second mix checkbox next to Random. Ticked, it 270→// cycles the scope folder's presets IN ORDER (the tree's exact 271→// top-to-bottom order) instead of shuffling — and when it moves 272→// past the last preset it wraps back to the first, so the folder 273→// loops forever. Random and Sequence are mutually exclusive 274→// (ticking either one unticks the other; both share the same 275→// delay and folder scope), Random now never loads the same preset 276→// twice in a row, and Sequence holds its breath through silence 277→// exactly like Random does. The Demo showcase now runs SEQUENCE — 278→// the one play-through walks every Zai-Magic preset in order 279→// (20 s each) rather than repeating random picks, and when the 280→// demo stops, whichever mode it checked is unchecked again 281→// 7.3 — demo-flow refinements: the Demo Track is a single pass — it plays 282→// "Neural Mecha Pulse" once through and stops (a demo, not a radio 283→// station). However it ends (natural finish, Stop, or switching to 284→// another source) the showcase unwinds: the randomizer the Demo 285→// flow force-checked is unchecked again, and the visitor lands 286→// back on the "Select an audio source" overlay. Preset-change 287→// popups are gone ("Preset loaded" / "Random preset" / "Default 288→// preset running") — the footer's SCRIPT: label already echoes 289→// every change, so the toasts were pure intrusion; only compile 290→// errors still toast. Club-mode randomizer now respects silence: 291→// when no music is playing it never re-shuffles the selected 292→// preset, even with the checkbox on — it waits for the next track 293→// 7.2.1 — Supernova (Zai-Magic) preset fix: the particle kill condition 294→// (|px|+|py| > 2.4) was physically unreachable — drag caps a 295→// particle's total travel at vel/1.6 ≈ 1.0, so nothing ever 296→// crossed 1.4, nothing ever died, and since respawning requires a 297→// dead particle, the per-beat "budget = n" detonation could never 298→// fire after the initial one — the preset exploded once on load, 299→// then faded to permanent black. Now every beat force-clears the 300→// whole 620-particle field in [beat] (guaranteed full re-detonation 301→// on the very same frame), fully-cooled particles die by fade so 302→// long beat gaps leave a gentle ember drizzle instead of black, 303→// and each blast re-randomizes its geometry (no two explosions 304→// alike). Embedded library re-seeded (seed marker 7.01) 305→// 7.2 — new-visitor showcase flow. Defaults for first-time visitors: 306→// the Black + Purple Outline theme with the Blue highlight 307→// (previously royal purple + yellow), and Zai-Magic "Bass Bloom" 308→// as the startup preset (previously the first Synth-C classic). 309→// The idle overlay's Demo Track button now carries an animated 310→// reflective shimmer (skewed light band sweeping left→right every 311→// 3.2 s, reduced-motion safe) to draw the click — and that one 312→// click starts the full showcase: the demo track plays while the 313→// preset tree selects + expands Zai-Magic, randomize switches on 314→// scoped to that folder with a 20-second change interval. Music 315→// started with NO preset active (own draft running / selection 316→// cleared) auto-loads Bass Bloom. Preset changes — manual load, 317→// the random mix, the Demo flow — no longer hard-cut: the engine 318→// snapshots the outgoing frame and blur-dissolves it into the new 319→// preset over 700 ms (progressive blur + easeOutCubic fade + a 320→// slight scale-up so the softening edges never gap; plain 321→// crossfade where ctx.filter is unsupported) 322→// 7.1 — real demo track + a beat detector that survives real music. 323→// The Demo button now plays "Neural Mecha Pulse" (the operator's 324→// mastered industrial-EDM banger, public/demo-track.mp3, loops 325→// gaplessly) instead of the synthetic techno pattern — a real 326→// recording is the honest test: deep sub for the bass bands, 327→// driving mids, crisp highs, and a kick buried in sustained 328→// sub-bass. That last part exposed the 6.x detector as fundamentally 329→// broken on mastered music: a LEVEL threshold can never see the 330→// kick through the sub (peak-to-mean caps at ~1.2× → sporadic 331→// fires, phantom 120-BPM locks from the minInterval floor). 332→// v7.1 rewrites detection the way real beat trackers work 333→// (aubio/librosa-style): a full-band spectral-flux ONSET envelope 334→// → autocorrelation over 300–1000 ms lags with a log-BPM prior → 335→// comb-filter phase with grid hysteresis → beats fired on an 336→// absolute grid kept glued by a PLL pull. Headlessly validated 337→// against the actual track PCM (AnalyserNode chain replicated 338→// frame-for-frame): locks the true ~133 BPM (the song drifts 339→// 130.7→133.1 — the grid TRACKS it) in 1.9 s, 92% beat-gap 340→// regularity, confidence 0.99 through the body; techno/hip-hop 341→// patterns lock exactly; silence yields zero beats. Known limit: 342→// sparse kick-vs-snare backbeats (dnb) may lock half-tempo — 343→// stable, and two taps of Set Beat correct it. Visitor-stored 344→// beat configs migrate (floorLevel changed units, level → flux). 345→// The demo BPM slider is gone (a real track's tempo is detected, 346→// not dialed in) — replaced by a track-name chip in the header 347→// 7.00 — Zai-Magic: ten hand-authored presets in root/embedded/Zai-Magic 348→// built around BEAT-SYNCED COLOR — the color wheel turns on the 349→// kick, palettes swap per beat / per 4-beat bar, megabuf particles 350→// remember the hue of the beat that spawned them + new script 351→// tools: hsvr/hsvg/hsvb (HSV→RGB channels), smoothstep and the 352→// beatage kick-envelope variable — all documented in the help 353→// panel. (Numbered 7.00 rather than 6.10 — a new preset family 354→// plus new script-language features is a major release; 6.9.3 was 355→// the last patch of the 6.x line.) Beat engine fixes underneath so 356→// the Demo track actually 357→// locks tempo (was sporadic): demo synth remixed for detection 358→// (hot kick owning the 20–100 Hz band, off-beat bassline voiced 359→// C3+ with a sidechain pump — chronological envelope events!), 360→// and the detector's default threshold 1.4 → 1.2 (the 170 ms FFT 361→// window caps a clean kick's peak-to-mean at ~1.5×, so 1.4 fired 362→// only sporadically and the BPM never locked) 363→// 6.9.3 — canonical domain fix: metadataBase/og:url/og:image pointed at 364→// openvisualengine.com (a DEAD domain baked in by 6.9.1) while the 365→// real site is openvisengine.com — Facebook's "corrupted image" 366→// error was its crawler fetching the image from a host that serves 367→// nothing; all share URLs now use the correct short domain 368→// 6.9.2 — share-image fix: og:image re-encoded as BASELINE JPEG (the 369→// 6.9.1 progressive encoding rendered fine in every browser but 370→// Facebook's Sharing Debugger rejected it as "corrupted"), swapped 371→// in the title-card artwork (gradient wordmark over three 372→// visualizations) and added a ?v=2 cache-bust so crawlers fetch it 373→// 6.9.1 — social share preview: Open Graph + Twitter Card metadata with 374→// a 1200×630 screenshot of the app (public/og-image.jpg) — sharing 375→// openvisualengine.com on Facebook/X/WhatsApp/Discord now shows the 376→// big image card instead of a bare link 377→// 6.9 — house account: anonymous visitors play YouTube links through 378→// the operator's shared disposable-account cookie (server-side 379→// fallback under secrets/, never exposed) — YouTube bot walls stop 380→// applying to guests; visitor's own login always wins. Engine fix 381→// underneath: Deno runtime shipped in bin/ (YouTube requires a JS 382→// runtime for signature/n-challenge solving on authenticated 383→// sessions — without it, logged-in extractions return no formats) 384→// 6.8.2 — login friction cut to ~4 clicks: the exported cookies.txt / 385→// Cookie-Editor JSON file can be dragged straight onto the 386→// login box (or picked via “Import the exported file…”) — no 387→// opening the file, no copy/paste; “Log out” / sign-out 388→// revocation note added (the official Google OAuth flow can't 389→// power the site: Google's APIs never hand out stream URLs and 390→// restricted-scope verification would reject an audio player) 391→// 6.8.1 — cookie login made foolproof: the login dialog recognizes raw 392→// Cookie headers, cookies.txt exports AND Cookie-Editor JSON, 393→// validates the paste LIVE through one shared parser (a single 394→// cookie's value — like a bare LOGIN_INFO string — gets an honest 395→// "this will not log you in" warning), and the how-to steps lead 396→// with the one-click extension export for Chrome AND Firefox 397→// 6.8 — self-healing audio engine: when YouTube changes their mechanics 398→// the server detects the stale extractor, updates yt-dlp in the 399→// background (verified GitHub release download, atomic swap) and the 400→// player retries seamlessly behind an "Updating in Background" 401→// notice; bot walls answer "Google login required…" with an Open 402→// Settings action, and unfixable failures get the honest 403→// "A back-end problem has occurred. Please try again later." 404→// 6.7 — lazy playlist queue: a playlist link becomes a queue of URLs that 405→// streams ONE song at a time (each track is resolved by yt-dlp only 406→// right before it plays — next-track look-ahead at ≤15 s to go), 407→// queue bar with skip/jump/replay + server-side caps (max 2 parallel 408→// extractions); music-service framework: Spotify + Apple Music are 409→// registered providers whose playback ships in a later update 410→// (logins already save, links answer with an honest notice) 411→// 6.6 — music service logins (Settings tab): YouTube / YouTube Music, 412→// SoundCloud, Spotify, Apple Music cookie login, encrypted on the 413→// visitor's computer; Link button gains playlist picking + uses the 414→// login to bypass bot walls 415→// 6.5 — Settings tab (replaces the parked Native tab): theme picker with 416→// base chrome (Royal Purple / Matte Black / Black + Purple Outline / 417→// Light) × selectable highlight color (Purple, Yellow, Blue, Green, 418→// Red, Orange), all per-visitor; simple OVE logo as the site favicon 419→// 6.4 — beat detection suspends when no music is playing/loaded (no ghost 420→// beats after Stop, BPM resets) + dark theme restyle: royal purple 421→// chrome with light-yellow highlights 422→// 6.3 — light/dark theme tick switch in the header (white theme via .light 423→// palette remap; per-visitor, persisted by next-themes) 424→// 6.2 — security hardening: read-only embedded library (server refuses all 425→// writes) + per-visitor "user created" presets stored in the browser 426→// 6.1 — preset library quality pass: every embedded stand-in is now a UNIQUE 427→// generated visual (16 families × seeded parameters), approx badge 428→// 6.0 — renamed to "Open Visualization Engine" + footer version stamp 429→// 5.0 — folder-tree Presets page (Task 9–11 state, restored in Task 12) 430→// 4.0 — link audio source (yt-dlp resolve + stream proxy) 431→ 432→export const APP_NAME = 'Open Visualization Engine' 433→export const APP_SHORT = 'OVE' 434→export const APP_VERSION = '7.13.1' 435→