svelte-attach-sound 0.1.1 → 0.1.4

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.1",
4
- "description": "A Svelte attachment for binding sound playback to DOM events using Howler.js",
3
+ "version": "0.1.4",
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,14 +23,9 @@
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
31
  "scripts": {
@@ -41,19 +36,16 @@
41
36
  "prepublishOnly": "bun run build"
42
37
  },
43
38
  "devDependencies": {
44
- "@types/howler": "^2.2.12",
45
39
  "@types/node": "^25.0.3",
46
- "bumpp": "^10.3.2",
47
- "howler": "^2.2.4",
48
- "oxfmt": "^0.40.0",
49
- "oxlint": "^1.50.0",
50
- "svelte": "^5.53.11",
51
- "tsdown": "^0.21.2",
52
- "typescript": "^5.9.3",
53
- "vitest": "^4.0.16"
40
+ "bumpp": "^11.0.1",
41
+ "oxfmt": "^0.41.0",
42
+ "oxlint": "^1.56.0",
43
+ "svelte": "^5.55.0",
44
+ "tsdown": "^0.21.4",
45
+ "typescript": "^6.0.2",
46
+ "vitest": "^4.1.1"
54
47
  },
55
48
  "peerDependencies": {
56
- "howler": ">=2.0.0",
57
- "svelte": ">=5.53.11"
49
+ "svelte": ">=5.55.0"
58
50
  }
59
51
  }
package/dist/index.cjs DELETED
@@ -1,105 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
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
- config;
9
- howl;
10
- ready;
11
- constructor(src, options = {}) {
12
- this.config = {
13
- ...options,
14
- src
15
- };
16
- this.ready = this.create();
17
- }
18
- whenReady(fn) {
19
- this.ready.then(fn).catch(() => {});
20
- }
21
- async create() {
22
- const { Howl } = await import("howler/src/howler.core");
23
- this.howl = new Howl(this.config);
24
- }
25
- play() {
26
- this.whenReady(() => this.howl?.play());
27
- }
28
- stop() {
29
- this.whenReady(() => this.howl?.stop());
30
- }
31
- destroy() {
32
- this.whenReady(() => {
33
- this.howl?.stop();
34
- this.howl?.unload();
35
- });
36
- }
37
- };
38
- /**
39
- * Creates a sound attachment that binds playback to DOM events on the element.
40
- *
41
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
42
- * will automatically tear down and recreate when that state changes.
43
- *
44
- * @param options Options including `src`, `events`, and any Howler options.
45
- * @returns A Svelte attachment.
46
- *
47
- * @example
48
- * ```svelte
49
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
50
- * Click me
51
- * </button>
52
- * ```
53
- */
54
- function sound(options) {
55
- return (element) => {
56
- const { src, events, ...howlOptions } = options;
57
- const [playEvent, stopEvent] = events;
58
- const instance = new Sound(src, howlOptions);
59
- const handlePlay = () => instance.play();
60
- const handleStop = () => instance.stop();
61
- element.addEventListener(playEvent, handlePlay);
62
- if (stopEvent) element.addEventListener(stopEvent, handleStop);
63
- return () => {
64
- element.removeEventListener(playEvent, handlePlay);
65
- if (stopEvent) element.removeEventListener(stopEvent, handleStop);
66
- instance.destroy();
67
- };
68
- };
69
- }
70
- /**
71
- * Creates a pre-configured sound attachment factory that can be reused across
72
- * multiple elements with optional per-element overrides.
73
- *
74
- * @param src The source URL(s) of the sound.
75
- * @param events The `[playEvent, stopEvent?]` tuple.
76
- * @param options Optional base Howler options.
77
- * @returns A factory function that returns a Svelte attachment.
78
- *
79
- * @example
80
- * ```svelte
81
- * <script>
82
- * import { useSound } from "svelte-attach-sound";
83
- * import click_mp3 from "./assets/click.mp3";
84
- *
85
- * const click = useSound(click_mp3, ["click"]);
86
- * <\/script>
87
- *
88
- * <button {@attach click()}>Click me</button>
89
- *
90
- * <!-- Override options per-element -->
91
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
92
- * ```
93
- */
94
- function useSound(src, events, options) {
95
- return (overrideOptions) => sound({
96
- src,
97
- events,
98
- ...options,
99
- ...overrideOptions
100
- });
101
- }
102
- //#endregion
103
- exports.Sound = Sound;
104
- exports.sound = sound;
105
- exports.useSound = useSound;
package/dist/index.d.cts DELETED
@@ -1,72 +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 config;
20
- private howl;
21
- private ready;
22
- constructor(src: SoundSource, options?: SoundOptions);
23
- private whenReady;
24
- private create;
25
- play(): void;
26
- stop(): void;
27
- destroy(): void;
28
- }
29
- /**
30
- * Creates a sound attachment that binds playback to DOM events on the element.
31
- *
32
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
33
- * will automatically tear down and recreate when that state changes.
34
- *
35
- * @param options Options including `src`, `events`, and any Howler options.
36
- * @returns A Svelte attachment.
37
- *
38
- * @example
39
- * ```svelte
40
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
41
- * Click me
42
- * </button>
43
- * ```
44
- */
45
- declare function sound(options: Options): Attachment<HTMLElement>;
46
- /**
47
- * Creates a pre-configured sound attachment factory that can be reused across
48
- * multiple elements with optional per-element overrides.
49
- *
50
- * @param src The source URL(s) of the sound.
51
- * @param events The `[playEvent, stopEvent?]` tuple.
52
- * @param options Optional base Howler options.
53
- * @returns A factory function that returns a Svelte attachment.
54
- *
55
- * @example
56
- * ```svelte
57
- * <script>
58
- * import { useSound } from "svelte-attach-sound";
59
- * import click_mp3 from "./assets/click.mp3";
60
- *
61
- * const click = useSound(click_mp3, ["click"]);
62
- * </script>
63
- *
64
- * <button {@attach click()}>Click me</button>
65
- *
66
- * <!-- Override options per-element -->
67
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
68
- * ```
69
- */
70
- declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
71
- //#endregion
72
- export { Sound, sound, useSound };
package/dist/index.d.mts DELETED
@@ -1,72 +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 config;
20
- private howl;
21
- private ready;
22
- constructor(src: SoundSource, options?: SoundOptions);
23
- private whenReady;
24
- private create;
25
- play(): void;
26
- stop(): void;
27
- destroy(): void;
28
- }
29
- /**
30
- * Creates a sound attachment that binds playback to DOM events on the element.
31
- *
32
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
33
- * will automatically tear down and recreate when that state changes.
34
- *
35
- * @param options Options including `src`, `events`, and any Howler options.
36
- * @returns A Svelte attachment.
37
- *
38
- * @example
39
- * ```svelte
40
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
41
- * Click me
42
- * </button>
43
- * ```
44
- */
45
- declare function sound(options: Options): Attachment<HTMLElement>;
46
- /**
47
- * Creates a pre-configured sound attachment factory that can be reused across
48
- * multiple elements with optional per-element overrides.
49
- *
50
- * @param src The source URL(s) of the sound.
51
- * @param events The `[playEvent, stopEvent?]` tuple.
52
- * @param options Optional base Howler options.
53
- * @returns A factory function that returns a Svelte attachment.
54
- *
55
- * @example
56
- * ```svelte
57
- * <script>
58
- * import { useSound } from "svelte-attach-sound";
59
- * import click_mp3 from "./assets/click.mp3";
60
- *
61
- * const click = useSound(click_mp3, ["click"]);
62
- * </script>
63
- *
64
- * <button {@attach click()}>Click me</button>
65
- *
66
- * <!-- Override options per-element -->
67
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
68
- * ```
69
- */
70
- declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
71
- //#endregion
72
- export { Sound, sound, useSound };
package/dist/index.mjs DELETED
@@ -1,102 +0,0 @@
1
- //#region src/index.ts
2
- /**
3
- * A class representing a synthetic sound.
4
- * Can be used standalone for programmatic playback without any DOM dependency.
5
- */
6
- var Sound = class {
7
- config;
8
- howl;
9
- ready;
10
- constructor(src, options = {}) {
11
- this.config = {
12
- ...options,
13
- src
14
- };
15
- this.ready = this.create();
16
- }
17
- whenReady(fn) {
18
- this.ready.then(fn).catch(() => {});
19
- }
20
- async create() {
21
- const { Howl } = await import("howler/src/howler.core");
22
- this.howl = new Howl(this.config);
23
- }
24
- play() {
25
- this.whenReady(() => this.howl?.play());
26
- }
27
- stop() {
28
- this.whenReady(() => this.howl?.stop());
29
- }
30
- destroy() {
31
- this.whenReady(() => {
32
- this.howl?.stop();
33
- this.howl?.unload();
34
- });
35
- }
36
- };
37
- /**
38
- * Creates a sound attachment that binds playback to DOM events on the element.
39
- *
40
- * Runs inside a Svelte effect — if options contain reactive state, the attachment
41
- * will automatically tear down and recreate when that state changes.
42
- *
43
- * @param options Options including `src`, `events`, and any Howler options.
44
- * @returns A Svelte attachment.
45
- *
46
- * @example
47
- * ```svelte
48
- * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
49
- * Click me
50
- * </button>
51
- * ```
52
- */
53
- function sound(options) {
54
- return (element) => {
55
- const { src, events, ...howlOptions } = options;
56
- const [playEvent, stopEvent] = events;
57
- const instance = new Sound(src, howlOptions);
58
- const handlePlay = () => instance.play();
59
- const handleStop = () => instance.stop();
60
- element.addEventListener(playEvent, handlePlay);
61
- if (stopEvent) element.addEventListener(stopEvent, handleStop);
62
- return () => {
63
- element.removeEventListener(playEvent, handlePlay);
64
- if (stopEvent) element.removeEventListener(stopEvent, handleStop);
65
- instance.destroy();
66
- };
67
- };
68
- }
69
- /**
70
- * Creates a pre-configured sound attachment factory that can be reused across
71
- * multiple elements with optional per-element overrides.
72
- *
73
- * @param src The source URL(s) of the sound.
74
- * @param events The `[playEvent, stopEvent?]` tuple.
75
- * @param options Optional base Howler options.
76
- * @returns A factory function that returns a Svelte attachment.
77
- *
78
- * @example
79
- * ```svelte
80
- * <script>
81
- * import { useSound } from "svelte-attach-sound";
82
- * import click_mp3 from "./assets/click.mp3";
83
- *
84
- * const click = useSound(click_mp3, ["click"]);
85
- * <\/script>
86
- *
87
- * <button {@attach click()}>Click me</button>
88
- *
89
- * <!-- Override options per-element -->
90
- * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
91
- * ```
92
- */
93
- function useSound(src, events, options) {
94
- return (overrideOptions) => sound({
95
- src,
96
- events,
97
- ...options,
98
- ...overrideOptions
99
- });
100
- }
101
- //#endregion
102
- export { Sound, sound, useSound };