svelte-attach-sound 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,15 +2,7 @@
2
2
 
3
3
  A Svelte attachment for binding sound playback to DOM events using the [Svelte 5 attachments API](https://svelte.dev/docs/svelte/attachments).
4
4
 
5
- Uses [Howler.js](https://howlerjs.com/) as a peer dependency.
6
-
7
- Find CC-Zero licensed sounds at [freesound.org](https://freesound.org/)
8
-
9
- ## Installation
10
-
11
- ```
12
- npm install svelte-attach-sound howler
13
- ```
5
+ You can find CC-Zero licensed sounds at [freesound.org](https://freesound.org/)
14
6
 
15
7
  ## Example
16
8
 
@@ -24,6 +16,7 @@ npm install svelte-attach-sound howler
24
16
 
25
17
  <!-- Inline -->
26
18
  <button {@attach sound({ src: click_mp3, events: ["click"] })}>Click</button>
19
+ <button {@attach sound({ src: click_mp3, events: ["mouseenter"] })}>Enter</button>
27
20
 
28
21
  <!-- Factory: reusable with shared defaults -->
29
22
  <button {@attach click()}>Click</button>
@@ -31,41 +24,3 @@ npm install svelte-attach-sound howler
31
24
  ```
32
25
 
33
26
  [Demo](https://joknoll.github.io/svelte-attach-sound/) | [npm](https://www.npmjs.com/package/svelte-attach-sound)
34
-
35
- ## API
36
-
37
- ### `sound(options)`
38
-
39
- Svelte attachment that plays a sound on a DOM event.
40
-
41
- | Option | Type | Required | Description |
42
- | --------- | -------------------------------------------------------------- | -------- | -------------------------------------------------- |
43
- | `src` | `string \| string[]` | Yes | Audio file URL(s), with fallbacks |
44
- | `events` | `[playEvent, stopEvent?]` | Yes | DOM event to trigger play, and optionally stop |
45
- | `...rest` | [`HowlOptions`](https://github.com/goldfire/howler.js#options) | No | Any Howler option (`volume`, `loop`, `rate`, etc.) |
46
-
47
- ### `useSound(src, events, options?)`
48
-
49
- Factory that returns a reusable attachment with preset defaults. The returned function accepts optional per-call overrides.
50
-
51
- ```svelte
52
- <script lang="ts">
53
- const click = useSound(click_mp3, ["pointerdown"], { volume: 0.8 });
54
- </script>
55
-
56
- <button {@attach click()}>Uses defaults</button>
57
- <button {@attach click({ volume: 0.3 })}>Override volume</button>
58
- ```
59
-
60
- ### `Sound` class
61
-
62
- For manual control outside of attachments:
63
-
64
- ```ts
65
- import { Sound } from "svelte-attach-sound";
66
-
67
- const s = new Sound(click_mp3, { volume: 0.5 });
68
- s.play();
69
- s.stop();
70
- s.destroy(); // stops playback and frees resources
71
- ```
@@ -0,0 +1,53 @@
1
+ import { Attachment } from "svelte/attachments";
2
+
3
+ //#region src/index.d.ts
4
+ type SoundSource = string;
5
+ type SoundEvents = [keyof HTMLElementEventMap, (keyof HTMLElementEventMap)?];
6
+ type SoundOptions = {
7
+ /** Playback volume, 0.0 to 1.0. Default: 1. */volume?: number; /** Whether the sound loops. Default: false. */
8
+ loop?: boolean; /** Playback rate multiplier. Default: 1. */
9
+ rate?: number;
10
+ };
11
+ type Options = {
12
+ src: SoundSource;
13
+ events: SoundEvents;
14
+ } & SoundOptions;
15
+ declare class Sound {
16
+ private buffer;
17
+ private source;
18
+ private options;
19
+ constructor(src: SoundSource, options?: SoundOptions);
20
+ private load;
21
+ play(): void;
22
+ stop(): void;
23
+ }
24
+ /**
25
+ * Creates a sound attachment that binds playback to DOM events on the element.
26
+ *
27
+ * @example
28
+ * ```svelte
29
+ * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
30
+ * Click me
31
+ * </button>
32
+ * ```
33
+ */
34
+ declare function sound(options: Options): Attachment<HTMLElement>;
35
+ /**
36
+ * Creates a pre-configured sound attachment factory reusable across elements.
37
+ *
38
+ * @example
39
+ * ```svelte
40
+ * <script>
41
+ * import { useSound } from "svelte-attach-sound";
42
+ * import click_mp3 from "./assets/click.mp3";
43
+ *
44
+ * const click = useSound(click_mp3, ["click"]);
45
+ * </script>
46
+ *
47
+ * <button {@attach click()}>Click me</button>
48
+ * <button {@attach click({ volume: 0.5 })}>Quieter</button>
49
+ * ```
50
+ */
51
+ declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
52
+ //#endregion
53
+ export { Sound, sound, useSound };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{on as e}from"svelte/events";let t=null;function n(){return typeof AudioContext>`u`?null:t??=new AudioContext}var r=class{buffer;source=null;options;constructor(e,t={}){this.options=t,this.buffer=this.load(e)}async load(e){let t=n();if(!t)return null;try{let n=await(await fetch(e)).arrayBuffer();return await t.decodeAudioData(n)}catch(t){return console.warn(`[svelte-attach-sound] Failed to load sound:`,e,t),null}}play(){this.buffer.then(e=>{if(!e)return;let t=n();if(!t)return;t.resume();let r=t.createBufferSource();r.buffer=e,r.loop=this.options.loop??!1,r.playbackRate.value=this.options.rate??1;let i=t.createGain();i.gain.value=this.options.volume??1,r.connect(i).connect(t.destination),this.source=r,r.start()})}stop(){try{this.source?.stop()}catch{}this.source=null}};function i(t){return n=>{let{src:i,events:a,...o}=t,[s,c]=a,l=new r(i,o),u=e(n,s,()=>l.play()),d=c?e(n,c,()=>l.stop()):null;return()=>{u(),d?.(),l.stop()}}}function a(e,t,n){return r=>i({src:e,events:t,...n,...r})}export{r as Sound,i as sound,a as useSound};
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "svelte-attach-sound",
3
- "version": "0.1.3",
4
- "description": "A Svelte attachment for binding sound playback to DOM events using Howler.js",
3
+ "version": "0.1.5",
4
+ "description": "A Svelte attachment for binding sound playback to DOM events using the Web Audio API",
5
5
  "keywords": [
6
6
  "attachment",
7
- "howler",
8
7
  "sound",
9
8
  "svelte",
10
- "svelte5"
9
+ "svelte5",
10
+ "web-audio"
11
11
  ],
12
12
  "homepage": "https://github.com/joknoll/svelte-attach-sound#readme",
13
13
  "bugs": {
@@ -23,37 +23,26 @@
23
23
  "dist"
24
24
  ],
25
25
  "type": "module",
26
- "main": "./dist/index.cjs",
27
- "module": "./dist/index.mjs",
28
26
  "types": "./dist/index.d.cts",
29
27
  "exports": {
30
- ".": {
31
- "import": "./dist/index.mjs",
32
- "require": "./dist/index.cjs"
33
- },
28
+ ".": "./dist/index.js",
34
29
  "./package.json": "./package.json"
35
30
  },
36
- "scripts": {
37
- "build": "tsdown",
38
- "dev": "tsdown --watch",
39
- "test": "vitest",
40
- "typecheck": "tsc --noEmit",
41
- "prepublishOnly": "bun run build"
42
- },
43
31
  "devDependencies": {
44
- "@types/howler": "^2.2.12",
45
32
  "@types/node": "^25.0.3",
46
33
  "bumpp": "^11.0.1",
47
- "howler": "^2.2.4",
48
- "oxfmt": "^0.41.0",
49
- "oxlint": "^1.56.0",
50
34
  "svelte": "^5.55.0",
51
- "tsdown": "^0.21.4",
52
35
  "typescript": "^6.0.2",
53
- "vitest": "^4.1.1"
36
+ "vite-plus": "latest",
37
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
54
38
  },
55
39
  "peerDependencies": {
56
- "howler": ">=2.0.0",
57
40
  "svelte": ">=5.55.0"
41
+ },
42
+ "scripts": {
43
+ "build": "vp pack",
44
+ "dev": "vp pack --watch",
45
+ "test": "vp test",
46
+ "typecheck": "tsc --noEmit"
58
47
  }
59
- }
48
+ }
package/dist/index.cjs DELETED
@@ -1,99 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let svelte_events = require("svelte/events");
3
- //#region src/index.ts
4
- /**
5
- * A class representing a synthetic sound.
6
- * Can be used standalone for programmatic playback without any DOM dependency.
7
- */
8
- var Sound = class {
9
- howl;
10
- constructor(src, options = {}) {
11
- this.howl = import("howler/src/howler.core").then(({ Howl }) => new Howl({
12
- ...options,
13
- src
14
- })).catch((e) => {
15
- console.warn("[svelte-attach-sound] Failed to load sound:", e);
16
- throw e;
17
- });
18
- }
19
- play() {
20
- this.howl.then((h) => h.play()).catch(() => {});
21
- }
22
- stop() {
23
- this.howl.then((h) => h.stop()).catch(() => {});
24
- }
25
- destroy() {
26
- this.howl.then((h) => {
27
- h.stop();
28
- h.unload();
29
- }).catch(() => {});
30
- }
31
- };
32
- /**
33
- * Creates a sound attachment that binds playback to DOM events on the element.
34
- *
35
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
36
- * will automatically tear down and recreate when that state changes.
37
- *
38
- * @param options Options including `src`, `events`, and any Howler options.
39
- * @returns A Svelte attachment.
40
- *
41
- * @example
42
- * ```svelte
43
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
44
- * Click me
45
- * </button>
46
- * ```
47
- */
48
- function sound(options) {
49
- return (element) => {
50
- const { src, events, ...howlOptions } = options;
51
- const [playEvent, stopEvent] = events;
52
- const instance = new Sound(src, howlOptions);
53
- const handlePlay = () => instance.play();
54
- const handleStop = () => instance.stop();
55
- const offPlay = (0, svelte_events.on)(element, playEvent, handlePlay);
56
- const offStop = stopEvent ? (0, svelte_events.on)(element, stopEvent, handleStop) : null;
57
- return () => {
58
- offPlay();
59
- offStop?.();
60
- instance.destroy();
61
- };
62
- };
63
- }
64
- /**
65
- * Creates a pre-configured sound attachment factory that can be reused across
66
- * multiple elements with optional per-element overrides.
67
- *
68
- * @param src The source URL(s) of the sound.
69
- * @param events The `[playEvent, stopEvent?]` tuple.
70
- * @param options Optional base Howler options.
71
- * @returns A factory function that returns a Svelte attachment.
72
- *
73
- * @example
74
- * ```svelte
75
- * <script>
76
- * import { useSound } from "svelte-attach-sound";
77
- * import click_mp3 from "./assets/click.mp3";
78
- *
79
- * const click = useSound(click_mp3, ["click"]);
80
- * <\/script>
81
- *
82
- * <button {@attach click()}>Click me</button>
83
- *
84
- * <!-- Override options per-element -->
85
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
86
- * ```
87
- */
88
- function useSound(src, events, options) {
89
- return (overrideOptions) => sound({
90
- src,
91
- events,
92
- ...options,
93
- ...overrideOptions
94
- });
95
- }
96
- //#endregion
97
- exports.Sound = Sound;
98
- exports.sound = sound;
99
- exports.useSound = useSound;
package/dist/index.d.cts DELETED
@@ -1,68 +0,0 @@
1
- import { HowlOptions } from "howler";
2
- import { Attachment } from "svelte/attachments";
3
-
4
- //#region src/index.d.ts
5
- type SoundSource = HowlOptions["src"];
6
- type SoundEvents = [keyof HTMLElementEventMap, (keyof HTMLElementEventMap)?];
7
- type SoundOptions = Omit<HowlOptions, "src">;
8
- /**
9
- * Options for the sound attachment factory.
10
- */
11
- type Options = {
12
- events: SoundEvents;
13
- } & HowlOptions;
14
- /**
15
- * A class representing a synthetic sound.
16
- * Can be used standalone for programmatic playback without any DOM dependency.
17
- */
18
- declare class Sound {
19
- private howl;
20
- constructor(src: SoundSource, options?: SoundOptions);
21
- play(): void;
22
- stop(): void;
23
- destroy(): void;
24
- }
25
- /**
26
- * Creates a sound attachment that binds playback to DOM events on the element.
27
- *
28
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
29
- * will automatically tear down and recreate when that state changes.
30
- *
31
- * @param options Options including `src`, `events`, and any Howler options.
32
- * @returns A Svelte attachment.
33
- *
34
- * @example
35
- * ```svelte
36
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
37
- * Click me
38
- * </button>
39
- * ```
40
- */
41
- declare function sound(options: Options): Attachment<HTMLElement>;
42
- /**
43
- * Creates a pre-configured sound attachment factory that can be reused across
44
- * multiple elements with optional per-element overrides.
45
- *
46
- * @param src The source URL(s) of the sound.
47
- * @param events The `[playEvent, stopEvent?]` tuple.
48
- * @param options Optional base Howler options.
49
- * @returns A factory function that returns a Svelte attachment.
50
- *
51
- * @example
52
- * ```svelte
53
- * <script>
54
- * import { useSound } from "svelte-attach-sound";
55
- * import click_mp3 from "./assets/click.mp3";
56
- *
57
- * const click = useSound(click_mp3, ["click"]);
58
- * </script>
59
- *
60
- * <button {@attach click()}>Click me</button>
61
- *
62
- * <!-- Override options per-element -->
63
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
64
- * ```
65
- */
66
- declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
67
- //#endregion
68
- export { Sound, sound, useSound };
package/dist/index.d.mts DELETED
@@ -1,68 +0,0 @@
1
- import { HowlOptions } from "howler";
2
- import { Attachment } from "svelte/attachments";
3
-
4
- //#region src/index.d.ts
5
- type SoundSource = HowlOptions["src"];
6
- type SoundEvents = [keyof HTMLElementEventMap, (keyof HTMLElementEventMap)?];
7
- type SoundOptions = Omit<HowlOptions, "src">;
8
- /**
9
- * Options for the sound attachment factory.
10
- */
11
- type Options = {
12
- events: SoundEvents;
13
- } & HowlOptions;
14
- /**
15
- * A class representing a synthetic sound.
16
- * Can be used standalone for programmatic playback without any DOM dependency.
17
- */
18
- declare class Sound {
19
- private howl;
20
- constructor(src: SoundSource, options?: SoundOptions);
21
- play(): void;
22
- stop(): void;
23
- destroy(): void;
24
- }
25
- /**
26
- * Creates a sound attachment that binds playback to DOM events on the element.
27
- *
28
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
29
- * will automatically tear down and recreate when that state changes.
30
- *
31
- * @param options Options including `src`, `events`, and any Howler options.
32
- * @returns A Svelte attachment.
33
- *
34
- * @example
35
- * ```svelte
36
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
37
- * Click me
38
- * </button>
39
- * ```
40
- */
41
- declare function sound(options: Options): Attachment<HTMLElement>;
42
- /**
43
- * Creates a pre-configured sound attachment factory that can be reused across
44
- * multiple elements with optional per-element overrides.
45
- *
46
- * @param src The source URL(s) of the sound.
47
- * @param events The `[playEvent, stopEvent?]` tuple.
48
- * @param options Optional base Howler options.
49
- * @returns A factory function that returns a Svelte attachment.
50
- *
51
- * @example
52
- * ```svelte
53
- * <script>
54
- * import { useSound } from "svelte-attach-sound";
55
- * import click_mp3 from "./assets/click.mp3";
56
- *
57
- * const click = useSound(click_mp3, ["click"]);
58
- * </script>
59
- *
60
- * <button {@attach click()}>Click me</button>
61
- *
62
- * <!-- Override options per-element -->
63
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
64
- * ```
65
- */
66
- declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
67
- //#endregion
68
- export { Sound, sound, useSound };
package/dist/index.mjs DELETED
@@ -1,96 +0,0 @@
1
- import { on } from "svelte/events";
2
- //#region src/index.ts
3
- /**
4
- * A class representing a synthetic sound.
5
- * Can be used standalone for programmatic playback without any DOM dependency.
6
- */
7
- var Sound = class {
8
- howl;
9
- constructor(src, options = {}) {
10
- this.howl = import("howler/src/howler.core").then(({ Howl }) => new Howl({
11
- ...options,
12
- src
13
- })).catch((e) => {
14
- console.warn("[svelte-attach-sound] Failed to load sound:", e);
15
- throw e;
16
- });
17
- }
18
- play() {
19
- this.howl.then((h) => h.play()).catch(() => {});
20
- }
21
- stop() {
22
- this.howl.then((h) => h.stop()).catch(() => {});
23
- }
24
- destroy() {
25
- this.howl.then((h) => {
26
- h.stop();
27
- h.unload();
28
- }).catch(() => {});
29
- }
30
- };
31
- /**
32
- * Creates a sound attachment that binds playback to DOM events on the element.
33
- *
34
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
35
- * will automatically tear down and recreate when that state changes.
36
- *
37
- * @param options Options including `src`, `events`, and any Howler options.
38
- * @returns A Svelte attachment.
39
- *
40
- * @example
41
- * ```svelte
42
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
43
- * Click me
44
- * </button>
45
- * ```
46
- */
47
- function sound(options) {
48
- return (element) => {
49
- const { src, events, ...howlOptions } = options;
50
- const [playEvent, stopEvent] = events;
51
- const instance = new Sound(src, howlOptions);
52
- const handlePlay = () => instance.play();
53
- const handleStop = () => instance.stop();
54
- const offPlay = on(element, playEvent, handlePlay);
55
- const offStop = stopEvent ? on(element, stopEvent, handleStop) : null;
56
- return () => {
57
- offPlay();
58
- offStop?.();
59
- instance.destroy();
60
- };
61
- };
62
- }
63
- /**
64
- * Creates a pre-configured sound attachment factory that can be reused across
65
- * multiple elements with optional per-element overrides.
66
- *
67
- * @param src The source URL(s) of the sound.
68
- * @param events The `[playEvent, stopEvent?]` tuple.
69
- * @param options Optional base Howler options.
70
- * @returns A factory function that returns a Svelte attachment.
71
- *
72
- * @example
73
- * ```svelte
74
- * <script>
75
- * import { useSound } from "svelte-attach-sound";
76
- * import click_mp3 from "./assets/click.mp3";
77
- *
78
- * const click = useSound(click_mp3, ["click"]);
79
- * <\/script>
80
- *
81
- * <button {@attach click()}>Click me</button>
82
- *
83
- * <!-- Override options per-element -->
84
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
85
- * ```
86
- */
87
- function useSound(src, events, options) {
88
- return (overrideOptions) => sound({
89
- src,
90
- events,
91
- ...options,
92
- ...overrideOptions
93
- });
94
- }
95
- //#endregion
96
- export { Sound, sound, useSound };