@novely/core 0.45.2 → 0.46.0-next.1

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,54 +94,60 @@ 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;
97
+ // src/utilities/assertions.ts
98
+ var isNumber = (val) => {
99
+ return typeof val === "number";
137
100
  };
138
- var supportsMap2 = {
139
- avif: false,
140
- jxl: false,
141
- webp: false
101
+ var isNull = (val) => {
102
+ return val === null;
142
103
  };
143
- var formatsMap = {
144
- avif,
145
- jxl,
146
- webp
104
+ var isString = (val) => {
105
+ return typeof val === "string";
147
106
  };
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);
107
+ var isFunction = (val) => {
108
+ return typeof val === "function";
109
+ };
110
+ var isPromise = (val) => {
111
+ return Boolean(val) && (typeof val === "object" || isFunction(val)) && isFunction(val.then);
112
+ };
113
+ var isEmpty = (val) => {
114
+ return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
115
+ };
116
+ var isCSSImageURL = (url) => {
117
+ const startsWith = String.prototype.startsWith.bind(url);
118
+ return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
119
+ };
120
+ var isUserRequiredAction = ([action, ...meta]) => {
121
+ return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
122
+ };
123
+ var isBlockStatement = (statement) => {
124
+ return BLOCK_STATEMENTS.has(statement);
125
+ };
126
+ var isBlockExitStatement = (statement) => {
127
+ return BLOCK_EXIT_STATEMENTS.has(statement);
128
+ };
129
+ var isSkippedDuringRestore = (item) => {
130
+ return SKIPPED_DURING_RESTORE.has(item);
131
+ };
132
+ var isAudioAction = (action) => {
133
+ return AUDIO_ACTIONS.has(action);
134
+ };
135
+ var isAction = (element) => {
136
+ return Array.isArray(element) && isString(element[0]);
137
+ };
138
+ var isImageAsset = (asset2) => {
139
+ return isString(asset2) && isCSSImageURL(asset2);
140
+ };
141
+ var isBlockingAction = (action) => {
142
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
143
+ };
144
+ var isAsset = (suspect) => {
145
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
157
146
  };
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
147
 
170
- // src/utils.ts
171
- var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
148
+ // src/utilities/match-action.ts
149
+ var matchAction = (callbacks, values) => {
150
+ const { getContext, onBeforeActionCall, push, forward } = callbacks;
172
151
  return (action, props, { ctx, data }) => {
173
152
  const context = typeof ctx === "string" ? getContext(ctx) : ctx;
174
153
  onBeforeActionCall({
@@ -193,32 +172,10 @@ var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) =>
193
172
  );
194
173
  };
195
174
  };
196
- var isNumber = (val) => {
197
- return typeof val === "number";
198
- };
199
- var isNull = (val) => {
200
- return val === null;
201
- };
202
- var isString = (val) => {
203
- return typeof val === "string";
204
- };
205
- var isFunction = (val) => {
206
- return typeof val === "function";
207
- };
208
- var isPromise = (val) => {
209
- return Boolean(val) && (typeof val === "object" || isFunction(val)) && isFunction(val.then);
210
- };
211
- var isEmpty = (val) => {
212
- return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
213
- };
214
- var isCSSImage = (str2) => {
215
- const startsWith = String.prototype.startsWith.bind(str2);
216
- return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
217
- };
218
- var str = String;
219
- var isUserRequiredAction = ([action, ...meta]) => {
220
- return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
221
- };
175
+
176
+ // src/utilities/ungrupped.ts
177
+ import { memoize } from "es-toolkit/function";
178
+ import { DEV } from "esm-env";
222
179
  var getLanguage = (languages) => {
223
180
  let { language } = navigator;
224
181
  if (languages.includes(language)) {
@@ -230,65 +187,60 @@ var getLanguage = (languages) => {
230
187
  }
231
188
  return languages[0];
232
189
  };
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;
190
+ var noop = () => {
254
191
  };
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;
192
+ var mapSet = (set2, fn) => {
193
+ return [...set2].map(fn);
261
194
  };
262
- var isBlockStatement = (statement) => {
263
- return BLOCK_STATEMENTS.has(statement);
195
+ var capitalize = (str) => {
196
+ return str[0].toUpperCase() + str.slice(1);
264
197
  };
265
- var isBlockExitStatement = (statement) => {
266
- return BLOCK_EXIT_STATEMENTS.has(statement);
198
+ var getIntlLanguageDisplayName = memoize((lang) => {
199
+ try {
200
+ const intl = new Intl.DisplayNames([lang], {
201
+ type: "language"
202
+ });
203
+ return intl.of(lang) || lang;
204
+ } catch {
205
+ return lang;
206
+ }
207
+ });
208
+ var unwrapAsset = (asset2) => {
209
+ return isAsset(asset2) ? asset2.source : asset2;
267
210
  };
268
- var isSkippedDuringRestore = (item) => {
269
- return SKIPPED_DURING_RESTORE.has(item);
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);
270
216
  };
271
- var isAudioAction = (action) => {
272
- return AUDIO_ACTIONS.has(action);
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);
273
222
  };
274
- var noop = () => {
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);
275
227
  };
276
- var isAction = (element) => {
277
- return Array.isArray(element) && isString(element[0]);
228
+ var toArray = (target) => {
229
+ return Array.isArray(target) ? target : [target];
278
230
  };
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
- });
231
+ var getLanguageFromStore = (store2) => {
232
+ return store2.get().meta[0];
285
233
  };
286
- var flattenStory = (story) => {
287
- const entries = Object.entries(story).map(([name, items]) => {
288
- return [name, flatActions(items)];
289
- });
290
- return Object.fromEntries(entries);
234
+ var getVolumeFromStore = (store2) => {
235
+ const { meta } = store2.get();
236
+ return {
237
+ music: meta[2],
238
+ sound: meta[3],
239
+ voice: meta[4]
240
+ };
291
241
  };
242
+
243
+ // src/utilities/actions-processing.ts
292
244
  var isExitImpossible = (path) => {
293
245
  const blockStatements = path.filter(([item]) => isBlockStatement(item));
294
246
  const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
@@ -300,14 +252,166 @@ var isExitImpossible = (path) => {
300
252
  }
301
253
  return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
302
254
  };
303
- var getOppositeAction = (action) => {
304
- const MAP = {
305
- showCharacter: "hideCharacter",
306
- playSound: "stopSound",
307
- playMusic: "stopMusic",
308
- voice: "stopVoice"
255
+ var createReferFunction = (story) => {
256
+ const refer = (path) => {
257
+ let current = story;
258
+ let precurrent = story;
259
+ const blocks = [];
260
+ for (const [type, val] of path) {
261
+ if (type === "jump") {
262
+ precurrent = story;
263
+ current = current[val];
264
+ } else if (type === null) {
265
+ precurrent = current;
266
+ current = current[val];
267
+ } else if (type === "choice") {
268
+ blocks.push(precurrent);
269
+ current = current[val + 1][1];
270
+ } else if (type === "condition") {
271
+ blocks.push(precurrent);
272
+ current = current[2][val];
273
+ } else if (type === "block") {
274
+ blocks.push(precurrent);
275
+ current = story[val];
276
+ } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
277
+ current = blocks.pop();
278
+ }
279
+ }
280
+ return current;
309
281
  };
310
- return MAP[action];
282
+ return refer;
283
+ };
284
+ var exitPath = ({ path, refer, onExitImpossible }) => {
285
+ const last = path.at(-1);
286
+ const ignore = [];
287
+ let wasExitImpossible = false;
288
+ if (!isAction(refer(path))) {
289
+ if (last && isNull(last[0]) && isNumber(last[1])) {
290
+ last[1]--;
291
+ } else {
292
+ path.pop();
293
+ }
294
+ }
295
+ if (isExitImpossible(path)) {
296
+ const referred = refer(path);
297
+ if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
298
+ onExitImpossible?.();
299
+ }
300
+ wasExitImpossible = true;
301
+ return {
302
+ exitImpossible: wasExitImpossible
303
+ };
304
+ }
305
+ for (let i = path.length - 1; i > 0; i--) {
306
+ const [name] = path[i];
307
+ if (isBlockExitStatement(name)) {
308
+ ignore.push(name);
309
+ }
310
+ if (!isBlockStatement(name)) continue;
311
+ if (ignore.at(-1)?.startsWith(name)) {
312
+ ignore.pop();
313
+ continue;
314
+ }
315
+ path.push([`${name}:exit`]);
316
+ const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
317
+ if (prev) path.push([null, prev[1] + 1]);
318
+ if (!isAction(refer(path))) {
319
+ path.pop();
320
+ continue;
321
+ }
322
+ break;
323
+ }
324
+ return {
325
+ exitImpossible: wasExitImpossible
326
+ };
327
+ };
328
+ var nextPath = (path) => {
329
+ const last = path.at(-1);
330
+ if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
331
+ last[1]++;
332
+ } else {
333
+ path.push([null, 0]);
334
+ }
335
+ return path;
336
+ };
337
+ var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
338
+ const collection = [];
339
+ let action = refer(path);
340
+ while (true) {
341
+ if (action == void 0) {
342
+ const { exitImpossible } = exitPath({
343
+ path,
344
+ refer
345
+ });
346
+ if (exitImpossible) {
347
+ break;
348
+ }
349
+ }
350
+ if (!action) {
351
+ break;
352
+ }
353
+ if (isBlockingAction(action)) {
354
+ const [name, ...props] = action;
355
+ if (name === "choice") {
356
+ const choiceProps = props;
357
+ for (let i = 0; i < choiceProps.length; i++) {
358
+ const branchContent = choiceProps[i];
359
+ if (!Array.isArray(branchContent)) continue;
360
+ const virtualPath = clone(path);
361
+ virtualPath.push(["choice", i], [null, 0]);
362
+ const innerActions = collectActionsBeforeBlockingAction({
363
+ path: virtualPath,
364
+ refer,
365
+ clone
366
+ });
367
+ collection.push(...innerActions);
368
+ }
369
+ } else if (name === "condition") {
370
+ const conditionProps = props;
371
+ const conditions = Object.keys(conditionProps[1]);
372
+ for (const condition of conditions) {
373
+ const virtualPath = clone(path);
374
+ virtualPath.push(["condition", condition], [null, 0]);
375
+ const innerActions = collectActionsBeforeBlockingAction({
376
+ path: virtualPath,
377
+ refer,
378
+ clone
379
+ });
380
+ collection.push(...innerActions);
381
+ }
382
+ }
383
+ break;
384
+ }
385
+ collection.push(action);
386
+ if (action[0] === "jump") {
387
+ path = [
388
+ ["jump", action[1]],
389
+ [null, 0]
390
+ ];
391
+ } else if (action[0] == "block") {
392
+ path.push(["block", action[1]], [null, 0]);
393
+ } else {
394
+ nextPath(path);
395
+ }
396
+ action = refer(path);
397
+ }
398
+ return collection;
399
+ };
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;
406
+ };
407
+ var getOppositeAction = (action) => {
408
+ const MAP = {
409
+ showCharacter: "hideCharacter",
410
+ playSound: "stopSound",
411
+ playMusic: "stopMusic",
412
+ voice: "stopVoice"
413
+ };
414
+ return MAP[action];
311
415
  };
312
416
  var getActionsFromPath = (story, path, filter) => {
313
417
  let current = story;
@@ -405,7 +509,7 @@ var createQueueProcessor = (queue, options) => {
405
509
  const c1 = fn;
406
510
  const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
407
511
  const isIdenticalByReference = c0 === c1;
408
- return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
512
+ return isIdenticalID || isIdenticalByReference || String(c0) === String(c1);
409
513
  });
410
514
  if (notLatest) continue;
411
515
  } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
@@ -472,59 +576,40 @@ var createQueueProcessor = (queue, options) => {
472
576
  }
473
577
  };
474
578
  };
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
- };
579
+
580
+ // src/utilities/controlled-promise.ts
581
+ var createControlledPromise = () => {
582
+ const object = {
583
+ resolve: null,
584
+ reject: null,
585
+ promise: null,
586
+ cancel: null
513
587
  };
514
- return useStack;
515
- };
516
- var mapSet = (set2, fn) => {
517
- return [...set2].map(fn);
518
- };
519
- var isImageAsset = (asset2) => {
520
- return isString(asset2) && isCSSImage(asset2);
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;
521
602
  };
603
+
604
+ // src/utilities/resources.ts
605
+ import { memoize as memoize2 } from "es-toolkit/function";
606
+ import { DEV as DEV2 } from "esm-env";
522
607
  var getUrlFileExtension = (address) => {
523
608
  try {
524
609
  const { pathname } = new URL(address, location.href);
525
610
  return pathname.split(".").at(-1).split("!")[0].split(":")[0];
526
611
  } catch (error) {
527
- if (DEV) {
612
+ if (DEV2) {
528
613
  console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
529
614
  }
530
615
  return "";
@@ -537,15 +622,15 @@ var fetchContentType = async (url, request) => {
537
622
  });
538
623
  return response.headers.get("Content-Type") || "";
539
624
  } catch (error) {
540
- if (DEV) {
625
+ if (DEV2) {
541
626
  console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
542
627
  }
543
628
  return "";
544
629
  }
545
630
  };
546
- var getResourseType = memoize(
631
+ var getResourseType = memoize2(
547
632
  async ({ url, request }) => {
548
- if (!isCSSImage(url)) {
633
+ if (!isCSSImageURL(url)) {
549
634
  return "other";
550
635
  }
551
636
  const extension = getUrlFileExtension(url);
@@ -554,282 +639,86 @@ var getResourseType = memoize(
554
639
  }
555
640
  if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
556
641
  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);
573
- };
574
- var getIntlLanguageDisplayName = memoize((lang) => {
575
- try {
576
- const intl = new Intl.DisplayNames([lang], {
577
- type: "language"
578
- });
579
- return intl.of(lang) || lang;
580
- } catch {
581
- return lang;
582
- }
583
- });
584
- var createReferFunction = (story) => {
585
- const refer = (path) => {
586
- let current = story;
587
- let precurrent = story;
588
- const blocks = [];
589
- for (const [type, val] of path) {
590
- if (type === "jump") {
591
- precurrent = story;
592
- current = current[val];
593
- } else if (type === null) {
594
- precurrent = current;
595
- current = current[val];
596
- } else if (type === "choice") {
597
- blocks.push(precurrent);
598
- current = current[val + 1][1];
599
- } else if (type === "condition") {
600
- blocks.push(precurrent);
601
- current = current[2][val];
602
- } else if (type === "block") {
603
- blocks.push(precurrent);
604
- current = story[val];
605
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
606
- current = blocks.pop();
607
- }
608
- }
609
- return current;
610
- };
611
- return refer;
612
- };
613
- var exitPath = ({ path, refer, onExitImpossible }) => {
614
- const last = path.at(-1);
615
- const ignore = [];
616
- let wasExitImpossible = false;
617
- if (!isAction(refer(path))) {
618
- if (last && isNull(last[0]) && isNumber(last[1])) {
619
- last[1]--;
620
- } else {
621
- path.pop();
622
- }
623
- }
624
- if (isExitImpossible(path)) {
625
- const referred = refer(path);
626
- if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
627
- onExitImpossible?.();
628
- }
629
- wasExitImpossible = true;
630
- return {
631
- exitImpossible: wasExitImpossible
632
- };
633
- }
634
- for (let i = path.length - 1; i > 0; i--) {
635
- const [name] = path[i];
636
- if (isBlockExitStatement(name)) {
637
- ignore.push(name);
638
- }
639
- if (!isBlockStatement(name)) continue;
640
- if (ignore.at(-1)?.startsWith(name)) {
641
- ignore.pop();
642
- continue;
643
- }
644
- path.push([`${name}:exit`]);
645
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
646
- if (prev) path.push([null, prev[1] + 1]);
647
- if (!isAction(refer(path))) {
648
- path.pop();
649
- continue;
650
- }
651
- break;
652
- }
653
- return {
654
- exitImpossible: wasExitImpossible
655
- };
656
- };
657
- var nextPath = (path) => {
658
- const last = path.at(-1);
659
- if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
660
- last[1]++;
661
- } else {
662
- path.push([null, 0]);
663
- }
664
- return path;
665
- };
666
- var isBlockingAction = (action) => {
667
- return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
668
- };
669
- var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
670
- const collection = [];
671
- let action = refer(path);
672
- while (true) {
673
- if (action == void 0) {
674
- const { exitImpossible } = exitPath({
675
- path,
676
- refer
677
- });
678
- if (exitImpossible) {
679
- break;
680
- }
681
- }
682
- if (!action) {
683
- break;
684
- }
685
- if (isBlockingAction(action)) {
686
- const [name, ...props] = action;
687
- if (name === "choice") {
688
- const choiceProps = props;
689
- for (let i = 0; i < choiceProps.length; i++) {
690
- const branchContent = choiceProps[i];
691
- if (!Array.isArray(branchContent)) continue;
692
- const virtualPath = clone(path);
693
- virtualPath.push(["choice", i], [null, 0]);
694
- const innerActions = collectActionsBeforeBlockingAction({
695
- path: virtualPath,
696
- refer,
697
- clone
698
- });
699
- collection.push(...innerActions);
700
- }
701
- } else if (name === "condition") {
702
- const conditionProps = props;
703
- const conditions = Object.keys(conditionProps[1]);
704
- for (const condition of conditions) {
705
- const virtualPath = clone(path);
706
- virtualPath.push(["condition", condition], [null, 0]);
707
- const innerActions = collectActionsBeforeBlockingAction({
708
- path: virtualPath,
709
- refer,
710
- clone
711
- });
712
- collection.push(...innerActions);
713
- }
714
- }
715
- break;
716
- }
717
- collection.push(action);
718
- if (action[0] === "jump") {
719
- path = [
720
- ["jump", action[1]],
721
- [null, 0]
722
- ];
723
- } else if (action[0] == "block") {
724
- path.push(["block", action[1]], [null, 0]);
725
- } else {
726
- nextPath(path);
727
- }
728
- action = refer(path);
729
- }
730
- return collection;
731
- };
732
- var unwrapAsset = (asset2) => {
733
- return isAsset(asset2) ? asset2.source : asset2;
734
- };
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);
740
- };
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 });
744
- }
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];
757
- };
758
- var getVolumeFromStore = (store2) => {
759
- const { meta } = store2.get();
760
- return {
761
- music: meta[2],
762
- sound: meta[3],
763
- voice: meta[4]
764
- };
765
- };
766
-
767
- // src/asset.ts
768
- var getType = memoize2(
769
- (extensions) => {
770
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
642
+ }
643
+ const contentType = await fetchContentType(url, request);
644
+ if (contentType.includes("audio")) {
771
645
  return "audio";
772
646
  }
773
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
647
+ if (contentType.includes("image")) {
774
648
  return "image";
775
649
  }
776
- throw extensions;
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();
@@ -1199,7 +1088,8 @@ var novely = ({
1199
1088
  throw new Error(`Attempt to call Say action with unknown character "${character}"`);
1200
1089
  }
1201
1090
  } else if (action2 === "choice") {
1202
- if (props.slice(1).every((choice) => !Array.isArray(choice))) {
1091
+ const actions = props.slice(1);
1092
+ if (actions.every((choice) => !Array.isArray(choice))) {
1203
1093
  for (let i = 1; i < props.length; i++) {
1204
1094
  const choice = props[i];
1205
1095
  props[i] = [
@@ -1211,6 +1101,13 @@ var novely = ({
1211
1101
  choice.image
1212
1102
  ];
1213
1103
  }
1104
+ } else {
1105
+ for (let i = 1; i < props.length; i++) {
1106
+ const choice = props[i];
1107
+ if (Array.isArray(choice) && choice.length === 2) {
1108
+ choice[1] = flatActions(choice[1]);
1109
+ }
1110
+ }
1214
1111
  }
1215
1112
  }
1216
1113
  if (preloadAssets === "blocking") {
@@ -1235,7 +1132,8 @@ var novely = ({
1235
1132
  [null, 0]
1236
1133
  ],
1237
1134
  state,
1238
- [intime(Date.now()), "auto"]
1135
+ [intime(Date.now()), "auto"],
1136
+ []
1239
1137
  ];
1240
1138
  };
1241
1139
  const getLanguageWithoutParameters = () => {
@@ -1556,6 +1454,61 @@ var novely = ({
1556
1454
  const getCharacterAssets = (character, emotion) => {
1557
1455
  return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1558
1456
  };
1457
+ const getCharacterName = (character) => {
1458
+ const c = character;
1459
+ const cs = characters;
1460
+ const [lang] = storageData.get().meta;
1461
+ if (c && c in cs) {
1462
+ const block = cs[c].name;
1463
+ if (typeof block === "string") {
1464
+ return block;
1465
+ }
1466
+ if (lang in block) {
1467
+ return block[lang];
1468
+ }
1469
+ }
1470
+ return String(c) || "";
1471
+ };
1472
+ const getDialogOverview = () => {
1473
+ const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1474
+ const stateSnapshots = save2[3];
1475
+ const { queue } = getActionsFromPath(story, save2[0], false);
1476
+ const [lang] = storageData.get().meta;
1477
+ const dialogItem = [];
1478
+ for (let p = 0, i = 0; i < queue.length; i++) {
1479
+ const action2 = queue[i];
1480
+ if (action2[0] === "dialog") {
1481
+ const [_, name, text] = action2;
1482
+ let voice = void 0;
1483
+ for (let j = i - 1; j > p; j--) {
1484
+ const action3 = queue[j];
1485
+ if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1486
+ if (action3[0] === "stopVoice") break;
1487
+ if (action3[0] === "voice") {
1488
+ voice = action3[1];
1489
+ break;
1490
+ }
1491
+ }
1492
+ dialogItem.push({
1493
+ name,
1494
+ text,
1495
+ voice
1496
+ });
1497
+ p = i;
1498
+ }
1499
+ }
1500
+ const entries = dialogItem.map(({ name, text, voice }, i) => {
1501
+ const state = stateSnapshots[i];
1502
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1503
+ name = name ? getCharacterName(name) : "";
1504
+ return {
1505
+ name: templateReplace(name, state),
1506
+ text: templateReplace(text, state),
1507
+ voice: audioSource ? handleAudioAsset(audioSource) : ""
1508
+ };
1509
+ });
1510
+ return entries;
1511
+ };
1559
1512
  const renderer = createRenderer({
1560
1513
  mainContextKey: MAIN_CONTEXT_KEY,
1561
1514
  characters: getCharactersData(characters),
@@ -1577,6 +1530,7 @@ var novely = ({
1577
1530
  getLanguageDisplayName,
1578
1531
  getCharacterColor,
1579
1532
  getCharacterAssets,
1533
+ getDialogOverview,
1580
1534
  getResourseType: getResourseTypeForRenderer
1581
1535
  });
1582
1536
  const useStack = createUseStackFunction(renderer);
@@ -1639,9 +1593,9 @@ var novely = ({
1639
1593
  }
1640
1594
  };
1641
1595
  const match = matchAction(matchActionOptions, {
1642
- wait({ ctx, push }, [time]) {
1596
+ wait({ ctx, data: data2, push }, [time]) {
1643
1597
  if (ctx.meta.restoring) return;
1644
- setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
1598
+ setTimeout(push, isFunction(time) ? time(data2) : time);
1645
1599
  },
1646
1600
  showBackground({ ctx, push }, [background]) {
1647
1601
  if (isString(background) || isAsset(background)) {
@@ -1711,21 +1665,11 @@ var novely = ({
1711
1665
  ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
1712
1666
  },
1713
1667
  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
- })();
1668
+ const name = getCharacterName(character);
1669
+ const stack = useStack(ctx);
1670
+ if (!ctx.meta.restoring && !ctx.meta.goingBack) {
1671
+ stack.value[3].push(clone(data2));
1672
+ }
1729
1673
  ctx.clearBlockingActions("dialog");
1730
1674
  ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
1731
1675
  },
@@ -1752,19 +1696,19 @@ var novely = ({
1752
1696
  const transformedChoices = choices.map(([content, _children, active, visible, onSelect, image]) => {
1753
1697
  const active$ = store(false);
1754
1698
  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({
1699
+ const lang = getLanguageFromStore(storageData);
1700
+ const getCheckValue = (fn) => {
1701
+ if (!fn) {
1702
+ return true;
1703
+ }
1704
+ return fn({
1763
1705
  lang,
1764
- state
1706
+ state: getStateAtCtx(ctx)
1765
1707
  });
1766
- active$.set(activeValue);
1767
- visible$.set(visibleValue);
1708
+ };
1709
+ const update = () => {
1710
+ active$.set(getCheckValue(active));
1711
+ visible$.set(getCheckValue(visible));
1768
1712
  };
1769
1713
  update();
1770
1714
  const onSelectGuarded = onSelect || noop;
@@ -1808,6 +1752,7 @@ var novely = ({
1808
1752
  ["jump", scene],
1809
1753
  [null, -1]
1810
1754
  ];
1755
+ stack.value[3] = [];
1811
1756
  match("clear", [], {
1812
1757
  ctx,
1813
1758
  data: data2
@@ -1822,12 +1767,12 @@ var novely = ({
1822
1767
  push
1823
1768
  );
1824
1769
  },
1825
- condition({ ctx }, [condition, variants]) {
1770
+ condition({ ctx, data: data2 }, [condition, variants]) {
1826
1771
  if (DEV3 && Object.values(variants).length === 0) {
1827
1772
  throw new Error(`Attempt to use Condition action with empty variants object`);
1828
1773
  }
1829
1774
  if (!ctx.meta.restoring) {
1830
- const val = String(condition(getStateAtCtx(ctx)));
1775
+ const val = String(condition(data2));
1831
1776
  if (DEV3 && !variants[val]) {
1832
1777
  throw new Error(`Attempt to go to unknown variant "${val}"`);
1833
1778
  }
@@ -1946,22 +1891,23 @@ var novely = ({
1946
1891
  });
1947
1892
  const render = (ctx) => {
1948
1893
  const stack = useStack(ctx);
1949
- const referred = refer(stack.value[0]);
1894
+ const [path, state] = stack.value;
1895
+ const referred = refer(path);
1950
1896
  if (isAction(referred)) {
1951
1897
  const [action2, ...props] = referred;
1952
1898
  match(action2, props, {
1953
1899
  ctx,
1954
- data: stack.value[1]
1900
+ data: state
1955
1901
  });
1956
1902
  } else if (Object.values(story).some((branch) => branch === referred)) {
1957
1903
  match("end", [], {
1958
1904
  ctx,
1959
- data: stack.value[1]
1905
+ data: state
1960
1906
  });
1961
1907
  } else {
1962
1908
  match("exit", [], {
1963
1909
  ctx,
1964
- data: stack.value[1]
1910
+ data: state
1965
1911
  });
1966
1912
  }
1967
1913
  };
@@ -1974,10 +1920,10 @@ var novely = ({
1974
1920
  meta: [lang]
1975
1921
  } = storageData.get();
1976
1922
  const obj = values || data2;
1977
- const str2 = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1923
+ const str = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1978
1924
  const t2 = translation[lang];
1979
1925
  const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
1980
- return replace(str2, obj, t2.plural, t2.actions, pluralRules);
1926
+ return replace(str, obj, t2.plural, t2.actions, pluralRules);
1981
1927
  };
1982
1928
  const data = (value) => {
1983
1929
  const _data = storageData.get().data;
@@ -2174,7 +2120,9 @@ var RU = {
2174
2120
  CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2175
2121
  MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2176
2122
  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"
2123
+ VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2124
+ Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2125
+ DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2178
2126
  };
2179
2127
  var EN = {
2180
2128
  NewGame: "New Game",
@@ -2210,7 +2158,9 @@ var EN = {
2210
2158
  CloseMenu: "Close menu",
2211
2159
  MusicVolume: "Music volume",
2212
2160
  SoundVolume: "Sound volume",
2213
- VoiceVolume: "Voice volume"
2161
+ VoiceVolume: "Voice volume",
2162
+ Close: "Close",
2163
+ DialogOverview: "Dialog Overview"
2214
2164
  };
2215
2165
  var KK = {
2216
2166
  NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
@@ -2246,7 +2196,9 @@ var KK = {
2246
2196
  CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2247
2197
  MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2248
2198
  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"
2199
+ VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2200
+ Close: "\u0416\u0430\u0431\u0443",
2201
+ DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2250
2202
  };
2251
2203
  var JP = {
2252
2204
  NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
@@ -2282,7 +2234,137 @@ var JP = {
2282
2234
  CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2283
2235
  MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2284
2236
  SoundVolume: "\u97F3\u91CF",
2285
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF"
2237
+ VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2238
+ Close: "\u9589\u3058\u308B",
2239
+ DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2240
+ };
2241
+
2242
+ // src/asset.ts
2243
+ import { memoize as memoize4, once } from "es-toolkit/function";
2244
+ import { DEV as DEV4 } from "esm-env";
2245
+
2246
+ // src/audio-codecs.ts
2247
+ var cut = (str) => str.replace(/^no$/, "");
2248
+ var audio = new Audio();
2249
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
2250
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2251
+ var supportsMap = {
2252
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2253
+ mpeg: canPlay("audio/mpeg;"),
2254
+ opus: canPlay('audio/ogg; codecs="opus"'),
2255
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
2256
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
2257
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2258
+ aac: canPlay("audio/aac;"),
2259
+ caf: canPlay("audio/x-caf;"),
2260
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2261
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2262
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2263
+ weba: canPlay('audio/webm; codecs="vorbis"'),
2264
+ webm: canPlay('audio/webm; codecs="vorbis"'),
2265
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
2266
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2267
+ };
2268
+
2269
+ // src/image-formats.ts
2270
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2271
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2272
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2273
+ var supportsFormat = (source) => {
2274
+ const { promise, resolve } = Promise.withResolvers();
2275
+ const img = Object.assign(document.createElement("img"), {
2276
+ src: source
2277
+ });
2278
+ img.onload = img.onerror = () => {
2279
+ resolve(img.height === 2);
2280
+ };
2281
+ return promise;
2282
+ };
2283
+ var supportsMap2 = {
2284
+ avif: false,
2285
+ jxl: false,
2286
+ webp: false
2287
+ };
2288
+ var formatsMap = {
2289
+ avif,
2290
+ jxl,
2291
+ webp
2292
+ };
2293
+ var loadImageFormatsSupport = async () => {
2294
+ const promises = [];
2295
+ for (const [format, source] of Object.entries(formatsMap)) {
2296
+ const promise = supportsFormat(source).then((supported) => {
2297
+ supportsMap2[format] = supported;
2298
+ });
2299
+ promises.push(promise);
2300
+ }
2301
+ await Promise.all(promises);
2302
+ };
2303
+ loadImageFormatsSupport();
2304
+
2305
+ // src/asset.ts
2306
+ var getType = memoize4(
2307
+ (extensions) => {
2308
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2309
+ return "audio";
2310
+ }
2311
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2312
+ return "image";
2313
+ }
2314
+ throw extensions;
2315
+ },
2316
+ {
2317
+ getCacheKey: (extensions) => extensions.join("~")
2318
+ }
2319
+ );
2320
+ var SUPPORT_MAPS = {
2321
+ image: supportsMap2,
2322
+ audio: supportsMap
2323
+ };
2324
+ var assetPrivate = memoize4(
2325
+ (variants) => {
2326
+ if (DEV4 && variants.length === 0) {
2327
+ throw new Error(`Attempt to use "asset" function without arguments`);
2328
+ }
2329
+ const map = {};
2330
+ const extensions = [];
2331
+ for (const v of variants) {
2332
+ const e = getUrlFileExtension(v);
2333
+ map[e] = v;
2334
+ extensions.push(e);
2335
+ }
2336
+ const type = getType(extensions);
2337
+ const getSource = once(() => {
2338
+ const support = SUPPORT_MAPS[type];
2339
+ for (const extension of extensions) {
2340
+ if (extension in support) {
2341
+ if (support[extension]) {
2342
+ return map[extension];
2343
+ }
2344
+ } else {
2345
+ return map[extension];
2346
+ }
2347
+ }
2348
+ if (DEV4) {
2349
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2350
+ }
2351
+ return "";
2352
+ });
2353
+ return {
2354
+ get source() {
2355
+ return getSource();
2356
+ },
2357
+ get type() {
2358
+ return type;
2359
+ }
2360
+ };
2361
+ },
2362
+ {
2363
+ getCacheKey: (variants) => variants.join("~")
2364
+ }
2365
+ );
2366
+ var asset = (...variants) => {
2367
+ return assetPrivate(variants);
2286
2368
  };
2287
2369
  export {
2288
2370
  EN,