Skip to content

About

Astro component wrapper for @arraypress/waveform-sounds — server-rendered sound list, typed props as data-* attributes, ClientRouter-safe, one external script for strict CSPs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Waveform Sounds for Astro

Typed Astro component for @arraypress/waveform-sounds. A searchable, filterable list of sound previews — server-rendered, then brought to life — with a mini waveform per row and one shared audio engine.

npm version license

Documentation · npm


Install

npm install @arraypress/waveform-sounds-astro @arraypress/waveform-sounds @arraypress/waveform-player

Import the two stylesheets once, in your layout. The component does not inject CSS (none of the family's wrappers do):

---
import '@arraypress/waveform-player/styles.css';
import '@arraypress/waveform-sounds/styles.css';
---

The JavaScript is handled for you: the component ships a bundled client script that loads the sounds runtime, and the player too unless the page already has one (see below).

Usage

From an array — rendered on the server

---
import WaveformSounds from '@arraypress/waveform-sounds-astro';
---
<WaveformSounds
  sounds={[
    { url: '/previews/kick-01.mp3', title: 'Kick 01', type: 'Drums', bpm: 128, key: 'F minor', duration: 2.4 },
    { url: '/previews/bass-04.mp3', title: 'Bass Loop 04', type: 'Bass', bpm: 128, key: 'Fmin', peaks: 'a1b2c3…' },
  ]}
  pageSize={100}
  columns={['type', 'bpm', 'key']}
/>

The whole list — toolbar, filter chips, every row — is in the HTML. The browser runtime adopts that markup instead of rebuilding it, so the list is readable and crawlable before any script runs. Give rows peaks (what waveform-gen --manifest writes) and the row waveforms draw without decoding any audio. A sound with download: '/files/kick-01.wav' gets a download button on its row (a plain <a download> — gating is up to your site).

From a manifest — fetched in the browser

<WaveformSounds manifest="/sounds.json" player="strip" />

With only manifest (and no sounds), the component renders an empty container with data-manifest, and the runtime fetches and renders the list. sounds={[]} counts as "given" — it renders an empty list, as the core does.

Generate a manifest from a folder of previews (--base-url is the folder's public URL; without it every URL is relative to the site root, /kick.mp3):

npx @arraypress/waveform-gen ./public/previews/*.mp3 --manifest ./public/sounds.json --base-url /previews/

To server-render a manifest instead, read it at build time and pass its sounds array.

Props

Every prop is optional. An omitted prop emits nothing, so the core's default applies. Types come straight from the core's WaveformSoundsOptions.

Prop Type Default Notes
sounds SoundInput[] — Rendered on the server.
manifest string — URL, fetched in the browser when there are no sounds.
player 'inline' | 'strip' 'inline' Mini waveform per row, or one docked player.
search boolean true Search box.
filters ('type' | 'key' | 'bpm')[] all three [] = no filter controls. Drop one from filters, columns and sorts to remove it everywhere.
sorts SoundsSort[] all five Orders the Sort menu offers; the first is the start order. [] = no menu.
showCount boolean true The "12 of 300 sounds" line.
menuSearch number 8 Type / key / sort dropdowns with more options than this get a search field.
urlState boolean | string false Keep filters + sort in the address (?q=…&type=…), shareable and refresh-proof. A string prefixes the parameter names, for several lists on a page.
idPrefix string the id, else a hash of the sounds Prefix for the dropdowns' element ids. Two lists of the same sounds on one page need an id or idPrefix each.
loopToggle boolean true Loop button.
pageSize number 50 Rows before "Show more"; 0 = all.
maxTypeChips number 10 More types than this become a menu.
columns ('type' | 'bpm' | 'key' | 'duration')[] all four Order is kept.
waveformStyle 'mirror' | 'bars' 'mirror' Row waveform.
waveformColor string CSS --ws-wave-color Default derives from currentColor.
progressColor string CSS --ws-progress-color Default derives from currentColor.
barWidth number 2 Row bar width, CSS px.
barGap number 1 Gap between row bars, CSS px.
loop boolean false Start with Loop on.
autoAdvance boolean false Play the next visible sound at the end.
arrowAudition boolean true ↑/↓ play the next row while playing.
strings Partial<WaveformSoundsStrings> English UI words; emitted as JSON.
playerOptions object — Options for the engine WaveformPlayer; emitted as JSON (functions are dropped).
id string — Container id.
class string — Added to waveform-sounds waveform-sounds--<player>.

Not available as props: the on* callbacks (see Events) and playerClass (the page's window.WaveformPlayer is used — see below).

Theming

No accent is needed: like the rest of the family the list is colour-agnostic. Everything derives from currentColor, so it fits light and dark pages as is, and the "on" states (selected chip, playing row) invert. Opt into a brand colour with --ws-accent / --ws-on-accent if you want one.

Set --ws-surface to your page background. The runtime detects it, but a server-rendered list paints before any script runs, so the first paint (dropdown and button fills) uses the CSS value:

.waveform-sounds { --ws-surface: var(--color-bg); }

Other properties: --ws-wave-color, --ws-progress-color, --ws-border, --ws-radius, --ws-control-radius, … (see the core's README).

Events

Callbacks can't cross server rendering. Listen for the core's bubbling DOM events instead — on the element, or on document:

<WaveformSounds id="pack" manifest="/sounds.json" />

<script>
  document.addEventListener('waveformsounds:play', (e) => {
    console.log('playing', e.detail.sound.title);
  });
</script>
Event detail
waveformsounds:ready { sounds, instance }
waveformsounds:play / :pause / :end { sound, index, instance }
waveformsounds:filter { visible, total, filter, sort, instance }
waveformsounds:error { error, sound?, index?, instance }

For the imperative API, window.WaveformSounds.getInstance('#pack') returns the instance (play(), setFilter(), setSort(), …). It is created by the client script, so call it from an event handler or after waveformsounds:ready.

Content Security Policy

The client script is a processed Astro <script>: one bundled module, emitted as an external file (/_astro/…) and identical on every page — no is:inline, no define:vars. Everything per-instance travels in data-* attributes. A strict hash-based CSP needs no extra hashes for this component.

View Transitions (<ClientRouter />)

Works with and without Astro's router. The script initialises on load, again on every astro:page-load, and on astro:after-swap destroys the instances whose element left the page (stopping their audio). A list inside a transition:persist element is carried over and keeps playing.

Using it next to other players (WaveformBar, <WaveformPlayer>, …)

The list plays through a WaveformPlayer, and singlePlay (one sound at a time across the page) only works between players of the same class. So the client script never replaces a player the page already has: it waits for the page's own scripts to run (DOMContentLoaded) and uses window.WaveformPlayer if it's there — a persistent WaveformBar's, or one loaded by a <script> tag. Only when there is none does it load the bundled @arraypress/waveform-player (a separate chunk, by dynamic import).

A player your code loads LATER than that (from its own dynamic import) can't be detected; load it from a static <script>/import instead.

License

MIT © ArrayPress

About

Astro component wrapper for @arraypress/waveform-sounds — server-rendered sound list, typed props as data-* attributes, ClientRouter-safe, one external script for strict CSPs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages