// OVE -- release notes for the "What's New!" dialog (v7.6). // // The visitor-facing changelog, newest release first. This is the ONE place // to update when shipping a version: every bump of APP_VERSION in version.ts // must come with a matching entry at the top of RELEASE_NOTES (a dev-mode // guard below warns in the console when the two drift apart). // // VOICE (v7.10): I write these notes myself, in the first person -- "I // added…", "I fixed…" -- as a build diary. Never address the reader ("you" // / "your"), never speak of myself in the third person; when the visitor is // relevant, "the visitor" / "each browser". Punctuation: no em dashes -- // a plain double minus ( -- ) everywhere. // // Keep entries brief -- one line per change, written for visitors (what they // can now do / what stopped breaking), not for the build log. The verbose // engineering history still lives in version.ts; this file is the digest. // // Change kinds shown in the dialog: // added -- new capability // improved -- made better/faster/ friendlier // fixed -- a bug that stopped reproducing // removed -- taken away (with the reason in the text) import { APP_VERSION } from '@/lib/version' export type ChangeKind = 'added' | 'improved' | 'fixed' | 'removed' export interface ReleaseChange { kind: ChangeKind text: string } export interface ReleaseNote { /** matches APP_VERSION of that release, e.g. '7.5.1' */ version: string /** short release name shown next to the version (optional) */ name?: string changes: ReleaseChange[] } export const RELEASE_NOTES: ReleaseNote[] = [ { version: '7.20.0', name: 'Paste-to-decode + a compact Directory', changes: [ { kind: 'added', text: 'Pasting a share string straight into the Script editor now decodes it on the spot -- the hash never lands; the whole script it carries appears in the editor instead, right where the paste went, ready to Apply or Save. The Hash⇢Script button still works whenever I want the decode preview first.', }, { kind: 'improved', text: 'Directory entries are much more compact. The Get button moved to each card\'s top right with its date right beside it, so the rating row no longer wraps the button onto a line of its own -- every entry card dropped from 126 px to 94 px, a quarter shorter.', }, ], }, { version: '7.19.0', name: 'Automatic true-black', changes: [ { kind: 'fixed', text: 'Trails now fade all the way to pure black on their own. The fade works in 8-bit steps, so a persist of 0.5 or more used to leave a faint stuck residue behind everything that moved -- a colored smear where rings or blooms had passed. The engine now sweeps that residue away every frame, in every script, with no header needed.', }, { kind: 'improved', text: 'The sweep runs on the GPU as an exact -1/255 step that also preserves transparency, so behind/clear art keeps its holes (they heal to truly empty now too), and black stays a true black with no flicker. Scripts with a low persist pay nothing -- the sweep only switches on in the range where the 8-bit math could stick.', }, { kind: 'improved', text: 'The trueblack: header grows an auto mode (the new default) next to on and off, and the Help panel documents all three.', }, ], }, { version: '7.18.0', name: 'Beautiful Spectrogram', changes: [ { kind: 'added', text: 'I added a twelfth Zai-Magic preset: "Beautiful Spectrogram" -- a true scrolling spectrogram. Every frame inks a fresh frequency column at the right edge (bass at the bottom, treble at the top) and the whole picture drifts left through a deep dark blue field that never fades, so the music leaves its heat-print hanging in the air until it scrolls off the edge. Quiet bins whisper pink; loud ones climb red, orange, yellow and finally white, with hits lingering as a glow and the newest ink flaring on the beat.', }, { kind: 'improved', text: 'The preset is built entirely from script-engine features that already existed -- the background slide, the zero-fade framebuffer and megabuf bin memory -- so it doubles as a worked example of a whole new visual family (data-plots that write their own history) without any engine changes.', }, ], }, { version: '7.17.0', name: 'True-black trails', changes: [ { kind: 'added', text: 'New trueblack: header for scripts. The trail fade works in 8-bit steps, so a high persist leaves a stuck gray residue that never fully fades -- with the flag on, a tiny subtractive tail sweeps it away and trails end in pure black, so long echo memories (a background blow-out, say) stay clean instead of washing the screen gray.', }, { kind: 'improved', text: 'The script editor\'s Help panel documents the new header in the file-format section, and the fade\'s gray-wash quirk is now mentioned right where persist is explained.', }, ], }, { version: '7.16.0', name: 'Presets tab, rearranged', changes: [ { kind: 'improved', text: 'The Presets tab dropped its side-by-side split. The preset controls (Load Preset and friends) now run across the full top of the section at exactly the height they need -- the dead space that used to sit under them when a preset was selected is gone -- and the preset tree takes the whole width below, so long preset names stop getting cut off by the old narrow column.', }, { kind: 'improved', text: 'On desktop the action buttons share one compact row (or two, for a folder), the tree grows to fill whatever height the panel has left, and the random/sequence status pill sits beside its scope line instead of below it.', }, ], }, { version: '7.15.2', name: 'Facebook page + theme switch in Settings', changes: [ { kind: 'added', text: "OVE has a Facebook page now -- the footer carries the link (the Facebook icon beside the version stamp), so that is where news and showcase clips will land.", }, { kind: 'improved', text: 'The light/dark tick switch moved from the header to the Settings tab, as the first row of the Appearance section -- every look-and-feel control now lives in one place. It flips exactly as before: back to dark it returns to the base picked below it, and both choices persist.', }, ], }, { version: '7.15.1', name: 'Silence holds the beat grid', changes: [ { kind: 'fixed', text: 'When the audio goes silent — a pause, the gap between tracks, a stream stalling — the beat tracker used to keep faking beats for the first couple of seconds. It now places an immediate hold: nothing fires, the phase ring freezes where it is, and the tempo, confidence and grid alignment are kept exactly as they were. Waveform and band data still flow; they are just near zero, because the audio is gone.', }, { kind: 'added', text: 'The hold releases the instant real audio returns, and the beat grid continues from the phase it froze at — a pause and resume keeps perfect time. Scripts get a silent variable (1 while held) and the beat indicator shows an amber HOLD badge while it is active.', }, ], }, { version: '7.15.0', name: 'The complete blend mode collection', changes: [ { kind: 'added', text: 'blend: now takes 41 modes instead of six. The full Photoshop set is there (color burn, linear burn, darker color, color dodge, lighter color, overlay, soft light, hard light, vivid light, linear light, pin light, hard mix, difference, exclusion, divide, negation, phoenix, reflect, glow, hue, saturation, color, luminosity), plus GIMP\'s grain extract/merge, After Effects\' stencil and silhouette masks, a dissolve that re-rolls its speckle every frame, and plus-lighter / plus-darker. Any spelling works: color burn, color-burn and ColorBurn are the same keyword.', }, { kind: 'added', text: 'blend: subtract is now a true clamped subtract (base - blend, floored at black). Until 7.14.1 it was approximated with difference, which could never subtract downward past the stroke color.', }, { kind: 'added', text: 'The blend variable can switch modes per frame, like persist already could -- paint with replace (3), then sculpt with subtract (6) on the kick; every mode\'s number is in the Help panel\'s table. The framebuffer also keeps real transparency now, so behind (strokes slip under existing art) and clear (an eraser pen) work honestly.', }, { kind: 'improved', text: 'Every existing preset renders exactly as before -- the new transparency-aware fades and wipes read pixel-identically over the black stage. Modes without a Canvas2D operator run through a small WebGL pass; browsers without WebGL fall back to the nearest native operator.', }, ], }, { version: '7.14.1', name: 'New blend modes + zero-fade trails', changes: [ { kind: 'added', text: 'Scripts can now choose how fresh strokes mix with what is already on screen: blend: multiply, screen, subtract and replace join the old additive and alpha. replace is the interesting one -- new lines overwrite the color underneath instead of stacking on it, so a figure redrawn over its own trails keeps its color instead of blooming to white.', }, { kind: 'added', text: 'persist: 1 is now legal: a framebuffer that never fades. Combined with replace and the background zoom it finally makes the classic "colors ride outward forever" blow-out work -- the blow-out itself carries old color off screen while the fresh drawing replaces it in the center, at constant speed, no fading.', }, ], }, { version: '7.14', name: 'Arctic Rainbow re-sprayed', changes: [ { kind: 'improved', text: 'The old Arctic Rainbow was just white dots sweeping upward. I rebuilt it as a music-driven water spray: droplets rise slowly, hang at the top, arch left or right and fall back down. The drop count now follows the music -- silence runs completely dry, loud passages throw a full spray of up to 120 droplets -- every droplet carries its own color that shifts on each bass hit, and falling droplets trail a tapered RGB tail while rising ones stay crisp dots.', }, ], }, { version: '7.13.1', name: 'Beat Matrix grid fixed', changes: [ { kind: 'fixed', text: 'The rightmost column of Beat Matrix no longer sits one row short. Its last dot used to land one column past the wall\u2019s right edge while the column itself was left without its bottom dot -- a classic off-by-one between the grid math and the engine\u2019s inclusive 0-to-1 point range. I rebuilt the mapping through cell centers, so all 16 columns now carry their full 10 dots, perfectly aligned.', }, ], }, { version: '7.13', name: 'One build, every feature', changes: [ { kind: 'improved', text: 'The two development lines are merged into one build. Everything from the parallel line -- the unfrozen spectrum edge, the screen wake lock, the respectful Demo button, one-click saving, the background movement family and the "osc-rotations" preset -- now lives alongside the community OVE-Directory in this release.', }, { kind: 'improved', text: 'The Script tab toolbar carries the one-click Save and "Upload to OVE-Directory" side by side, and the release list below now reads as one timeline (the parallel line\u2019s wake-lock release is folded in as 7.10.2).', }, ], }, { version: '7.12', name: 'Background movement (AVS-style) + osc-rotations', changes: [ { kind: 'added', text: 'Scripts can move the whole picture now: bgrot spins the background clockwise or counter-clockwise, bgx / bgy slide it up, down, left or right, and bgzoom draws it in or blows it out -- the movement family the classic Winamp AVS plugins were built on. The speed can ride the music: accelerate on every beat, flip the spin direction on a hard hit, and let a slow fade to black blend the colors. All four variables are documented in the script editor\'s help panel.', }, { kind: 'added', text: 'I added an eleventh Zai-Magic preset: "osc-rotations" -- a stationary centered oscilloscope whose left and right points color-shift together while the center points hold their own hue, over a background that rotates clockwise and counter-clockwise to the music and temporarily accelerates with the beat. Hard hits flip the spin; the fade stays slow so the colors blend.', }, ], }, { version: '7.11', name: 'The community OVE-Directory', changes: [ { kind: 'added', text: 'A community script directory on its own server -- a sixth tab, Directory, lists every published script with its name, type and date, a 1–5 star rater with the live "Average: #.#" beside the stars, and sorting by Name, Type, Date or Rating. I reached it through this site\u2019s own backend, so the two servers stay fully independent: either can go down and the other keeps running.', }, { kind: 'added', text: '"Upload to OVE-Directory" in the Script tab (enabled while the script shows \u2714 Compiled OK with no runtime error). The script travels as its compact Script\u21e2Hash string -- the directory stores a fraction of the source -- and every upload is re-checked server-side: the string must decode back into a script that still compiles.', }, { kind: 'added', text: '"Get" on any directory entry fetches the exact script back, files it in this browser\u2019s preset tree under "user created/OVE-Directory" (identical copies are never duplicated; name collisions number themselves "(1) (2) (3)\u2026"), and offers a one-click "Open in editor".', }, { kind: 'added', text: 'Ratings are one vote per script per visitor, enforced by the directory itself; a second attempt just shows the stored average. The anonymous voter id is a cookie minted by this site\u2019s backend -- no accounts, no identity stored.', }, ], }, { version: '7.10.2', name: 'Wake lock, a respectful Demo, one-click save', changes: [ { kind: 'added', text: 'The screen stays on while music plays: on phones and tablets the site now holds a screen wake lock for as long as any audio source is live, then lets go the moment playback stops so the device\'s normal screen timeout takes over again.', }, { kind: 'improved', text: 'The Demo Track button stopped hijacking the preset selection. When a preset is already loaded (tree double-click, Load, a copied preset) or the editor holds custom script material, Demo just plays the track and leaves everything untouched -- the Zai-Magic Sequence showcase now only starts for a visit that never picked or typed anything, so a custom script can be built and debugged against the demo track without fear of it being replaced.', }, { kind: 'improved', text: 'One-click saving in the Script tab: a valid script shows the Save button even with no save target picked, and one click stores it under "user created", auto-named from the script\'s name header (a hash import of "Spectrum Bars:[▼…]" saves as "Spectrum Bars"). Nameless scripts land as "Empty Name", collisions as "(1)", "(2)"… -- and in those cases a "Give This Preset a Name" dialog opens immediately so the just-saved preset can be renamed on the spot.', }, ], }, { version: '7.10.1', name: 'Spectrum edge unfrozen', changes: [ { kind: 'fixed', text: 'Spectrum visuals used to sit dead along the right edge: the bar field stretched all the way to the top of the FFT (~24 kHz), where music carries almost nothing and MP3/YouTube audio is already cut off, so the rightmost bars read permanent silence no matter how loud the track got. I capped the field at 16 kHz -- the range where music actually lives -- so every bar has something to react to.', }, ], }, { version: '7.10', name: 'Ten demo tracks', changes: [ { kind: 'added', text: 'A ten-track Demo collection replaces the single demo track. Each Demo Track click plays once through; the next click (or the Stop button) advances to the next track in filename order, wrapping back to Track 1 after Track 10. Each visitor’s place in the collection is saved in their browser, so returning later picks up where they left off.', }, { kind: 'removed', text: 'Redundant helper text across the site -- the landing menu’s source list, the presets panel’s read-only paragraph, the mix-mode hints, the settings descriptions, the link dialog’s footnote and the footer’s “engine ok” dot. The interface trusts its own labels now.', }, ], }, { version: '7.9', name: 'Footer credit + changelog polish', changes: [ { kind: 'added', text: 'My signature in the footer -- “© 2026 · Site Created & Deployed by Sum’ting White” (the year always current) with a link to my portfolio at zcreations.dev.', }, { kind: 'improved', text: 'What’s New tidy-up -- a theme-matched divider above every release box, and the change tags right-aligned against an invisible column so every description lines up on the same edge.', }, ], }, { version: '7.8', name: 'Share strings, supersized', changes: [ { kind: 'improved', text: 'I rebuilt the share codec -- Script⇢Hash strings are now ~72% shorter than the script (was ~49%); the median share string is about a QUARTER of the script’s length. “1400” now costs less than one character: the whole script packs into CJK + Hangul digits carrying ~15 bits apiece.', }, { kind: 'improved', text: 'I grew the symbol dictionary by ~170 data-mined entries -- every function with its open paren, every audio variable, AVS idioms like “hue = hue + dt * ”, header lines like “linewidth: ”, and ~70 number literals.', }, { kind: 'added', text: 'v7.7 strings (the ▲ ones) still paste back perfectly -- and short scripts keep the readable symbol form, since the encoder always picks whichever is shorter.', }, ], }, { version: '7.7', name: 'Share scripts as one string', changes: [ { kind: 'added', text: 'I built Script⇢Hash / Hash⇢Script -- turn a script into ONE short Unicode string (♠ ♡ ♦ built-in references, ~half the length) and post it anywhere; anyone pastes it back and the script returns exactly as written.', }, { kind: 'added', text: 'Share strings work even when the script doesn’t compile -- share work in progress to get help, advice or debugging.', }, { kind: 'added', text: 'Every string ends in check digits, so one cut off by a character limit is refused honestly instead of decoding into garbage.', }, { kind: 'removed', text: 'The Music Services log in / “log out of all” section in Settings -- the built-in account already plays links for everyone (it returns, reworked, in a later update).', }, ], }, { version: '7.6.1', changes: [ { kind: 'removed', text: 'The “Set Beat” button from the header -- tap-tempo lives in the Beat tab (and Space still works anywhere). The beat indicator stays.' }, ], }, { version: '7.6', name: 'What’s New!', changes: [ { kind: 'added', text: 'The “What’s New!” button in the header -- every release’s additions, improvements and fixes at a glance.' }, { kind: 'added', text: 'A small dot on the button marks updates a visitor hasn’t seen yet; opening the list clears it.' }, ], }, { version: '7.5.1', changes: [ { kind: 'fixed', text: 'The “Stream a music link” dialog no longer stretches past its own border when a saved link title is long.' }, { kind: 'improved', text: 'The link dialog is a little wider, and long titles end with an ellipsis instead of overflowing.' }, ], }, { version: '7.5', name: 'Music links that just play', changes: [ { kind: 'fixed', text: 'Many YouTube links failing with “media resource not suitable” -- tracks are now downloaded audio-only and converted to MP3 on the server, instead of streaming expiring, IP-locked URLs.' }, { kind: 'improved', text: 'The Playing label shows the video’s real title.' }, { kind: 'improved', text: 'Over-long tracks get an honest “too long” answer, and playlist transitions no longer stall behind conversions.' }, ], }, { version: '7.4', name: 'Sequence mode', changes: [ { kind: 'added', text: '“Sequence” mix mode -- cycles the folder’s presets in order, wrapping back to the first so the folder loops forever.' }, { kind: 'improved', text: 'Random never picks the same preset twice in a row; the demo showcase now walks all ten Zai-Magic presets in order.' }, ], }, { version: '7.3', name: 'Demo-flow refinements', changes: [ { kind: 'improved', text: 'The demo track plays once through and returns to the source picker -- a demo, not a radio station.' }, { kind: 'fixed', text: 'The randomizer no longer switches presets while no music is playing.' }, { kind: 'removed', text: 'Preset-change popups (“Preset loaded”…) -- the footer’s SCRIPT label already echoes every change.' }, ], }, { version: '7.2.1', changes: [ { kind: 'fixed', text: 'Zai-Magic “Supernova” fading to black after its first explosion -- every beat now re-detonates the field with fresh geometry.' }, ], }, { version: '7.2', name: 'Showcase flow', changes: [ { kind: 'added', text: 'Animated shimmer on the Demo Track button -- one click starts the full showcase (demo music + the Zai-Magic folder on mix).' }, { kind: 'added', text: 'Preset changes blur-dissolve over 0.7 s instead of hard-cutting.' }, { kind: 'improved', text: 'Fresh visitors start on the Black + Purple Outline theme with Bass Bloom loaded.' }, ], }, { version: '7.1', name: 'Real demo track + beat detector rewrite', changes: [ { kind: 'added', text: 'The Demo button plays a real mastered recording -- with live tempo detection and a track chip in the header.' }, { kind: 'improved', text: 'I rewrote beat detection the way real trackers work (spectral flux → autocorrelation → phase-locked grid): the true BPM of real music locks in about two seconds.' }, { kind: 'removed', text: 'The demo BPM slider -- a real track’s tempo is detected, not dialed in.' }, ], }, { version: '7.00', name: 'Zai-Magic', changes: [ { kind: 'added', text: 'Ten hand-authored presets built around beat-synced color -- the color wheel turns on the kick, palettes swap per beat and per bar.' }, { kind: 'added', text: 'Script tools hsvr/hsvg/hsvb, smoothstep and the beatage kick-envelope variable, documented in the help panel.' }, ], }, { version: '6.9.3', changes: [{ kind: 'fixed', text: 'Share URLs point at the correct domain.' }], }, { version: '6.9.2', changes: [{ kind: 'fixed', text: 'The share preview image now passes Facebook’s checks; new title-card artwork.' }], }, { version: '6.9.1', changes: [ { kind: 'added', text: 'Social share previews -- sharing the site shows the big image card on Facebook, X, WhatsApp and Discord.' }, ], }, { version: '6.9', name: 'House account', changes: [ { kind: 'added', text: 'Anonymous visitors play YouTube links through my shared house account -- bot walls stop applying to guests.' }, ], }, { version: '6.8.2', changes: [ { kind: 'improved', text: 'Music-service login cut to about four clicks -- the exported cookie file can be dragged straight onto the login box.' }, ], }, { version: '6.8.1', changes: [ { kind: 'improved', text: 'The login box recognizes raw Cookie headers, cookies.txt and Cookie-Editor JSON, and validates them live.' }, ], }, { version: '6.8', name: 'Self-healing engine', changes: [ { kind: 'added', text: 'When YouTube changes their mechanics, the server updates yt-dlp itself and retries behind an “Updating in Background” notice.' }, ], }, { version: '6.7', name: 'Lazy playlist queue', changes: [ { kind: 'added', text: 'Playlist links become a queue that streams one song at a time -- each track is fetched only right before it plays.' }, { kind: 'added', text: 'Queue bar with skip, jump and replay.' }, ], }, { version: '6.6', name: 'Music service logins', changes: [ { kind: 'added', text: 'Settings-tab logins for YouTube, SoundCloud, Spotify and Apple Music, encrypted on the visitor’s computer.' }, ], }, { version: '6.5', name: 'Settings tab & themes', changes: [{ kind: 'added', text: 'Theme picker -- base chrome × highlight color, saved per visitor.' }], }, { version: '6.4', changes: [ { kind: 'fixed', text: 'Beat detection suspends when no music is playing -- no ghost beats after Stop.' }, { kind: 'added', text: 'Royal purple dark theme (later joined by more bases).' }, ], }, { version: '6.3', changes: [{ kind: 'added', text: 'Light/dark tick switch in the header.' }], }, { version: '6.2', changes: [ { kind: 'improved', text: 'The embedded preset library is read-only; each visitor’s own presets live in their browser.' }, ], }, { version: '6.1', changes: [ { kind: 'improved', text: 'Every embedded preset is a unique generated visual -- sixteen families, seeded parameters.' }, ], }, { version: '6.0', changes: [ { kind: 'improved', text: 'Renamed to Open Visualization Engine, with a version stamp in the footer.' }, ], }, { version: '5.0', changes: [{ kind: 'added', text: 'Folder-tree Presets page.' }], }, { version: '4.0', changes: [{ kind: 'added', text: 'Link audio source -- paste a URL, hear the music.' }], }, ] // Dev-mode guard: the newest entry must describe the running version. If you // bumped APP_VERSION without adding its release note, this warns in the // browser console AND the server log so it gets noticed before shipping. if ( process.env.NODE_ENV !== 'production' && RELEASE_NOTES.length > 0 && RELEASE_NOTES[0].version !== APP_VERSION ) { console.warn( `[release-notes] RELEASE_NOTES[0] (v${RELEASE_NOTES[0].version}) does not match APP_VERSION (v${APP_VERSION}) -- add the new release's entry to src/lib/release-notes.ts.`, ) }