@novely/core 0.45.2 → 0.46.0-next.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.js CHANGED
@@ -13,30 +13,30 @@ function set(obj, key, val) {
13
13
  }
14
14
  function klona(x) {
15
15
  if (typeof x !== "object") return x;
16
- var i = 0, k, list, tmp, str2 = Object.prototype.toString.call(x);
17
- if (str2 === "[object Object]") {
16
+ var i = 0, k, list, tmp, str = Object.prototype.toString.call(x);
17
+ if (str === "[object Object]") {
18
18
  tmp = Object.create(x.__proto__ || null);
19
- } else if (str2 === "[object Array]") {
19
+ } else if (str === "[object Array]") {
20
20
  tmp = Array(x.length);
21
- } else if (str2 === "[object Set]") {
21
+ } else if (str === "[object Set]") {
22
22
  tmp = /* @__PURE__ */ new Set();
23
23
  x.forEach(function(val) {
24
24
  tmp.add(klona(val));
25
25
  });
26
- } else if (str2 === "[object Map]") {
26
+ } else if (str === "[object Map]") {
27
27
  tmp = /* @__PURE__ */ new Map();
28
28
  x.forEach(function(val, key) {
29
29
  tmp.set(klona(key), klona(val));
30
30
  });
31
- } else if (str2 === "[object Date]") {
31
+ } else if (str === "[object Date]") {
32
32
  tmp = /* @__PURE__ */ new Date(+x);
33
- } else if (str2 === "[object RegExp]") {
33
+ } else if (str === "[object RegExp]") {
34
34
  tmp = new RegExp(x.source, x.flags);
35
- } else if (str2 === "[object DataView]") {
35
+ } else if (str === "[object DataView]") {
36
36
  tmp = new x.constructor(klona(x.buffer));
37
- } else if (str2 === "[object ArrayBuffer]") {
37
+ } else if (str === "[object ArrayBuffer]") {
38
38
  tmp = x.slice(0);
39
- } else if (str2.slice(-6) === "Array]") {
39
+ } else if (str.slice(-6) === "Array]") {
40
40
  tmp = new x.constructor(x);
41
41
  }
42
42
  if (tmp) {
@@ -54,33 +54,6 @@ function klona(x) {
54
54
  // src/novely.ts
55
55
  import pLimit from "p-limit";
56
56
 
57
- // src/asset.ts
58
- import { memoize as memoize2, once } from "es-toolkit/function";
59
- import { DEV as DEV2 } from "esm-env";
60
-
61
- // src/audio-codecs.ts
62
- var cut = (str2) => str2.replace(/^no$/, "");
63
- var audio = new Audio();
64
- var canPlay = (type) => !!cut(audio.canPlayType(type));
65
- var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
66
- var supportsMap = {
67
- mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
68
- mpeg: canPlay("audio/mpeg;"),
69
- opus: canPlay('audio/ogg; codecs="opus"'),
70
- ogg: canPlay('audio/ogg; codecs="vorbis"'),
71
- oga: canPlay('audio/ogg; codecs="vorbis"'),
72
- wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
73
- aac: canPlay("audio/aac;"),
74
- caf: canPlay("audio/x-caf;"),
75
- m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
76
- m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
77
- mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
78
- weba: canPlay('audio/webm; codecs="vorbis"'),
79
- webm: canPlay('audio/webm; codecs="vorbis"'),
80
- dolby: canPlay('audio/mp4; codecs="ec-3"'),
81
- flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
82
- };
83
-
84
57
  // src/constants.ts
85
58
  var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
86
59
  var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
@@ -121,78 +94,7 @@ var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
121
94
  ]);
122
95
  var MAIN_CONTEXT_KEY = "$MAIN";
123
96
 
124
- // src/image-formats.ts
125
- var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
126
- var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
127
- var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
128
- var supportsFormat = (source) => {
129
- const { promise, resolve } = Promise.withResolvers();
130
- const img = Object.assign(document.createElement("img"), {
131
- src: source
132
- });
133
- img.onload = img.onerror = () => {
134
- resolve(img.height === 2);
135
- };
136
- return promise;
137
- };
138
- var supportsMap2 = {
139
- avif: false,
140
- jxl: false,
141
- webp: false
142
- };
143
- var formatsMap = {
144
- avif,
145
- jxl,
146
- webp
147
- };
148
- var loadImageFormatsSupport = async () => {
149
- const promises = [];
150
- for (const [format, source] of Object.entries(formatsMap)) {
151
- const promise = supportsFormat(source).then((supported) => {
152
- supportsMap2[format] = supported;
153
- });
154
- promises.push(promise);
155
- }
156
- await Promise.all(promises);
157
- };
158
- loadImageFormatsSupport();
159
-
160
- // src/utils.ts
161
- import { memoize } from "es-toolkit/function";
162
- import { DEV } from "esm-env";
163
-
164
- // src/shared.ts
165
- var STACK_MAP = /* @__PURE__ */ new Map();
166
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
167
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
168
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
169
-
170
- // src/utils.ts
171
- var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
172
- return (action, props, { ctx, data }) => {
173
- const context = typeof ctx === "string" ? getContext(ctx) : ctx;
174
- onBeforeActionCall({
175
- action,
176
- props,
177
- ctx: context
178
- });
179
- return values[action](
180
- {
181
- ctx: context,
182
- data,
183
- push() {
184
- if (context.meta.preview) return;
185
- push(context);
186
- },
187
- forward() {
188
- if (context.meta.preview) return;
189
- forward(context);
190
- }
191
- },
192
- props
193
- );
194
- };
195
- };
97
+ // src/utilities/assertions.ts
196
98
  var isNumber = (val) => {
197
99
  return typeof val === "number";
198
100
  };
@@ -211,54 +113,13 @@ var isPromise = (val) => {
211
113
  var isEmpty = (val) => {
212
114
  return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
213
115
  };
214
- var isCSSImage = (str2) => {
215
- const startsWith = String.prototype.startsWith.bind(str2);
116
+ var isCSSImageURL = (url) => {
117
+ const startsWith = String.prototype.startsWith.bind(url);
216
118
  return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
217
119
  };
218
- var str = String;
219
120
  var isUserRequiredAction = ([action, ...meta]) => {
220
121
  return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
221
122
  };
222
- var getLanguage = (languages) => {
223
- let { language } = navigator;
224
- if (languages.includes(language)) {
225
- return language;
226
- } else if (languages.includes(language = language.slice(0, 2))) {
227
- return language;
228
- } else if (language = languages.find((value) => navigator.languages.includes(value))) {
229
- return language;
230
- }
231
- return languages[0];
232
- };
233
- var createControlledPromise = () => {
234
- const object = {
235
- resolve: null,
236
- reject: null,
237
- promise: null,
238
- cancel: null
239
- };
240
- const init = () => {
241
- const promise = new Promise((resolve, reject) => {
242
- object.reject = reject;
243
- object.resolve = (value) => {
244
- resolve({ cancelled: false, value });
245
- };
246
- object.cancel = () => {
247
- resolve({ cancelled: true, value: null });
248
- init();
249
- };
250
- });
251
- object.promise = promise;
252
- };
253
- return init(), object;
254
- };
255
- var findLastPathItemBeforeItemOfType = (path, name) => {
256
- const item = path.findLast(([_name, _value], i, array) => {
257
- const next = array[i + 1];
258
- return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
259
- });
260
- return item;
261
- };
262
123
  var isBlockStatement = (statement) => {
263
124
  return BLOCK_STATEMENTS.has(statement);
264
125
  };
@@ -271,305 +132,68 @@ var isSkippedDuringRestore = (item) => {
271
132
  var isAudioAction = (action) => {
272
133
  return AUDIO_ACTIONS.has(action);
273
134
  };
274
- var noop = () => {
275
- };
276
135
  var isAction = (element) => {
277
136
  return Array.isArray(element) && isString(element[0]);
278
137
  };
279
- var flatActions = (item) => {
280
- return item.flatMap((data) => {
281
- const type = data[0];
282
- if (Array.isArray(type)) return flatActions(data);
283
- return [data];
284
- });
285
- };
286
- var flattenStory = (story) => {
287
- const entries = Object.entries(story).map(([name, items]) => {
288
- return [name, flatActions(items)];
289
- });
290
- return Object.fromEntries(entries);
138
+ var isImageAsset = (asset2) => {
139
+ return isString(asset2) && isCSSImageURL(asset2);
291
140
  };
292
- var isExitImpossible = (path) => {
293
- const blockStatements = path.filter(([item]) => isBlockStatement(item));
294
- const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
295
- if (blockStatements.length === 0 && blockExitStatements.length === 0) {
296
- return true;
297
- }
298
- if (blockStatements.length > blockExitStatements.length) {
299
- return false;
300
- }
301
- return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
141
+ var isBlockingAction = (action) => {
142
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
302
143
  };
303
- var getOppositeAction = (action) => {
304
- const MAP = {
305
- showCharacter: "hideCharacter",
306
- playSound: "stopSound",
307
- playMusic: "stopMusic",
308
- voice: "stopVoice"
309
- };
310
- return MAP[action];
144
+ var isAsset = (suspect) => {
145
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
311
146
  };
312
- var getActionsFromPath = (story, path, filter) => {
313
- let current = story;
314
- let precurrent;
315
- let ignoreNestedBefore = null;
316
- let index = 0;
317
- let skipPreserve = void 0;
318
- const skip = /* @__PURE__ */ new Set();
319
- const max = path.reduce((acc, [type, val]) => {
320
- if (isNull(type) && isNumber(val)) {
321
- return acc + 1;
322
- }
323
- return acc;
324
- }, 0);
325
- const queue = [];
326
- const blocks = [];
327
- for (const [type, val] of path) {
328
- if (type === "jump") {
329
- precurrent = story;
330
- current = current[val];
331
- } else if (type === null) {
332
- precurrent = current;
333
- if (isNumber(val)) {
334
- index++;
335
- let startIndex = 0;
336
- if (ignoreNestedBefore) {
337
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), ignoreNestedBefore);
338
- if (prev) {
339
- startIndex = prev[1];
340
- ignoreNestedBefore = null;
341
- }
342
- }
343
- for (let i = startIndex; i <= val; i++) {
344
- const item = current[i];
345
- if (!isAction(item)) continue;
346
- const [action] = item;
347
- const last = index === max && i === val;
348
- const shouldSkip = isSkippedDuringRestore(action) || isUserRequiredAction(item);
349
- if (shouldSkip) {
350
- skip.add(item);
351
- }
352
- if (shouldSkip && last) {
353
- skipPreserve = item;
354
- }
355
- if (filter && shouldSkip && !last) {
356
- continue;
357
- } else {
358
- queue.push(item);
359
- }
147
+
148
+ // src/utilities/match-action.ts
149
+ var matchAction = (callbacks, values) => {
150
+ const { getContext, onBeforeActionCall, push, forward } = callbacks;
151
+ return (action, props, { ctx, data }) => {
152
+ const context = typeof ctx === "string" ? getContext(ctx) : ctx;
153
+ onBeforeActionCall({
154
+ action,
155
+ props,
156
+ ctx: context
157
+ });
158
+ return values[action](
159
+ {
160
+ ctx: context,
161
+ data,
162
+ push() {
163
+ if (context.meta.preview) return;
164
+ push(context);
165
+ },
166
+ forward() {
167
+ if (context.meta.preview) return;
168
+ forward(context);
360
169
  }
361
- }
362
- current = current[val];
363
- } else if (type === "choice") {
364
- blocks.push(precurrent);
365
- current = current[val + 1][1];
366
- } else if (type === "condition") {
367
- blocks.push(precurrent);
368
- current = current[2][val];
369
- } else if (type === "block") {
370
- blocks.push(precurrent);
371
- current = story[val];
372
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
373
- current = blocks.pop();
374
- ignoreNestedBefore = type.slice(0, -5);
375
- }
376
- }
377
- return {
378
- queue,
379
- skip,
380
- skipPreserve
170
+ },
171
+ props
172
+ );
381
173
  };
382
174
  };
383
- var createQueueProcessor = (queue, options) => {
384
- const processedQueue = [];
385
- const keep = /* @__PURE__ */ new Set();
386
- const characters = /* @__PURE__ */ new Set();
387
- const audio2 = {
388
- music: /* @__PURE__ */ new Set(),
389
- sound: /* @__PURE__ */ new Set()
390
- };
391
- const next = (i) => queue.slice(i + 1);
392
- for (const [i, item] of queue.entries()) {
393
- const [action, ...params] = item;
394
- if (options.skip.has(item) && item !== options.skipPreserve) {
395
- continue;
396
- }
397
- keep.add(action);
398
- if (action === "function" || action === "custom") {
399
- if (action === "custom") {
400
- const fn = params[0];
401
- if ("callOnlyLatest" in fn && fn.callOnlyLatest) {
402
- const notLatest = next(i).some(([, func]) => {
403
- if (!isFunction(func)) return false;
404
- const c0 = func;
405
- const c1 = fn;
406
- const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
407
- const isIdenticalByReference = c0 === c1;
408
- return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
409
- });
410
- if (notLatest) continue;
411
- } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
412
- if (fn.skipOnRestore(() => next(i))) {
413
- continue;
414
- }
415
- }
416
- }
417
- processedQueue.push(item);
418
- } else if (action === "showCharacter" || action === "playSound" || action === "playMusic" || action === "voice") {
419
- const closing = getOppositeAction(action);
420
- const skip = next(i).some(([_action, target]) => {
421
- if (target !== params[0] && action !== "voice") {
422
- return false;
423
- }
424
- const musicGonnaBePaused = action === "playMusic" && _action === "pauseMusic";
425
- const soundGonnaBePaused = action === "playSound" && _action === "pauseSound";
426
- return musicGonnaBePaused || soundGonnaBePaused || _action === closing || _action === action;
427
- });
428
- if (skip) continue;
429
- if (action === "showCharacter") {
430
- characters.add(params[0]);
431
- } else if (action === "playMusic") {
432
- audio2.music.add(unwrapAsset(params[0]));
433
- } else if (action === "playSound") {
434
- audio2.sound.add(unwrapAsset(params[0]));
435
- }
436
- processedQueue.push(item);
437
- } else if (action === "showBackground" || action === "preload") {
438
- const skip = next(i).some(([_action]) => action === _action);
439
- if (skip) continue;
440
- processedQueue.push(item);
441
- } else if (action === "animateCharacter") {
442
- const skip = next(i).some(([_action, character], j, array) => {
443
- if (action === _action && character === params[0]) {
444
- return true;
445
- }
446
- const next2 = array.slice(j);
447
- const characterWillAnimate = next2.some(([__action, __character]) => action === __action);
448
- const hasBlockingActions = next2.some((item2) => options.skip.has(item2));
449
- return characterWillAnimate && hasBlockingActions;
450
- });
451
- if (skip) continue;
452
- processedQueue.push(item);
453
- } else {
454
- processedQueue.push(item);
455
- }
175
+
176
+ // src/utilities/ungrupped.ts
177
+ import { memoize } from "es-toolkit/function";
178
+ import { DEV } from "esm-env";
179
+ var getLanguage = (languages) => {
180
+ let { language } = navigator;
181
+ if (languages.includes(language)) {
182
+ return language;
183
+ } else if (languages.includes(language = language.slice(0, 2))) {
184
+ return language;
185
+ } else if (language = languages.find((value) => navigator.languages.includes(value))) {
186
+ return language;
456
187
  }
457
- const run = async (match) => {
458
- for await (const item of processedQueue) {
459
- const result = match(item);
460
- if (isPromise(result)) {
461
- await result;
462
- }
463
- }
464
- processedQueue.length = 0;
465
- };
466
- return {
467
- run,
468
- keep: {
469
- keep,
470
- characters,
471
- audio: audio2
472
- }
473
- };
188
+ return languages[0];
474
189
  };
475
- var getStack = memoize(
476
- (_) => {
477
- return [];
478
- },
479
- {
480
- cache: STACK_MAP,
481
- getCacheKey: (ctx) => ctx.id
482
- }
483
- );
484
- var createUseStackFunction = (renderer) => {
485
- const useStack = (context) => {
486
- const ctx = typeof context === "string" ? renderer.getContext(context) : context;
487
- const stack = getStack(ctx);
488
- return {
489
- get previous() {
490
- return stack.previous;
491
- },
492
- get value() {
493
- return stack.at(-1);
494
- },
495
- set value(value) {
496
- stack[stack.length - 1] = value;
497
- },
498
- back() {
499
- if (stack.length > 1) {
500
- stack.previous = stack.pop();
501
- ctx.meta.goingBack = true;
502
- }
503
- },
504
- push(value) {
505
- stack.push(value);
506
- },
507
- clear() {
508
- stack.previous = void 0;
509
- stack.length = 0;
510
- stack.length = 1;
511
- }
512
- };
513
- };
514
- return useStack;
190
+ var noop = () => {
515
191
  };
516
192
  var mapSet = (set2, fn) => {
517
193
  return [...set2].map(fn);
518
194
  };
519
- var isImageAsset = (asset2) => {
520
- return isString(asset2) && isCSSImage(asset2);
521
- };
522
- var getUrlFileExtension = (address) => {
523
- try {
524
- const { pathname } = new URL(address, location.href);
525
- return pathname.split(".").at(-1).split("!")[0].split(":")[0];
526
- } catch (error) {
527
- if (DEV) {
528
- console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
529
- }
530
- return "";
531
- }
532
- };
533
- var fetchContentType = async (url, request) => {
534
- try {
535
- const response = await request(url, {
536
- method: "HEAD"
537
- });
538
- return response.headers.get("Content-Type") || "";
539
- } catch (error) {
540
- if (DEV) {
541
- console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
542
- }
543
- return "";
544
- }
545
- };
546
- var getResourseType = memoize(
547
- async ({ url, request }) => {
548
- if (!isCSSImage(url)) {
549
- return "other";
550
- }
551
- const extension = getUrlFileExtension(url);
552
- if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
553
- return "audio";
554
- }
555
- if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
556
- return "image";
557
- }
558
- const contentType = await fetchContentType(url, request);
559
- if (contentType.includes("audio")) {
560
- return "audio";
561
- }
562
- if (contentType.includes("image")) {
563
- return "image";
564
- }
565
- return "other";
566
- },
567
- {
568
- getCacheKey: ({ url }) => url
569
- }
570
- );
571
- var capitalize = (str2) => {
572
- return str2[0].toUpperCase() + str2.slice(1);
195
+ var capitalize = (str) => {
196
+ return str[0].toUpperCase() + str.slice(1);
573
197
  };
574
198
  var getIntlLanguageDisplayName = memoize((lang) => {
575
199
  try {
@@ -581,6 +205,53 @@ var getIntlLanguageDisplayName = memoize((lang) => {
581
205
  return lang;
582
206
  }
583
207
  });
208
+ var unwrapAsset = (asset2) => {
209
+ return isAsset(asset2) ? asset2.source : asset2;
210
+ };
211
+ var handleAudioAsset = (asset2) => {
212
+ if (DEV && isAsset(asset2) && asset2.type !== "audio") {
213
+ throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
214
+ }
215
+ return unwrapAsset(asset2);
216
+ };
217
+ var handleImageAsset = (asset2) => {
218
+ if (DEV && isAsset(asset2) && asset2.type !== "image") {
219
+ throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
220
+ }
221
+ return unwrapAsset(asset2);
222
+ };
223
+ var getCharactersData = (characters) => {
224
+ const entries = Object.entries(characters);
225
+ const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
226
+ return Object.fromEntries(mapped);
227
+ };
228
+ var toArray = (target) => {
229
+ return Array.isArray(target) ? target : [target];
230
+ };
231
+ var getLanguageFromStore = (store2) => {
232
+ return store2.get().meta[0];
233
+ };
234
+ var getVolumeFromStore = (store2) => {
235
+ const { meta } = store2.get();
236
+ return {
237
+ music: meta[2],
238
+ sound: meta[3],
239
+ voice: meta[4]
240
+ };
241
+ };
242
+
243
+ // src/utilities/actions-processing.ts
244
+ var isExitImpossible = (path) => {
245
+ const blockStatements = path.filter(([item]) => isBlockStatement(item));
246
+ const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
247
+ if (blockStatements.length === 0 && blockExitStatements.length === 0) {
248
+ return true;
249
+ }
250
+ if (blockStatements.length > blockExitStatements.length) {
251
+ return false;
252
+ }
253
+ return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
254
+ };
584
255
  var createReferFunction = (story) => {
585
256
  const refer = (path) => {
586
257
  let current = story;
@@ -663,9 +334,6 @@ var nextPath = (path) => {
663
334
  }
664
335
  return path;
665
336
  };
666
- var isBlockingAction = (action) => {
667
- return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
668
- };
669
337
  var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
670
338
  const collection = [];
671
339
  let action = refer(path);
@@ -729,107 +397,328 @@ var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
729
397
  }
730
398
  return collection;
731
399
  };
732
- var unwrapAsset = (asset2) => {
733
- return isAsset(asset2) ? asset2.source : asset2;
400
+ var findLastPathItemBeforeItemOfType = (path, name) => {
401
+ const item = path.findLast(([_name, _value], i, array) => {
402
+ const next = array[i + 1];
403
+ return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
404
+ });
405
+ return item;
734
406
  };
735
- var handleAudioAsset = (asset2) => {
736
- if (DEV && isAsset(asset2) && asset2.type !== "audio") {
737
- throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
738
- }
739
- return unwrapAsset(asset2);
407
+ var getOppositeAction = (action) => {
408
+ const MAP = {
409
+ showCharacter: "hideCharacter",
410
+ playSound: "stopSound",
411
+ playMusic: "stopMusic",
412
+ voice: "stopVoice"
413
+ };
414
+ return MAP[action];
740
415
  };
741
- var handleImageAsset = (asset2) => {
742
- if (DEV && isAsset(asset2) && asset2.type !== "image") {
743
- throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
416
+ var getActionsFromPath = (story, path, filter) => {
417
+ let current = story;
418
+ let precurrent;
419
+ let ignoreNestedBefore = null;
420
+ let index = 0;
421
+ let skipPreserve = void 0;
422
+ const skip = /* @__PURE__ */ new Set();
423
+ const max = path.reduce((acc, [type, val]) => {
424
+ if (isNull(type) && isNumber(val)) {
425
+ return acc + 1;
426
+ }
427
+ return acc;
428
+ }, 0);
429
+ const queue = [];
430
+ const blocks = [];
431
+ for (const [type, val] of path) {
432
+ if (type === "jump") {
433
+ precurrent = story;
434
+ current = current[val];
435
+ } else if (type === null) {
436
+ precurrent = current;
437
+ if (isNumber(val)) {
438
+ index++;
439
+ let startIndex = 0;
440
+ if (ignoreNestedBefore) {
441
+ const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), ignoreNestedBefore);
442
+ if (prev) {
443
+ startIndex = prev[1];
444
+ ignoreNestedBefore = null;
445
+ }
446
+ }
447
+ for (let i = startIndex; i <= val; i++) {
448
+ const item = current[i];
449
+ if (!isAction(item)) continue;
450
+ const [action] = item;
451
+ const last = index === max && i === val;
452
+ const shouldSkip = isSkippedDuringRestore(action) || isUserRequiredAction(item);
453
+ if (shouldSkip) {
454
+ skip.add(item);
455
+ }
456
+ if (shouldSkip && last) {
457
+ skipPreserve = item;
458
+ }
459
+ if (filter && shouldSkip && !last) {
460
+ continue;
461
+ } else {
462
+ queue.push(item);
463
+ }
464
+ }
465
+ }
466
+ current = current[val];
467
+ } else if (type === "choice") {
468
+ blocks.push(precurrent);
469
+ current = current[val + 1][1];
470
+ } else if (type === "condition") {
471
+ blocks.push(precurrent);
472
+ current = current[2][val];
473
+ } else if (type === "block") {
474
+ blocks.push(precurrent);
475
+ current = story[val];
476
+ } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
477
+ current = blocks.pop();
478
+ ignoreNestedBefore = type.slice(0, -5);
479
+ }
744
480
  }
745
- return unwrapAsset(asset2);
746
- };
747
- var getCharactersData = (characters) => {
748
- const entries = Object.entries(characters);
749
- const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
750
- return Object.fromEntries(mapped);
751
- };
752
- var toArray = (target) => {
753
- return Array.isArray(target) ? target : [target];
754
- };
755
- var getLanguageFromStore = (store2) => {
756
- return store2.get().meta[0];
481
+ return {
482
+ queue,
483
+ skip,
484
+ skipPreserve
485
+ };
757
486
  };
758
- var getVolumeFromStore = (store2) => {
759
- const { meta } = store2.get();
487
+ var createQueueProcessor = (queue, options) => {
488
+ const processedQueue = [];
489
+ const keep = /* @__PURE__ */ new Set();
490
+ const characters = /* @__PURE__ */ new Set();
491
+ const audio2 = {
492
+ music: /* @__PURE__ */ new Set(),
493
+ sound: /* @__PURE__ */ new Set()
494
+ };
495
+ const next = (i) => queue.slice(i + 1);
496
+ for (const [i, item] of queue.entries()) {
497
+ const [action, ...params] = item;
498
+ if (options.skip.has(item) && item !== options.skipPreserve) {
499
+ continue;
500
+ }
501
+ keep.add(action);
502
+ if (action === "function" || action === "custom") {
503
+ if (action === "custom") {
504
+ const fn = params[0];
505
+ if ("callOnlyLatest" in fn && fn.callOnlyLatest) {
506
+ const notLatest = next(i).some(([, func]) => {
507
+ if (!isFunction(func)) return false;
508
+ const c0 = func;
509
+ const c1 = fn;
510
+ const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
511
+ const isIdenticalByReference = c0 === c1;
512
+ return isIdenticalID || isIdenticalByReference || String(c0) === String(c1);
513
+ });
514
+ if (notLatest) continue;
515
+ } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
516
+ if (fn.skipOnRestore(() => next(i))) {
517
+ continue;
518
+ }
519
+ }
520
+ }
521
+ processedQueue.push(item);
522
+ } else if (action === "showCharacter" || action === "playSound" || action === "playMusic" || action === "voice") {
523
+ const closing = getOppositeAction(action);
524
+ const skip = next(i).some(([_action, target]) => {
525
+ if (target !== params[0] && action !== "voice") {
526
+ return false;
527
+ }
528
+ const musicGonnaBePaused = action === "playMusic" && _action === "pauseMusic";
529
+ const soundGonnaBePaused = action === "playSound" && _action === "pauseSound";
530
+ return musicGonnaBePaused || soundGonnaBePaused || _action === closing || _action === action;
531
+ });
532
+ if (skip) continue;
533
+ if (action === "showCharacter") {
534
+ characters.add(params[0]);
535
+ } else if (action === "playMusic") {
536
+ audio2.music.add(unwrapAsset(params[0]));
537
+ } else if (action === "playSound") {
538
+ audio2.sound.add(unwrapAsset(params[0]));
539
+ }
540
+ processedQueue.push(item);
541
+ } else if (action === "showBackground" || action === "preload") {
542
+ const skip = next(i).some(([_action]) => action === _action);
543
+ if (skip) continue;
544
+ processedQueue.push(item);
545
+ } else if (action === "animateCharacter") {
546
+ const skip = next(i).some(([_action, character], j, array) => {
547
+ if (action === _action && character === params[0]) {
548
+ return true;
549
+ }
550
+ const next2 = array.slice(j);
551
+ const characterWillAnimate = next2.some(([__action, __character]) => action === __action);
552
+ const hasBlockingActions = next2.some((item2) => options.skip.has(item2));
553
+ return characterWillAnimate && hasBlockingActions;
554
+ });
555
+ if (skip) continue;
556
+ processedQueue.push(item);
557
+ } else {
558
+ processedQueue.push(item);
559
+ }
560
+ }
561
+ const run = async (match) => {
562
+ for await (const item of processedQueue) {
563
+ const result = match(item);
564
+ if (isPromise(result)) {
565
+ await result;
566
+ }
567
+ }
568
+ processedQueue.length = 0;
569
+ };
760
570
  return {
761
- music: meta[2],
762
- sound: meta[3],
763
- voice: meta[4]
571
+ run,
572
+ keep: {
573
+ keep,
574
+ characters,
575
+ audio: audio2
576
+ }
764
577
  };
765
578
  };
766
579
 
767
- // src/asset.ts
768
- var getType = memoize2(
769
- (extensions) => {
770
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
580
+ // src/utilities/controlled-promise.ts
581
+ var createControlledPromise = () => {
582
+ const object = {
583
+ resolve: null,
584
+ reject: null,
585
+ promise: null,
586
+ cancel: null
587
+ };
588
+ const init = () => {
589
+ const promise = new Promise((resolve, reject) => {
590
+ object.reject = reject;
591
+ object.resolve = (value) => {
592
+ resolve({ cancelled: false, value });
593
+ };
594
+ object.cancel = () => {
595
+ resolve({ cancelled: true, value: null });
596
+ init();
597
+ };
598
+ });
599
+ object.promise = promise;
600
+ };
601
+ return init(), object;
602
+ };
603
+
604
+ // src/utilities/resources.ts
605
+ import { memoize as memoize2 } from "es-toolkit/function";
606
+ import { DEV as DEV2 } from "esm-env";
607
+ var getUrlFileExtension = (address) => {
608
+ try {
609
+ const { pathname } = new URL(address, location.href);
610
+ return pathname.split(".").at(-1).split("!")[0].split(":")[0];
611
+ } catch (error) {
612
+ if (DEV2) {
613
+ console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
614
+ }
615
+ return "";
616
+ }
617
+ };
618
+ var fetchContentType = async (url, request) => {
619
+ try {
620
+ const response = await request(url, {
621
+ method: "HEAD"
622
+ });
623
+ return response.headers.get("Content-Type") || "";
624
+ } catch (error) {
625
+ if (DEV2) {
626
+ console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
627
+ }
628
+ return "";
629
+ }
630
+ };
631
+ var getResourseType = memoize2(
632
+ async ({ url, request }) => {
633
+ if (!isCSSImageURL(url)) {
634
+ return "other";
635
+ }
636
+ const extension = getUrlFileExtension(url);
637
+ if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
771
638
  return "audio";
772
639
  }
773
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
640
+ if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
774
641
  return "image";
775
642
  }
776
- throw extensions;
643
+ const contentType = await fetchContentType(url, request);
644
+ if (contentType.includes("audio")) {
645
+ return "audio";
646
+ }
647
+ if (contentType.includes("image")) {
648
+ return "image";
649
+ }
650
+ return "other";
777
651
  },
778
652
  {
779
- getCacheKey: (extensions) => extensions.join("~")
653
+ getCacheKey: ({ url }) => url
780
654
  }
781
655
  );
782
- var SUPPORT_MAPS = {
783
- image: supportsMap2,
784
- audio: supportsMap
785
- };
786
- var assetPrivate = memoize2(
787
- (variants) => {
788
- if (DEV2 && variants.length === 0) {
789
- throw new Error(`Attempt to use "asset" function without arguments`);
790
- }
791
- const map = {};
792
- const extensions = [];
793
- for (const v of variants) {
794
- const e = getUrlFileExtension(v);
795
- map[e] = v;
796
- extensions.push(e);
797
- }
798
- const type = getType(extensions);
799
- const getSource = once(() => {
800
- const support = SUPPORT_MAPS[type];
801
- for (const extension of extensions) {
802
- if (extension in support) {
803
- if (support[extension]) {
804
- return map[extension];
805
- }
806
- } else {
807
- return map[extension];
808
- }
809
- }
810
- if (DEV2) {
811
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
812
- }
813
- return "";
814
- });
815
- return {
816
- get source() {
817
- return getSource();
818
- },
819
- get type() {
820
- return type;
821
- }
822
- };
656
+
657
+ // src/utilities/stack.ts
658
+ import { memoize as memoize3 } from "es-toolkit/function";
659
+
660
+ // src/shared.ts
661
+ var STACK_MAP = /* @__PURE__ */ new Map();
662
+ var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
663
+ var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
664
+ var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
665
+
666
+ // src/utilities/stack.ts
667
+ var getStack = memoize3(
668
+ (_) => {
669
+ return [];
823
670
  },
824
671
  {
825
- getCacheKey: (variants) => variants.join("~")
672
+ cache: STACK_MAP,
673
+ getCacheKey: (ctx) => ctx.id
826
674
  }
827
675
  );
828
- var asset = (...variants) => {
829
- return assetPrivate(variants);
676
+ var createUseStackFunction = (renderer) => {
677
+ const useStack = (context) => {
678
+ const ctx = typeof context === "string" ? renderer.getContext(context) : context;
679
+ const stack = getStack(ctx);
680
+ return {
681
+ get previous() {
682
+ return stack.previous;
683
+ },
684
+ get value() {
685
+ return stack.at(-1);
686
+ },
687
+ set value(value) {
688
+ stack[stack.length - 1] = value;
689
+ },
690
+ back() {
691
+ if (stack.length > 1) {
692
+ stack.previous = stack.pop();
693
+ ctx.meta.goingBack = true;
694
+ }
695
+ },
696
+ push(value) {
697
+ stack.push(value);
698
+ },
699
+ clear() {
700
+ stack.previous = void 0;
701
+ stack.length = 0;
702
+ stack.length = 1;
703
+ }
704
+ };
705
+ };
706
+ return useStack;
830
707
  };
831
- var isAsset = (suspect) => {
832
- return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
708
+
709
+ // src/utilities/story.ts
710
+ var flatActions = (item) => {
711
+ return item.flatMap((data) => {
712
+ const type = data[0];
713
+ if (Array.isArray(type)) return flatActions(data);
714
+ return [data];
715
+ });
716
+ };
717
+ var flatStory = (story) => {
718
+ const entries = Object.entries(story).map(([name, items]) => {
719
+ return [name, flatActions(items)];
720
+ });
721
+ return Object.fromEntries(entries);
833
722
  };
834
723
 
835
724
  // src/browser.ts
@@ -1156,7 +1045,7 @@ var novely = ({
1156
1045
  };
1157
1046
  const scriptBase = async (part) => {
1158
1047
  if (destroyed) return;
1159
- Object.assign(story, flattenStory(part));
1048
+ Object.assign(story, flatStory(part));
1160
1049
  let loadingIsShown = false;
1161
1050
  if (!initialScreenWasShown) {
1162
1051
  renderer.ui.showLoading();
@@ -1235,7 +1124,8 @@ var novely = ({
1235
1124
  [null, 0]
1236
1125
  ],
1237
1126
  state,
1238
- [intime(Date.now()), "auto"]
1127
+ [intime(Date.now()), "auto"],
1128
+ []
1239
1129
  ];
1240
1130
  };
1241
1131
  const getLanguageWithoutParameters = () => {
@@ -1556,6 +1446,61 @@ var novely = ({
1556
1446
  const getCharacterAssets = (character, emotion) => {
1557
1447
  return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1558
1448
  };
1449
+ const getCharacterName = (character) => {
1450
+ const c = character;
1451
+ const cs = characters;
1452
+ const [lang] = storageData.get().meta;
1453
+ if (c && c in cs) {
1454
+ const block = cs[c].name;
1455
+ if (typeof block === "string") {
1456
+ return block;
1457
+ }
1458
+ if (lang in block) {
1459
+ return block[lang];
1460
+ }
1461
+ }
1462
+ return String(c) || "";
1463
+ };
1464
+ const getDialogOverview = () => {
1465
+ const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1466
+ const stateSnapshots = save2[3];
1467
+ const { queue } = getActionsFromPath(story, save2[0], false);
1468
+ const [lang] = storageData.get().meta;
1469
+ const dialogItem = [];
1470
+ for (let p = 0, i = 0; i < queue.length; i++) {
1471
+ const action2 = queue[i];
1472
+ if (action2[0] === "dialog") {
1473
+ const [_, name, text] = action2;
1474
+ let voice = void 0;
1475
+ for (let j = i - 1; j > p; j--) {
1476
+ const action3 = queue[j];
1477
+ if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1478
+ if (action3[0] === "stopVoice") break;
1479
+ if (action3[0] === "voice") {
1480
+ voice = action3[1];
1481
+ break;
1482
+ }
1483
+ }
1484
+ dialogItem.push({
1485
+ name,
1486
+ text,
1487
+ voice
1488
+ });
1489
+ p = i;
1490
+ }
1491
+ }
1492
+ const entries = dialogItem.map(({ name, text, voice }, i) => {
1493
+ const state = stateSnapshots[i];
1494
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1495
+ name = name ? getCharacterName(name) : "";
1496
+ return {
1497
+ name: templateReplace(name, state),
1498
+ text: templateReplace(text, state),
1499
+ voice: audioSource ? handleAudioAsset(audioSource) : ""
1500
+ };
1501
+ });
1502
+ return entries;
1503
+ };
1559
1504
  const renderer = createRenderer({
1560
1505
  mainContextKey: MAIN_CONTEXT_KEY,
1561
1506
  characters: getCharactersData(characters),
@@ -1577,6 +1522,7 @@ var novely = ({
1577
1522
  getLanguageDisplayName,
1578
1523
  getCharacterColor,
1579
1524
  getCharacterAssets,
1525
+ getDialogOverview,
1580
1526
  getResourseType: getResourseTypeForRenderer
1581
1527
  });
1582
1528
  const useStack = createUseStackFunction(renderer);
@@ -1639,9 +1585,9 @@ var novely = ({
1639
1585
  }
1640
1586
  };
1641
1587
  const match = matchAction(matchActionOptions, {
1642
- wait({ ctx, push }, [time]) {
1588
+ wait({ ctx, data: data2, push }, [time]) {
1643
1589
  if (ctx.meta.restoring) return;
1644
- setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
1590
+ setTimeout(push, isFunction(time) ? time(data2) : time);
1645
1591
  },
1646
1592
  showBackground({ ctx, push }, [background]) {
1647
1593
  if (isString(background) || isAsset(background)) {
@@ -1711,21 +1657,11 @@ var novely = ({
1711
1657
  ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
1712
1658
  },
1713
1659
  dialog({ ctx, data: data2, forward }, [character, content, emotion]) {
1714
- const name = (() => {
1715
- const c = character;
1716
- const cs = characters;
1717
- const [lang] = storageData.get().meta;
1718
- if (c && c in cs) {
1719
- const block = cs[c].name;
1720
- if (typeof block === "string") {
1721
- return block;
1722
- }
1723
- if (lang in block) {
1724
- return block[lang];
1725
- }
1726
- }
1727
- return c || "";
1728
- })();
1660
+ const name = getCharacterName(character);
1661
+ const stack = useStack(ctx);
1662
+ if (!ctx.meta.restoring && !ctx.meta.goingBack) {
1663
+ stack.value[3].push(clone(data2));
1664
+ }
1729
1665
  ctx.clearBlockingActions("dialog");
1730
1666
  ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
1731
1667
  },
@@ -1752,19 +1688,19 @@ var novely = ({
1752
1688
  const transformedChoices = choices.map(([content, _children, active, visible, onSelect, image]) => {
1753
1689
  const active$ = store(false);
1754
1690
  const visible$ = store(false);
1755
- const update = () => {
1756
- const lang = getLanguageFromStore(storageData);
1757
- const state = getStateAtCtx(ctx);
1758
- const activeValue = !active || active({
1759
- lang,
1760
- state
1761
- });
1762
- const visibleValue = !visible || visible({
1691
+ const lang = getLanguageFromStore(storageData);
1692
+ const getCheckValue = (fn) => {
1693
+ if (!fn) {
1694
+ return true;
1695
+ }
1696
+ return fn({
1763
1697
  lang,
1764
- state
1698
+ state: getStateAtCtx(ctx)
1765
1699
  });
1766
- active$.set(activeValue);
1767
- visible$.set(visibleValue);
1700
+ };
1701
+ const update = () => {
1702
+ active$.set(getCheckValue(active));
1703
+ visible$.set(getCheckValue(visible));
1768
1704
  };
1769
1705
  update();
1770
1706
  const onSelectGuarded = onSelect || noop;
@@ -1808,6 +1744,7 @@ var novely = ({
1808
1744
  ["jump", scene],
1809
1745
  [null, -1]
1810
1746
  ];
1747
+ stack.value[3] = [];
1811
1748
  match("clear", [], {
1812
1749
  ctx,
1813
1750
  data: data2
@@ -1822,12 +1759,12 @@ var novely = ({
1822
1759
  push
1823
1760
  );
1824
1761
  },
1825
- condition({ ctx }, [condition, variants]) {
1762
+ condition({ ctx, data: data2 }, [condition, variants]) {
1826
1763
  if (DEV3 && Object.values(variants).length === 0) {
1827
1764
  throw new Error(`Attempt to use Condition action with empty variants object`);
1828
1765
  }
1829
1766
  if (!ctx.meta.restoring) {
1830
- const val = String(condition(getStateAtCtx(ctx)));
1767
+ const val = String(condition(data2));
1831
1768
  if (DEV3 && !variants[val]) {
1832
1769
  throw new Error(`Attempt to go to unknown variant "${val}"`);
1833
1770
  }
@@ -1946,22 +1883,23 @@ var novely = ({
1946
1883
  });
1947
1884
  const render = (ctx) => {
1948
1885
  const stack = useStack(ctx);
1949
- const referred = refer(stack.value[0]);
1886
+ const [path, state] = stack.value;
1887
+ const referred = refer(path);
1950
1888
  if (isAction(referred)) {
1951
1889
  const [action2, ...props] = referred;
1952
1890
  match(action2, props, {
1953
1891
  ctx,
1954
- data: stack.value[1]
1892
+ data: state
1955
1893
  });
1956
1894
  } else if (Object.values(story).some((branch) => branch === referred)) {
1957
1895
  match("end", [], {
1958
1896
  ctx,
1959
- data: stack.value[1]
1897
+ data: state
1960
1898
  });
1961
1899
  } else {
1962
1900
  match("exit", [], {
1963
1901
  ctx,
1964
- data: stack.value[1]
1902
+ data: state
1965
1903
  });
1966
1904
  }
1967
1905
  };
@@ -1974,10 +1912,10 @@ var novely = ({
1974
1912
  meta: [lang]
1975
1913
  } = storageData.get();
1976
1914
  const obj = values || data2;
1977
- const str2 = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1915
+ const str = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1978
1916
  const t2 = translation[lang];
1979
1917
  const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
1980
- return replace(str2, obj, t2.plural, t2.actions, pluralRules);
1918
+ return replace(str, obj, t2.plural, t2.actions, pluralRules);
1981
1919
  };
1982
1920
  const data = (value) => {
1983
1921
  const _data = storageData.get().data;
@@ -2174,7 +2112,9 @@ var RU = {
2174
2112
  CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2175
2113
  MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2176
2114
  SoundVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0437\u0432\u0443\u043A\u043E\u0432",
2177
- VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438"
2115
+ VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2116
+ Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2117
+ DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2178
2118
  };
2179
2119
  var EN = {
2180
2120
  NewGame: "New Game",
@@ -2210,7 +2150,9 @@ var EN = {
2210
2150
  CloseMenu: "Close menu",
2211
2151
  MusicVolume: "Music volume",
2212
2152
  SoundVolume: "Sound volume",
2213
- VoiceVolume: "Voice volume"
2153
+ VoiceVolume: "Voice volume",
2154
+ Close: "Close",
2155
+ DialogOverview: "Dialog Overview"
2214
2156
  };
2215
2157
  var KK = {
2216
2158
  NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
@@ -2246,7 +2188,9 @@ var KK = {
2246
2188
  CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2247
2189
  MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2248
2190
  SoundVolume: "\u0414\u044B\u0431\u044B\u0441\u0442\u0430\u0440\u0434\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2249
- VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456"
2191
+ VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2192
+ Close: "\u0416\u0430\u0431\u0443",
2193
+ DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2250
2194
  };
2251
2195
  var JP = {
2252
2196
  NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
@@ -2282,7 +2226,137 @@ var JP = {
2282
2226
  CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2283
2227
  MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2284
2228
  SoundVolume: "\u97F3\u91CF",
2285
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF"
2229
+ VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2230
+ Close: "\u9589\u3058\u308B",
2231
+ DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2232
+ };
2233
+
2234
+ // src/asset.ts
2235
+ import { memoize as memoize4, once } from "es-toolkit/function";
2236
+ import { DEV as DEV4 } from "esm-env";
2237
+
2238
+ // src/audio-codecs.ts
2239
+ var cut = (str) => str.replace(/^no$/, "");
2240
+ var audio = new Audio();
2241
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
2242
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2243
+ var supportsMap = {
2244
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2245
+ mpeg: canPlay("audio/mpeg;"),
2246
+ opus: canPlay('audio/ogg; codecs="opus"'),
2247
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
2248
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
2249
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2250
+ aac: canPlay("audio/aac;"),
2251
+ caf: canPlay("audio/x-caf;"),
2252
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2253
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2254
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2255
+ weba: canPlay('audio/webm; codecs="vorbis"'),
2256
+ webm: canPlay('audio/webm; codecs="vorbis"'),
2257
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
2258
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2259
+ };
2260
+
2261
+ // src/image-formats.ts
2262
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2263
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2264
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2265
+ var supportsFormat = (source) => {
2266
+ const { promise, resolve } = Promise.withResolvers();
2267
+ const img = Object.assign(document.createElement("img"), {
2268
+ src: source
2269
+ });
2270
+ img.onload = img.onerror = () => {
2271
+ resolve(img.height === 2);
2272
+ };
2273
+ return promise;
2274
+ };
2275
+ var supportsMap2 = {
2276
+ avif: false,
2277
+ jxl: false,
2278
+ webp: false
2279
+ };
2280
+ var formatsMap = {
2281
+ avif,
2282
+ jxl,
2283
+ webp
2284
+ };
2285
+ var loadImageFormatsSupport = async () => {
2286
+ const promises = [];
2287
+ for (const [format, source] of Object.entries(formatsMap)) {
2288
+ const promise = supportsFormat(source).then((supported) => {
2289
+ supportsMap2[format] = supported;
2290
+ });
2291
+ promises.push(promise);
2292
+ }
2293
+ await Promise.all(promises);
2294
+ };
2295
+ loadImageFormatsSupport();
2296
+
2297
+ // src/asset.ts
2298
+ var getType = memoize4(
2299
+ (extensions) => {
2300
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2301
+ return "audio";
2302
+ }
2303
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2304
+ return "image";
2305
+ }
2306
+ throw extensions;
2307
+ },
2308
+ {
2309
+ getCacheKey: (extensions) => extensions.join("~")
2310
+ }
2311
+ );
2312
+ var SUPPORT_MAPS = {
2313
+ image: supportsMap2,
2314
+ audio: supportsMap
2315
+ };
2316
+ var assetPrivate = memoize4(
2317
+ (variants) => {
2318
+ if (DEV4 && variants.length === 0) {
2319
+ throw new Error(`Attempt to use "asset" function without arguments`);
2320
+ }
2321
+ const map = {};
2322
+ const extensions = [];
2323
+ for (const v of variants) {
2324
+ const e = getUrlFileExtension(v);
2325
+ map[e] = v;
2326
+ extensions.push(e);
2327
+ }
2328
+ const type = getType(extensions);
2329
+ const getSource = once(() => {
2330
+ const support = SUPPORT_MAPS[type];
2331
+ for (const extension of extensions) {
2332
+ if (extension in support) {
2333
+ if (support[extension]) {
2334
+ return map[extension];
2335
+ }
2336
+ } else {
2337
+ return map[extension];
2338
+ }
2339
+ }
2340
+ if (DEV4) {
2341
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2342
+ }
2343
+ return "";
2344
+ });
2345
+ return {
2346
+ get source() {
2347
+ return getSource();
2348
+ },
2349
+ get type() {
2350
+ return type;
2351
+ }
2352
+ };
2353
+ },
2354
+ {
2355
+ getCacheKey: (variants) => variants.join("~")
2356
+ }
2357
+ );
2358
+ var asset = (...variants) => {
2359
+ return assetPrivate(variants);
2286
2360
  };
2287
2361
  export {
2288
2362
  EN,