@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.
@@ -57,7 +57,70 @@ var Novely = (() => {
57
57
  return foo !== foo && bar !== bar;
58
58
  }
59
59
 
60
- // ../../node_modules/.pnpm/es-toolkit@1.16.0/node_modules/es-toolkit/dist/function/once.mjs
60
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/debounce.mjs
61
+ function debounce(func, debounceMs, { signal, edges } = {}) {
62
+ let pendingThis = void 0;
63
+ let pendingArgs = null;
64
+ const leading = edges != null && edges.includes("leading");
65
+ const trailing = edges == null || edges.includes("trailing");
66
+ const invoke = () => {
67
+ if (pendingArgs !== null) {
68
+ func.apply(pendingThis, pendingArgs);
69
+ pendingThis = void 0;
70
+ pendingArgs = null;
71
+ }
72
+ };
73
+ const onTimerEnd = () => {
74
+ if (trailing) {
75
+ invoke();
76
+ }
77
+ cancel();
78
+ };
79
+ let timeoutId = null;
80
+ const schedule = () => {
81
+ if (timeoutId != null) {
82
+ clearTimeout(timeoutId);
83
+ }
84
+ timeoutId = setTimeout(() => {
85
+ timeoutId = null;
86
+ onTimerEnd();
87
+ }, debounceMs);
88
+ };
89
+ const cancelTimer = () => {
90
+ if (timeoutId !== null) {
91
+ clearTimeout(timeoutId);
92
+ timeoutId = null;
93
+ }
94
+ };
95
+ const cancel = () => {
96
+ cancelTimer();
97
+ pendingThis = void 0;
98
+ pendingArgs = null;
99
+ };
100
+ const flush = () => {
101
+ cancelTimer();
102
+ invoke();
103
+ };
104
+ const debounced = function(...args) {
105
+ if (signal?.aborted) {
106
+ return;
107
+ }
108
+ pendingThis = this;
109
+ pendingArgs = args;
110
+ const isFirstCall = timeoutId == null;
111
+ schedule();
112
+ if (leading && isFirstCall) {
113
+ invoke();
114
+ }
115
+ };
116
+ debounced.schedule = schedule;
117
+ debounced.cancel = cancel;
118
+ debounced.flush = flush;
119
+ signal?.addEventListener("abort", cancel, { once: true });
120
+ return debounced;
121
+ }
122
+
123
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/once.mjs
61
124
  function once(func) {
62
125
  let called = false;
63
126
  let cache;
@@ -72,20 +135,28 @@ var Novely = (() => {
72
135
  };
73
136
  }
74
137
 
75
- // ../../node_modules/.pnpm/es-toolkit@1.16.0/node_modules/es-toolkit/dist/function/throttle.mjs
76
- function throttle(func, throttleMs) {
77
- let lastCallTime;
78
- const throttledFunction = function(...args) {
79
- const now = Date.now();
80
- if (lastCallTime == null || now - lastCallTime >= throttleMs) {
81
- lastCallTime = now;
82
- func(...args);
138
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/throttle.mjs
139
+ function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) {
140
+ let pendingAt = null;
141
+ const debounced = debounce(func, throttleMs, { signal, edges });
142
+ const throttled = function(...args) {
143
+ if (pendingAt == null) {
144
+ pendingAt = Date.now();
145
+ } else {
146
+ if (Date.now() - pendingAt >= throttleMs) {
147
+ pendingAt = Date.now();
148
+ debounced.cancel();
149
+ debounced(...args);
150
+ }
83
151
  }
152
+ debounced(...args);
84
153
  };
85
- return throttledFunction;
154
+ throttled.cancel = debounced.cancel;
155
+ throttled.flush = debounced.flush;
156
+ return throttled;
86
157
  }
87
158
 
88
- // ../../node_modules/.pnpm/es-toolkit@1.16.0/node_modules/es-toolkit/dist/function/memoize.mjs
159
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/memoize.mjs
89
160
  function memoize(fn, options = {}) {
90
161
  const { cache = /* @__PURE__ */ new Map(), getCacheKey } = options;
91
162
  const memoizedFn = function(arg) {
@@ -101,12 +172,28 @@ var Novely = (() => {
101
172
  return memoizedFn;
102
173
  }
103
174
 
104
- // ../../node_modules/.pnpm/es-toolkit@1.16.0/node_modules/es-toolkit/dist/compat/predicate/isObjectLike.mjs
105
- function isObjectLike(value) {
106
- return typeof value === "object" && value !== null;
175
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
176
+ function isPlainObject(value) {
177
+ if (typeof value !== "object") {
178
+ return false;
179
+ }
180
+ if (value == null) {
181
+ return false;
182
+ }
183
+ if (Object.getPrototypeOf(value) === null) {
184
+ return true;
185
+ }
186
+ if (value.toString() !== "[object Object]") {
187
+ return false;
188
+ }
189
+ let proto = value;
190
+ while (Object.getPrototypeOf(proto) !== null) {
191
+ proto = Object.getPrototypeOf(proto);
192
+ }
193
+ return Object.getPrototypeOf(value) === proto;
107
194
  }
108
195
 
109
- // ../../node_modules/.pnpm/es-toolkit@1.16.0/node_modules/es-toolkit/dist/object/merge.mjs
196
+ // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/object/merge.mjs
110
197
  function merge(target, source) {
111
198
  const sourceKeys = Object.keys(source);
112
199
  for (let i = 0; i < sourceKeys.length; i++) {
@@ -114,9 +201,17 @@ var Novely = (() => {
114
201
  const sourceValue = source[key];
115
202
  const targetValue = target[key];
116
203
  if (Array.isArray(sourceValue)) {
117
- target[key] = merge(targetValue ?? [], sourceValue);
118
- } else if (isObjectLike(targetValue) && isObjectLike(sourceValue)) {
119
- target[key] = merge(targetValue ?? {}, sourceValue);
204
+ if (Array.isArray(targetValue)) {
205
+ target[key] = merge(targetValue, sourceValue);
206
+ } else {
207
+ target[key] = merge([], sourceValue);
208
+ }
209
+ } else if (isPlainObject(sourceValue)) {
210
+ if (isPlainObject(targetValue)) {
211
+ target[key] = merge(targetValue, sourceValue);
212
+ } else {
213
+ target[key] = merge({}, sourceValue);
214
+ }
120
215
  } else if (targetValue === void 0 || sourceValue !== void 0) {
121
216
  target[key] = sourceValue;
122
217
  }
@@ -136,30 +231,30 @@ var Novely = (() => {
136
231
  }
137
232
  function klona(x) {
138
233
  if (typeof x !== "object") return x;
139
- var i = 0, k, list, tmp, str2 = Object.prototype.toString.call(x);
140
- if (str2 === "[object Object]") {
234
+ var i = 0, k, list, tmp, str = Object.prototype.toString.call(x);
235
+ if (str === "[object Object]") {
141
236
  tmp = Object.create(x.__proto__ || null);
142
- } else if (str2 === "[object Array]") {
237
+ } else if (str === "[object Array]") {
143
238
  tmp = Array(x.length);
144
- } else if (str2 === "[object Set]") {
239
+ } else if (str === "[object Set]") {
145
240
  tmp = /* @__PURE__ */ new Set();
146
241
  x.forEach(function(val) {
147
242
  tmp.add(klona(val));
148
243
  });
149
- } else if (str2 === "[object Map]") {
244
+ } else if (str === "[object Map]") {
150
245
  tmp = /* @__PURE__ */ new Map();
151
246
  x.forEach(function(val, key) {
152
247
  tmp.set(klona(key), klona(val));
153
248
  });
154
- } else if (str2 === "[object Date]") {
249
+ } else if (str === "[object Date]") {
155
250
  tmp = /* @__PURE__ */ new Date(+x);
156
- } else if (str2 === "[object RegExp]") {
251
+ } else if (str === "[object RegExp]") {
157
252
  tmp = new RegExp(x.source, x.flags);
158
- } else if (str2 === "[object DataView]") {
253
+ } else if (str === "[object DataView]") {
159
254
  tmp = new x.constructor(klona(x.buffer));
160
- } else if (str2 === "[object ArrayBuffer]") {
255
+ } else if (str === "[object ArrayBuffer]") {
161
256
  tmp = x.slice(0);
162
- } else if (str2.slice(-6) === "Array]") {
257
+ } else if (str.slice(-6) === "Array]") {
163
258
  tmp = new x.constructor(x);
164
259
  }
165
260
  if (tmp) {
@@ -305,29 +400,6 @@ var Novely = (() => {
305
400
  }
306
401
  }
307
402
 
308
- // src/audio-codecs.ts
309
- var cut = (str2) => str2.replace(/^no$/, "");
310
- var audio = new Audio();
311
- var canPlay = (type) => !!cut(audio.canPlayType(type));
312
- var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
313
- var supportsMap = {
314
- mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
315
- mpeg: canPlay("audio/mpeg;"),
316
- opus: canPlay('audio/ogg; codecs="opus"'),
317
- ogg: canPlay('audio/ogg; codecs="vorbis"'),
318
- oga: canPlay('audio/ogg; codecs="vorbis"'),
319
- wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
320
- aac: canPlay("audio/aac;"),
321
- caf: canPlay("audio/x-caf;"),
322
- m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
323
- m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
324
- mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
325
- weba: canPlay('audio/webm; codecs="vorbis"'),
326
- webm: canPlay('audio/webm; codecs="vorbis"'),
327
- dolby: canPlay('audio/mp4; codecs="ec-3"'),
328
- flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
329
- };
330
-
331
403
  // src/constants.ts
332
404
  var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
333
405
  var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
@@ -368,50 +440,60 @@ var Novely = (() => {
368
440
  ]);
369
441
  var MAIN_CONTEXT_KEY = "$MAIN";
370
442
 
371
- // src/image-formats.ts
372
- var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
373
- var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
374
- var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
375
- var supportsFormat = (source) => {
376
- const { promise, resolve } = Promise.withResolvers();
377
- const img = Object.assign(document.createElement("img"), {
378
- src: source
379
- });
380
- img.onload = img.onerror = () => {
381
- resolve(img.height === 2);
382
- };
383
- return promise;
443
+ // src/utilities/assertions.ts
444
+ var isNumber = (val) => {
445
+ return typeof val === "number";
384
446
  };
385
- var supportsMap2 = {
386
- avif: false,
387
- jxl: false,
388
- webp: false
447
+ var isNull = (val) => {
448
+ return val === null;
389
449
  };
390
- var formatsMap = {
391
- avif,
392
- jxl,
393
- webp
450
+ var isString = (val) => {
451
+ return typeof val === "string";
394
452
  };
395
- var loadImageFormatsSupport = async () => {
396
- const promises = [];
397
- for (const [format, source] of Object.entries(formatsMap)) {
398
- const promise = supportsFormat(source).then((supported) => {
399
- supportsMap2[format] = supported;
400
- });
401
- promises.push(promise);
402
- }
403
- await Promise.all(promises);
453
+ var isFunction = (val) => {
454
+ return typeof val === "function";
455
+ };
456
+ var isPromise = (val) => {
457
+ return Boolean(val) && (typeof val === "object" || isFunction(val)) && isFunction(val.then);
458
+ };
459
+ var isEmpty = (val) => {
460
+ return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
461
+ };
462
+ var isCSSImageURL = (url) => {
463
+ const startsWith = String.prototype.startsWith.bind(url);
464
+ return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
465
+ };
466
+ var isUserRequiredAction = ([action, ...meta]) => {
467
+ return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
468
+ };
469
+ var isBlockStatement = (statement) => {
470
+ return BLOCK_STATEMENTS.has(statement);
471
+ };
472
+ var isBlockExitStatement = (statement) => {
473
+ return BLOCK_EXIT_STATEMENTS.has(statement);
474
+ };
475
+ var isSkippedDuringRestore = (item) => {
476
+ return SKIPPED_DURING_RESTORE.has(item);
477
+ };
478
+ var isAudioAction = (action) => {
479
+ return AUDIO_ACTIONS.has(action);
480
+ };
481
+ var isAction = (element) => {
482
+ return Array.isArray(element) && isString(element[0]);
483
+ };
484
+ var isImageAsset = (asset2) => {
485
+ return isString(asset2) && isCSSImageURL(asset2);
486
+ };
487
+ var isBlockingAction = (action) => {
488
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
489
+ };
490
+ var isAsset = (suspect) => {
491
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
404
492
  };
405
- loadImageFormatsSupport();
406
-
407
- // src/shared.ts
408
- var STACK_MAP = /* @__PURE__ */ new Map();
409
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
410
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
411
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
412
493
 
413
- // src/utils.ts
414
- var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
494
+ // src/utilities/match-action.ts
495
+ var matchAction = (callbacks, values) => {
496
+ const { getContext, onBeforeActionCall, push, forward } = callbacks;
415
497
  return (action, props, { ctx, data }) => {
416
498
  const context = typeof ctx === "string" ? getContext(ctx) : ctx;
417
499
  onBeforeActionCall({
@@ -436,32 +518,8 @@ var Novely = (() => {
436
518
  );
437
519
  };
438
520
  };
439
- var isNumber = (val) => {
440
- return typeof val === "number";
441
- };
442
- var isNull = (val) => {
443
- return val === null;
444
- };
445
- var isString = (val) => {
446
- return typeof val === "string";
447
- };
448
- var isFunction = (val) => {
449
- return typeof val === "function";
450
- };
451
- var isPromise = (val) => {
452
- return Boolean(val) && (typeof val === "object" || isFunction(val)) && isFunction(val.then);
453
- };
454
- var isEmpty = (val) => {
455
- return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
456
- };
457
- var isCSSImage = (str2) => {
458
- const startsWith = String.prototype.startsWith.bind(str2);
459
- return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
460
- };
461
- var str = String;
462
- var isUserRequiredAction = ([action, ...meta]) => {
463
- return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
464
- };
521
+
522
+ // src/utilities/ungrupped.ts
465
523
  var getLanguage = (languages) => {
466
524
  let { language } = navigator;
467
525
  if (languages.includes(language)) {
@@ -473,65 +531,60 @@ var Novely = (() => {
473
531
  }
474
532
  return languages[0];
475
533
  };
476
- var createControlledPromise = () => {
477
- const object = {
478
- resolve: null,
479
- reject: null,
480
- promise: null,
481
- cancel: null
482
- };
483
- const init = () => {
484
- const promise = new Promise((resolve, reject) => {
485
- object.reject = reject;
486
- object.resolve = (value) => {
487
- resolve({ cancelled: false, value });
488
- };
489
- object.cancel = () => {
490
- resolve({ cancelled: true, value: null });
491
- init();
492
- };
493
- });
494
- object.promise = promise;
495
- };
496
- return init(), object;
534
+ var noop = () => {
497
535
  };
498
- var findLastPathItemBeforeItemOfType = (path, name) => {
499
- const item = path.findLast(([_name, _value], i, array) => {
500
- const next = array[i + 1];
501
- return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
502
- });
503
- return item;
536
+ var mapSet = (set2, fn) => {
537
+ return [...set2].map(fn);
504
538
  };
505
- var isBlockStatement = (statement) => {
506
- return BLOCK_STATEMENTS.has(statement);
539
+ var capitalize = (str) => {
540
+ return str[0].toUpperCase() + str.slice(1);
507
541
  };
508
- var isBlockExitStatement = (statement) => {
509
- return BLOCK_EXIT_STATEMENTS.has(statement);
542
+ var getIntlLanguageDisplayName = memoize((lang) => {
543
+ try {
544
+ const intl = new Intl.DisplayNames([lang], {
545
+ type: "language"
546
+ });
547
+ return intl.of(lang) || lang;
548
+ } catch {
549
+ return lang;
550
+ }
551
+ });
552
+ var unwrapAsset = (asset2) => {
553
+ return isAsset(asset2) ? asset2.source : asset2;
510
554
  };
511
- var isSkippedDuringRestore = (item) => {
512
- return SKIPPED_DURING_RESTORE.has(item);
555
+ var handleAudioAsset = (asset2) => {
556
+ if (DEV && isAsset(asset2) && asset2.type !== "audio") {
557
+ throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
558
+ }
559
+ return unwrapAsset(asset2);
513
560
  };
514
- var isAudioAction = (action) => {
515
- return AUDIO_ACTIONS.has(action);
561
+ var handleImageAsset = (asset2) => {
562
+ if (DEV && isAsset(asset2) && asset2.type !== "image") {
563
+ throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
564
+ }
565
+ return unwrapAsset(asset2);
516
566
  };
517
- var noop = () => {
567
+ var getCharactersData = (characters) => {
568
+ const entries = Object.entries(characters);
569
+ const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
570
+ return Object.fromEntries(mapped);
518
571
  };
519
- var isAction = (element) => {
520
- return Array.isArray(element) && isString(element[0]);
572
+ var toArray = (target) => {
573
+ return Array.isArray(target) ? target : [target];
521
574
  };
522
- var flatActions = (item) => {
523
- return item.flatMap((data) => {
524
- const type = data[0];
525
- if (Array.isArray(type)) return flatActions(data);
526
- return [data];
527
- });
575
+ var getLanguageFromStore = (store2) => {
576
+ return store2.get().meta[0];
528
577
  };
529
- var flattenStory = (story) => {
530
- const entries = Object.entries(story).map(([name, items]) => {
531
- return [name, flatActions(items)];
532
- });
533
- return Object.fromEntries(entries);
578
+ var getVolumeFromStore = (store2) => {
579
+ const { meta } = store2.get();
580
+ return {
581
+ music: meta[2],
582
+ sound: meta[3],
583
+ voice: meta[4]
584
+ };
534
585
  };
586
+
587
+ // src/utilities/actions-processing.ts
535
588
  var isExitImpossible = (path) => {
536
589
  const blockStatements = path.filter(([item]) => isBlockStatement(item));
537
590
  const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
@@ -543,27 +596,179 @@ var Novely = (() => {
543
596
  }
544
597
  return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
545
598
  };
546
- var getOppositeAction = (action) => {
547
- const MAP = {
548
- showCharacter: "hideCharacter",
549
- playSound: "stopSound",
550
- playMusic: "stopMusic",
551
- voice: "stopVoice"
552
- };
553
- return MAP[action];
554
- };
555
- var getActionsFromPath = (story, path, filter) => {
556
- let current = story;
557
- let precurrent;
558
- let ignoreNestedBefore = null;
559
- let index = 0;
560
- let skipPreserve = void 0;
561
- const skip = /* @__PURE__ */ new Set();
562
- const max = path.reduce((acc, [type, val]) => {
563
- if (isNull(type) && isNumber(val)) {
564
- return acc + 1;
565
- }
566
- return acc;
599
+ var createReferFunction = (story) => {
600
+ const refer = (path) => {
601
+ let current = story;
602
+ let precurrent = story;
603
+ const blocks = [];
604
+ for (const [type, val] of path) {
605
+ if (type === "jump") {
606
+ precurrent = story;
607
+ current = current[val];
608
+ } else if (type === null) {
609
+ precurrent = current;
610
+ current = current[val];
611
+ } else if (type === "choice") {
612
+ blocks.push(precurrent);
613
+ current = current[val + 1][1];
614
+ } else if (type === "condition") {
615
+ blocks.push(precurrent);
616
+ current = current[2][val];
617
+ } else if (type === "block") {
618
+ blocks.push(precurrent);
619
+ current = story[val];
620
+ } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
621
+ current = blocks.pop();
622
+ }
623
+ }
624
+ return current;
625
+ };
626
+ return refer;
627
+ };
628
+ var exitPath = ({ path, refer, onExitImpossible }) => {
629
+ const last = path.at(-1);
630
+ const ignore = [];
631
+ let wasExitImpossible = false;
632
+ if (!isAction(refer(path))) {
633
+ if (last && isNull(last[0]) && isNumber(last[1])) {
634
+ last[1]--;
635
+ } else {
636
+ path.pop();
637
+ }
638
+ }
639
+ if (isExitImpossible(path)) {
640
+ const referred = refer(path);
641
+ if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
642
+ onExitImpossible?.();
643
+ }
644
+ wasExitImpossible = true;
645
+ return {
646
+ exitImpossible: wasExitImpossible
647
+ };
648
+ }
649
+ for (let i = path.length - 1; i > 0; i--) {
650
+ const [name] = path[i];
651
+ if (isBlockExitStatement(name)) {
652
+ ignore.push(name);
653
+ }
654
+ if (!isBlockStatement(name)) continue;
655
+ if (ignore.at(-1)?.startsWith(name)) {
656
+ ignore.pop();
657
+ continue;
658
+ }
659
+ path.push([`${name}:exit`]);
660
+ const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
661
+ if (prev) path.push([null, prev[1] + 1]);
662
+ if (!isAction(refer(path))) {
663
+ path.pop();
664
+ continue;
665
+ }
666
+ break;
667
+ }
668
+ return {
669
+ exitImpossible: wasExitImpossible
670
+ };
671
+ };
672
+ var nextPath = (path) => {
673
+ const last = path.at(-1);
674
+ if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
675
+ last[1]++;
676
+ } else {
677
+ path.push([null, 0]);
678
+ }
679
+ return path;
680
+ };
681
+ var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
682
+ const collection = [];
683
+ let action = refer(path);
684
+ while (true) {
685
+ if (action == void 0) {
686
+ const { exitImpossible } = exitPath({
687
+ path,
688
+ refer
689
+ });
690
+ if (exitImpossible) {
691
+ break;
692
+ }
693
+ }
694
+ if (!action) {
695
+ break;
696
+ }
697
+ if (isBlockingAction(action)) {
698
+ const [name, ...props] = action;
699
+ if (name === "choice") {
700
+ const choiceProps = props;
701
+ for (let i = 0; i < choiceProps.length; i++) {
702
+ const branchContent = choiceProps[i];
703
+ if (!Array.isArray(branchContent)) continue;
704
+ const virtualPath = clone(path);
705
+ virtualPath.push(["choice", i], [null, 0]);
706
+ const innerActions = collectActionsBeforeBlockingAction({
707
+ path: virtualPath,
708
+ refer,
709
+ clone
710
+ });
711
+ collection.push(...innerActions);
712
+ }
713
+ } else if (name === "condition") {
714
+ const conditionProps = props;
715
+ const conditions = Object.keys(conditionProps[1]);
716
+ for (const condition of conditions) {
717
+ const virtualPath = clone(path);
718
+ virtualPath.push(["condition", condition], [null, 0]);
719
+ const innerActions = collectActionsBeforeBlockingAction({
720
+ path: virtualPath,
721
+ refer,
722
+ clone
723
+ });
724
+ collection.push(...innerActions);
725
+ }
726
+ }
727
+ break;
728
+ }
729
+ collection.push(action);
730
+ if (action[0] === "jump") {
731
+ path = [
732
+ ["jump", action[1]],
733
+ [null, 0]
734
+ ];
735
+ } else if (action[0] == "block") {
736
+ path.push(["block", action[1]], [null, 0]);
737
+ } else {
738
+ nextPath(path);
739
+ }
740
+ action = refer(path);
741
+ }
742
+ return collection;
743
+ };
744
+ var findLastPathItemBeforeItemOfType = (path, name) => {
745
+ const item = path.findLast(([_name, _value], i, array) => {
746
+ const next = array[i + 1];
747
+ return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
748
+ });
749
+ return item;
750
+ };
751
+ var getOppositeAction = (action) => {
752
+ const MAP = {
753
+ showCharacter: "hideCharacter",
754
+ playSound: "stopSound",
755
+ playMusic: "stopMusic",
756
+ voice: "stopVoice"
757
+ };
758
+ return MAP[action];
759
+ };
760
+ var getActionsFromPath = (story, path, filter) => {
761
+ let current = story;
762
+ let precurrent;
763
+ let ignoreNestedBefore = null;
764
+ let index = 0;
765
+ let skipPreserve = void 0;
766
+ const skip = /* @__PURE__ */ new Set();
767
+ const max = path.reduce((acc, [type, val]) => {
768
+ if (isNull(type) && isNumber(val)) {
769
+ return acc + 1;
770
+ }
771
+ return acc;
567
772
  }, 0);
568
773
  const queue = [];
569
774
  const blocks = [];
@@ -648,7 +853,7 @@ var Novely = (() => {
648
853
  const c1 = fn;
649
854
  const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
650
855
  const isIdenticalByReference = c0 === c1;
651
- return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
856
+ return isIdenticalID || isIdenticalByReference || String(c0) === String(c1);
652
857
  });
653
858
  if (notLatest) continue;
654
859
  } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
@@ -715,53 +920,32 @@ var Novely = (() => {
715
920
  }
716
921
  };
717
922
  };
718
- var getStack = memoize(
719
- (_) => {
720
- return [];
721
- },
722
- {
723
- cache: STACK_MAP,
724
- getCacheKey: (ctx) => ctx.id
725
- }
726
- );
727
- var createUseStackFunction = (renderer) => {
728
- const useStack = (context) => {
729
- const ctx = typeof context === "string" ? renderer.getContext(context) : context;
730
- const stack = getStack(ctx);
731
- return {
732
- get previous() {
733
- return stack.previous;
734
- },
735
- get value() {
736
- return stack.at(-1);
737
- },
738
- set value(value) {
739
- stack[stack.length - 1] = value;
740
- },
741
- back() {
742
- if (stack.length > 1) {
743
- stack.previous = stack.pop();
744
- ctx.meta.goingBack = true;
745
- }
746
- },
747
- push(value) {
748
- stack.push(value);
749
- },
750
- clear() {
751
- stack.previous = void 0;
752
- stack.length = 0;
753
- stack.length = 1;
754
- }
755
- };
923
+
924
+ // src/utilities/controlled-promise.ts
925
+ var createControlledPromise = () => {
926
+ const object = {
927
+ resolve: null,
928
+ reject: null,
929
+ promise: null,
930
+ cancel: null
756
931
  };
757
- return useStack;
758
- };
759
- var mapSet = (set2, fn) => {
760
- return [...set2].map(fn);
761
- };
762
- var isImageAsset = (asset2) => {
763
- return isString(asset2) && isCSSImage(asset2);
932
+ const init = () => {
933
+ const promise = new Promise((resolve, reject) => {
934
+ object.reject = reject;
935
+ object.resolve = (value) => {
936
+ resolve({ cancelled: false, value });
937
+ };
938
+ object.cancel = () => {
939
+ resolve({ cancelled: true, value: null });
940
+ init();
941
+ };
942
+ });
943
+ object.promise = promise;
944
+ };
945
+ return init(), object;
764
946
  };
947
+
948
+ // src/utilities/resources.ts
765
949
  var getUrlFileExtension = (address) => {
766
950
  try {
767
951
  const { pathname } = new URL(address, location.href);
@@ -788,7 +972,7 @@ var Novely = (() => {
788
972
  };
789
973
  var getResourseType = memoize(
790
974
  async ({ url, request }) => {
791
- if (!isCSSImage(url)) {
975
+ if (!isCSSImageURL(url)) {
792
976
  return "other";
793
977
  }
794
978
  const extension = getUrlFileExtension(url);
@@ -805,274 +989,75 @@ var Novely = (() => {
805
989
  if (contentType.includes("image")) {
806
990
  return "image";
807
991
  }
808
- return "other";
809
- },
810
- {
811
- getCacheKey: ({ url }) => url
812
- }
813
- );
814
- var capitalize = (str2) => {
815
- return str2[0].toUpperCase() + str2.slice(1);
816
- };
817
- var getIntlLanguageDisplayName = memoize((lang) => {
818
- try {
819
- const intl = new Intl.DisplayNames([lang], {
820
- type: "language"
821
- });
822
- return intl.of(lang) || lang;
823
- } catch {
824
- return lang;
825
- }
826
- });
827
- var createReferFunction = (story) => {
828
- const refer = (path) => {
829
- let current = story;
830
- let precurrent = story;
831
- const blocks = [];
832
- for (const [type, val] of path) {
833
- if (type === "jump") {
834
- precurrent = story;
835
- current = current[val];
836
- } else if (type === null) {
837
- precurrent = current;
838
- current = current[val];
839
- } else if (type === "choice") {
840
- blocks.push(precurrent);
841
- current = current[val + 1][1];
842
- } else if (type === "condition") {
843
- blocks.push(precurrent);
844
- current = current[2][val];
845
- } else if (type === "block") {
846
- blocks.push(precurrent);
847
- current = story[val];
848
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
849
- current = blocks.pop();
850
- }
851
- }
852
- return current;
853
- };
854
- return refer;
855
- };
856
- var exitPath = ({ path, refer, onExitImpossible }) => {
857
- const last = path.at(-1);
858
- const ignore = [];
859
- let wasExitImpossible = false;
860
- if (!isAction(refer(path))) {
861
- if (last && isNull(last[0]) && isNumber(last[1])) {
862
- last[1]--;
863
- } else {
864
- path.pop();
865
- }
866
- }
867
- if (isExitImpossible(path)) {
868
- const referred = refer(path);
869
- if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
870
- onExitImpossible?.();
871
- }
872
- wasExitImpossible = true;
873
- return {
874
- exitImpossible: wasExitImpossible
875
- };
876
- }
877
- for (let i = path.length - 1; i > 0; i--) {
878
- const [name] = path[i];
879
- if (isBlockExitStatement(name)) {
880
- ignore.push(name);
881
- }
882
- if (!isBlockStatement(name)) continue;
883
- if (ignore.at(-1)?.startsWith(name)) {
884
- ignore.pop();
885
- continue;
886
- }
887
- path.push([`${name}:exit`]);
888
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
889
- if (prev) path.push([null, prev[1] + 1]);
890
- if (!isAction(refer(path))) {
891
- path.pop();
892
- continue;
893
- }
894
- break;
895
- }
896
- return {
897
- exitImpossible: wasExitImpossible
898
- };
899
- };
900
- var nextPath = (path) => {
901
- const last = path.at(-1);
902
- if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
903
- last[1]++;
904
- } else {
905
- path.push([null, 0]);
906
- }
907
- return path;
908
- };
909
- var isBlockingAction = (action) => {
910
- return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
911
- };
912
- var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
913
- const collection = [];
914
- let action = refer(path);
915
- while (true) {
916
- if (action == void 0) {
917
- const { exitImpossible } = exitPath({
918
- path,
919
- refer
920
- });
921
- if (exitImpossible) {
922
- break;
923
- }
924
- }
925
- if (!action) {
926
- break;
927
- }
928
- if (isBlockingAction(action)) {
929
- const [name, ...props] = action;
930
- if (name === "choice") {
931
- const choiceProps = props;
932
- for (let i = 0; i < choiceProps.length; i++) {
933
- const branchContent = choiceProps[i];
934
- if (!Array.isArray(branchContent)) continue;
935
- const virtualPath = clone(path);
936
- virtualPath.push(["choice", i], [null, 0]);
937
- const innerActions = collectActionsBeforeBlockingAction({
938
- path: virtualPath,
939
- refer,
940
- clone
941
- });
942
- collection.push(...innerActions);
943
- }
944
- } else if (name === "condition") {
945
- const conditionProps = props;
946
- const conditions = Object.keys(conditionProps[1]);
947
- for (const condition of conditions) {
948
- const virtualPath = clone(path);
949
- virtualPath.push(["condition", condition], [null, 0]);
950
- const innerActions = collectActionsBeforeBlockingAction({
951
- path: virtualPath,
952
- refer,
953
- clone
954
- });
955
- collection.push(...innerActions);
956
- }
957
- }
958
- break;
959
- }
960
- collection.push(action);
961
- if (action[0] === "jump") {
962
- path = [
963
- ["jump", action[1]],
964
- [null, 0]
965
- ];
966
- } else if (action[0] == "block") {
967
- path.push(["block", action[1]], [null, 0]);
968
- } else {
969
- nextPath(path);
970
- }
971
- action = refer(path);
972
- }
973
- return collection;
974
- };
975
- var unwrapAsset = (asset2) => {
976
- return isAsset(asset2) ? asset2.source : asset2;
977
- };
978
- var handleAudioAsset = (asset2) => {
979
- if (DEV && isAsset(asset2) && asset2.type !== "audio") {
980
- throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
981
- }
982
- return unwrapAsset(asset2);
983
- };
984
- var handleImageAsset = (asset2) => {
985
- if (DEV && isAsset(asset2) && asset2.type !== "image") {
986
- throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
987
- }
988
- return unwrapAsset(asset2);
989
- };
990
- var getCharactersData = (characters) => {
991
- const entries = Object.entries(characters);
992
- const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
993
- return Object.fromEntries(mapped);
994
- };
995
- var toArray = (target) => {
996
- return Array.isArray(target) ? target : [target];
997
- };
998
- var getLanguageFromStore = (store2) => {
999
- return store2.get().meta[0];
1000
- };
1001
- var getVolumeFromStore = (store2) => {
1002
- const { meta } = store2.get();
1003
- return {
1004
- music: meta[2],
1005
- sound: meta[3],
1006
- voice: meta[4]
1007
- };
1008
- };
1009
-
1010
- // src/asset.ts
1011
- var getType = memoize(
1012
- (extensions) => {
1013
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
1014
- return "audio";
1015
- }
1016
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
1017
- return "image";
1018
- }
1019
- throw extensions;
992
+ return "other";
1020
993
  },
1021
994
  {
1022
- getCacheKey: (extensions) => extensions.join("~")
995
+ getCacheKey: ({ url }) => url
1023
996
  }
1024
997
  );
1025
- var SUPPORT_MAPS = {
1026
- image: supportsMap2,
1027
- audio: supportsMap
1028
- };
1029
- var assetPrivate = memoize(
1030
- (variants) => {
1031
- if (DEV && variants.length === 0) {
1032
- throw new Error(`Attempt to use "asset" function without arguments`);
1033
- }
1034
- const map = {};
1035
- const extensions = [];
1036
- for (const v of variants) {
1037
- const e = getUrlFileExtension(v);
1038
- map[e] = v;
1039
- extensions.push(e);
1040
- }
1041
- const type = getType(extensions);
1042
- const getSource = once(() => {
1043
- const support = SUPPORT_MAPS[type];
1044
- for (const extension of extensions) {
1045
- if (extension in support) {
1046
- if (support[extension]) {
1047
- return map[extension];
1048
- }
1049
- } else {
1050
- return map[extension];
1051
- }
1052
- }
1053
- if (DEV) {
1054
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
1055
- }
1056
- return "";
1057
- });
1058
- return {
1059
- get source() {
1060
- return getSource();
1061
- },
1062
- get type() {
1063
- return type;
1064
- }
1065
- };
998
+
999
+ // src/shared.ts
1000
+ var STACK_MAP = /* @__PURE__ */ new Map();
1001
+ var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
1002
+ var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
1003
+ var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
1004
+
1005
+ // src/utilities/stack.ts
1006
+ var getStack = memoize(
1007
+ (_) => {
1008
+ return [];
1066
1009
  },
1067
1010
  {
1068
- getCacheKey: (variants) => variants.join("~")
1011
+ cache: STACK_MAP,
1012
+ getCacheKey: (ctx) => ctx.id
1069
1013
  }
1070
1014
  );
1071
- var asset = (...variants) => {
1072
- return assetPrivate(variants);
1015
+ var createUseStackFunction = (renderer) => {
1016
+ const useStack = (context) => {
1017
+ const ctx = typeof context === "string" ? renderer.getContext(context) : context;
1018
+ const stack = getStack(ctx);
1019
+ return {
1020
+ get previous() {
1021
+ return stack.previous;
1022
+ },
1023
+ get value() {
1024
+ return stack.at(-1);
1025
+ },
1026
+ set value(value) {
1027
+ stack[stack.length - 1] = value;
1028
+ },
1029
+ back() {
1030
+ if (stack.length > 1) {
1031
+ stack.previous = stack.pop();
1032
+ ctx.meta.goingBack = true;
1033
+ }
1034
+ },
1035
+ push(value) {
1036
+ stack.push(value);
1037
+ },
1038
+ clear() {
1039
+ stack.previous = void 0;
1040
+ stack.length = 0;
1041
+ stack.length = 1;
1042
+ }
1043
+ };
1044
+ };
1045
+ return useStack;
1073
1046
  };
1074
- var isAsset = (suspect) => {
1075
- return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
1047
+
1048
+ // src/utilities/story.ts
1049
+ var flatActions = (item) => {
1050
+ return item.flatMap((data) => {
1051
+ const type = data[0];
1052
+ if (Array.isArray(type)) return flatActions(data);
1053
+ return [data];
1054
+ });
1055
+ };
1056
+ var flatStory = (story) => {
1057
+ const entries = Object.entries(story).map(([name, items]) => {
1058
+ return [name, flatActions(items)];
1059
+ });
1060
+ return Object.fromEntries(entries);
1076
1061
  };
1077
1062
 
1078
1063
  // src/browser.ts
@@ -1399,7 +1384,7 @@ var Novely = (() => {
1399
1384
  };
1400
1385
  const scriptBase = async (part) => {
1401
1386
  if (destroyed) return;
1402
- Object.assign(story, flattenStory(part));
1387
+ Object.assign(story, flatStory(part));
1403
1388
  let loadingIsShown = false;
1404
1389
  if (!initialScreenWasShown) {
1405
1390
  renderer.ui.showLoading();
@@ -1442,7 +1427,8 @@ var Novely = (() => {
1442
1427
  throw new Error(`Attempt to call Say action with unknown character "${character}"`);
1443
1428
  }
1444
1429
  } else if (action2 === "choice") {
1445
- if (props.slice(1).every((choice) => !Array.isArray(choice))) {
1430
+ const actions = props.slice(1);
1431
+ if (actions.every((choice) => !Array.isArray(choice))) {
1446
1432
  for (let i = 1; i < props.length; i++) {
1447
1433
  const choice = props[i];
1448
1434
  props[i] = [
@@ -1454,6 +1440,13 @@ var Novely = (() => {
1454
1440
  choice.image
1455
1441
  ];
1456
1442
  }
1443
+ } else {
1444
+ for (let i = 1; i < props.length; i++) {
1445
+ const choice = props[i];
1446
+ if (Array.isArray(choice) && choice.length === 2) {
1447
+ choice[1] = flatActions(choice[1]);
1448
+ }
1449
+ }
1457
1450
  }
1458
1451
  }
1459
1452
  if (preloadAssets === "blocking") {
@@ -1478,7 +1471,8 @@ var Novely = (() => {
1478
1471
  [null, 0]
1479
1472
  ],
1480
1473
  state,
1481
- [intime(Date.now()), "auto"]
1474
+ [intime(Date.now()), "auto"],
1475
+ []
1482
1476
  ];
1483
1477
  };
1484
1478
  const getLanguageWithoutParameters = () => {
@@ -1799,6 +1793,61 @@ var Novely = (() => {
1799
1793
  const getCharacterAssets = (character, emotion) => {
1800
1794
  return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1801
1795
  };
1796
+ const getCharacterName = (character) => {
1797
+ const c = character;
1798
+ const cs = characters;
1799
+ const [lang] = storageData.get().meta;
1800
+ if (c && c in cs) {
1801
+ const block = cs[c].name;
1802
+ if (typeof block === "string") {
1803
+ return block;
1804
+ }
1805
+ if (lang in block) {
1806
+ return block[lang];
1807
+ }
1808
+ }
1809
+ return String(c) || "";
1810
+ };
1811
+ const getDialogOverview = () => {
1812
+ const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1813
+ const stateSnapshots = save2[3];
1814
+ const { queue } = getActionsFromPath(story, save2[0], false);
1815
+ const [lang] = storageData.get().meta;
1816
+ const dialogItem = [];
1817
+ for (let p = 0, i = 0; i < queue.length; i++) {
1818
+ const action2 = queue[i];
1819
+ if (action2[0] === "dialog") {
1820
+ const [_, name, text] = action2;
1821
+ let voice = void 0;
1822
+ for (let j = i - 1; j > p; j--) {
1823
+ const action3 = queue[j];
1824
+ if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1825
+ if (action3[0] === "stopVoice") break;
1826
+ if (action3[0] === "voice") {
1827
+ voice = action3[1];
1828
+ break;
1829
+ }
1830
+ }
1831
+ dialogItem.push({
1832
+ name,
1833
+ text,
1834
+ voice
1835
+ });
1836
+ p = i;
1837
+ }
1838
+ }
1839
+ const entries = dialogItem.map(({ name, text, voice }, i) => {
1840
+ const state = stateSnapshots[i];
1841
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1842
+ name = name ? getCharacterName(name) : "";
1843
+ return {
1844
+ name: templateReplace(name, state),
1845
+ text: templateReplace(text, state),
1846
+ voice: audioSource ? handleAudioAsset(audioSource) : ""
1847
+ };
1848
+ });
1849
+ return entries;
1850
+ };
1802
1851
  const renderer = createRenderer({
1803
1852
  mainContextKey: MAIN_CONTEXT_KEY,
1804
1853
  characters: getCharactersData(characters),
@@ -1820,6 +1869,7 @@ var Novely = (() => {
1820
1869
  getLanguageDisplayName,
1821
1870
  getCharacterColor,
1822
1871
  getCharacterAssets,
1872
+ getDialogOverview,
1823
1873
  getResourseType: getResourseTypeForRenderer
1824
1874
  });
1825
1875
  const useStack = createUseStackFunction(renderer);
@@ -1882,9 +1932,9 @@ var Novely = (() => {
1882
1932
  }
1883
1933
  };
1884
1934
  const match = matchAction(matchActionOptions, {
1885
- wait({ ctx, push }, [time]) {
1935
+ wait({ ctx, data: data2, push }, [time]) {
1886
1936
  if (ctx.meta.restoring) return;
1887
- setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
1937
+ setTimeout(push, isFunction(time) ? time(data2) : time);
1888
1938
  },
1889
1939
  showBackground({ ctx, push }, [background]) {
1890
1940
  if (isString(background) || isAsset(background)) {
@@ -1954,21 +2004,11 @@ var Novely = (() => {
1954
2004
  ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
1955
2005
  },
1956
2006
  dialog({ ctx, data: data2, forward }, [character, content, emotion]) {
1957
- const name = (() => {
1958
- const c = character;
1959
- const cs = characters;
1960
- const [lang] = storageData.get().meta;
1961
- if (c && c in cs) {
1962
- const block = cs[c].name;
1963
- if (typeof block === "string") {
1964
- return block;
1965
- }
1966
- if (lang in block) {
1967
- return block[lang];
1968
- }
1969
- }
1970
- return c || "";
1971
- })();
2007
+ const name = getCharacterName(character);
2008
+ const stack = useStack(ctx);
2009
+ if (!ctx.meta.restoring && !ctx.meta.goingBack) {
2010
+ stack.value[3].push(clone(data2));
2011
+ }
1972
2012
  ctx.clearBlockingActions("dialog");
1973
2013
  ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
1974
2014
  },
@@ -1995,19 +2035,19 @@ var Novely = (() => {
1995
2035
  const transformedChoices = choices.map(([content, _children, active, visible, onSelect, image]) => {
1996
2036
  const active$ = store(false);
1997
2037
  const visible$ = store(false);
1998
- const update = () => {
1999
- const lang = getLanguageFromStore(storageData);
2000
- const state = getStateAtCtx(ctx);
2001
- const activeValue = !active || active({
2002
- lang,
2003
- state
2004
- });
2005
- const visibleValue = !visible || visible({
2038
+ const lang = getLanguageFromStore(storageData);
2039
+ const getCheckValue = (fn) => {
2040
+ if (!fn) {
2041
+ return true;
2042
+ }
2043
+ return fn({
2006
2044
  lang,
2007
- state
2045
+ state: getStateAtCtx(ctx)
2008
2046
  });
2009
- active$.set(activeValue);
2010
- visible$.set(visibleValue);
2047
+ };
2048
+ const update = () => {
2049
+ active$.set(getCheckValue(active));
2050
+ visible$.set(getCheckValue(visible));
2011
2051
  };
2012
2052
  update();
2013
2053
  const onSelectGuarded = onSelect || noop;
@@ -2051,6 +2091,7 @@ var Novely = (() => {
2051
2091
  ["jump", scene],
2052
2092
  [null, -1]
2053
2093
  ];
2094
+ stack.value[3] = [];
2054
2095
  match("clear", [], {
2055
2096
  ctx,
2056
2097
  data: data2
@@ -2065,12 +2106,12 @@ var Novely = (() => {
2065
2106
  push
2066
2107
  );
2067
2108
  },
2068
- condition({ ctx }, [condition, variants]) {
2109
+ condition({ ctx, data: data2 }, [condition, variants]) {
2069
2110
  if (DEV && Object.values(variants).length === 0) {
2070
2111
  throw new Error(`Attempt to use Condition action with empty variants object`);
2071
2112
  }
2072
2113
  if (!ctx.meta.restoring) {
2073
- const val = String(condition(getStateAtCtx(ctx)));
2114
+ const val = String(condition(data2));
2074
2115
  if (DEV && !variants[val]) {
2075
2116
  throw new Error(`Attempt to go to unknown variant "${val}"`);
2076
2117
  }
@@ -2189,22 +2230,23 @@ var Novely = (() => {
2189
2230
  });
2190
2231
  const render = (ctx) => {
2191
2232
  const stack = useStack(ctx);
2192
- const referred = refer(stack.value[0]);
2233
+ const [path, state] = stack.value;
2234
+ const referred = refer(path);
2193
2235
  if (isAction(referred)) {
2194
2236
  const [action2, ...props] = referred;
2195
2237
  match(action2, props, {
2196
2238
  ctx,
2197
- data: stack.value[1]
2239
+ data: state
2198
2240
  });
2199
2241
  } else if (Object.values(story).some((branch) => branch === referred)) {
2200
2242
  match("end", [], {
2201
2243
  ctx,
2202
- data: stack.value[1]
2244
+ data: state
2203
2245
  });
2204
2246
  } else {
2205
2247
  match("exit", [], {
2206
2248
  ctx,
2207
- data: stack.value[1]
2249
+ data: state
2208
2250
  });
2209
2251
  }
2210
2252
  };
@@ -2217,10 +2259,10 @@ var Novely = (() => {
2217
2259
  meta: [lang]
2218
2260
  } = storageData.get();
2219
2261
  const obj = values || data2;
2220
- const str2 = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
2262
+ const str = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
2221
2263
  const t2 = translation[lang];
2222
2264
  const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
2223
- return replace(str2, obj, t2.plural, t2.actions, pluralRules);
2265
+ return replace(str, obj, t2.plural, t2.actions, pluralRules);
2224
2266
  };
2225
2267
  const data = (value) => {
2226
2268
  const _data = storageData.get().data;
@@ -2417,7 +2459,9 @@ var Novely = (() => {
2417
2459
  CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2418
2460
  MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2419
2461
  SoundVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0437\u0432\u0443\u043A\u043E\u0432",
2420
- VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438"
2462
+ VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2463
+ Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2464
+ DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2421
2465
  };
2422
2466
  var EN = {
2423
2467
  NewGame: "New Game",
@@ -2453,7 +2497,9 @@ var Novely = (() => {
2453
2497
  CloseMenu: "Close menu",
2454
2498
  MusicVolume: "Music volume",
2455
2499
  SoundVolume: "Sound volume",
2456
- VoiceVolume: "Voice volume"
2500
+ VoiceVolume: "Voice volume",
2501
+ Close: "Close",
2502
+ DialogOverview: "Dialog Overview"
2457
2503
  };
2458
2504
  var KK = {
2459
2505
  NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
@@ -2489,7 +2535,9 @@ var Novely = (() => {
2489
2535
  CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2490
2536
  MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2491
2537
  SoundVolume: "\u0414\u044B\u0431\u044B\u0441\u0442\u0430\u0440\u0434\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2492
- VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456"
2538
+ VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2539
+ Close: "\u0416\u0430\u0431\u0443",
2540
+ DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2493
2541
  };
2494
2542
  var JP = {
2495
2543
  NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
@@ -2525,7 +2573,133 @@ var Novely = (() => {
2525
2573
  CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2526
2574
  MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2527
2575
  SoundVolume: "\u97F3\u91CF",
2528
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF"
2576
+ VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2577
+ Close: "\u9589\u3058\u308B",
2578
+ DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2579
+ };
2580
+
2581
+ // src/audio-codecs.ts
2582
+ var cut = (str) => str.replace(/^no$/, "");
2583
+ var audio = new Audio();
2584
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
2585
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2586
+ var supportsMap = {
2587
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2588
+ mpeg: canPlay("audio/mpeg;"),
2589
+ opus: canPlay('audio/ogg; codecs="opus"'),
2590
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
2591
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
2592
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2593
+ aac: canPlay("audio/aac;"),
2594
+ caf: canPlay("audio/x-caf;"),
2595
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2596
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2597
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2598
+ weba: canPlay('audio/webm; codecs="vorbis"'),
2599
+ webm: canPlay('audio/webm; codecs="vorbis"'),
2600
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
2601
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2602
+ };
2603
+
2604
+ // src/image-formats.ts
2605
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2606
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2607
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2608
+ var supportsFormat = (source) => {
2609
+ const { promise, resolve } = Promise.withResolvers();
2610
+ const img = Object.assign(document.createElement("img"), {
2611
+ src: source
2612
+ });
2613
+ img.onload = img.onerror = () => {
2614
+ resolve(img.height === 2);
2615
+ };
2616
+ return promise;
2617
+ };
2618
+ var supportsMap2 = {
2619
+ avif: false,
2620
+ jxl: false,
2621
+ webp: false
2622
+ };
2623
+ var formatsMap = {
2624
+ avif,
2625
+ jxl,
2626
+ webp
2627
+ };
2628
+ var loadImageFormatsSupport = async () => {
2629
+ const promises = [];
2630
+ for (const [format, source] of Object.entries(formatsMap)) {
2631
+ const promise = supportsFormat(source).then((supported) => {
2632
+ supportsMap2[format] = supported;
2633
+ });
2634
+ promises.push(promise);
2635
+ }
2636
+ await Promise.all(promises);
2637
+ };
2638
+ loadImageFormatsSupport();
2639
+
2640
+ // src/asset.ts
2641
+ var getType = memoize(
2642
+ (extensions) => {
2643
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2644
+ return "audio";
2645
+ }
2646
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2647
+ return "image";
2648
+ }
2649
+ throw extensions;
2650
+ },
2651
+ {
2652
+ getCacheKey: (extensions) => extensions.join("~")
2653
+ }
2654
+ );
2655
+ var SUPPORT_MAPS = {
2656
+ image: supportsMap2,
2657
+ audio: supportsMap
2658
+ };
2659
+ var assetPrivate = memoize(
2660
+ (variants) => {
2661
+ if (DEV && variants.length === 0) {
2662
+ throw new Error(`Attempt to use "asset" function without arguments`);
2663
+ }
2664
+ const map = {};
2665
+ const extensions = [];
2666
+ for (const v of variants) {
2667
+ const e = getUrlFileExtension(v);
2668
+ map[e] = v;
2669
+ extensions.push(e);
2670
+ }
2671
+ const type = getType(extensions);
2672
+ const getSource = once(() => {
2673
+ const support = SUPPORT_MAPS[type];
2674
+ for (const extension of extensions) {
2675
+ if (extension in support) {
2676
+ if (support[extension]) {
2677
+ return map[extension];
2678
+ }
2679
+ } else {
2680
+ return map[extension];
2681
+ }
2682
+ }
2683
+ if (DEV) {
2684
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2685
+ }
2686
+ return "";
2687
+ });
2688
+ return {
2689
+ get source() {
2690
+ return getSource();
2691
+ },
2692
+ get type() {
2693
+ return type;
2694
+ }
2695
+ };
2696
+ },
2697
+ {
2698
+ getCacheKey: (variants) => variants.join("~")
2699
+ }
2700
+ );
2701
+ var asset = (...variants) => {
2702
+ return assetPrivate(variants);
2529
2703
  };
2530
2704
  return __toCommonJS(src_exports);
2531
2705
  })();