@hkdigital/lib-core 0.4.59 → 0.4.61

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.
@@ -0,0 +1,71 @@
1
+ /** @typedef {import('../../../network/loaders/typedef.js').SceneLoadingProgress} SceneLoadingProgress */
2
+ export default class AudioService extends ServiceBase {
3
+ /** @type {Record<string, AudioScene>} */
4
+ scenes: Record<string, AudioScene>;
5
+ muted: boolean;
6
+ mute(): void;
7
+ unmute(): void;
8
+ /**
9
+ * Play a sound from the specified audio scene
10
+ *
11
+ * @param {string} sceneLabel
12
+ * @param {string} soundLabel
13
+ * @param {{delay?:number, loop?:boolean}} options
14
+ *
15
+ * @returns {Promise<{stop: ()=>void, sourceNode:AudioBufferSourceNode}>}
16
+ */
17
+ playSound(sceneLabel: string, soundLabel: string, options?: {
18
+ delay?: number;
19
+ loop?: boolean;
20
+ }): Promise<{
21
+ stop: () => void;
22
+ sourceNode: AudioBufferSourceNode;
23
+ }>;
24
+ /**
25
+ * Get an audio scene
26
+ *
27
+ * @param {string} sceneLabel
28
+ * @param {import('../../../network/loaders/typedef.js').MemorySourceParams[]} memorySources
29
+ *
30
+ * @returns {AudioScene}
31
+ */
32
+ createScene(sceneLabel: string, memorySources: import("../../../network/loaders/typedef.js").MemorySourceParams[]): AudioScene;
33
+ /**
34
+ * Get an audio scene
35
+ *
36
+ * @param {string} label
37
+ */
38
+ destroyScene(label: string): void;
39
+ /**
40
+ * Get an audio scene
41
+ *
42
+ * @param {string} label
43
+ *
44
+ * @param {object} [options]
45
+ * @param {number} [options.timeoutMs=10000]
46
+ * Timeout in milliseconds
47
+ * @param {(progress: SceneLoadingProgress) => void} [options.onProgress]
48
+ * Progress callback function
49
+ *
50
+ * @returns {{promise: Promise<AudioScene>, abort: Function}}
51
+ * Object with promise that resolves when loaded and abort function
52
+ */
53
+ preloadScene(label: string, options?: {
54
+ timeoutMs?: number | undefined;
55
+ onProgress?: ((progress: SceneLoadingProgress) => void) | undefined;
56
+ }): {
57
+ promise: Promise<AudioScene>;
58
+ abort: Function;
59
+ };
60
+ /**
61
+ * Get an audio scene
62
+ *
63
+ * @param {string} label
64
+ *
65
+ * @returns {AudioScene}
66
+ */
67
+ getScene(label: string): AudioScene;
68
+ }
69
+ export type SceneLoadingProgress = import("../../../network/loaders/typedef.js").SceneLoadingProgress;
70
+ import { ServiceBase } from '../../../services/index.js';
71
+ import { AudioScene } from '../../../network/loaders.js';
@@ -0,0 +1,168 @@
1
+ import { ServiceBase } from '../../../services/index.js';
2
+
3
+ import { AudioScene } from '../../../network/loaders.js';
4
+
5
+ /** @typedef {import('../../../network/loaders/typedef.js').SceneLoadingProgress} SceneLoadingProgress */
6
+
7
+ export default class AudioService extends ServiceBase {
8
+ /** @type {Record<string, AudioScene>} */
9
+ scenes = {};
10
+
11
+ muted = $state(false);
12
+
13
+ /**
14
+ * Configure the service (handles both initial config and reconfiguration)
15
+ *
16
+ * @param {any} newConfig - Configuration to apply
17
+ * @param {any} [oldConfig=null] - Previous config (null = initial setup)
18
+ */
19
+ // eslint-disable-next-line no-unused-vars
20
+ async _configure(newConfig, oldConfig) {
21
+ // console.debug('Configure AudioService');
22
+ }
23
+
24
+ mute() {
25
+ //this.logger.debug('mute all');
26
+
27
+ for (const label in this.scenes) {
28
+ const scene = this.scenes[label];
29
+ scene.mute();
30
+ }
31
+
32
+ this.muted = true;
33
+ }
34
+
35
+ unmute() {
36
+ // this.logger.debug('unmute all');
37
+
38
+ for (const label in this.scenes) {
39
+ const scene = this.scenes[label];
40
+ scene.unmute();
41
+ }
42
+
43
+ this.muted = false;
44
+ }
45
+
46
+ /**
47
+ * Play a sound from the specified audio scene
48
+ *
49
+ * @param {string} sceneLabel
50
+ * @param {string} soundLabel
51
+ * @param {{delay?:number, loop?:boolean}} options
52
+ *
53
+ * @returns {Promise<{stop: ()=>void, sourceNode:AudioBufferSourceNode}>}
54
+ */
55
+ async playSound(sceneLabel, soundLabel, options = {}) {
56
+ const audioScene = await this.getScene(sceneLabel);
57
+
58
+ const sourceNode = await audioScene.getSourceNode(soundLabel);
59
+
60
+ const { delay = 0, loop = false } = options;
61
+
62
+ sourceNode.loop = loop;
63
+
64
+ let started = false;
65
+
66
+ /** @type {ReturnType<typeof setTimeout>|null} */
67
+ let timer = setTimeout(() => {
68
+ // console.debug('playSound', soundLabel);
69
+ sourceNode.start();
70
+ started = true;
71
+ }, delay);
72
+
73
+ return {
74
+ stop: () => {
75
+ if (timer) {
76
+ clearTimeout(timer);
77
+ timer = null;
78
+ }
79
+
80
+ if (started) {
81
+ sourceNode.stop();
82
+ started = false;
83
+ }
84
+ },
85
+ sourceNode
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Get an audio scene
91
+ *
92
+ * @param {string} sceneLabel
93
+ * @param {import('../../../network/loaders/typedef.js').MemorySourceParams[]} memorySources
94
+ *
95
+ * @returns {AudioScene}
96
+ */
97
+ createScene(sceneLabel, memorySources) {
98
+ let audioScene = this.scenes[sceneLabel];
99
+
100
+ if (audioScene) {
101
+ // Scene already created
102
+ // => Do nothing
103
+ return audioScene;
104
+ }
105
+
106
+ audioScene = new AudioScene();
107
+
108
+ for (const memorySource of memorySources) {
109
+ audioScene.defineMemorySource(memorySource);
110
+ }
111
+
112
+ this.scenes[sceneLabel] = audioScene;
113
+
114
+ return audioScene;
115
+ }
116
+
117
+ /**
118
+ * Get an audio scene
119
+ *
120
+ * @param {string} label
121
+ */
122
+ destroyScene(label) {
123
+ const scene = this.scenes[label];
124
+
125
+ if (scene) {
126
+ scene.destroy();
127
+ delete this.scenes[label];
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Get an audio scene
133
+ *
134
+ * @param {string} label
135
+ *
136
+ * @param {object} [options]
137
+ * @param {number} [options.timeoutMs=10000]
138
+ * Timeout in milliseconds
139
+ * @param {(progress: SceneLoadingProgress) => void} [options.onProgress]
140
+ * Progress callback function
141
+ *
142
+ * @returns {{promise: Promise<AudioScene>, abort: Function}}
143
+ * Object with promise that resolves when loaded and abort function
144
+ */
145
+ preloadScene(label, options) {
146
+ const audioScene = this.getScene(label);
147
+
148
+ // @ts-ignore (AudioScene extends SceneBase)
149
+ return audioScene.preload(options);
150
+ }
151
+
152
+ /**
153
+ * Get an audio scene
154
+ *
155
+ * @param {string} label
156
+ *
157
+ * @returns {AudioScene}
158
+ */
159
+ getScene(label) {
160
+ const audioScene = this.scenes[label];
161
+
162
+ if (!audioScene) {
163
+ throw new Error(`AudioScene [${label}] not found`);
164
+ }
165
+
166
+ return audioScene;
167
+ }
168
+ }
@@ -0,0 +1,73 @@
1
+ /** @typedef {import('../../../network/loaders/typedef.js').SceneLoadingProgress} SceneLoadingProgress */
2
+ export default class ImageService extends ServiceBase {
3
+ /** @type {Record<string, ImageScene>} */
4
+ scenes: Record<string, ImageScene>;
5
+ /**
6
+ * Get an image from the specified image scene
7
+ *
8
+ * @param {string} sceneLabel
9
+ * @param {string} imageLabel
10
+ *
11
+ * @returns {string} Object URL for the image
12
+ */
13
+ getObjectURL(sceneLabel: string, imageLabel: string): string;
14
+ /**
15
+ * Get image metadata from the specified image scene
16
+ *
17
+ * @param {string} sceneLabel
18
+ * @param {string} imageLabel
19
+ *
20
+ * @returns {import('../../../network/loaders/image/typedef.js').ImageMeta}
21
+ */
22
+ getImageMeta(sceneLabel: string, imageLabel: string): import("../../../network/loaders/image/typedef.js").ImageMeta;
23
+ /**
24
+ * Create an image scene
25
+ *
26
+ * @param {string} sceneLabel
27
+ * @param {Array<{label: string, imageSource: import('../../../config/typedef.js').ImageSource}>} imageSources
28
+ *
29
+ * @returns {ImageScene}
30
+ */
31
+ createScene(sceneLabel: string, imageSources: Array<{
32
+ label: string;
33
+ imageSource: import("../../../config/typedef.js").ImageSource;
34
+ }>): ImageScene;
35
+ /**
36
+ * Destroy an image scene
37
+ *
38
+ * @param {string} label
39
+ */
40
+ destroyScene(label: string): void;
41
+ /**
42
+ * Get an image scene
43
+ *
44
+ * @param {string} label
45
+ *
46
+ * @param {object} [options]
47
+ * @param {number} [options.timeoutMs=10000]
48
+ * Timeout in milliseconds
49
+ * @param {(progress: SceneLoadingProgress) => void} [options.onProgress]
50
+ * Progress callback function
51
+ *
52
+ * @returns {{promise: Promise<ImageScene>, abort: Function}}
53
+ * Object with promise that resolves when loaded and abort function
54
+ */
55
+ preloadScene(label: string, options?: {
56
+ timeoutMs?: number | undefined;
57
+ onProgress?: ((progress: SceneLoadingProgress) => void) | undefined;
58
+ }): {
59
+ promise: Promise<ImageScene>;
60
+ abort: Function;
61
+ };
62
+ /**
63
+ * Get an image scene
64
+ *
65
+ * @param {string} label
66
+ *
67
+ * @returns {ImageScene}
68
+ */
69
+ getScene(label: string): ImageScene;
70
+ }
71
+ export type SceneLoadingProgress = import("../../../network/loaders/typedef.js").SceneLoadingProgress;
72
+ import { ServiceBase } from '../../../services/index.js';
73
+ import { ImageScene } from '../../../network/loaders.js';
@@ -0,0 +1,127 @@
1
+ import { ServiceBase } from '../../../services/index.js';
2
+
3
+ import { ImageScene } from '../../../network/loaders.js';
4
+
5
+ /** @typedef {import('../../../network/loaders/typedef.js').SceneLoadingProgress} SceneLoadingProgress */
6
+
7
+ export default class ImageService extends ServiceBase {
8
+ /** @type {Record<string, ImageScene>} */
9
+ scenes = {};
10
+
11
+ /**
12
+ * Configure the service (handles both initial config and reconfiguration)
13
+ *
14
+ * @param {any} newConfig - Configuration to apply
15
+ * @param {any} [oldConfig=null] - Previous config (null = initial setup)
16
+ */
17
+ // eslint-disable-next-line no-unused-vars
18
+ async _configure(newConfig, oldConfig) {
19
+ // console.debug('Configure ImageService');
20
+ }
21
+
22
+ /**
23
+ * Get an image from the specified image scene
24
+ *
25
+ * @param {string} sceneLabel
26
+ * @param {string} imageLabel
27
+ *
28
+ * @returns {string} Object URL for the image
29
+ */
30
+ getObjectURL(sceneLabel, imageLabel) {
31
+ const imageScene = this.getScene(sceneLabel);
32
+ return imageScene.getObjectURL(imageLabel);
33
+ }
34
+
35
+ /**
36
+ * Get image metadata from the specified image scene
37
+ *
38
+ * @param {string} sceneLabel
39
+ * @param {string} imageLabel
40
+ *
41
+ * @returns {import('../../../network/loaders/image/typedef.js').ImageMeta}
42
+ */
43
+ getImageMeta(sceneLabel, imageLabel) {
44
+ const imageScene = this.getScene(sceneLabel);
45
+ return imageScene.getImageMeta(imageLabel);
46
+ }
47
+
48
+ /**
49
+ * Create an image scene
50
+ *
51
+ * @param {string} sceneLabel
52
+ * @param {Array<{label: string, imageSource: import('../../../config/typedef.js').ImageSource}>} imageSources
53
+ *
54
+ * @returns {ImageScene}
55
+ */
56
+ createScene(sceneLabel, imageSources) {
57
+ let imageScene = this.scenes[sceneLabel];
58
+
59
+ if (imageScene) {
60
+ // Scene already created
61
+ // => Do nothing
62
+ return imageScene;
63
+ }
64
+
65
+ imageScene = new ImageScene();
66
+
67
+ for (const imageSource of imageSources) {
68
+ imageScene.defineImage(imageSource);
69
+ }
70
+
71
+ this.scenes[sceneLabel] = imageScene;
72
+
73
+ return imageScene;
74
+ }
75
+
76
+ /**
77
+ * Destroy an image scene
78
+ *
79
+ * @param {string} label
80
+ */
81
+ destroyScene(label) {
82
+ const scene = this.scenes[label];
83
+
84
+ if (scene) {
85
+ scene.destroy();
86
+ delete this.scenes[label];
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get an image scene
92
+ *
93
+ * @param {string} label
94
+ *
95
+ * @param {object} [options]
96
+ * @param {number} [options.timeoutMs=10000]
97
+ * Timeout in milliseconds
98
+ * @param {(progress: SceneLoadingProgress) => void} [options.onProgress]
99
+ * Progress callback function
100
+ *
101
+ * @returns {{promise: Promise<ImageScene>, abort: Function}}
102
+ * Object with promise that resolves when loaded and abort function
103
+ */
104
+ preloadScene(label, options) {
105
+ const imageScene = this.getScene(label);
106
+
107
+ // @ts-ignore (ImageScene extends SceneBase)
108
+ return imageScene.preload(options);
109
+ }
110
+
111
+ /**
112
+ * Get an image scene
113
+ *
114
+ * @param {string} label
115
+ *
116
+ * @returns {ImageScene}
117
+ */
118
+ getScene(label) {
119
+ const imageScene = this.scenes[label];
120
+
121
+ if (!imageScene) {
122
+ throw new Error(`ImageScene [${label}] not found`);
123
+ }
124
+
125
+ return imageScene;
126
+ }
127
+ }
@@ -0,0 +1 @@
1
+ export { default as AudioService } from "./client/AudioService.svelte.js";
@@ -0,0 +1 @@
1
+ export { default as AudioService } from './client/AudioService.svelte.js';
@@ -64,11 +64,24 @@ export default class SceneBase {
64
64
  }
65
65
  }
66
66
 
67
+ let percentageLoaded;
68
+ if (totalSize > 0) {
69
+ // Byte-based progress
70
+ percentageLoaded = Math.round((totalBytesLoaded / totalSize) * 100);
71
+ } else if (numberOfSources > 0) {
72
+ // Source-based progress
73
+ percentageLoaded = Math.round((sourcesLoaded / numberOfSources) * 100);
74
+ } else {
75
+ // No sources to load
76
+ percentageLoaded = 0;
77
+ }
78
+
67
79
  return {
68
80
  totalBytesLoaded,
69
81
  totalSize,
70
82
  sourcesLoaded,
71
- numberOfSources
83
+ numberOfSources,
84
+ percentageLoaded
72
85
  };
73
86
  });
74
87
 
@@ -201,7 +214,7 @@ export default class SceneBase {
201
214
  // Set up progress tracking with polling
202
215
  if (onProgress) {
203
216
  progressIntervalId = setInterval(() => {
204
- if (!isAborted) {
217
+ if (!isAborted && this.state === STATE_LOADING) {
205
218
  const currentProgress = this.progress;
206
219
  lastSentProgress = currentProgress;
207
220
  onProgress(currentProgress);
@@ -234,17 +247,16 @@ export default class SceneBase {
234
247
  }
235
248
 
236
249
  if (progressIntervalId) {
250
+ clearInterval(progressIntervalId);
251
+ progressIntervalId = null;
252
+
237
253
  if (onProgress) {
238
254
  const finalProgress = this.progress;
239
- // Only send final update if progress has changed
240
- if (!lastSentProgress ||
241
- finalProgress.sourcesLoaded !== lastSentProgress.sourcesLoaded ||
242
- finalProgress.totalBytesLoaded !== lastSentProgress.totalBytesLoaded) {
255
+ // Always send final progress when loading completes
256
+ if (this.loaded) {
243
257
  onProgress(finalProgress);
244
258
  }
245
259
  }
246
- clearInterval(progressIntervalId);
247
- progressIntervalId = null;
248
260
  }
249
261
 
250
262
  if (isAborted || this.state === STATE_ABORTED) {
@@ -17,4 +17,8 @@ export type SceneLoadingProgress = {
17
17
  * - Total number of sources
18
18
  */
19
19
  numberOfSources: number;
20
+ /**
21
+ * - Loading percentage (0-100)
22
+ */
23
+ percentageLoaded: number;
20
24
  };
@@ -4,6 +4,7 @@
4
4
  * @property {number} totalSize - Total size across all sources
5
5
  * @property {number} sourcesLoaded - Number of sources fully loaded
6
6
  * @property {number} numberOfSources - Total number of sources
7
+ * @property {number} percentageLoaded - Loading percentage (0-100)
7
8
  */
8
9
 
9
10
  export default {};
@@ -4,8 +4,8 @@ type PanelGridRow = {
4
4
  $set?(props: Partial<{
5
5
  base?: string | undefined;
6
6
  bg?: string | undefined;
7
- justify?: "normal" | "center" | "start" | "end" | "between" | "around" | "evenly" | "stretch" | undefined;
8
- justifyItems?: "center" | "start" | "end" | "stretch" | undefined;
7
+ justify?: "normal" | "start" | "center" | "end" | "between" | "around" | "evenly" | "stretch" | undefined;
8
+ justifyItems?: "start" | "center" | "end" | "stretch" | undefined;
9
9
  gap?: string | undefined;
10
10
  classes?: string | undefined;
11
11
  children?: Snippet<[]> | undefined;
@@ -4,8 +4,8 @@ type PanelRow2 = {
4
4
  $set?(props: Partial<{
5
5
  base?: string | undefined;
6
6
  bg?: string | undefined;
7
- justify?: "normal" | "center" | "start" | "end" | "between" | "around" | "evenly" | "stretch" | undefined;
8
- justifyItems?: "center" | "start" | "end" | "stretch" | undefined;
7
+ justify?: "normal" | "start" | "center" | "end" | "between" | "around" | "evenly" | "stretch" | undefined;
8
+ justifyItems?: "start" | "center" | "end" | "stretch" | undefined;
9
9
  gap?: string | undefined;
10
10
  classes?: string | undefined;
11
11
  children?: Snippet<[]> | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.4.59",
3
+ "version": "0.4.61",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"