@novely/core 0.38.2 → 0.39.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.
@@ -25,6 +25,7 @@ var Novely = (() => {
25
25
  JP: () => JP,
26
26
  KK: () => KK,
27
27
  RU: () => RU,
28
+ asset: () => asset,
28
29
  extendAction: () => extendAction,
29
30
  localStorageStorage: () => localStorageStorage,
30
31
  novely: () => novely
@@ -81,7 +82,7 @@ var Novely = (() => {
81
82
  var STACK_MAP = /* @__PURE__ */ new Map();
82
83
  var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
83
84
  var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
84
- var RESOURCE_TYPE_CACHE = /* @__PURE__ */ new Map();
85
+ var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
85
86
 
86
87
  // ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
87
88
  var DEV = false;
@@ -113,6 +114,404 @@ var Novely = (() => {
113
114
  return val;
114
115
  }
115
116
 
117
+ // ../../node_modules/.pnpm/micro-memoize@4.1.2/node_modules/micro-memoize/dist/micro-memoize.esm.js
118
+ var DEFAULT_OPTIONS_KEYS = {
119
+ isEqual: true,
120
+ isMatchingKey: true,
121
+ isPromise: true,
122
+ maxSize: true,
123
+ onCacheAdd: true,
124
+ onCacheChange: true,
125
+ onCacheHit: true,
126
+ transformKey: true
127
+ };
128
+ var slice = Array.prototype.slice;
129
+ function cloneArray(arrayLike) {
130
+ var length = arrayLike.length;
131
+ if (!length) {
132
+ return [];
133
+ }
134
+ if (length === 1) {
135
+ return [arrayLike[0]];
136
+ }
137
+ if (length === 2) {
138
+ return [arrayLike[0], arrayLike[1]];
139
+ }
140
+ if (length === 3) {
141
+ return [arrayLike[0], arrayLike[1], arrayLike[2]];
142
+ }
143
+ return slice.call(arrayLike, 0);
144
+ }
145
+ function getCustomOptions(options) {
146
+ var customOptions = {};
147
+ for (var key in options) {
148
+ if (!DEFAULT_OPTIONS_KEYS[key]) {
149
+ customOptions[key] = options[key];
150
+ }
151
+ }
152
+ return customOptions;
153
+ }
154
+ function isMemoized(fn) {
155
+ return typeof fn === "function" && fn.isMemoized;
156
+ }
157
+ function isSameValueZero(object1, object2) {
158
+ return object1 === object2 || object1 !== object1 && object2 !== object2;
159
+ }
160
+ function mergeOptions(existingOptions, newOptions) {
161
+ var target = {};
162
+ for (var key in existingOptions) {
163
+ target[key] = existingOptions[key];
164
+ }
165
+ for (var key in newOptions) {
166
+ target[key] = newOptions[key];
167
+ }
168
+ return target;
169
+ }
170
+ var Cache = (
171
+ /** @class */
172
+ function() {
173
+ function Cache2(options) {
174
+ this.keys = [];
175
+ this.values = [];
176
+ this.options = options;
177
+ var isMatchingKeyFunction = typeof options.isMatchingKey === "function";
178
+ if (isMatchingKeyFunction) {
179
+ this.getKeyIndex = this._getKeyIndexFromMatchingKey;
180
+ } else if (options.maxSize > 1) {
181
+ this.getKeyIndex = this._getKeyIndexForMany;
182
+ } else {
183
+ this.getKeyIndex = this._getKeyIndexForSingle;
184
+ }
185
+ this.canTransformKey = typeof options.transformKey === "function";
186
+ this.shouldCloneArguments = this.canTransformKey || isMatchingKeyFunction;
187
+ this.shouldUpdateOnAdd = typeof options.onCacheAdd === "function";
188
+ this.shouldUpdateOnChange = typeof options.onCacheChange === "function";
189
+ this.shouldUpdateOnHit = typeof options.onCacheHit === "function";
190
+ }
191
+ Object.defineProperty(Cache2.prototype, "size", {
192
+ /**
193
+ * The number of cached [key,value] results.
194
+ */
195
+ get: function() {
196
+ return this.keys.length;
197
+ },
198
+ enumerable: false,
199
+ configurable: true
200
+ });
201
+ Object.defineProperty(Cache2.prototype, "snapshot", {
202
+ /**
203
+ * A copy of the cache at a moment in time. This is useful
204
+ * to compare changes over time, since the cache mutates
205
+ * internally for performance reasons.
206
+ */
207
+ get: function() {
208
+ return {
209
+ keys: cloneArray(this.keys),
210
+ size: this.size,
211
+ values: cloneArray(this.values)
212
+ };
213
+ },
214
+ enumerable: false,
215
+ configurable: true
216
+ });
217
+ Cache2.prototype._getKeyIndexFromMatchingKey = function(keyToMatch) {
218
+ var _a = this.options, isMatchingKey = _a.isMatchingKey, maxSize = _a.maxSize;
219
+ var keys2 = this.keys;
220
+ var keysLength = keys2.length;
221
+ if (!keysLength) {
222
+ return -1;
223
+ }
224
+ if (isMatchingKey(keys2[0], keyToMatch)) {
225
+ return 0;
226
+ }
227
+ if (maxSize > 1) {
228
+ for (var index = 1; index < keysLength; index++) {
229
+ if (isMatchingKey(keys2[index], keyToMatch)) {
230
+ return index;
231
+ }
232
+ }
233
+ }
234
+ return -1;
235
+ };
236
+ Cache2.prototype._getKeyIndexForMany = function(keyToMatch) {
237
+ var isEqual = this.options.isEqual;
238
+ var keys2 = this.keys;
239
+ var keysLength = keys2.length;
240
+ if (!keysLength) {
241
+ return -1;
242
+ }
243
+ if (keysLength === 1) {
244
+ return this._getKeyIndexForSingle(keyToMatch);
245
+ }
246
+ var keyLength = keyToMatch.length;
247
+ var existingKey;
248
+ var argIndex;
249
+ if (keyLength > 1) {
250
+ for (var index = 0; index < keysLength; index++) {
251
+ existingKey = keys2[index];
252
+ if (existingKey.length === keyLength) {
253
+ argIndex = 0;
254
+ for (; argIndex < keyLength; argIndex++) {
255
+ if (!isEqual(existingKey[argIndex], keyToMatch[argIndex])) {
256
+ break;
257
+ }
258
+ }
259
+ if (argIndex === keyLength) {
260
+ return index;
261
+ }
262
+ }
263
+ }
264
+ } else {
265
+ for (var index = 0; index < keysLength; index++) {
266
+ existingKey = keys2[index];
267
+ if (existingKey.length === keyLength && isEqual(existingKey[0], keyToMatch[0])) {
268
+ return index;
269
+ }
270
+ }
271
+ }
272
+ return -1;
273
+ };
274
+ Cache2.prototype._getKeyIndexForSingle = function(keyToMatch) {
275
+ var keys2 = this.keys;
276
+ if (!keys2.length) {
277
+ return -1;
278
+ }
279
+ var existingKey = keys2[0];
280
+ var length = existingKey.length;
281
+ if (keyToMatch.length !== length) {
282
+ return -1;
283
+ }
284
+ var isEqual = this.options.isEqual;
285
+ if (length > 1) {
286
+ for (var index = 0; index < length; index++) {
287
+ if (!isEqual(existingKey[index], keyToMatch[index])) {
288
+ return -1;
289
+ }
290
+ }
291
+ return 0;
292
+ }
293
+ return isEqual(existingKey[0], keyToMatch[0]) ? 0 : -1;
294
+ };
295
+ Cache2.prototype.orderByLru = function(key, value, startingIndex) {
296
+ var keys2 = this.keys;
297
+ var values = this.values;
298
+ var currentLength = keys2.length;
299
+ var index = startingIndex;
300
+ while (index--) {
301
+ keys2[index + 1] = keys2[index];
302
+ values[index + 1] = values[index];
303
+ }
304
+ keys2[0] = key;
305
+ values[0] = value;
306
+ var maxSize = this.options.maxSize;
307
+ if (currentLength === maxSize && startingIndex === currentLength) {
308
+ keys2.pop();
309
+ values.pop();
310
+ } else if (startingIndex >= maxSize) {
311
+ keys2.length = values.length = maxSize;
312
+ }
313
+ };
314
+ Cache2.prototype.updateAsyncCache = function(memoized) {
315
+ var _this = this;
316
+ var _a = this.options, onCacheChange = _a.onCacheChange, onCacheHit = _a.onCacheHit;
317
+ var firstKey = this.keys[0];
318
+ var firstValue = this.values[0];
319
+ this.values[0] = firstValue.then(function(value) {
320
+ if (_this.shouldUpdateOnHit) {
321
+ onCacheHit(_this, _this.options, memoized);
322
+ }
323
+ if (_this.shouldUpdateOnChange) {
324
+ onCacheChange(_this, _this.options, memoized);
325
+ }
326
+ return value;
327
+ }, function(error) {
328
+ var keyIndex = _this.getKeyIndex(firstKey);
329
+ if (keyIndex !== -1) {
330
+ _this.keys.splice(keyIndex, 1);
331
+ _this.values.splice(keyIndex, 1);
332
+ }
333
+ throw error;
334
+ });
335
+ };
336
+ return Cache2;
337
+ }()
338
+ );
339
+ function createMemoizedFunction(fn, options) {
340
+ if (options === void 0) {
341
+ options = {};
342
+ }
343
+ if (isMemoized(fn)) {
344
+ return createMemoizedFunction(fn.fn, mergeOptions(fn.options, options));
345
+ }
346
+ if (typeof fn !== "function") {
347
+ throw new TypeError("You must pass a function to `memoize`.");
348
+ }
349
+ var _a = options.isEqual, isEqual = _a === void 0 ? isSameValueZero : _a, isMatchingKey = options.isMatchingKey, _b = options.isPromise, isPromise2 = _b === void 0 ? false : _b, _c = options.maxSize, maxSize = _c === void 0 ? 1 : _c, onCacheAdd = options.onCacheAdd, onCacheChange = options.onCacheChange, onCacheHit = options.onCacheHit, transformKey = options.transformKey;
350
+ var normalizedOptions = mergeOptions({
351
+ isEqual,
352
+ isMatchingKey,
353
+ isPromise: isPromise2,
354
+ maxSize,
355
+ onCacheAdd,
356
+ onCacheChange,
357
+ onCacheHit,
358
+ transformKey
359
+ }, getCustomOptions(options));
360
+ var cache = new Cache(normalizedOptions);
361
+ var keys2 = cache.keys, values = cache.values, canTransformKey = cache.canTransformKey, shouldCloneArguments = cache.shouldCloneArguments, shouldUpdateOnAdd = cache.shouldUpdateOnAdd, shouldUpdateOnChange = cache.shouldUpdateOnChange, shouldUpdateOnHit = cache.shouldUpdateOnHit;
362
+ var memoized = function() {
363
+ var key = shouldCloneArguments ? cloneArray(arguments) : arguments;
364
+ if (canTransformKey) {
365
+ key = transformKey(key);
366
+ }
367
+ var keyIndex = keys2.length ? cache.getKeyIndex(key) : -1;
368
+ if (keyIndex !== -1) {
369
+ if (shouldUpdateOnHit) {
370
+ onCacheHit(cache, normalizedOptions, memoized);
371
+ }
372
+ if (keyIndex) {
373
+ cache.orderByLru(keys2[keyIndex], values[keyIndex], keyIndex);
374
+ if (shouldUpdateOnChange) {
375
+ onCacheChange(cache, normalizedOptions, memoized);
376
+ }
377
+ }
378
+ } else {
379
+ var newValue = fn.apply(this, arguments);
380
+ var newKey = shouldCloneArguments ? key : cloneArray(arguments);
381
+ cache.orderByLru(newKey, newValue, keys2.length);
382
+ if (isPromise2) {
383
+ cache.updateAsyncCache(memoized);
384
+ }
385
+ if (shouldUpdateOnAdd) {
386
+ onCacheAdd(cache, normalizedOptions, memoized);
387
+ }
388
+ if (shouldUpdateOnChange) {
389
+ onCacheChange(cache, normalizedOptions, memoized);
390
+ }
391
+ }
392
+ return values[0];
393
+ };
394
+ memoized.cache = cache;
395
+ memoized.fn = fn;
396
+ memoized.isMemoized = true;
397
+ memoized.options = normalizedOptions;
398
+ return memoized;
399
+ }
400
+
401
+ // src/audio-codecs.ts
402
+ var cut = (str2) => str2.replace(/^no$/, "");
403
+ var audio = new Audio();
404
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
405
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
406
+ var supportsMap = {
407
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
408
+ mpeg: canPlay("audio/mpeg;"),
409
+ opus: canPlay('audio/ogg; codecs="opus"'),
410
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
411
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
412
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
413
+ aac: canPlay("audio/aac;"),
414
+ caf: canPlay("audio/x-caf;"),
415
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
416
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
417
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
418
+ weba: canPlay('audio/webm; codecs="vorbis"'),
419
+ webm: canPlay('audio/webm; codecs="vorbis"'),
420
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
421
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
422
+ };
423
+
424
+ // src/image-formats.ts
425
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
426
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
427
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
428
+ var supportsFormat = (source) => {
429
+ const { promise, resolve } = Promise.withResolvers();
430
+ const img = Object.assign(document.createElement("img"), {
431
+ src: source
432
+ });
433
+ img.onload = img.onerror = () => {
434
+ resolve(img.height === 2);
435
+ };
436
+ return promise;
437
+ };
438
+ var supportsMap2 = {
439
+ avif: false,
440
+ jxl: false,
441
+ webp: false
442
+ };
443
+ var formatsMap = {
444
+ avif,
445
+ jxl,
446
+ webp
447
+ };
448
+ var loadImageFormatsSupport = async () => {
449
+ const promises = [];
450
+ for (const [format, source] of Object.entries(formatsMap)) {
451
+ const promise = supportsFormat(source).then((supported) => {
452
+ supportsMap2[format] = supported;
453
+ });
454
+ promises.push(promise);
455
+ }
456
+ await Promise.all(promises);
457
+ };
458
+ loadImageFormatsSupport();
459
+
460
+ // src/asset.ts
461
+ var getType = createMemoizedFunction((extensions) => {
462
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
463
+ return "audio";
464
+ }
465
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
466
+ return "image";
467
+ }
468
+ throw extensions;
469
+ });
470
+ var SUPPORT_MAPS = {
471
+ "image": supportsMap2,
472
+ "audio": supportsMap
473
+ };
474
+ var asset = createMemoizedFunction((...variants) => {
475
+ if (DEV && variants.length === 0) {
476
+ throw new Error(`Attempt to use "asset" function without arguments`);
477
+ }
478
+ const map = {};
479
+ const extensions = [];
480
+ for (const v of variants) {
481
+ const e = getUrlFileExtension(v);
482
+ map[e] = v;
483
+ extensions.push(e);
484
+ }
485
+ const type = getType(extensions);
486
+ const getSource = createMemoizedFunction(() => {
487
+ const support = SUPPORT_MAPS[type];
488
+ for (const extension of extensions) {
489
+ if (extension in support) {
490
+ if (support[extension]) {
491
+ return map[extension];
492
+ }
493
+ } else {
494
+ return map[extension];
495
+ }
496
+ }
497
+ if (DEV) {
498
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
499
+ }
500
+ return "";
501
+ });
502
+ return {
503
+ get source() {
504
+ return getSource();
505
+ },
506
+ get type() {
507
+ return type;
508
+ }
509
+ };
510
+ });
511
+ var isAsset = (suspect) => {
512
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
513
+ };
514
+
116
515
  // src/utils.ts
117
516
  var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
118
517
  return (action, props, { ctx, data }) => {
@@ -357,7 +756,7 @@ var Novely = (() => {
357
756
  const processedQueue = [];
358
757
  const keep = /* @__PURE__ */ new Set();
359
758
  const characters = /* @__PURE__ */ new Set();
360
- const audio = {
759
+ const audio2 = {
361
760
  music: /* @__PURE__ */ new Set(),
362
761
  sound: /* @__PURE__ */ new Set()
363
762
  };
@@ -382,14 +781,9 @@ var Novely = (() => {
382
781
  });
383
782
  if (notLatest) continue;
384
783
  } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
385
- let getNext = () => {
386
- const nextActions = next(i);
387
- getNext = () => {
388
- return nextActions;
389
- };
390
- return nextActions;
391
- };
392
- if (fn.skipOnRestore(getNext)) continue;
784
+ if (fn.skipOnRestore(() => next(i))) {
785
+ continue;
786
+ }
393
787
  }
394
788
  }
395
789
  processedQueue.push(item);
@@ -407,9 +801,9 @@ var Novely = (() => {
407
801
  if (action === "showCharacter") {
408
802
  characters.add(params[0]);
409
803
  } else if (action === "playMusic") {
410
- audio.music.add(params[0]);
804
+ audio2.music.add(unwrapAsset(params[0]));
411
805
  } else if (action === "playSound") {
412
- audio.sound.add(params[0]);
806
+ audio2.sound.add(unwrapAsset(params[0]));
413
807
  }
414
808
  processedQueue.push(item);
415
809
  } else if (action === "showBackground" || action === "preload") {
@@ -446,7 +840,7 @@ var Novely = (() => {
446
840
  keep: {
447
841
  keep,
448
842
  characters,
449
- audio
843
+ audio: audio2
450
844
  }
451
845
  };
452
846
  };
@@ -493,8 +887,8 @@ var Novely = (() => {
493
887
  var mapSet = (set, fn) => {
494
888
  return [...set].map(fn);
495
889
  };
496
- var isImageAsset = (asset) => {
497
- return isString(asset) && isCSSImage(asset);
890
+ var isImageAsset = (asset2) => {
891
+ return isString(asset2) && isCSSImage(asset2);
498
892
  };
499
893
  var getUrlFileExtension = (address) => {
500
894
  try {
@@ -520,37 +914,35 @@ var Novely = (() => {
520
914
  return "";
521
915
  }
522
916
  };
523
- var getResourseType = async (request, url) => {
524
- if (RESOURCE_TYPE_CACHE.has(url)) {
525
- return RESOURCE_TYPE_CACHE.get(url);
526
- }
527
- const encache = (value) => {
528
- RESOURCE_TYPE_CACHE.set(url, value);
529
- return value;
530
- };
531
- if (!isCSSImage(url)) {
532
- return encache("other");
533
- }
534
- const extension = getUrlFileExtension(url);
535
- if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
536
- return encache("audio");
537
- }
538
- if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
539
- return encache("image");
540
- }
541
- const contentType = await fetchContentType(request, url);
542
- if (contentType.includes("audio")) {
543
- return encache("audio");
544
- }
545
- if (contentType.includes("image")) {
546
- return encache("image");
917
+ var getResourseType = createMemoizedFunction(
918
+ async (request, url) => {
919
+ if (!isCSSImage(url)) {
920
+ return "other";
921
+ }
922
+ const extension = getUrlFileExtension(url);
923
+ if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
924
+ return "audio";
925
+ }
926
+ if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
927
+ return "image";
928
+ }
929
+ const contentType = await fetchContentType(request, url);
930
+ if (contentType.includes("audio")) {
931
+ return "audio";
932
+ }
933
+ if (contentType.includes("image")) {
934
+ return "image";
935
+ }
936
+ return "other";
937
+ },
938
+ {
939
+ isPromise: true
547
940
  }
548
- return encache("other");
549
- };
941
+ );
550
942
  var capitalize = (str2) => {
551
943
  return str2[0].toUpperCase() + str2.slice(1);
552
944
  };
553
- var getIntlLanguageDisplayName = (lang) => {
945
+ var getIntlLanguageDisplayName = createMemoizedFunction((lang) => {
554
946
  try {
555
947
  const intl = new Intl.DisplayNames([lang], {
556
948
  type: "language"
@@ -559,7 +951,7 @@ var Novely = (() => {
559
951
  } catch {
560
952
  return lang;
561
953
  }
562
- };
954
+ });
563
955
  var createReferFunction = (story) => {
564
956
  const refer = (path) => {
565
957
  let current = story;
@@ -703,6 +1095,29 @@ var Novely = (() => {
703
1095
  }
704
1096
  return collection;
705
1097
  };
1098
+ var unwrapAsset = (asset2) => {
1099
+ return isAsset(asset2) ? asset2.source : asset2;
1100
+ };
1101
+ var handleAudioAsset = (asset2) => {
1102
+ if (DEV && isAsset(asset2) && asset2.type !== "audio") {
1103
+ throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
1104
+ }
1105
+ return unwrapAsset(asset2);
1106
+ };
1107
+ var handleImageAsset = (asset2) => {
1108
+ if (DEV && isAsset(asset2) && asset2.type !== "image") {
1109
+ throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
1110
+ }
1111
+ return unwrapAsset(asset2);
1112
+ };
1113
+ var getCharactersData = (characters) => {
1114
+ const entries = Object.entries(characters);
1115
+ const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
1116
+ return Object.fromEntries(mapped);
1117
+ };
1118
+ var toArray = (target) => {
1119
+ return Array.isArray(target) ? target : [target];
1120
+ };
706
1121
 
707
1122
  // ../../node_modules/.pnpm/dequal@2.0.3/node_modules/dequal/lite/index.mjs
708
1123
  var has = Object.prototype.hasOwnProperty;
@@ -1105,6 +1520,66 @@ var Novely = (() => {
1105
1520
  }
1106
1521
  }
1107
1522
 
1523
+ // src/preloading.ts
1524
+ var enqueueAssetForPreloading = (asset2) => {
1525
+ if (!PRELOADED_ASSETS.has(asset2)) {
1526
+ ASSETS_TO_PRELOAD.add(asset2);
1527
+ }
1528
+ };
1529
+ var handleAssetsPreloading = async ({ request, limiter, preloadAudioBlocking, preloadImageBlocking }) => {
1530
+ const list = mapSet(ASSETS_TO_PRELOAD, (asset2) => {
1531
+ return limiter(async () => {
1532
+ const type = await getResourseType(request, asset2);
1533
+ switch (type) {
1534
+ case "audio": {
1535
+ await preloadAudioBlocking(asset2);
1536
+ break;
1537
+ }
1538
+ case "image": {
1539
+ await preloadImageBlocking(asset2);
1540
+ break;
1541
+ }
1542
+ }
1543
+ ASSETS_TO_PRELOAD.delete(asset2);
1544
+ PRELOADED_ASSETS.add(asset2);
1545
+ });
1546
+ });
1547
+ await Promise.allSettled(list);
1548
+ ASSETS_TO_PRELOAD.clear();
1549
+ };
1550
+ var huntAssets = ({ characters, action, props, handle }) => {
1551
+ if (action === "showBackground") {
1552
+ if (isString(props[0])) {
1553
+ handle(handleAudioAsset(props[0]));
1554
+ }
1555
+ if (props[0] && typeof props[0] === "object") {
1556
+ for (const value of Object.values(props[0])) {
1557
+ if (isImageAsset(value)) {
1558
+ handle(value);
1559
+ }
1560
+ }
1561
+ }
1562
+ return;
1563
+ }
1564
+ if (isAudioAction(action) && isString(props[0])) {
1565
+ handle(handleAudioAsset(props[0]));
1566
+ return;
1567
+ }
1568
+ if (action === "showCharacter" && isString(props[0]) && isString(props[1])) {
1569
+ const images = toArray(characters[props[0]].emotions[props[1]]);
1570
+ for (const asset2 of images) {
1571
+ handle(handleImageAsset(asset2));
1572
+ }
1573
+ return;
1574
+ }
1575
+ if (action === "custom" && props[0].assets && props[0].assets.length > 0) {
1576
+ for (const asset2 of props[0].assets) {
1577
+ handle(asset2);
1578
+ }
1579
+ return;
1580
+ }
1581
+ };
1582
+
1108
1583
  // src/novely.ts
1109
1584
  var novely = ({
1110
1585
  characters,
@@ -1134,38 +1609,12 @@ var Novely = (() => {
1134
1609
  const limitAssetsDownload = pLimit(parallelAssetsDownloadLimit);
1135
1610
  const story = {};
1136
1611
  const times = /* @__PURE__ */ new Set();
1137
- const ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
1138
1612
  const dataLoaded = createControlledPromise();
1139
1613
  let initialScreenWasShown = false;
1140
1614
  let destroyed = false;
1141
1615
  const intime = (value) => {
1142
1616
  return times.add(value), value;
1143
1617
  };
1144
- const handleAssetsPreloading = async () => {
1145
- const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
1146
- const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
1147
- return limitAssetsDownload(async () => {
1148
- const type = await getResourseType(request, asset);
1149
- switch (type) {
1150
- case "audio": {
1151
- await preloadAudioBlocking(asset);
1152
- break;
1153
- }
1154
- case "image": {
1155
- await preloadImageBlocking(asset);
1156
- break;
1157
- }
1158
- }
1159
- });
1160
- });
1161
- await Promise.allSettled(list);
1162
- ASSETS_TO_PRELOAD.clear();
1163
- };
1164
- const assetsToPreloadAdd = (asset) => {
1165
- if (!PRELOADED_ASSETS.has(asset)) {
1166
- ASSETS_TO_PRELOAD.add(asset);
1167
- }
1168
- };
1169
1618
  const scriptBase = async (part) => {
1170
1619
  if (destroyed) return;
1171
1620
  Object.assign(story, flattenStory(part));
@@ -1178,7 +1627,11 @@ var Novely = (() => {
1178
1627
  if (!loadingIsShown) {
1179
1628
  renderer.ui.showLoading();
1180
1629
  }
1181
- await handleAssetsPreloading();
1630
+ await handleAssetsPreloading({
1631
+ ...renderer.misc,
1632
+ limiter: limitAssetsDownload,
1633
+ request
1634
+ });
1182
1635
  }
1183
1636
  await dataLoaded.promise;
1184
1637
  renderer.ui.hideLoading();
@@ -1194,40 +1647,6 @@ var Novely = (() => {
1194
1647
  const script = (part) => {
1195
1648
  return limitScript(() => scriptBase(part));
1196
1649
  };
1197
- const assetPreloadingCheck = (action2, props, doAction = assetsToPreloadAdd) => {
1198
- if (action2 === "showBackground") {
1199
- if (isImageAsset(props[0])) {
1200
- doAction(props[0]);
1201
- }
1202
- if (props[0] && typeof props[0] === "object") {
1203
- for (const value of Object.values(props[0])) {
1204
- if (!isImageAsset(value)) continue;
1205
- doAction(value);
1206
- }
1207
- }
1208
- return;
1209
- }
1210
- if (isAudioAction(action2) && isString(props[0])) {
1211
- doAction(props[0]);
1212
- return;
1213
- }
1214
- if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
1215
- const images = characters[props[0]].emotions[props[1]];
1216
- if (Array.isArray(images)) {
1217
- for (const asset of images) {
1218
- doAction(asset);
1219
- }
1220
- } else {
1221
- doAction(images);
1222
- }
1223
- return;
1224
- }
1225
- if (action2 === "custom" && props[0].assets && props[0].assets.length > 0) {
1226
- for (const asset of props[0].assets) {
1227
- doAction(asset);
1228
- }
1229
- }
1230
- };
1231
1650
  const action = new Proxy({}, {
1232
1651
  get(_, action2) {
1233
1652
  if (action2 in renderer.actions) {
@@ -1235,7 +1654,7 @@ var Novely = (() => {
1235
1654
  }
1236
1655
  return (...props) => {
1237
1656
  if (preloadAssets === "blocking") {
1238
- assetPreloadingCheck(action2, props);
1657
+ huntAssets({ characters, action: action2, props, handle: enqueueAssetForPreloading });
1239
1658
  }
1240
1659
  return [action2, ...props];
1241
1660
  };
@@ -1393,7 +1812,7 @@ var Novely = (() => {
1393
1812
  const [path] = stack.value = latest;
1394
1813
  renderer.ui.showScreen("game");
1395
1814
  const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
1396
- const { run, keep: { keep, characters: characters2, audio } } = createQueueProcessor(queue, {
1815
+ const { run, keep: { keep, characters: characters2, audio: audio2 } } = createQueueProcessor(queue, {
1397
1816
  skip,
1398
1817
  skipPreserve
1399
1818
  });
@@ -1411,7 +1830,7 @@ var Novely = (() => {
1411
1830
  }
1412
1831
  }
1413
1832
  if (context.meta.goingBack) {
1414
- match("clear", [keep, characters2, audio], {
1833
+ match("clear", [keep, characters2, audio2], {
1415
1834
  ctx: context,
1416
1835
  data: latest[1]
1417
1836
  });
@@ -1498,7 +1917,7 @@ var Novely = (() => {
1498
1917
  if (isAudioAction(action2)) return;
1499
1918
  if (action2 === "vibrate") return;
1500
1919
  if (action2 === "end") return;
1501
- assetPreloadingCheck(action2, props, assets.push.bind(assets));
1920
+ huntAssets({ characters, action: action2, props, handle: assets.push.bind(assets) });
1502
1921
  return match(action2, props, {
1503
1922
  ctx,
1504
1923
  data: data2
@@ -1541,9 +1960,15 @@ var Novely = (() => {
1541
1960
  const getResourseTypeForRenderer = (url) => {
1542
1961
  return getResourseType(request, url);
1543
1962
  };
1963
+ const getCharacterColor = (c) => {
1964
+ return c in characters ? characters[c].color : "#000000";
1965
+ };
1966
+ const getCharacterAssets = (character, emotion) => {
1967
+ return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1968
+ };
1544
1969
  const renderer = createRenderer({
1545
1970
  mainContextKey: MAIN_CONTEXT_KEY,
1546
- characters,
1971
+ characters: getCharactersData(characters),
1547
1972
  characterAssetSizes,
1548
1973
  set,
1549
1974
  restore,
@@ -1560,6 +1985,8 @@ var Novely = (() => {
1560
1985
  storageData,
1561
1986
  coreData,
1562
1987
  getLanguageDisplayName,
1988
+ getCharacterColor,
1989
+ getCharacterAssets,
1563
1990
  getResourseType: getResourseTypeForRenderer
1564
1991
  });
1565
1992
  const useStack = createUseStackFunction(renderer);
@@ -1600,26 +2027,12 @@ var Novely = (() => {
1600
2027
  refer
1601
2028
  });
1602
2029
  for (const [action3, ...props2] of collection) {
1603
- assetPreloadingCheck(action3, props2);
2030
+ huntAssets({ characters, action: action3, props: props2, handle: enqueueAssetForPreloading });
1604
2031
  }
1605
- const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
1606
- ASSETS_TO_PRELOAD.forEach(async (asset) => {
1607
- ASSETS_TO_PRELOAD.delete(asset);
1608
- const type = await getResourseType(request, asset);
1609
- switch (type) {
1610
- case "audio": {
1611
- preloadAudioBlocking(asset).then(() => {
1612
- PRELOADED_ASSETS.add(asset);
1613
- });
1614
- break;
1615
- }
1616
- case "image": {
1617
- preloadImageBlocking(asset).then(() => {
1618
- PRELOADED_ASSETS.add(asset);
1619
- });
1620
- break;
1621
- }
1622
- }
2032
+ handleAssetsPreloading({
2033
+ ...renderer.misc,
2034
+ request,
2035
+ limiter: limitAssetsDownload
1623
2036
  });
1624
2037
  } catch (cause) {
1625
2038
  console.error(cause);
@@ -1632,41 +2045,47 @@ var Novely = (() => {
1632
2045
  setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
1633
2046
  },
1634
2047
  showBackground({ ctx, push }, [background]) {
1635
- ctx.background(background);
2048
+ if (isString(background) || isAsset(background)) {
2049
+ ctx.background({
2050
+ "all": handleImageAsset(background)
2051
+ });
2052
+ } else {
2053
+ ctx.background(Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)])));
2054
+ }
1636
2055
  push();
1637
2056
  },
1638
2057
  playMusic({ ctx, push }, [source]) {
1639
- ctx.audio.music(source, "music").play(true);
2058
+ ctx.audio.music(handleAudioAsset(source), "music").play(true);
1640
2059
  push();
1641
2060
  },
1642
2061
  pauseMusic({ ctx, push }, [source]) {
1643
- ctx.audio.music(source, "music").pause();
2062
+ ctx.audio.music(handleAudioAsset(source), "music").pause();
1644
2063
  push();
1645
2064
  },
1646
2065
  stopMusic({ ctx, push }, [source]) {
1647
- ctx.audio.music(source, "music").stop();
2066
+ ctx.audio.music(handleAudioAsset(source), "music").stop();
1648
2067
  push();
1649
2068
  },
1650
2069
  playSound({ ctx, push }, [source, loop]) {
1651
- ctx.audio.music(source, "sound").play(loop || false);
2070
+ ctx.audio.music(handleAudioAsset(source), "sound").play(loop || false);
1652
2071
  push();
1653
2072
  },
1654
2073
  pauseSound({ ctx, push }, [source]) {
1655
- ctx.audio.music(source, "sound").pause();
2074
+ ctx.audio.music(handleAudioAsset(source), "sound").pause();
1656
2075
  push();
1657
2076
  },
1658
2077
  stopSound({ ctx, push }, [source]) {
1659
- ctx.audio.music(source, "sound").stop();
2078
+ ctx.audio.music(handleAudioAsset(source), "sound").stop();
1660
2079
  push();
1661
2080
  },
1662
2081
  voice({ ctx, push }, [source]) {
1663
2082
  const [lang] = storageData.get().meta;
1664
- const audioSource = isString(source) ? source : source[lang];
2083
+ const audioSource = isString(source) ? source : isAsset(source) ? source : source[lang];
1665
2084
  if (!audioSource) {
1666
2085
  push();
1667
2086
  return;
1668
2087
  }
1669
- ctx.audio.voice(audioSource);
2088
+ ctx.audio.voice(handleAudioAsset(audioSource));
1670
2089
  push();
1671
2090
  },
1672
2091
  stopVoice({ ctx, push }) {
@@ -1789,12 +2208,12 @@ var Novely = (() => {
1789
2208
  data: data2
1790
2209
  });
1791
2210
  },
1792
- clear({ ctx, push }, [keep, characters2, audio]) {
2211
+ clear({ ctx, push }, [keep, characters2, audio2]) {
1793
2212
  ctx.vibrate(0);
1794
2213
  ctx.clear(
1795
2214
  keep || EMPTY_SET,
1796
2215
  characters2 || EMPTY_SET,
1797
- audio || { music: EMPTY_SET, sounds: EMPTY_SET },
2216
+ audio2 || { music: EMPTY_SET, sounds: EMPTY_SET },
1798
2217
  push
1799
2218
  );
1800
2219
  },
@@ -1983,7 +2402,7 @@ var Novely = (() => {
1983
2402
  const setStorageData = (data2) => {
1984
2403
  if (destroyed) {
1985
2404
  if (DEV) {
1986
- throw new Error(`function \`setStorageData\` was called after novely instance was destroyed.`);
2405
+ throw new Error(`function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`);
1987
2406
  }
1988
2407
  return;
1989
2408
  }