@adea-ai/audio 0.78.0 → 0.79.0

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { AUDIO_BASE_URL, MUSIC_FILES, type MusicId } from './config';
2
2
  export { SoundController, soundController, type MusicOptions } from './controller';
3
3
  export { musicForScene, sceneMusicTracks } from './scene-music';
4
- export { MusicToggle, SoundProvider, useSceneMusic, useSound, type SoundContextValue, } from './sound';
4
+ export { MusicToggle, SoundProvider, useOptionalSound, useSceneMusic, useSound, type SoundContextValue, } from './sound';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AUDIO_BASE_URL, MUSIC_FILES } from "./config.js";
2
2
  import { SoundController, soundController } from "./controller.js";
3
3
  import { musicForScene, sceneMusicTracks } from "./scene-music.js";
4
- import { MusicToggle, SoundProvider, useSceneMusic, useSound } from "./sound.js";
5
- export { AUDIO_BASE_URL, MUSIC_FILES, MusicToggle, SoundController, SoundProvider, musicForScene, sceneMusicTracks, soundController, useSceneMusic, useSound };
4
+ import { MusicToggle, SoundProvider, useOptionalSound, useSceneMusic, useSound } from "./sound.js";
5
+ export { AUDIO_BASE_URL, MUSIC_FILES, MusicToggle, SoundController, SoundProvider, musicForScene, sceneMusicTracks, soundController, useOptionalSound, useSceneMusic, useSound };
package/dist/sound.d.ts CHANGED
@@ -10,5 +10,14 @@ export type SoundContextValue = {
10
10
  };
11
11
  export declare function SoundProvider(props: ParentProps): import("solid-js").JSX.Element;
12
12
  export declare function useSound(): SoundContextValue;
13
+ /**
14
+ * The sound context when one exists, `null` otherwise. Hosts that render
15
+ * shared components outside a `<SoundProvider>` (tests, alternate shells, a
16
+ * dialog tree mounted before the app providers) read this instead of letting
17
+ * `useSound`'s guard throw during render and tear down the whole host tree —
18
+ * the settings dialog's soundtrack row white-screened the app that way when
19
+ * the "Input & notifications" section mounted `<MusicToggle />` unprovided.
20
+ */
21
+ export declare function useOptionalSound(): SoundContextValue | null;
13
22
  export declare function useSceneMusic(sceneId: string | null | undefined): void;
14
23
  export declare function MusicToggle(): import("solid-js").JSX.Element;
package/dist/sound.js CHANGED
@@ -51,6 +51,17 @@ function useSound() {
51
51
  if (!value) throw new Error("useSound must be used within <SoundProvider>");
52
52
  return value;
53
53
  }
54
+ /**
55
+ * The sound context when one exists, `null` otherwise. Hosts that render
56
+ * shared components outside a `<SoundProvider>` (tests, alternate shells, a
57
+ * dialog tree mounted before the app providers) read this instead of letting
58
+ * `useSound`'s guard throw during render and tear down the whole host tree —
59
+ * the settings dialog's soundtrack row white-screened the app that way when
60
+ * the "Input & notifications" section mounted `<MusicToggle />` unprovided.
61
+ */
62
+ function useOptionalSound() {
63
+ return useContext(SoundContext);
64
+ }
54
65
  function useSceneMusic(sceneId) {
55
66
  createEffect(on(() => sceneId, (scene) => {
56
67
  const timeout = window.setTimeout(() => {
@@ -88,11 +99,22 @@ function MusicButton(props) {
88
99
  });
89
100
  }
90
101
  function MusicToggle() {
91
- const { musicMuted, toggleMusicMute } = useSound();
102
+ const context = useOptionalSound();
103
+ if (context) return createComponent(MusicButton, {
104
+ get muted() {
105
+ return context.musicMuted;
106
+ },
107
+ get onToggle() {
108
+ return context.toggleMusicMute;
109
+ }
110
+ });
111
+ const [unprovidedMuted, setUnprovidedMuted] = createSignal(soundController.musicMuted);
92
112
  return createComponent(MusicButton, {
93
- muted: musicMuted,
94
- onToggle: toggleMusicMute
113
+ get muted() {
114
+ return unprovidedMuted();
115
+ },
116
+ onToggle: () => setUnprovidedMuted(soundController.toggleMusicMute())
95
117
  });
96
118
  }
97
119
  //#endregion
98
- export { MusicToggle, SoundProvider, useSceneMusic, useSound };
120
+ export { MusicToggle, SoundProvider, useOptionalSound, useSceneMusic, useSound };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/audio",
3
- "version": "0.78.0",
3
+ "version": "0.79.0",
4
4
  "description": "Optional, lazy music controller and Solid bindings for Adea. Audio is non-blocking so scene loading never waits on sound assets.",
5
5
  "keywords": [
6
6
  "audio",
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export { musicForScene, sceneMusicTracks } from './scene-music'
6
6
  export {
7
7
  MusicToggle,
8
8
  SoundProvider,
9
+ useOptionalSound,
9
10
  useSceneMusic,
10
11
  useSound,
11
12
  type SoundContextValue,
package/src/sound.tsx CHANGED
@@ -72,6 +72,18 @@ export function useSound(): SoundContextValue {
72
72
  return value
73
73
  }
74
74
 
75
+ /**
76
+ * The sound context when one exists, `null` otherwise. Hosts that render
77
+ * shared components outside a `<SoundProvider>` (tests, alternate shells, a
78
+ * dialog tree mounted before the app providers) read this instead of letting
79
+ * `useSound`'s guard throw during render and tear down the whole host tree —
80
+ * the settings dialog's soundtrack row white-screened the app that way when
81
+ * the "Input & notifications" section mounted `<MusicToggle />` unprovided.
82
+ */
83
+ export function useOptionalSound(): SoundContextValue | null {
84
+ return useContext(SoundContext)
85
+ }
86
+
75
87
  export function useSceneMusic(sceneId: string | null | undefined): void {
76
88
  createEffect(
77
89
  on(
@@ -107,6 +119,18 @@ function MusicButton(props: { muted: boolean; onToggle: () => void }) {
107
119
  }
108
120
 
109
121
  export function MusicToggle() {
110
- const { musicMuted, toggleMusicMute } = useSound()
111
- return <MusicButton muted={musicMuted} onToggle={toggleMusicMute} />
122
+ const context = useOptionalSound()
123
+ if (context) {
124
+ return <MusicButton muted={context.musicMuted} onToggle={context.toggleMusicMute} />
125
+ }
126
+ // No provider: drive the singleton controller directly so the control stays
127
+ // honest and operable instead of throwing during render. The mute state
128
+ // lives on the controller, so the button only needs to mirror it.
129
+ const [unprovidedMuted, setUnprovidedMuted] = createSignal(soundController.musicMuted)
130
+ return (
131
+ <MusicButton
132
+ muted={unprovidedMuted()}
133
+ onToggle={() => setUnprovidedMuted(soundController.toggleMusicMute())}
134
+ />
135
+ )
112
136
  }