// OVE — app identity + version stamp.
//
// Single source of truth for the product name and version shown in the UI
// (header, footer, document title). Bump APP_VERSION whenever we ship a
// large change so the running build can always be identified at a glance —
// useful both for the z.ai Version History and for naming local backups
// (v6.0 = rename to Open Visualization Engine + version indicator).
//
// STANDING RULE (since v7.6): every APP_VERSION bump MUST come with a
// matching entry at the top of RELEASE_NOTES in src/lib/release-notes.ts —
// that list powers the header's "What's New!" dialog. A dev-mode guard
// warns in the console when the two drift apart.
//
// History:
// 7.11 — the community OVE-Directory. A sixth tab (Directory) lists every
// script published to the community directory — a separate VPS at
// s3374dir.openvisengine.com, reached ONLY through this server's
// backend proxy (/api/ove-directory/*), which holds the API key in
// env and re-validates every upload (the share string must decode
// AND compile). Publishing: the Script tab gains "Upload to
// OVE-Directory" (enabled only while the editor shows ✔ Compiled OK
// with no runtime error); the script travels as its Script⇢Hash
// string, so the directory stores a fraction of the source. The
// listing carries each entry's name, type and date, a 1–5 star
// widget (the voter id is an HttpOnly cookie minted by the proxy;
// one vote per script per visitor, enforced upstream), the live
// "Average: #.#" to the right of the stars, and sorting by Name /
// Type / Date / Rating (done upstream, both directions). "Get"
// fetches the full record, decodes the hash back into the exact
// script, files it in this browser's preset tree under
// root/user created/OVE-Directory (content-deduped, names numbered
// "(1) (2) (3)…" on collision) and offers "Open in editor".
// Duplicate uploads resolve as success (409 DUPLICATE_HASH carries
// the existing row); duplicate votes show the stored aggregates.
// 7.10 — ten demo tracks + first-person changelog + declutter. The demo
// source is now a COLLECTION: ten mastered tracks in
// public/demo-tracks/ (demo-01.mp3 … demo-10.mp3, filename order
// 1→10, replacing the single Neural Mecha Pulse file). Each Demo
// click plays the visitor's PLACE in the collection once through;
// the place advances when a pass truly ends — natural finish or
// the Stop button ('ended'/'stop' teardown reasons, a new
// onDemoEnded signature) — and wraps 10→1; switching to another
// source keeps the place. The place persists per visitor in
// localStorage (ove.demoTrack.next), so returning picks up where
// they left off. The engine caches one decoded buffer at a time,
// keyed by track index (~50–60 MB PCM each — all ten would be half
// a gigabyte). The header chip + footer SRC label show the current
// track's name (Demo Track N). RELEASE_NOTES rewritten in the
// operator's first-person voice (no more third-person marketing
// copy or addressing the visitor), and every em dash in the
// interface replaced with a plain double minus ( -- ) — including
// the embedded preset library's descriptions and script comments
// (EMBEDDED_SEED_VERSION 7.01 → 7.10 re-seed). Declutter: the
// landing menu's source list line, the presets panel's two
// read-only paragraphs, the Random/Sequence hint line, the empty
// user-folder note reworded, the Settings theme/highlight/Native
// paragraphs, the link dialog's privacy footnote and the footer's
// engine-ok dot are all gone — compile errors still toast, so the
// always-green badge was noise
// 7.9 — footer credit + What's New polish. The footer's blank middle now
// carries the maker's signature: "© {year} · Site Created &
// Deployed by Sum'ting White" (year generated from the clock,
// hydration-guarded) with a "View my portfolio at zcreations.dev"
// link that opens zcreations.dev in a new tab — centered in the
// blank area from xl (mx-auto between the stats and the version
// stamp), dropping to its own centered row on narrower screens so
// nothing ever crowds. The What's New dialog got a typographic
// tidy-up: a hairline divider above every release box
// (border-zinc-800 — a ramp step, so it matches every base theme
// through the globals.css remaps), and each change row became an
// invisible two-column grid — the kind tag top-right justified
// against the column boundary, the description top-left with word
// wrap — so all descriptions start on the same pixel column
// 7.8 — share strings 2.0: ~72% shorter than the script (was ~49%). The
// base-62 idea, cranked to 11,172… 32,164: after the symbol
// dictionary (which grew ~170 data-mined entries — every function
// WITH its open paren, every engine variable, the AVS idioms
// "hue = hue + dt * " and "t = t + dt * ", " * 0.", ") * ",
// blank-line+section skeletons, "linewidth: " / "blend: additive"
// headers, ~70 number literals, and the library's common
// variable names incl. chroma) the whole body is read as ONE BIG
// NUMBER and rewritten in base 32,164 — one digit per character
// from the CJK + Hangul blocks, ~15 bits a character vs base-62's
// 5.95 (so "1400" costs ~0.85 of a character). The decoder gets
// the body's alphabet back from a ~32-character bitmap header
// instead of ~80 literal symbols. Short scripts where the header
// wouldn't pay keep the v7.7 symbol form (encoder picks whichever
// is shorter); ▲ strings from v7.7 still decode; still lossless
// for ANY text, still copy-safe, still self-checking. Corpus
// measure: median share string is 27% of the script's length
// (best 22%), e.g. Bass Bloom 1341 → 326 characters
// 7.7 — script sharing + settings declutter. Script⇢Hash / Hash⇢Script:
// two buttons in the Script tab turn the editor's script into ONE
// short shareable Unicode string ("Bass Bloom:[▲…①①①①①①①①]") and
// paste one back — the script returns exactly as written (lossless
// for ANY text, compiling NOT required, so WIP scripts can be
// shared for debugging). The string is built from built-in symbol
// references (♤ = line return, ♡ = beats, ♠ = beatage, ♦ = space,
// ♩♪♫♬ = the four sections, one symbol per function/variable, plus
// a pair-packing layer that folds leftover identifier/number text
// two chars per CJK code point) — ~50% shorter than the script on
// real presets. Copy-safe by construction (no raw whitespace or
// control chars; platform-injected variation selectors / zero-
// widths stripped on paste) and self-checking: 8 check digits (an
// FNV-1a digest rendered as ①–⑯) reject strings that were cut off
// by a character limit or altered in transit, instead of decoding
// into garbage. Also: the Music Services log in / "log out of all"
// section in Settings is parked (MUSIC_SERVICES_UI flag) — the
// built-in house account already powers Stream-a-Link for
// everyone; the section returns reworked in a later update
// 7.6.1 — header declutter: the Set Beat button is gone from the header
// (tap-tempo lives in the Beat tab, and the Space shortcut works
// anywhere — both unchanged). The beat indicator dot stays.
// 7.6 — "What's New!" dialog: a Sparkles button in the header (left of
// the theme switch) opens the visitor-facing changelog — every
// release from v4.0 on, newest first, each with one-line Added /
// Improved / Fixed / Removed notes (src/lib/release-notes.ts,
// color-coded tags, the running version badged "current",
// scrollable with the thin scrollbar). A small emerald dot on the
// button marks updates the visitor hasn't seen yet — per visitor
// in localStorage, cleared on open (mount-guarded, no hydration
// mismatch). Standing rule added: version bumps ship with their
// release note; a dev-mode guard warns when they drift
// 7.5.1 — Link dialog width fix: text no longer escapes the dialog box.
// Root cause: Radix dialogs lay their children in an implicit CSS
// grid whose single column sizes to the widest child's min-content;
// the RECENT LINKS rows carry truncate spans (white-space: nowrap),
// and Chromium measures a nowrap flex item's full text width for
// that measurement — so a long saved title (e.g. "Bobby Foxx — This
// Is Why We Can't Have Nice Things (Official Music Video)") blew
// the column ~40 px past the 448 px dialog, and EVERY child (title,
// description, input + Load, rows, footer) laid out beyond the
// right border. Fix: the shared Dialog/AlertDialog content now pins
// the column to minmax(0,1fr) — capped at the available width, so
// no nowrap content can ever stretch a dialog again (covers every
// dialog app-wide) — the Link dialog widened to max-w-lg for
// comfier wrapping, and min-w-0 + break-words added on the
// recents/playlist containers and error text as belt-and-braces
// 7.5 — yt-dlp compatibility: single tracks resolve by DOWNLOAD + CONVERT
// instead of proxying the extractor's stream URL. The old stream
// URLs were IP-locked (googlevideo bakes in the resolver's IP),
// expired within hours, and arrived as webm/opus — a container
// Safari & co. refuse ("The media resource … was not suitable").
// The server now runs the operator's pipeline per track:
// yt-dlp -f bestaudio/best -x --audio-format mp3
// --audio-quality 0 -o "
/%(title)s.%(ext)s" URL
// — audio-only download, universal MP3 at VBR-0 (~245 kbps), the
// video's real title preserved for the Playing: label. The
// finished file streams same-origin with full Range support from
// a per-track workspace dir (30-min sliding TTL + orphan sweeper);
// /api/audio/stream gained a file-serving mode. Hardening around
// it: shared failure classifier for both yt-dlp passes (metadata +
// download), 180 s download budget, 300 MB cap, bot-wall/stale-
// extractor answers unchanged, ffmpeg-missing degrades to the old
// proxy path (logged once), queue look-ahead lead 15 s → 60 s so
// playlist transitions don't stall behind conversions
// 7.4 — Sequence mode: a second mix checkbox next to Random. Ticked, it
// cycles the scope folder's presets IN ORDER (the tree's exact
// top-to-bottom order) instead of shuffling — and when it moves
// past the last preset it wraps back to the first, so the folder
// loops forever. Random and Sequence are mutually exclusive
// (ticking either one unticks the other; both share the same
// delay and folder scope), Random now never loads the same preset
// twice in a row, and Sequence holds its breath through silence
// exactly like Random does. The Demo showcase now runs SEQUENCE —
// the one play-through walks every Zai-Magic preset in order
// (20 s each) rather than repeating random picks, and when the
// demo stops, whichever mode it checked is unchecked again
// 7.3 — demo-flow refinements: the Demo Track is a single pass — it plays
// "Neural Mecha Pulse" once through and stops (a demo, not a radio
// station). However it ends (natural finish, Stop, or switching to
// another source) the showcase unwinds: the randomizer the Demo
// flow force-checked is unchecked again, and the visitor lands
// back on the "Select an audio source" overlay. Preset-change
// popups are gone ("Preset loaded" / "Random preset" / "Default
// preset running") — the footer's SCRIPT: label already echoes
// every change, so the toasts were pure intrusion; only compile
// errors still toast. Club-mode randomizer now respects silence:
// when no music is playing it never re-shuffles the selected
// preset, even with the checkbox on — it waits for the next track
// 7.2.1 — Supernova (Zai-Magic) preset fix: the particle kill condition
// (|px|+|py| > 2.4) was physically unreachable — drag caps a
// particle's total travel at vel/1.6 ≈ 1.0, so nothing ever
// crossed 1.4, nothing ever died, and since respawning requires a
// dead particle, the per-beat "budget = n" detonation could never
// fire after the initial one — the preset exploded once on load,
// then faded to permanent black. Now every beat force-clears the
// whole 620-particle field in [beat] (guaranteed full re-detonation
// on the very same frame), fully-cooled particles die by fade so
// long beat gaps leave a gentle ember drizzle instead of black,
// and each blast re-randomizes its geometry (no two explosions
// alike). Embedded library re-seeded (seed marker 7.01)
// 7.2 — new-visitor showcase flow. Defaults for first-time visitors:
// the Black + Purple Outline theme with the Blue highlight
// (previously royal purple + yellow), and Zai-Magic "Bass Bloom"
// as the startup preset (previously the first Synth-C classic).
// The idle overlay's Demo Track button now carries an animated
// reflective shimmer (skewed light band sweeping left→right every
// 3.2 s, reduced-motion safe) to draw the click — and that one
// click starts the full showcase: the demo track plays while the
// preset tree selects + expands Zai-Magic, randomize switches on
// scoped to that folder with a 20-second change interval. Music
// started with NO preset active (own draft running / selection
// cleared) auto-loads Bass Bloom. Preset changes — manual load,
// the random mix, the Demo flow — no longer hard-cut: the engine
// snapshots the outgoing frame and blur-dissolves it into the new
// preset over 700 ms (progressive blur + easeOutCubic fade + a
// slight scale-up so the softening edges never gap; plain
// crossfade where ctx.filter is unsupported)
// 7.1 — real demo track + a beat detector that survives real music.
// The Demo button now plays "Neural Mecha Pulse" (the operator's
// mastered industrial-EDM banger, public/demo-track.mp3, loops
// gaplessly) instead of the synthetic techno pattern — a real
// recording is the honest test: deep sub for the bass bands,
// driving mids, crisp highs, and a kick buried in sustained
// sub-bass. That last part exposed the 6.x detector as fundamentally
// broken on mastered music: a LEVEL threshold can never see the
// kick through the sub (peak-to-mean caps at ~1.2× → sporadic
// fires, phantom 120-BPM locks from the minInterval floor).
// v7.1 rewrites detection the way real beat trackers work
// (aubio/librosa-style): a full-band spectral-flux ONSET envelope
// → autocorrelation over 300–1000 ms lags with a log-BPM prior →
// comb-filter phase with grid hysteresis → beats fired on an
// absolute grid kept glued by a PLL pull. Headlessly validated
// against the actual track PCM (AnalyserNode chain replicated
// frame-for-frame): locks the true ~133 BPM (the song drifts
// 130.7→133.1 — the grid TRACKS it) in 1.9 s, 92% beat-gap
// regularity, confidence 0.99 through the body; techno/hip-hop
// patterns lock exactly; silence yields zero beats. Known limit:
// sparse kick-vs-snare backbeats (dnb) may lock half-tempo —
// stable, and two taps of Set Beat correct it. Visitor-stored
// beat configs migrate (floorLevel changed units, level → flux).
// The demo BPM slider is gone (a real track's tempo is detected,
// not dialed in) — replaced by a track-name chip in the header
// 7.00 — Zai-Magic: ten hand-authored presets in root/embedded/Zai-Magic
// built around BEAT-SYNCED COLOR — the color wheel turns on the
// kick, palettes swap per beat / per 4-beat bar, megabuf particles
// remember the hue of the beat that spawned them + new script
// tools: hsvr/hsvg/hsvb (HSV→RGB channels), smoothstep and the
// beatage kick-envelope variable — all documented in the help
// panel. (Numbered 7.00 rather than 6.10 — a new preset family
// plus new script-language features is a major release; 6.9.3 was
// the last patch of the 6.x line.) Beat engine fixes underneath so
// the Demo track actually
// locks tempo (was sporadic): demo synth remixed for detection
// (hot kick owning the 20–100 Hz band, off-beat bassline voiced
// C3+ with a sidechain pump — chronological envelope events!),
// and the detector's default threshold 1.4 → 1.2 (the 170 ms FFT
// window caps a clean kick's peak-to-mean at ~1.5×, so 1.4 fired
// only sporadically and the BPM never locked)
// 6.9.3 — canonical domain fix: metadataBase/og:url/og:image pointed at
// openvisualengine.com (a DEAD domain baked in by 6.9.1) while the
// real site is openvisengine.com — Facebook's "corrupted image"
// error was its crawler fetching the image from a host that serves
// nothing; all share URLs now use the correct short domain
// 6.9.2 — share-image fix: og:image re-encoded as BASELINE JPEG (the
// 6.9.1 progressive encoding rendered fine in every browser but
// Facebook's Sharing Debugger rejected it as "corrupted"), swapped
// in the title-card artwork (gradient wordmark over three
// visualizations) and added a ?v=2 cache-bust so crawlers fetch it
// 6.9.1 — social share preview: Open Graph + Twitter Card metadata with
// a 1200×630 screenshot of the app (public/og-image.jpg) — sharing
// openvisualengine.com on Facebook/X/WhatsApp/Discord now shows the
// big image card instead of a bare link
// 6.9 — house account: anonymous visitors play YouTube links through
// the operator's shared disposable-account cookie (server-side
// fallback under secrets/, never exposed) — YouTube bot walls stop
// applying to guests; visitor's own login always wins. Engine fix
// underneath: Deno runtime shipped in bin/ (YouTube requires a JS
// runtime for signature/n-challenge solving on authenticated
// sessions — without it, logged-in extractions return no formats)
// 6.8.2 — login friction cut to ~4 clicks: the exported cookies.txt /
// Cookie-Editor JSON file can be dragged straight onto the
// login box (or picked via “Import the exported file…”) — no
// opening the file, no copy/paste; “Log out” / sign-out
// revocation note added (the official Google OAuth flow can't
// power the site: Google's APIs never hand out stream URLs and
// restricted-scope verification would reject an audio player)
// 6.8.1 — cookie login made foolproof: the login dialog recognizes raw
// Cookie headers, cookies.txt exports AND Cookie-Editor JSON,
// validates the paste LIVE through one shared parser (a single
// cookie's value — like a bare LOGIN_INFO string — gets an honest
// "this will not log you in" warning), and the how-to steps lead
// with the one-click extension export for Chrome AND Firefox
// 6.8 — self-healing audio engine: when YouTube changes their mechanics
// the server detects the stale extractor, updates yt-dlp in the
// background (verified GitHub release download, atomic swap) and the
// player retries seamlessly behind an "Updating in Background"
// notice; bot walls answer "Google login required…" with an Open
// Settings action, and unfixable failures get the honest
// "A back-end problem has occurred. Please try again later."
// 6.7 — lazy playlist queue: a playlist link becomes a queue of URLs that
// streams ONE song at a time (each track is resolved by yt-dlp only
// right before it plays — next-track look-ahead at ≤15 s to go),
// queue bar with skip/jump/replay + server-side caps (max 2 parallel
// extractions); music-service framework: Spotify + Apple Music are
// registered providers whose playback ships in a later update
// (logins already save, links answer with an honest notice)
// 6.6 — music service logins (Settings tab): YouTube / YouTube Music,
// SoundCloud, Spotify, Apple Music cookie login, encrypted on the
// visitor's computer; Link button gains playlist picking + uses the
// login to bypass bot walls
// 6.5 — Settings tab (replaces the parked Native tab): theme picker with
// base chrome (Royal Purple / Matte Black / Black + Purple Outline /
// Light) × selectable highlight color (Purple, Yellow, Blue, Green,
// Red, Orange), all per-visitor; simple OVE logo as the site favicon
// 6.4 — beat detection suspends when no music is playing/loaded (no ghost
// beats after Stop, BPM resets) + dark theme restyle: royal purple
// chrome with light-yellow highlights
// 6.3 — light/dark theme tick switch in the header (white theme via .light
// palette remap; per-visitor, persisted by next-themes)
// 6.2 — security hardening: read-only embedded library (server refuses all
// writes) + per-visitor "user created" presets stored in the browser
// 6.1 — preset library quality pass: every embedded stand-in is now a UNIQUE
// generated visual (16 families × seeded parameters), approx badge
// 6.0 — renamed to "Open Visualization Engine" + footer version stamp
// 5.0 — folder-tree Presets page (Task 9–11 state, restored in Task 12)
// 4.0 — link audio source (yt-dlp resolve + stream proxy)
export const APP_NAME = 'Open Visualization Engine'
export const APP_SHORT = 'OVE'
export const APP_VERSION = '7.11'