Framework wrappers
The thin framework wrappers version independently of the core libraries they wrap.
@arraypress/waveform-player-astro
Section titled “@arraypress/waveform-player-astro”[Unreleased]
Section titled “[Unreleased]”[0.8.0] — 2026-09-24
Section titled “[0.8.0] — 2026-09-24”- Forwarding-drift test.
test/forwarding-drift.test.tsenumerates the installed core’s option surface (DEFAULT_OPTIONSplus theWaveformPlayerOptionskeys), renders each option, and feeds the emitteddata-*attributes through the installed core’s ownparseDataAttributes()— failing for any option that doesn’t round-trip and isn’t listed inNOT_FORWARDEDwith a reason. Test-only; adds@types/nodeas a dev dependency.
Changed
Section titled “Changed”onNextTrack/onPreviousTrackare no longer props. They were typed —WaveformPlayerPropsomitted the lifecycle callbacks but not these two — yet a server-rendered component can’t carry a function into adata-*attribute, so they type-checked and did nothing. Now omitted with the other callbacks. Lock-screen skip buttons need a player constructed in client JS (or@arraypress/waveform-bar/-playlist, which supply the handlers).- Core peer floor raised to
@arraypress/waveform-player@^1.23.0(was^1.20.0). The component emitsdata-button-radius/data-artwork-position(core 1.22.0) anddata-cross-origin(1.23.0); an older core ignores them, so those props silently did nothing on the declared range. The dev dependency moves to^1.27.1(was^1.22.0, which predates thecrossOrigintype). A new test pins the floor to the newest attribute emitted.
waveformGradientandseekHandlenow reach the player, asdata-waveform-gradient(the axis string) anddata-seek-handle. Both have been typed props since the core added them (1.18.0 / 1.17.0), but thedata-*emission is hand-written and never listed them, so they were silently dropped.- View Transitions no longer leave players running after navigation. The
component only re-initialised on
astro:page-load; nothing tore the outgoing players down, so a playing track kept going from its detachednew Audio()with no UI left to pause it, and every visited page’s instances stayed registered. Each<WaveformPlayer>(lazy or not) now also ships a one-timeastro:before-swaphandler that destroys the declarative players whose containers are leaving the page, keeping those inside atransition:persisthost the next page carries. Players built by framework islands are left to their framework.
[0.7.0] — 2026-07-22
Section titled “[0.7.0] — 2026-07-22”crossOriginprop. Exposes the option added in@arraypress/waveform-player@1.23.0, emitted asdata-cross-origin('anonymous'|'use-credentials'). Omitted by default so the player behaves like a native<audio>and never forces a CORS request that would break CDN media withoutAccess-Control-Allow-Origin.
[0.6.0] — 2026-07-17
Section titled “[0.6.0] — 2026-07-17”-
buttonRadiusandartworkPositionprops. Exposes the two options added in@arraypress/waveform-player1.22.0:buttonRadiussets the play button’s corner radius (0for square, or any CSS length), andartworkPosition('info'|'button') chooses whether the cover renders in the info row or becomes the play button itself.Both prop types already flowed through automatically, since the props derive from the core’s
WaveformPlayerOptions— but thedata-*emission is written by hand, so until now they would have type-checked and then silently done nothing. Tests now cover the mapping for exactly that reason.The peer range stays
^1.20.0: the props only appear once the consumer’s own core is on 1.22.0, so nothing breaks on an older one.
[0.5.0] — 2026-07-05
Section titled “[0.5.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
Removed
Section titled “Removed”- BREAKING: the six DOM-chrome colour props —
buttonColor,buttonHoverColor,textColor,textSecondaryColor,backgroundColor, andborderColor— are gone (the core library moved this chrome to CSS variables). Theme the button and text via--wfp-button-color,--wfp-text-color,--wfp-text-secondary-color, etc. in your own CSS instead.waveformColor,progressColor,colorPreset, andwaveformGradientremain props.
Changed
Section titled “Changed”- Types are now sourced directly from the core
@arraypress/waveform-playerpackage — a single source of truth. The shared option types (WaveformStyle,ColorPreset,AudioMode,AudioPreload,ButtonAlign,WaveformMarker,WaveformPeaks) are re-exported from the core, andWaveformPlayerPropsnowextendsthe core’sWaveformPlayerOptionsinstead of re-declaring every option, so the two packages can no longer drift. Every previously-exported type name is preserved. - Bumped the
@arraypress/waveform-playerpeer (and dev) dependency to^1.8.0, which ships the hand-authoredindex.d.tsthese types adopt.
accessibleSeek,seekLabel,barRadius, and the gradient-array forms ofwaveformColor/progressColorare now exposed onWaveformPlayerProps(inherited from the core option surface), filling gaps where the previous hand-maintained copy had missed or drifted.
[0.1.2] — 2026-06-27
Section titled “[0.1.2] — 2026-06-27”Changed
Section titled “Changed”- Bumped the
@arraypress/waveform-playerpeer (and dev) dependency to^1.7.2. Consumers now get the native accessible keyboard / ARIA seek slider by default. No component API changes.
[0.1.1] — Unreleased
Section titled “[0.1.1] — Unreleased”Changed
Section titled “Changed”- Widened the
astropeerDependency to^6.0.0 || ^7.0.0for Astro 7 readiness. No runtime changes — the component is unaffected by the Astro 7 compiler / Vite 8 (Rolldown) upgrade.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlayer>Astro component wrapping every option exposed by@arraypress/waveform-player1.6.x as a typed prop:- Audio source props (
url,audioMode,preload) - Waveform visualisation props (
waveformStyle,height,samples,barWidth,barSpacing,waveform) - Colour props (
colorPreset,waveformColor,progressColor,buttonColor,buttonHoverColor,textColor,textSecondaryColor,backgroundColor,borderColor) - Playback control props (
playbackRate,showPlaybackSpeed,playbackRates) - UI toggle props (
showControls,showInfo,showTime,showHoverTime,showBPM,buttonAlign) - Marker props (
markers,showMarkers) - Content metadata props (
title,artist,artwork,album) - Behaviour flags (
autoplay,singlePlay,playOnSeek,enableMediaSession) - Icon props (
playIcon,pauseIcon)
- Audio source props (
- Astro-specific
lazyprop that switches the init attribute todata-waveform-player-lazyand ships a single deduplicatedIntersectionObserverfor grids of many previews. - Astro-specific
id,class, andstylepass-throughs. - Public TypeScript types:
WaveformPlayerProps,WaveformStyle,WaveformMarker,WaveformPeaks,ColorPreset,AudioMode,AudioPreload,ButtonAlign. - Ambient declaration for
window.WaveformPlayerto type consumer scripts that reach for the global. - Vitest suite (29 tests) covering attribute mapping, omission semantics, JSON serialisation for array props, lazy-mount script presence, and pass-through props.
- Documentation: full prop reference, setup guide, seven usage
examples (
examples/basic.astro).
@arraypress/waveform-player-react
Section titled “@arraypress/waveform-player-react”[Unreleased]
Section titled “[Unreleased]”[0.9.0] — 2026-09-24
Section titled “[0.9.0] — 2026-09-24”- Forwarding-drift test.
test/forwarding-drift.test.tsxenumerates the installed core’s option surface (DEFAULT_OPTIONSplus theWaveformPlayerOptionskeys) and fails for any option that isn’t forwarded, doesn’t remount on change, or isn’t listed inNOT_FORWARDEDwith a reason — so the next core option can’t be dropped the way the ones above were. Test-only; adds@types/nodeas a dev dependency.
waveformGradientandseekHandlenow reach the player. Both have been typed props since the core added them (1.18.0 / 1.17.0), because the props derive fromWaveformPlayerOptions— butbuildLibraryOptionsis a hand-written allowlist that never listed them, so they type-checked and were silently dropped.onNextTrack/onPreviousTracknow reach the player, so the lock-screen / system media controls show skip buttons when you supply them. They were typed but never forwarded. They’re passed only when set: the core registers the Media Session action whenever the option is a function, so an unconditional wrapper would show buttons that do nothing. Like the other callbacks they’re routed through a ref — a fresh inline handler doesn’t remount — but adding or removing one does, since the core reads it at construction.- Changing
layout,buttonStyleorbpmat runtime now remounts the player. They were forwarded on first mount but missing from the remountuseEffectdeps, so later changes did nothing. - Changing only
classNameno longer strips the player’s own classes. The core writes classes onto the host —waveform-player,waveform-layout-preview,waveform-theme-light,waveform-is-placeholder— and aclassName-only change (correctly) doesn’t remount, so when React rewrote theclassattribute those were gone until some other prop happened to change, leaving an unstyled player. React now rendersclassonce (so server markup and hydration are unchanged) and laterclassNamechanges are applied withclassList, adding and removing only the user’s tokens. classNameandwfp-hostsurvive mount. The core’screateDOM()replaces the host’s whole class list withwaveform-player, so both were silently dropped as soon as the player built —classNameonly ever styled the pre-mount placeholder. They’re re-applied right after construction. The DOM structure is unchanged: still one host<div>, which is also the.waveform-playerroot. (Mounting the core into an inner element was considered and rejected:--wfp-*variables set throughstyleorclassNamewould then sit on a parent, shadowed by the core’s own.waveform-playerdefaults.)WaveformPlayerHandle.setPlaybackRatedocuments the real range —0.25..4, what the core clamps to — instead of0.5..2.
[0.8.0] — 2026-09-22
Section titled “[0.8.0] — 2026-09-22”Changed
Section titled “Changed”- Loads
@arraypress/waveform-player/no-autoinitinstead of the package root. Importing the root scans the whole document for[data-waveform-player]markup and builds a player for every match. This component constructs its own player on its own ref and wants none of that. In a pure React app the scan found nothing and merely cost aquerySelectorAll; as an island on a page that does carry such markup — a CMS page, a WordPress template, an Astro or Rails view — mounting this component silently mounted players the React tree never asked for, and owned them for the rest of the page’s life. Same class, same options, same behaviour for everything this component builds; the only thing that changes is that nothing else on the page gets touched.
Breaking
Section titled “Breaking”- Peer floor raised to
@arraypress/waveform-player@^1.27.0, the release that added the/no-autoinitentry point. This is the first hard floor in the family rather than the usual soft one: on an older core the subpath does not exist, so it fails at mount, in the browser. Bump the core alongside this package.
[0.7.0] — 2026-07-22
Section titled “[0.7.0] — 2026-07-22”crossOriginprop. Exposes the option added in@arraypress/waveform-player@1.23.0: sets the CORS mode of the underlying<audio>('anonymous'|'use-credentials'). Omitted from the options bag by default so the player behaves like a native<audio>and never forces a CORS request that would break CDN media withoutAccess-Control-Allow-Origin.
[0.6.0] — 2026-07-17
Section titled “[0.6.0] — 2026-07-17”-
buttonRadiusandartworkPositionprops. Exposes the two options added in@arraypress/waveform-player1.22.0:buttonRadiussets the play button’s corner radius (0for square, or any CSS length), andartworkPosition('info'|'button') chooses whether the cover renders in the info row or becomes the play button itself.Both prop types already flowed through automatically, since the props derive from the core’s
WaveformPlayerOptions— but the options bag is written by hand, so until now they would have type-checked and then silently done nothing. Tests now cover the mapping for exactly that reason.The peer range stays
^1.20.0: the props only appear once the consumer’s own core is on 1.22.0, so nothing breaks on an older one.
[0.5.0] — 2026-07-05
Section titled “[0.5.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
Changed
Section titled “Changed”- Public types are now adopted from the core
@arraypress/waveform-player(v1.8.0+), which ships a hand-authoredindex.d.ts. The shared option surface (WaveformStyle,ColorPreset,AudioMode,AudioPreload,ButtonAlign,WaveformMarker,WaveformPeaks) is re-exported from the core, andWaveformPlayerPropsnowextendsthe core’sWaveformPlayerOptionsinstead of re-declaring every field. The core is the single source of truth, so the wrapper’s types can no longer drift out of sync. Bumped the@arraypress/waveform-playerpeer (and dev) dependency to^1.8.0. - Callback props (
onLoad,onPlay,onPause,onEnd,onTimeUpdate,onError) and theWaveformPlayerHandle.instanceaccessor are now typed with the core’sWaveformPlayerclass instead ofunknown.
accessibleSeek,seekLabel,barRadius, and gradient-array waveform / progress colours (string[]) are now exposed as typed props — picked up for free by extending the core options and wired through to the underlying player.
Removed
Section titled “Removed”- Deleted
src/core-module-shim.d.ts, the loose ambientdeclare module '@arraypress/waveform-player'shim that only existed while the core had no shipped types.
[0.1.1] — 2026-06-27
Section titled “[0.1.1] — 2026-06-27”Changed
Section titled “Changed”- Bumped the
@arraypress/waveform-playerpeer (and dev) dependency to^1.7.2. Consumers now get the native accessible keyboard / ARIA seek slider by default. No component API changes.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlayer>React component wrapping every option exposed by@arraypress/waveform-player1.6.x as a typed prop:- Audio source (
url,audioMode,preload) - Waveform visualisation (
waveformStyle,height,samples,barWidth,barSpacing,waveform) - Colours (
colorPreset,waveformColor,progressColor,buttonColor,buttonHoverColor,textColor,textSecondaryColor,backgroundColor,borderColor) - Playback (
playbackRate,showPlaybackSpeed,playbackRates) - UI toggles (
showControls,showInfo,showTime,showHoverTime,showBPM,buttonAlign) - Markers (
markers,showMarkers) - Metadata (
title,artist,artwork,album) - Behaviour (
autoplay,singlePlay,playOnSeek,enableMediaSession) - Icons (
playIcon,pauseIcon)
- Audio source (
- Callback props (
onLoad,onPlay,onPause,onEnd,onTimeUpdate,onError) that map to the library’s same-named option fields. Callbacks are deliberately NOT in the effect dep array, so a parent re-rendering with new inline functions doesn’t tear the player down. - React-specific extras:
id,className,style, andrefforwarding viaWaveformPlayerHandle. WaveformPlayerHandleimperative API on the forwarded ref —play(),pause(),togglePlay(),seekTo(),seekToPercent(),setVolume(),setPlaybackRate(),setPlayingState(),setProgress(),loadTrack(), plus the rawinstance.- SSR / RSC safe: the core library is loaded via dynamic
import('@arraypress/waveform-player')inside the effect so the browser-only audio surface never runs server-side. - Identity-prop re-mount: when any library-construction prop
changes (
url,audioMode, etc.), the wrapper destroys the existing instance and creates a new one with the updated options. Simpler and more correct than diffing every option + calling granular updaters. - Public TypeScript types:
WaveformPlayerProps,WaveformPlayerHandle,WaveformStyle,WaveformMarker,WaveformPeaks,ColorPreset,AudioMode,AudioPreload,ButtonAlign. - Ambient module shim for
@arraypress/waveform-playerso the wrapper typechecks cleanly until the core library ships its own.d.ts. - Vitest test suite (17 tests, jsdom +
@testing-library/react) covering mount, unmount destroy, option pass-through, callback forwarding, identity-prop re-mount, callback-churn protection, ref forwarding, and the full imperative handle surface. The core library is mocked at the module boundary because jsdom has no Web Audio API. - Dual ESM (
dist/index.js) + CJS (dist/index.cjs) build viatsup..d.tsfor both. React + the core library are externalised so they resolve to the consumer’s copies. - README with full prop reference, seven usage patterns, and the
imperative-ref control example.
examples/basic.tsxwith seven copy-paste-ready snippets.
@arraypress/waveform-player-vue
Section titled “@arraypress/waveform-player-vue”[Unreleased]
Section titled “[Unreleased]”[0.7.0] — 2026-09-24
Section titled “[0.7.0] — 2026-09-24”@next-track/@previous-track(onNextTrack/onPreviousTrackprops). Supplying one shows the lock-screen / system media controls’ skip button. The core options were already typed onWaveformPlayerPropsbut had no runtime prop declaration, so they never reached the player and the buttons never appeared. They’re declared as function props rather than emits because the wrapper has to know whether anyone is listening — the core shows the button whenever the option is a function. Adding or removing one remounts the player; swapping one handler for another doesn’t.- Forwarding-drift test.
test/forwarding-drift.test.tsenumerates the installed core’s option surface (DEFAULT_OPTIONSplus theWaveformPlayerOptionskeys) and fails for any option that isn’t declared and forwarded, doesn’t remount on change, or isn’t listed inNOT_FORWARDEDwith a reason — so the next core option can’t be dropped the way the ones above were. Test-only; adds@types/nodeas a dev dependency.
Changed
Section titled “Changed”WaveformPlayerPropsomits the core’sstyle(a shorthand alias forwaveformStyle), which typedstyleas aWaveformStylewhile Vue applies it as the fall-through CSS attribute. Matches the other wrappers: usewaveformStylefor the visual style.
waveformGradientandseekHandlenow reach the player. Both have been typed props since the core added them (1.18.0 / 1.17.0), but with no runtime prop declaration Vue treated them as fall-through attributes on the<div>. Both are now declared, forwarded, and in the remountwatch().setPlaybackRatedocuments the real range —0.25..4, what the core clamps to — instead of0.5..2.- Changing only the fall-through
classno longer strips the player’s own classes. The core writes classes onto the host —waveform-player,waveform-layout-preview,waveform-theme-light,waveform-is-placeholder— and a class-only change (correctly) doesn’t remount, so when Vue re-patched theclassattribute those were gone until some other prop happened to change, leaving an unstyled player. The component now rendersclassonce (server markup and hydration are unchanged) and applies later changes withclassList, adding and removing only the user’s tokens. To keepclassout of Vue’s patching it setsinheritAttrs: falseand forwards every other attribute (id,style, listeners,data-*) itself — same result on the element. - The fall-through
classandwfp-hostsurvive mount. The core’screateDOM()replaces the host’s whole class list withwaveform-player, so both were silently dropped as soon as the player built. They’re re-applied right after construction. The DOM structure is unchanged: still one host<div>, which is also the.waveform-playerroot. (Mounting the core into an inner element was considered and rejected:--wfp-*variables set throughstyleorclasswould then sit on a parent, shadowed by the core’s own.waveform-playerdefaults.)
[0.6.0] — 2026-09-22
Section titled “[0.6.0] — 2026-09-22”Changed
Section titled “Changed”- Loads
@arraypress/waveform-player/no-autoinitinstead of the package root. Importing the root scans the whole document for[data-waveform-player]markup and builds a player for every match. This component constructs its own player on its own ref and wants none of that. In a pure Vue app the scan found nothing and merely cost aquerySelectorAll; as an island on a page that does carry such markup — a CMS page, a WordPress template, an Astro or Laravel view — mounting this component silently mounted players the Vue app never asked for, and owned them for the rest of the page’s life. Same class, same options, same behaviour for everything this component builds; the only thing that changes is that nothing else on the page gets touched.
Breaking
Section titled “Breaking”- Peer floor raised to
@arraypress/waveform-player@^1.27.0, the release that added the/no-autoinitentry point. This is the first hard floor in the family rather than the usual soft one: on an older core the subpath does not exist, so it fails at mount, in the browser. Bump the core alongside this package.
[0.5.0] — 2026-07-22
Section titled “[0.5.0] — 2026-07-22”crossOriginprop. Exposes the option added in@arraypress/waveform-player@1.23.0: sets the CORS mode of the underlying<audio>('anonymous'|'use-credentials'). Declared as a runtime prop and omitted from the options bag by default so the player behaves like a native<audio>and never forces a CORS request that would break CDN media withoutAccess-Control-Allow-Origin.
[0.4.0] — 2026-07-17
Section titled “[0.4.0] — 2026-07-17”-
buttonRadiusandartworkPositionprops. Exposes the two options added in@arraypress/waveform-player1.22.0:buttonRadiussets the play button’s corner radius (0for square, or any CSS length), andartworkPosition('info'|'button') chooses whether the cover renders in the info row or becomes the play button itself.Both prop types already flowed through automatically, since the props derive from the core’s
WaveformPlayerOptions— but the options mapping is written by hand, so until now they would have type-checked and then silently done nothing. Tests now cover the mapping for exactly that reason.The peer range stays
^1.20.0: the props only appear once the consumer’s own core is on 1.22.0, so nothing breaks on an older one.
buttonSizeaccepts a number again. The prop was declaredtype: String, so:button-size="64"failed Vue’s runtime type check and warned, even though the core accepts a number (px) or a unit string. It now declares[String, Number], matching the core.buttonRadiustakes the same pair.
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlayer>Vue 3 component wrapping every option exposed by@arraypress/waveform-playeras a typed prop:- Audio source (
url,srcalias,audioMode,preload) - Waveform visualisation (
waveformStyle,height,samples,barWidth,barSpacing,barRadius,waveform) - Colours (
colorPreset,waveformColor,progressColor,waveformGradient— strings orstring[]gradients). DOM chrome (button, title, meta text) is themed via CSS variables (--wfp-button-color,--wfp-text-color,--wfp-text-secondary-color), not JS options. - Playback (
playbackRate,showPlaybackSpeed,playbackRates) - UI toggles (
showControls,showInfo,showTime,showHoverTime,showBPM,buttonAlign,accessibleSeek,seekLabel,errorText) - Markers (
markers,showMarkers) - Metadata (
title,artist,artwork,album) - Behaviour (
autoplay,singlePlay,playOnSeek,enableMediaSession) - Icons (
playIcon,pauseIcon)
- Audio source (
- Lifecycle emits (
load,play,pause,end,timeupdate,error), each forwarding the liveWaveformPlayerinstance. Wired through Vue’s stableemit, so listeners can change without tearing the player down. - Imperative API exposed via a template
ref(WaveformPlayerExpose):play(),pause(),togglePlay(),seekTo(),seekToPercent(),setVolume(),setPlaybackRate(),setPlayingState(),setProgress(),loadTrack(), plus the rawinstance. class,style, andidfall through to the host element via Vue’s attribute inheritance; the base classwfp-hostalways applies.- SSR / Nuxt safe: the core library is loaded via dynamic
import('@arraypress/waveform-player')insideonMountedso the browser-only audio surface never runs server-side. - Identity-prop re-mount: when any library-construction prop changes, the wrapper destroys the existing instance and creates a new one with the updated options. A monotonic mount token discards any in-flight async import that a newer mount (or unmount) has superseded.
- Public types are adopted from the core
@arraypress/waveform-player(WaveformStyle,ColorPreset,AudioMode,AudioPreload,ButtonAlign,WaveformMarker,WaveformPeaks), re-exported here so the wrapper’s types can never drift out of sync.WaveformPlayerPropsis derived from the core’sWaveformPlayerOptions. - Dual ESM (
dist/index.js) + CJS (dist/index.cjs) build viatsup, with.d.tsfor both. Vue + the core library are externalised so they resolve to the consumer’s copies. - Vitest test suite (jsdom +
@vue/test-utils) covering mount, option pass-through, thesrc → urlalias, boolean-prop omission, emit forwarding, destroy-on-unmount, identity-prop re-mount, and the exposed imperative API. The core is mocked at the module boundary because jsdom has no Web Audio API. - README with full prop reference, seven usage patterns, and the
imperative-ref control example.
examples/basic.vuewith seven copy-paste-ready snippets.
@arraypress/waveform-player-svelte
Section titled “@arraypress/waveform-player-svelte”[Unreleased]
Section titled “[Unreleased]”[0.7.0] — 2026-09-24
Section titled “[0.7.0] — 2026-09-24”onnexttrack/onprevioustrackcallback props. Supplying one shows the lock-screen / system media controls’ skip button and forwards to the core’sonNextTrack/onPreviousTrack. The core options were already typed onWaveformPlayerPropsbut never forwarded (they fell into...rest), so the buttons never appeared. They’re passed only when set — the core shows the button whenever the option is a function — and adding or removing one remounts the player, while swapping in a fresh inline handler doesn’t.- Forwarding-drift test.
test/forwarding-drift.test.tsenumerates the installed core’s option surface (DEFAULT_OPTIONSplus theWaveformPlayerOptionskeys) and fails for any option that isn’t forwarded, doesn’t remount on change, or isn’t listed inNOT_FORWARDEDwith a reason — so the next core option can’t be dropped the way the ones above were. Remounts are checked through a fine-grained parent (test/Harness.svelte), because testing-library’srerender()invalidates every prop at once. Test-only; adds@types/nodeas a dev dependency.
Changed
Section titled “Changed”styleis typed as the host<div>’s CSS attribute.WaveformPlayerPropsnow omits the core’sstyle(a shorthand alias forwaveformStyle), which typedstyleas aWaveformStylewhile the component spread it onto the element as CSS. Matches the other wrappers: usewaveformStylefor the visual style. The camelCaseonNextTrack/onPreviousTrackare omitted too, in favour of the lowercase props above.
waveformGradientandseekHandlenow reach the player. Both have been typed props since the core added them (1.18.0 / 1.17.0), but they weren’t in the$props()destructure, so they fell into...restand were spread onto the host<div>as attributes instead.setPlaybackRatedocuments the real range —0.25..4, what the core clamps to — instead of0.5..2.- Changing only
classno longer strips the player’s own classes. The core writes classes onto the host —waveform-player,waveform-layout-preview,waveform-theme-light,waveform-is-placeholder— and a class-only change (correctly) doesn’t remount, so when Svelte rewrote theclassattribute those were gone until some other prop happened to change, leaving an unstyled player. The host now binds a class value frozen at init (server markup and hydration are unchanged) and laterclasschanges are applied withclassList, adding and removing only the user’s tokens. classandwfp-hostsurvive mount. The core’screateDOM()replaces the host’s whole class list withwaveform-player, so both were silently dropped as soon as the player built. They’re re-applied right after construction. The DOM structure is unchanged: still one host<div>, which is also the.waveform-playerroot. (Mounting the core into an inner element was considered and rejected:--wfp-*variables set throughstyleorclasswould then sit on a parent, shadowed by the core’s own.waveform-playerdefaults.)
[0.6.0] — 2026-09-22
Section titled “[0.6.0] — 2026-09-22”Changed
Section titled “Changed”- Loads
@arraypress/waveform-player/no-autoinitinstead of the package root. Importing the root scans the whole document for[data-waveform-player]markup and builds a player for every match. This component constructs its own player on its own ref and wants none of that. In a pure Svelte app the scan found nothing and merely cost aquerySelectorAll; as an island on a page that does carry such markup — a CMS page, a WordPress template, an Astro or Rails view — mounting this component silently mounted players the Svelte app never asked for, and owned them for the rest of the page’s life. Same class, same options, same behaviour for everything this component builds; the only thing that changes is that nothing else on the page gets touched.
Breaking
Section titled “Breaking”- Peer floor raised to
@arraypress/waveform-player@^1.27.0, the release that added the/no-autoinitentry point. This is the first hard floor in the family rather than the usual soft one: on an older core the subpath does not exist, so it fails at mount, in the browser. Bump the core alongside this package.
[0.5.0] — 2026-07-22
Section titled “[0.5.0] — 2026-07-22”crossOriginprop. Exposes the option added in@arraypress/waveform-player@1.23.0: sets the CORS mode of the underlying<audio>('anonymous'|'use-credentials'). Omitted from the options bag by default so the player behaves like a native<audio>and never forces a CORS request that would break CDN media withoutAccess-Control-Allow-Origin.
[0.4.0] — 2026-07-17
Section titled “[0.4.0] — 2026-07-17”-
buttonRadiusandartworkPositionprops. Exposes the two options added in@arraypress/waveform-player1.22.0:buttonRadiussets the play button’s corner radius (0for square, or any CSS length), andartworkPosition('info'|'button') chooses whether the cover renders in the info row or becomes the play button itself.Both prop types already flowed through automatically, since the props derive from the core’s
WaveformPlayerOptions— but the options mapping is written by hand, so until now they would have type-checked and then silently done nothing. Tests now cover the mapping for exactly that reason.The peer range stays
^1.20.0: the props only appear once the consumer’s own core is on 1.22.0, so nothing breaks on an older one.
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlayer>Svelte 5 component (built with runes) wrapping every option exposed by@arraypress/waveform-playeras a typed prop:- Audio source (
url,srcalias,audioMode,preload) - Waveform visualisation (
waveformStyle,height,samples,barWidth,barSpacing,barRadius,waveform) - Colours (
colorPreset,waveformColor,progressColor— strings orstring[]gradients). DOM chrome (button, title, meta text) is themed via CSS variables (--wfp-button-color,--wfp-text-color,--wfp-text-secondary-color), not JS options. - Playback (
playbackRate,showPlaybackSpeed,playbackRates) - UI toggles (
showControls,showInfo,showTime,showHoverTime,showBPM,buttonAlign,accessibleSeek,seekLabel,errorText) - Markers (
markers,showMarkers) - Metadata (
title,artist,artwork,album) - Behaviour (
autoplay,singlePlay,playOnSeek,enableMediaSession) - Icons (
playIcon,pauseIcon)
- Audio source (
- Lowercase lifecycle callback props (
onload,onplay,onpause,onend,ontimeupdate,onerror), each forwarding the liveWaveformPlayerinstance. Wired through reactive closures, so changing a handler never tears the player down. - Imperative API exported by the component instance (reachable via
bind:this):play(),pause(),togglePlay(),seekTo(),seekToPercent(),setVolume(),setPlaybackRate(),setPlayingState(),setProgress(),loadTrack(), andgetInstance(). class,style,id, and other element attributes fall through to the host element via...rest; the base classwfp-hostalways applies.- SSR / SvelteKit safe: the core library is loaded via dynamic
import('@arraypress/waveform-player')inside a browser-only$effect, so the audio surface never runs server-side. - Identity-prop re-mount: a single
$effectreads every construction prop, so changing any of them destroys the existing instance and creates a new one. A monotonic mount token discards any in-flight async import that a newer mount (or unmount) has superseded. - Public types adopted from the core
@arraypress/waveform-player(WaveformStyle,ColorPreset,AudioMode,AudioPreload,ButtonAlign,WaveformMarker,WaveformPeaks), re-exported here so the wrapper’s types can never drift.WaveformPlayerPropsis derived from the core’sWaveformPlayerOptions. - Built with
svelte-package(dist/ships the preprocessed.svelte+ generated.d.ts). Svelte + the core library are peer dependencies. - Vitest test suite (jsdom +
@testing-library/svelte) covering mount, option pass-through, thesrc → urlalias, boolean-prop omission, callback forwarding, destroy-on-unmount, identity-prop re-mount, and the exported imperative API. The core is mocked at the module boundary because jsdom has no Web Audio API. - README with full prop reference, seven usage patterns, and the
imperative
bind:thiscontrol example.examples/Basic.sveltewith seven copy-paste-ready snippets.
@arraypress/waveform-bar-astro
Section titled “@arraypress/waveform-bar-astro”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”waveformGradientconfig key ('vertical' | 'horizontal' | 'diagonal'). The bar has forwarded it to the embedded player since 1.8.0, but it was missing fromWaveformBarConfig, so TypeScript rejected a working option.barRadiusconfig key (number | null) — rounded bar caps in px,0= square,null= the player’s default. Requires@arraypress/waveform-bar1.12.0+; older bars ignore it.
Changed
Section titled “Changed”- Peer floors raised:
@arraypress/waveform-bar^1.10.0→^1.11.0and@arraypress/waveform-player^1.7.2→^1.8.0.WaveformBarConfighas declaredcrossOriginsince 0.4.0, but the bar only accepts it from 1.11.0; and the bar drivesaudioMode: 'external'inline players, which crashed before core player 1.8.0. The optionalbarRadius/ workingshowTime(bar 1.12.0) don’t raise the floor — older bars ignore them.
waveformColor/progressColoraccept gradient stop arrays. They were typedstring | null, but the bar forwards them verbatim to a player that takesstring | string[] | null—['#fafafa', '#71717a']works at runtime and now typechecks.showTimedoc comment notes thatfalseactually hides the time display from@arraypress/waveform-bar1.12.0 (earlier bars ignored it).- The
barSpacingdoc comment claimed@default 0; the bar’s default is2.
[0.4.0] — 2026-07-22
Section titled “[0.4.0] — 2026-07-22”crossOriginconfig key. Added toWaveformBarConfigand forwarded verbatim towindow.WaveformBar.init(). Requires@arraypress/waveform-bar@^1.11.0for it to reach the embedded player.
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Accept the new localizable player-string keys —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— in the bar config; they are forwarded to the embedded player. Requires@arraypress/waveform-bar@^1.10.0.
[0.2.0] — 2026-07-01
Section titled “[0.2.0] — 2026-07-01”Changed
Section titled “Changed”- Sync
WaveformBarConfigto the bar’smodeAPI:layout→mode('waveform' | 'classic'), removemaxWidth, addshowShuffle/shuffle. Matches@arraypress/waveform-bar1.7.0.
[0.1.3] — 2026-06-27
Section titled “[0.1.3] — 2026-06-27”Changed
Section titled “Changed”- Bumped the
@arraypress/waveform-playerpeer (and dev) dependency to^1.7.2, which adds the native accessible keyboard / ARIA seek slider to the underlying player. No component API changes.
[0.1.2] — Unreleased
Section titled “[0.1.2] — Unreleased”Changed
Section titled “Changed”- Widened the
astropeerDependency to^6.0.0 || ^7.0.0for Astro 7 readiness. No runtime changes — the component is unaffected by the Astro 7 compiler / Vite 8 (Rolldown) upgrade.
[0.1.1] — Unreleased
Section titled “[0.1.1] — Unreleased”<WaveformBarTrigger>’s default pause-icon SVG no longer carries an inlinestyle="display:none;". The previous value beat the core library’s class-based toggle (.wb-icon-swap.wb-playing .wb-show-pause { display: inline; }), leaving the pause icon permanently hidden once a track started playing. The library’s own CSS already covers the initial-hidden state via.wb-icon-swap .wb-show-pause { display: none; }, so the inline style is redundant as well as harmful.- Added two regression tests pinning the no-inline-display contract so the bug can’t return.
[0.1.0]
Section titled “[0.1.0]”Initial release.
<WaveformBar>— singleton mount component for the persistent bottom bar. Renders atransition:persisthost div and an inline init script that callswindow.WaveformBar.init(config)on everyastro:page-load. Relocates the library’s.waveform-barelement into the persist host so view transitions keep it onscreen between navigations.- Typed
WaveformBarConfigcovering every optionwindow.WaveformBar.init()accepts:- Persistence:
persist,autoResume,continuous,repeat - UI toggles:
showQueue,showPrevNext,showRepeat,showVolume,showMute,showTime,showTrackLink,showMeta,maxMeta - Theming:
theme,defaultArtwork - Waveform visualisation:
waveformStyle,waveformHeight,barWidth,barSpacing,waveformColor,progressColor,markerColor - Volume + persistence keys:
volume,storageKey - Server-side actions:
actions.favorite/actions.cartwithendpointURL + optionalmethod/headers
- Persistence:
<WaveformBarTrigger>— polymorphic click trigger. Renders a<button>by default; override viaas="a" | "div" | "span". Emits the fulldata-wb-*attribute contract:- Track identity:
url,id(falls back tourl),title,artist,album,artwork,link - Display chips:
duration,bpm,key,meta - Audio data:
waveform(peaks array, URL, or inline JSON),markers(DJ-mode chapters) - Initial state:
favorited,inCart - Behaviour:
mode='play' | 'queue',href(whenas="a"),ariaLabel,class,noDefaultIcons
- Track identity:
- Default slot ships the play / pause SVG pair the core library
toggles via
wb-show-play/wb-show-pauseclasses. Passing children replaces them. - Auto-generated
aria-labelwhen one isn’t supplied —Play {title}for play triggers,Add {title} to queuefor queue triggers. - Public TypeScript types:
WaveformBarProps,WaveformBarConfig,WaveformBarTriggerProps,WaveformBarTrackData,WaveformBarMarker,WaveformBarActions,WaveformBarAction,WaveformBarTheme,RepeatMode,TriggerMode,WaveformStyle. - Ambient declaration for
window.WaveformBarcovering the full public surface (play,pause,next,previous,addToQueue,setVolume,setRepeat,seekToMarker, etc.). - Vitest suite of 46 tests via Astro’s
experimental_AstroContainercovering attribute mapping, omission semantics, JSON serialisation for arrays, polymorphicas, default-slot behaviour, aria-label generation, and a kitchen-sink scenario. - Documentation: full README with setup, prop tables, every usage
pattern, and the
waveformbar:*custom-event API.examples/basic.astroreference page with six demonstrations.
@arraypress/waveform-bar-react
Section titled “@arraypress/waveform-bar-react”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”waveformGradientconfig key ('vertical' | 'horizontal' | 'diagonal'). The bar has forwarded it to the embedded player since 1.8.0, but it was missing fromWaveformBarConfig, so TypeScript rejected a working option.barRadiusconfig key (number | null) — rounded bar caps in px,0= square,null= the player’s default. Requires@arraypress/waveform-bar1.12.0+; older bars ignore it.
Changed
Section titled “Changed”- Peer floors raised:
@arraypress/waveform-bar^1.10.0→^1.11.0and@arraypress/waveform-player^1.7.2→^1.8.0.WaveformBarConfighas declaredcrossOriginsince 0.4.0, but the bar only accepts it from 1.11.0; and the bar drivesaudioMode: 'external'inline players, which crashed before core player 1.8.0. The optionalbarRadius/ workingshowTime(bar 1.12.0) don’t raise the floor — older bars ignore them.
waveformColor/progressColoraccept gradient stop arrays. They were typedstring | null, but the bar forwards them verbatim to a player that takesstring | string[] | null—['#fafafa', '#71717a']works at runtime and now typechecks.showTimedoc comment notes thatfalseactually hides the time display from@arraypress/waveform-bar1.12.0 (earlier bars ignored it).
[0.4.0] — 2026-07-22
Section titled “[0.4.0] — 2026-07-22”crossOriginconfig key. Added toWaveformBarConfigand forwarded verbatim towindow.WaveformBar.init(). Requires@arraypress/waveform-bar@^1.11.0for it to reach the embedded player.
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Accept the new localizable player-string keys —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— in the bar config; they are forwarded to the embedded player. Requires@arraypress/waveform-bar@^1.10.0.
[0.2.0] — 2026-07-01
Section titled “[0.2.0] — 2026-07-01”Changed
Section titled “Changed”- Sync
WaveformBarConfigto the bar’smodeAPI:layout→mode('waveform' | 'classic'), removemaxWidth, addshowShuffle/shuffle. Matches@arraypress/waveform-bar1.7.0.
[0.1.1] — 2026-06-27
Section titled “[0.1.1] — 2026-06-27”Changed
Section titled “Changed”- Bumped the
@arraypress/waveform-playerpeer (and dev) dependency to^1.7.2for the native accessible keyboard / ARIA seek slider. No component API changes.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformBar>— singleton mount component for the persistent bottom bar. Renders a persist host<div>and runswindow.WaveformBar.init(config)inside an effect. Tears down on unmount (StrictMode-safe) and re-inits only when the structural shape of the config changes — passing a fresh object reference with the same shape doesn’t trigger churn. Relocates the bar element into the persist host so route changes / re-renders don’t tear it down.<WaveformBarTrigger>— polymorphic click trigger. Defaults to<button>; override viaas="a" | "div" | "span". Emits the fulldata-wb-*attribute contract:- Track identity:
url,id,title,artist,album,artwork,link - Display chips:
duration,bpm,musicalKey,meta - Audio data:
waveform(peaks array, URL, or inline JSON),markers(DJ-mode chapters) - Initial state:
favorited,inCart - Behaviour:
mode='play' | 'queue',href(whenas="a"),aria-label,className,style,noDefaultIcons,children
- Track identity:
- Default
play / pauseSVG pair rendered as the trigger’s children when no custom content is passed. The library’swb-icon-swapCSS toggles them based on the active track state. - Auto-generated
aria-labelwhen one isn’t supplied —Play {title}for play triggers,Add {title} to queuefor queue triggers. - Public TypeScript types:
WaveformBarProps,WaveformBarConfig,WaveformBarTriggerProps,WaveformBarTrackData,WaveformBarMarker,WaveformBarActions,WaveformBarAction,WaveformBarTheme,RepeatMode,TriggerMode,WaveformStyle. - Ambient module shim for
@arraypress/waveform-barand@arraypress/waveform-playerso the wrapper typechecks cleanly until the core libraries ship.d.tsof their own. - SSR / RSC safe: the core library loads via dynamic
import()inside the effect, so the browser-only audio surface never evaluates server-side. - 46 Vitest tests via jsdom +
@testing-library/react:- 28 tests for
<WaveformBarTrigger>rendering + attribute mapping (no module mocking needed) - 18 tests for
<WaveformBar>lifecycle (window global mocked since jsdom has no Web Audio)
- 28 tests for
- Dual ESM (
dist/index.js) + CJS (dist/index.cjs) build viatsup..d.tsfor both. React + the two core libraries externalised so they resolve to the consumer’s copies. - README with full prop reference, seven usage patterns, and the
waveformbar:*custom-event API documented as the callback alternative. examples/basic.tsxwith seven copy-paste-ready snippets.
@arraypress/waveform-bar-vue
Section titled “@arraypress/waveform-bar-vue”[Unreleased]
Section titled “[Unreleased]”[0.4.0] — 2026-09-24
Section titled “[0.4.0] — 2026-09-24”waveformGradientconfig key ('vertical' | 'horizontal' | 'diagonal'). The bar has forwarded it to the embedded player since 1.8.0, but it was missing fromWaveformBarConfig, so TypeScript rejected a working option.barRadiusconfig key (number | null) — rounded bar caps in px,0= square,null= the player’s default. Requires@arraypress/waveform-bar1.12.0+; older bars ignore it.
Changed
Section titled “Changed”- Peer floors raised:
@arraypress/waveform-bar^1.10.0→^1.11.0and@arraypress/waveform-player^1.7.2→^1.8.0.WaveformBarConfighas declaredcrossOriginsince 0.3.0, but the bar only accepts it from 1.11.0; and the bar drivesaudioMode: 'external'inline players, which crashed before core player 1.8.0. The optionalbarRadius/ workingshowTime(bar 1.12.0) don’t raise the floor — older bars ignore them.
waveformColor/progressColoraccept gradient stop arrays. They were typedstring | null, but the bar forwards them verbatim to a player that takesstring | string[] | null—['#fafafa', '#71717a']works at runtime and now typechecks.showTimedoc comment notes thatfalseactually hides the time display from@arraypress/waveform-bar1.12.0 (earlier bars ignored it).
[0.3.0] — 2026-07-22
Section titled “[0.3.0] — 2026-07-22”crossOriginconfig key. Added toWaveformBarConfigand forwarded verbatim towindow.WaveformBar.init(). Requires@arraypress/waveform-bar@^1.11.0for it to reach the embedded player.
[0.2.0] — 2026-07-05
Section titled “[0.2.0] — 2026-07-05”- Accept the new localizable player-string keys —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— in the bar config; they are forwarded to the embedded player. Requires@arraypress/waveform-bar@^1.10.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformBar>— singleton mount for the persistent bottom bar. Render once in your root layout. On mount it dynamically imports@arraypress/waveform-bar(SSR-safe) and callswindow.WaveformBar.init(config); re-inits when the config’s structural shape changes (compared viaJSON.stringify, so a fresh object with the same shape doesn’t churn); relocates the bar element into a persist host<div>; and callsdestroy()on unmount.config,persist,hostIdprops;class/stylefall through to the host (base classwb-host).<WaveformBarTrigger>— polymorphic (as="button" | "a" | "div" | "span", defaultbutton) click trigger that emits thedata-wb-*attribute contract the core library scans for. Maps track-data props (url,id,title,artist,album,artwork,link,duration,bpm,musicalKey,meta,waveform,markers,favorited,inCart) to attributes (arrays JSON-encoded; absent props emit no attribute).mode="play" | "queue",href(foras="a"),noDefaultIcons. Renders default play/pause SVGs unless slot content is provided. Auto-generates anaria-labelfromtitle.class, native listeners (@click), and other attributes fall through; the base classwb-icon-swapis always applied.- No lifecycle callback props — the bar dispatches every state change as a
bubbling
waveformbar:*CustomEvent; listen withaddEventListener. This keeps callbacks from forcing a bar re-init (matching the React wrapper). - Public types mirroring the core surface:
WaveformBarConfig,WaveformBarProps,WaveformBarTriggerProps,WaveformBarTrackData,WaveformBarMarker,WaveformBarActions,WaveformBarAction,WaveformBarTheme,RepeatMode,TriggerMode,WaveformStyle. - Dual ESM + CJS build via
tsupwith.d.tsfor both. Vue + the core libraries are peer dependencies. - Vitest test suite (jsdom +
@vue/test-utils, 13 tests) covering the singleton’s host render +init/ re-init /destroylifecycle, and the trigger’sdata-wb-*contract, polymorphism, modes, default-icon handling, class merge, and click forwarding.
@arraypress/waveform-bar-svelte
Section titled “@arraypress/waveform-bar-svelte”[Unreleased]
Section titled “[Unreleased]”[0.4.0] — 2026-09-24
Section titled “[0.4.0] — 2026-09-24”waveformGradientconfig key ('vertical' | 'horizontal' | 'diagonal'). The bar has forwarded it to the embedded player since 1.8.0, but it was missing fromWaveformBarConfig, so TypeScript rejected a working option.barRadiusconfig key (number | null) — rounded bar caps in px,0= square,null= the player’s default. Requires@arraypress/waveform-bar1.12.0+; older bars ignore it.
Changed
Section titled “Changed”- Peer floors raised:
@arraypress/waveform-bar^1.10.0→^1.11.0and@arraypress/waveform-player^1.7.2→^1.8.0.WaveformBarConfighas declaredcrossOriginsince 0.3.0, but the bar only accepts it from 1.11.0; and the bar drivesaudioMode: 'external'inline players, which crashed before core player 1.8.0. The optionalbarRadius/ workingshowTime(bar 1.12.0) don’t raise the floor — older bars ignore them.
waveformColor/progressColoraccept gradient stop arrays. They were typedstring | null, but the bar forwards them verbatim to a player that takesstring | string[] | null—['#fafafa', '#71717a']works at runtime and now typechecks.showTimedoc comment notes thatfalseactually hides the time display from@arraypress/waveform-bar1.12.0 (earlier bars ignored it).
[0.3.0] — 2026-07-22
Section titled “[0.3.0] — 2026-07-22”crossOriginconfig key. Added toWaveformBarConfigand forwarded verbatim towindow.WaveformBar.init(). Requires@arraypress/waveform-bar@^1.11.0for it to reach the embedded player.
[0.2.0] — 2026-07-05
Section titled “[0.2.0] — 2026-07-05”- Accept the new localizable player-string keys —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— in the bar config; they are forwarded to the embedded player. Requires@arraypress/waveform-bar@^1.10.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformBar>— singleton mount (Svelte 5 runes) for the persistent bottom bar. Render once in your root layout. A browser-only$effectdynamically imports@arraypress/waveform-bar(SSR-safe) and callswindow.WaveformBar.init(config); a$derivedconfig key re-inits only when the config’s structural shape (orpersist) changes; the bar element is relocated into a persist host<div>;destroy()is called on unmount.config,persist,hostIdprops;class/ attributes fall through to the host (base classwb-host).<WaveformBarTrigger>— polymorphic (as="button" | "a" | "div" | "span"via<svelte:element>, defaultbutton) click trigger that emits thedata-wb-*attribute contract the core library scans for. Maps track-data props (url,id,title,artist,album,artwork,link,duration,bpm,musicalKey,meta,waveform,markers,favorited,inCart) to attributes (arrays JSON-encoded; absent props emit no attribute).mode="play" | "queue",href(foras="a"),noDefaultIcons. Renders default play/pause SVGs unless children are slotted. Auto-generates anaria-labelfromtitle.class, native listeners (onclick), and other attributes fall through via...rest; the base classwb-icon-swapis always applied.- No lifecycle callback props — the bar dispatches every state change as a
bubbling
waveformbar:*CustomEvent; listen withaddEventListener. - Public types mirroring the core surface:
WaveformBarConfig,WaveformBarProps,WaveformBarTriggerProps,WaveformBarTrackData,WaveformBarMarker,WaveformBarActions,WaveformBarAction,WaveformBarTheme,RepeatMode,TriggerMode,WaveformStyle. - Built with
svelte-package(dist/ships the preprocessed.svelte+ generated.d.ts). Svelte + the core libraries are peer dependencies. - Vitest test suite (jsdom +
@testing-library/svelte, 12 tests) covering the singleton’s host render +init/ re-init / no-churn /destroylifecycle, and the trigger’sdata-wb-*contract, polymorphism, modes, default-icon handling, class merge, and click forwarding.
@arraypress/waveform-playlist-astro
Section titled “@arraypress/waveform-playlist-astro”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”- The hero and grid layouts:
layoutnow accepts'hero'and'grid'alongside'list'/'minimal'(it was typed'list' | 'minimal'although both ship in the core), and their options are typed props, emitted as the container attributes the playlist parses:showArtist(data-show-artist),coverSize(data-cover-size),thumbnailSize(data-thumbnail-size),density('comfortable' | 'compact',data-density),coverPosition('left' | 'top',data-cover-position) andbarPosition('top' | 'bottom',data-bar-position). Exported asWaveformPlaylistLayoutProps. Types come from the playlist core’sindex.d.ts(1.8.0 declares them all); against an older core they fall back to the same 1.8.0 shapes instead of degrading tounknown. - Per-track
waveformpeaks onWaveformPlaylistTrackInput(number[] | string), rendered as the track’sdata-waveform: an array is JSON-encoded (an empty one is omitted), a string (e.g. a.jsonpeaks URL) emitted verbatim. With peaks the player skips decoding that track’s audio. Playlist 1.8.0 is the first version that readsdata-waveform.
waveformGradient,seekHandle,buttonSize,buttonRadiusandartworkPositionreach the embedded player, asdata-waveform-gradient,data-seek-handle,data-button-size,data-button-radiusanddata-artwork-position. All five are core player options the props type inherited, but the component never destructured or emitted them — they typechecked and were silently dropped.buttonRadius={0}emits"0".
Changed
Section titled “Changed”- Requires
@arraypress/waveform-playlist@^1.8.0(was^1.7.2) — the upcoming release that makes these props work. It is the first version that reads the per-trackdata-waveformthis wrapper now emits, and it fixes the hero layout’s cover art for artless tracks, the chapter list / play-state overlay landing on the wrong track, chapter seeks into another track or underpreload: 'none',H:MM:SSchapter times, the lock-screen album sticking between tracks, and keyboard shortcuts hijacking Cmd/Ctrl combinations. The range also drops 1.7.2 and 1.7.3, where a hero / grid container’sdata-layoutleaked into the embedded player (fixed in 1.7.4). - Requires
@arraypress/waveform-player@^1.24.5(was^1.23.0), the playlist core’s own floor.
[0.4.0] — 2026-08-07
Section titled “[0.4.0] — 2026-08-07”- Forward the core player’s
crossOriginoption to the embedded player. Added to theAstro.propsdestructure and emitted as thedata-cross-origincontainer attribute. This option shipped across the rest of the waveform family in@arraypress/waveform-player@1.23.0but was missed in the playlist wrappers, so it was previously accepted by the types and silently dropped at runtime. Requires@arraypress/waveform-player@^1.23.0and@arraypress/waveform-playlist@^1.7.2(the version that began forwarding it to each track’s player).
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Initial release
Section titled “[0.1.0] — Initial release”<WaveformPlaylist>Astro component wrapping@arraypress/waveform-playlist. Renders the library’s nested markup contract — a[data-waveform-playlist]container with one[data-track]child per track and one[data-chapter]child per chapter — from a single typedtracksarray.- Typed props for the playlist’s own options:
layout('list' | 'minimal'),continuous,expandChapters,showDuration,showChapterMarkers(boolean | null),chapterMarkerColor,showPlayState.
- Typed pass-through of the WaveformPlayer options the playlist forwards
to its embedded player —
waveformStyle,height,samples,barWidth,barSpacing,barRadius,colorPreset, the colour options (including gradient-arraywaveformColor/progressColor),playbackRate,showPlaybackSpeed,playbackRates,showControls,showInfo,showTime,showHoverTime,showBPM,bpm,buttonAlign,buttonStyle,accessibleSeek,seekLabel,errorText,showMarkers,autoplay,singlePlay,playOnSeek,enableMediaSession,preload,playIcon,pauseIcon. The per-track content fields (url,title,artist,artwork,album,markers) live on eachtracksentry instead, and the player’slayout/audioModeare intentionally not exposed (the playlist ownsdata-layoutand always drives a self-mode player). - A typed
tracksarray (WaveformPlaylistTrackInput[]) with per-trackurl,title,artist,artwork,album,duration,markers, andchapters({ time, label, color? }). - Astro-specific
lazyprop that switches the init attribute todata-waveform-playlist-lazyand ships a single deduplicatedIntersectionObserverthat promotes the playlist on viewport entry, plus a non-lazyastro:page-loadre-init for Astro View Transitions. - Astro-specific
id,class, andstylepass-throughs (wfpl-hostis always applied to the container). - Public TypeScript types derived from the two core packages so they
never drift:
WaveformPlaylistProps,WaveformPlaylistTrackInput, and re-exports ofWaveformPlaylistOptions/WaveformPlaylistTrack/WaveformPlaylistChapter/WaveformPlaylistMarker(from the playlist core) andWaveformStyle/WaveformMarker/WaveformPeaks/ColorPreset/AudioMode/AudioPreload/ButtonAlign(from the player core). - Vitest suite covering container option serialisation, omission semantics, JSON serialisation for array props, per-track and per-chapter rendering, lazy-mount and View-Transitions script presence, and the Astro extras.
- Documentation: full prop reference, setup guide, and usage examples
(
examples/basic.astro).
@arraypress/waveform-playlist-react
Section titled “@arraypress/waveform-playlist-react”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”- The hero and grid layouts:
layoutnow accepts'hero'and'grid'alongside'list'/'minimal'(it was typed'list' | 'minimal'although both ship in the core), and their options are typed props, forwarded to the playlist and in the remount dependency array:showArtist,coverSize,thumbnailSize,density('comfortable' | 'compact'),coverPosition('left' | 'top') andbarPosition('top' | 'bottom'). Exported asWaveformPlaylistLayoutProps. Types come from the playlist core’sindex.d.ts(1.8.0 declares them all); against an older core they fall back to the same 1.8.0 shapes instead of degrading tounknown. - Player callback props —
onLoad,onPlay,onPause,onEnd,onTimeUpdate,onError,onNextTrack,onPreviousTrack— forwarded to the embedded player. Playlist 1.8.0 runs them after its own handling (before, it overwrote them, which is why the wrapper didn’t offer them;onNextTrack/onPreviousTrackwere even accepted by the props type and silently dropped). Handed over as stable trampolines that read the latest prop, so a new handler never re-mounts the playlist. - Per-track
waveformpeaks onWaveformPlaylistTrackInput(number[] | string), rendered as the track’sdata-waveform: an array is JSON-encoded, a string (e.g. a.jsonpeaks URL) passed through. With peaks the player skips decoding that track’s audio. Playlist 1.8.0 is the first version that readsdata-waveform.
waveformGradient,seekHandle,buttonSize,buttonRadiusandartworkPositionreach the embedded player. All five are core player options the props type inherited, but the options builder never forwarded them — they typechecked and were silently dropped. They are now forwarded and in the remount dependency array.- Changing only
classNameno longer strips the playlist’s own host classes (waveform-playlist,wp-hero-layout,wp-grid-layout,wp-density-compact,wp-cover-top,wp-no-artist,wp-minimal). AclassName-only change (correctly) doesn’t remount, so when React rewrote theclassattribute those were gone until some other prop changed — hero/grid layouts collapsed and density/artist styling reverted. React now rendersclassonce (server markup and hydration are unchanged) and laterclassNamechanges are applied withclassList, adding and removing only the user’s tokens. The DOM structure is unchanged — the tracks and the playlist UI still live directly in the one host<div>. (Mounting the playlist into an inner element was considered and rejected: it would break.your-class.waveform-playlistselectors and push CSS variables set viastyle/classNameonto a parent, where the core’s defaults shadow them.)
Changed
Section titled “Changed”- Requires
@arraypress/waveform-playlist@^1.8.0(was^1.7.2) — the upcoming release that makes these props work. Before it, the playlist ignored the constructor options this wrapper passes (expandChapters,showDuration,showPlayState,showChapterMarkers,chapterMarkerColor), leakedlayoutinto the embedded player (fixed in 1.7.4), overwrote the forwarded callbacks, never readdata-waveform, and itsdestroy()wiped the rendered tracks, so any prop change re-mounted an empty playlist. With 1.8.0 a re-mount keeps the tracks (now covered by a test). - Requires
@arraypress/waveform-player@^1.24.5(was^1.23.0), the playlist core’s own floor.
Removed
Section titled “Removed”- The
audioModeprop. The playlist always owns its audio, and an'external'embedded player dispatches request-play events nobody answers — a playlist that never plays. It was forwarded to the constructor;@arraypress/waveform-playlist@1.8.0ignores it, and the wrapper no longer accepts or forwards it.
[0.4.0] — 2026-08-07
Section titled “[0.4.0] — 2026-08-07”- Forward the core player’s
crossOriginoption to the embedded player. Forwarded in the constructor options builder and added to the remountuseEffectdependency array. This option shipped across the rest of the waveform family in@arraypress/waveform-player@1.23.0but was missed in the playlist wrappers, so it was previously accepted by the types and silently dropped at runtime. Requires@arraypress/waveform-player@^1.23.0and@arraypress/waveform-playlist@^1.7.2(the version that began forwarding it to each track’s player).
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlaylist>React component wrapping@arraypress/waveform-playlist:- A declarative, required
tracksarray (WaveformPlaylistTrackInput[]) rendered into the[data-track]/[data-chapter]child markup the playlist constructor parses on mount. Each track acceptsurl,title,artist,artwork,album,duration,markers, andchapters({ time, label, color? }, wheretimeis a seconds number or a'M:SS'string). - Playlist options as typed props:
layout('list' | 'minimal'),continuous,expandChapters,showDuration,showChapterMarkers,chapterMarkerColor,showPlayState. - Pass-through player options forwarded to the embedded player
(
waveformStyle,height,samples,barWidth,barSpacing,barRadius, colours,playbackRate,showPlaybackSpeed,playbackRates, UI toggles,accessibleSeek,seekLabel,errorText, behaviour flags, icons,audioMode,preload). - React-specific extras:
id,className,style, andrefforwarding viaWaveformPlaylistHandle.
- A declarative, required
WaveformPlaylistHandleimperative API on the forwarded ref —selectTrack(),seekToChapter(),nextTrack(),previousTrack(),getPlayer(),getCurrentTrackIndex(),getTracks(), plus the rawinstanceescape hatch.- SSR / RSC safe: the playlist library is loaded via dynamic
import('@arraypress/waveform-playlist')inside the effect so the browser-only audio surface never runs server-side. - Identity-prop re-mount: when any construction prop changes — the
serialised
tracks,layout,continuous, colours, sizing, etc. — the wrapper destroys the existing instance and creates a new one against the freshly-rendered markup. DOM-only props (className,style,id) do not trigger a re-mount. - The host container deliberately omits
data-waveform-playlistso the library’s global auto-init never double-mounts on top of the instance the wrapper creates explicitly. - Public TypeScript types:
WaveformPlaylistProps,WaveformPlaylistHandle,WaveformPlaylistTrackInput,WaveformPlaylistChapterInput, plus the re-exported core types (WaveformPlaylistOptions,WaveformPlaylistTrack,WaveformPlaylistChapter,WaveformPlaylistMarker,WaveformStyle,WaveformMarker,WaveformPeaks,ColorPreset,AudioMode,AudioPreload,ButtonAlign). - Vitest test suite (jsdom +
@testing-library/react) covering track / chapter markup rendering, mount, unmount destroy, option pass-through, identity-prop re-mount, ref forwarding, and the full imperative handle surface. The core library is mocked at the module boundary because jsdom has no Web Audio API. - Dual ESM (
dist/index.js) + CJS (dist/index.cjs) build viatsup, with.d.tsfor both. React and both@arraypress/waveform-*cores are externalised so they resolve to the consumer’s copies. - README with full prop reference and usage patterns, and
examples/basic.tsxwith seven copy-paste-ready snippets.
Peer dependencies
Section titled “Peer dependencies”@arraypress/waveform-playlist^1.3.0@arraypress/waveform-player^1.8.0react^18.0.0 || ^19.0.0
@arraypress/waveform-playlist-vue
Section titled “@arraypress/waveform-playlist-vue”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”- The hero and grid layouts:
layoutnow accepts'hero'and'grid'alongside'list'/'minimal'(both the runtime prop and the type said'list' | 'minimal'although both ship in the core), and their options are runtime props, forwarded to the playlist and in the remount watcher:showArtist,coverSize,thumbnailSize,density('comfortable' | 'compact'),coverPosition('left' | 'top') andbarPosition('top' | 'bottom'). Exported asWaveformPlaylistLayoutProps. Types come from the playlist core’sindex.d.ts(1.8.0 declares them all); against an older core they fall back to the same 1.8.0 shapes instead of degrading tounknown. - Lifecycle emits —
load,play,pause,end,timeupdate,error,nexttrack,previoustrack— with the core player’s arguments, the same idiom as@arraypress/waveform-player-vue. Playlist 1.8.0 runs the embedded player’s callbacks after its own handling (before, it overwrote them, which is why this wrapper offered none).emitis stable, so a new listener never re-mounts the playlist. - Per-track
waveformpeaks onWaveformPlaylistTrackInput(number[] | string), rendered as the track’sdata-waveform: an array is JSON-encoded, a string (e.g. a.jsonpeaks URL) passed through. With peaks the player skips decoding that track’s audio. Playlist 1.8.0 is the first version that readsdata-waveform.
waveformGradient,seekHandle,buttonSize,buttonRadiusandartworkPositionreach the embedded player. All five are core player options the props type inherited, but they had no runtime prop declaration — Vue treated them as fallthrough attributes and they never reached the playlist. They are now runtime props, forwarded and in the remount watcher.- Changing only the fall-through
classno longer strips the playlist’s own host classes (waveform-playlist,wp-hero-layout,wp-grid-layout,wp-density-compact,wp-cover-top,wp-no-artist,wp-minimal). A class-only change (correctly) doesn’t remount, so when Vue re-patched theclassattribute those were gone until some other prop changed — hero/grid layouts collapsed and density/artist styling reverted. The component now rendersclassonce (server markup and hydration are unchanged) and applies later changes withclassList, adding and removing only the user’s tokens. To keepclassout of Vue’s patching it setsinheritAttrs: falseand forwards every other attribute (id,style, listeners,data-*) itself — same result on the element. The DOM structure is unchanged — the tracks and the playlist UI still live directly in the one host<div>. (Mounting the playlist into an inner element was considered and rejected: it would break.your-class.waveform-playlistselectors and push CSS variables set viastyle/classonto a parent, where the core’s defaults shadow them.)
Changed
Section titled “Changed”- Requires
@arraypress/waveform-playlist@^1.8.0(was^1.7.2) — the upcoming release that makes these props work. Before it, the playlist ignored the constructor options this wrapper passes (expandChapters,showDuration,showPlayState,showChapterMarkers,chapterMarkerColor), leakedlayoutinto the embedded player (fixed in 1.7.4), overwrote the player callbacks the new emits ride on, never readdata-waveform, and itsdestroy()wiped the rendered tracks, so any prop change re-mounted an empty playlist. With 1.8.0 a re-mount keeps the tracks (now covered by a test). - Requires
@arraypress/waveform-player@^1.24.5(was^1.23.0), the playlist core’s own floor.
Removed
Section titled “Removed”- The
audioModeprop. The playlist always owns its audio, and an'external'embedded player dispatches request-play events nobody answers — a playlist that never plays. It was forwarded to the constructor;@arraypress/waveform-playlist@1.8.0ignores it, and the wrapper no longer declares or forwards it.
[0.4.0] — 2026-08-07
Section titled “[0.4.0] — 2026-08-07”- Forward the core player’s
crossOriginoption to the embedded player. Added as a runtimepropsdeclaration (PropType<AudioCrossOrigin>), set in the options builder, and added to the remount watcher. This option shipped across the rest of the waveform family in@arraypress/waveform-player@1.23.0but was missed in the playlist wrappers, so it was previously accepted by the types and silently dropped at runtime. Requires@arraypress/waveform-player@^1.23.0and@arraypress/waveform-playlist@^1.7.2(the version that began forwarding it to each track’s player).
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlaylist>Vue 3 component wrapping@arraypress/waveform-playlist:- Declarative
tracksprop (with optional per-trackchaptersandmarkers), rendered into the[data-track]/[data-chapter]markup the playlist constructor parses on mount. - Playlist options as typed props:
layout('list' | 'minimal'),continuous,expandChapters,showDuration,showChapterMarkers,chapterMarkerColor,showPlayState. - The full pass-through player-option surface (waveform style, sizing,
colours, playback, UI toggles, accessibility, icons) — inherited from
the core
WaveformPlayerOptionsviaOmit<>, minus per-track content fields (which come fromtracks).
- Declarative
- Imperative navigation API exposed via a template
ref(WaveformPlaylistExpose):selectTrack(),seekToChapter(),nextTrack(),previousTrack(),getPlayer(),getCurrentTrackIndex(),getTracks(), plus the rawinstance. class,style, andidfall through to the host element via Vue’s attribute inheritance; the base classwfp-hostalways applies.- SSR / Nuxt safe: the core library is loaded via dynamic
import('@arraypress/waveform-playlist')insideonMounted. - Identity-prop re-mount: any construction-prop change (a serialised
trackschange, layout, options, …) destroys and rebuilds the instance. A monotonic mount token discards a superseded in-flight import; the watcher usesflush: 'post'so the fresh markup is in the DOM before the constructor re-parses it. - No lifecycle emits — the playlist owns the embedded player’s callbacks
internally (matching the React wrapper). Observe playback via the
embedded player from
getPlayer(). - Public types adopted from both cores (
@arraypress/waveform-playlist+@arraypress/waveform-player), re-exported so they can never drift. - Dual ESM + CJS build via
tsupwith.d.tsfor both. Vue + both cores are peer dependencies. - Vitest test suite (jsdom +
@vue/test-utils) covering host + track markup rendering, option mapping (tracks excluded), boolean-prop omission, destroy-on-unmount, identity-prop re-mount, and the exposed navigation API. The core is mocked at the module boundary.
@arraypress/waveform-playlist-svelte
Section titled “@arraypress/waveform-playlist-svelte”[Unreleased]
Section titled “[Unreleased]”[0.5.0] — 2026-09-24
Section titled “[0.5.0] — 2026-09-24”- The hero and grid layouts:
layoutnow accepts'hero'and'grid'alongside'list'/'minimal'(it was typed'list' | 'minimal'although both ship in the core), and their options are typed props, destructured from$props()and forwarded to the playlist:showArtist,coverSize,thumbnailSize,density('comfortable' | 'compact'),coverPosition('left' | 'top') andbarPosition('top' | 'bottom'). Exported asWaveformPlaylistLayoutProps. Types come from the playlist core’sindex.d.ts(1.8.0 declares them all); against an older core they fall back to the same 1.8.0 shapes instead of degrading tounknown. onnexttrack/onprevioustrackcallback props (Media Session next/previous), forwarded like the other callbacks. The camelCaseonNextTrack/onPreviousTrackthe props type used to inherit are removed: they were never destructured, so they fell into...rest.- Per-track
waveformpeaks onWaveformPlaylistTrackInput(number[] | string), rendered as the track’sdata-waveform: an array is JSON-encoded, a string (e.g. a.jsonpeaks URL) passed through. With peaks the player skips decoding that track’s audio. Playlist 1.8.0 is the first version that readsdata-waveform.
onload,onplay,onpause,onend,ontimeupdateandonerrorfire. They were wired correctly, but the playlist overwrote the embedded player’s callbacks with its own, so they never ran;@arraypress/waveform-playlist@1.8.0chains them after its own handling. Every argument the core passes now reaches the handler, and a new handler still never re-mounts the playlist (now tested with a harness that updates one prop at a time).waveformGradient,seekHandle,buttonRadiusandartworkPositionreach the embedded player. All four are core player options the props type inherited, but they were never destructured from$props(), so each fell into...rest, landed on the host<div>as an attribute and was never forwarded. They are now destructured and set in the options builder (buttonSizealready was).- Changing only
classno longer strips the playlist’s own host classes (waveform-playlist,wp-hero-layout,wp-grid-layout,wp-density-compact,wp-cover-top,wp-no-artist,wp-minimal). A class-only change (correctly) doesn’t remount, so when Svelte rewrote theclassattribute those were gone until some other prop changed — hero/grid layouts collapsed and density/artist styling reverted. The host now binds a class value frozen at init (server markup and hydration are unchanged) and laterclasschanges are applied withclassList, adding and removing only the user’s tokens. The DOM structure is unchanged — the tracks and the playlist UI still live directly in the one host<div>. (Mounting the playlist into an inner element was considered and rejected: it would break.your-class.waveform-playlistselectors and push CSS variables set viastyle/classonto a parent, where the core’s defaults shadow them.)
Changed
Section titled “Changed”- Requires
@arraypress/waveform-playlist@^1.8.0(was^1.7.2) — the upcoming release that makes these props work. Before it, the playlist ignored the constructor options this wrapper passes (expandChapters,showDuration,showPlayState,showChapterMarkers,chapterMarkerColor), leakedlayoutinto the embedded player (fixed in 1.7.4), overwrote the forwarded callbacks, never readdata-waveform, and itsdestroy()wiped the rendered tracks, so any prop change re-mounted an empty playlist. With 1.8.0 a re-mount keeps the tracks (now covered by a test). - Requires
@arraypress/waveform-player@^1.24.5(was^1.23.0), the playlist core’s own floor.
Removed
Section titled “Removed”- The
audioModeprop. The playlist always owns its audio, and an'external'embedded player dispatches request-play events nobody answers — a playlist that never plays. It was forwarded to the constructor;@arraypress/waveform-playlist@1.8.0ignores it, and the wrapper no longer accepts or forwards it (a stray one is swallowed rather than landing on the host element via...rest).
[0.4.0] — 2026-08-07
Section titled “[0.4.0] — 2026-08-07”- Forward the core player’s
crossOriginoption to the embedded player. Added to the$props()destructure (an undestructured prop falls into...restand is never forwarded) and set in the options builder. This option shipped across the rest of the waveform family in@arraypress/waveform-player@1.23.0but was missed in the playlist wrappers, so it was previously accepted by the types and silently dropped at runtime. Requires@arraypress/waveform-player@^1.23.0and@arraypress/waveform-playlist@^1.7.2(the version that began forwarding it to each track’s player).
[0.3.0] — 2026-07-05
Section titled “[0.3.0] — 2026-07-05”- Forward the core player’s new localizable UI-string options —
seekValueText,playPauseLabel,speedLabel,artworkAlt, andunknownTrackText— through to the underlying player. Requires@arraypress/waveform-player@^1.20.0.
[0.1.0] — Unreleased
Section titled “[0.1.0] — Unreleased”Initial release.
<WaveformPlaylist>Svelte 5 component (built with runes) wrapping@arraypress/waveform-playlist:- Declarative
tracksprop (with optional per-trackchaptersandmarkers), rendered into the[data-track]/[data-chapter]markup the playlist constructor parses on mount. - Playlist options as typed props:
layout('list' | 'minimal'),continuous,expandChapters,showDuration,showChapterMarkers,chapterMarkerColor,showPlayState. - The full pass-through player-option surface (waveform style, sizing,
colours, playback, UI toggles, accessibility, icons) — inherited from
the core
WaveformPlayerOptionsviaOmit<>, minus per-track content fields (which come fromtracks).
- Declarative
- Imperative navigation API exported by the component instance (reachable
via
bind:this):selectTrack(),seekToChapter(),nextTrack(),previousTrack(),getPlayer(),getCurrentTrackIndex(),getTracks(), andgetInstance(). class,style,id, and other element attributes fall through to the host element via...rest; the base classwfp-hostalways applies.- SSR / SvelteKit safe: the core library is loaded via dynamic
import('@arraypress/waveform-playlist')inside a browser-only$effect. - Identity-prop re-mount: a single
$effectreadsJSON.stringify(tracks)- every construction option, so any change destroys and rebuilds the instance over the freshly-rendered markup. A monotonic mount token discards a superseded in-flight import.
- Public types adopted from both cores (
@arraypress/waveform-playlist+@arraypress/waveform-player), re-exported so they can never drift. - Built with
svelte-package(dist/ships the preprocessed.svelte+ generated.d.ts). Svelte + both cores are peer dependencies. - Vitest test suite (jsdom +
@testing-library/svelte) covering host + track markup rendering, option mapping (tracks excluded), boolean-prop omission, destroy-on-unmount, identity-prop re-mount, and the exported navigation API. The core is mocked at the module boundary.