@novely/core 0.45.1 → 0.46.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,15 +1,64 @@
1
+ // src/novely.ts
2
+ import { dequal } from "dequal/lite";
3
+ import { throttle } from "es-toolkit/function";
4
+ import { merge as deepmerge } from "es-toolkit/object";
5
+ import { DEV as DEV3 } from "esm-env";
6
+
7
+ // ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/full/index.mjs
8
+ function set(obj, key, val) {
9
+ if (typeof val.value === "object") val.value = klona(val.value);
10
+ if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") {
11
+ Object.defineProperty(obj, key, val);
12
+ } else obj[key] = val.value;
13
+ }
14
+ function klona(x) {
15
+ if (typeof x !== "object") return x;
16
+ var i = 0, k, list, tmp, str = Object.prototype.toString.call(x);
17
+ if (str === "[object Object]") {
18
+ tmp = Object.create(x.__proto__ || null);
19
+ } else if (str === "[object Array]") {
20
+ tmp = Array(x.length);
21
+ } else if (str === "[object Set]") {
22
+ tmp = /* @__PURE__ */ new Set();
23
+ x.forEach(function(val) {
24
+ tmp.add(klona(val));
25
+ });
26
+ } else if (str === "[object Map]") {
27
+ tmp = /* @__PURE__ */ new Map();
28
+ x.forEach(function(val, key) {
29
+ tmp.set(klona(key), klona(val));
30
+ });
31
+ } else if (str === "[object Date]") {
32
+ tmp = /* @__PURE__ */ new Date(+x);
33
+ } else if (str === "[object RegExp]") {
34
+ tmp = new RegExp(x.source, x.flags);
35
+ } else if (str === "[object DataView]") {
36
+ tmp = new x.constructor(klona(x.buffer));
37
+ } else if (str === "[object ArrayBuffer]") {
38
+ tmp = x.slice(0);
39
+ } else if (str.slice(-6) === "Array]") {
40
+ tmp = new x.constructor(x);
41
+ }
42
+ if (tmp) {
43
+ for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
44
+ set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
45
+ }
46
+ for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
47
+ if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue;
48
+ set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
49
+ }
50
+ }
51
+ return tmp || x;
52
+ }
53
+
54
+ // src/novely.ts
55
+ import pLimit from "p-limit";
56
+
1
57
  // src/constants.ts
2
58
  var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
3
59
  var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
4
60
  var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
5
- var AUDIO_ACTIONS = /* @__PURE__ */ new Set([
6
- "playMusic",
7
- "stopMusic",
8
- "playSound",
9
- "stopSound",
10
- "voice",
11
- "stopVoice"
12
- ]);
61
+ var AUDIO_ACTIONS = /* @__PURE__ */ new Set(["playMusic", "stopMusic", "playSound", "stopSound", "voice", "stopVoice"]);
13
62
  var EMPTY_SET = /* @__PURE__ */ new Set();
14
63
  var DEFAULT_TYPEWRITER_SPEED = "Medium";
15
64
  var HOWLER_SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set([
@@ -45,170 +94,7 @@ var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
45
94
  ]);
46
95
  var MAIN_CONTEXT_KEY = "$MAIN";
47
96
 
48
- // src/shared.ts
49
- var STACK_MAP = /* @__PURE__ */ new Map();
50
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
51
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
52
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
53
-
54
- // src/utils.ts
55
- import { DEV as DEV2 } from "esm-env";
56
- import { memoize as memoize2 } from "es-toolkit/function";
57
-
58
- // src/asset.ts
59
- import { DEV } 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
- // src/image-formats.ts
85
- var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
86
- var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
87
- var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
88
- var supportsFormat = (source) => {
89
- const { promise, resolve } = Promise.withResolvers();
90
- const img = Object.assign(document.createElement("img"), {
91
- src: source
92
- });
93
- img.onload = img.onerror = () => {
94
- resolve(img.height === 2);
95
- };
96
- return promise;
97
- };
98
- var supportsMap2 = {
99
- avif: false,
100
- jxl: false,
101
- webp: false
102
- };
103
- var formatsMap = {
104
- avif,
105
- jxl,
106
- webp
107
- };
108
- var loadImageFormatsSupport = async () => {
109
- const promises = [];
110
- for (const [format, source] of Object.entries(formatsMap)) {
111
- const promise = supportsFormat(source).then((supported) => {
112
- supportsMap2[format] = supported;
113
- });
114
- promises.push(promise);
115
- }
116
- await Promise.all(promises);
117
- };
118
- loadImageFormatsSupport();
119
-
120
- // src/asset.ts
121
- import { memoize, once } from "es-toolkit/function";
122
- var getType = memoize(
123
- (extensions) => {
124
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
125
- return "audio";
126
- }
127
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
128
- return "image";
129
- }
130
- throw extensions;
131
- },
132
- {
133
- getCacheKey: (extensions) => extensions.join("~")
134
- }
135
- );
136
- var SUPPORT_MAPS = {
137
- "image": supportsMap2,
138
- "audio": supportsMap
139
- };
140
- var assetPrivate = memoize(
141
- (variants) => {
142
- if (DEV && variants.length === 0) {
143
- throw new Error(`Attempt to use "asset" function without arguments`);
144
- }
145
- const map = {};
146
- const extensions = [];
147
- for (const v of variants) {
148
- const e = getUrlFileExtension(v);
149
- map[e] = v;
150
- extensions.push(e);
151
- }
152
- const type = getType(extensions);
153
- const getSource = once(() => {
154
- const support = SUPPORT_MAPS[type];
155
- for (const extension of extensions) {
156
- if (extension in support) {
157
- if (support[extension]) {
158
- return map[extension];
159
- }
160
- } else {
161
- return map[extension];
162
- }
163
- }
164
- if (DEV) {
165
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
166
- }
167
- return "";
168
- });
169
- return {
170
- get source() {
171
- return getSource();
172
- },
173
- get type() {
174
- return type;
175
- }
176
- };
177
- },
178
- {
179
- getCacheKey: (variants) => variants.join("~")
180
- }
181
- );
182
- var asset = (...variants) => {
183
- return assetPrivate(variants);
184
- };
185
- var isAsset = (suspect) => {
186
- return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
187
- };
188
-
189
- // src/utils.ts
190
- var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
191
- return (action, props, { ctx, data }) => {
192
- const context = typeof ctx === "string" ? getContext(ctx) : ctx;
193
- onBeforeActionCall({
194
- action,
195
- props,
196
- ctx: context
197
- });
198
- return values[action]({
199
- ctx: context,
200
- data,
201
- push() {
202
- if (context.meta.preview) return;
203
- push(context);
204
- },
205
- forward() {
206
- if (context.meta.preview) return;
207
- forward(context);
208
- }
209
- }, props);
210
- };
211
- };
97
+ // src/utilities/assertions.ts
212
98
  var isNumber = (val) => {
213
99
  return typeof val === "number";
214
100
  };
@@ -227,54 +113,13 @@ var isPromise = (val) => {
227
113
  var isEmpty = (val) => {
228
114
  return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
229
115
  };
230
- var isCSSImage = (str2) => {
231
- const startsWith = String.prototype.startsWith.bind(str2);
116
+ var isCSSImageURL = (url) => {
117
+ const startsWith = String.prototype.startsWith.bind(url);
232
118
  return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
233
119
  };
234
- var str = String;
235
120
  var isUserRequiredAction = ([action, ...meta]) => {
236
121
  return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
237
122
  };
238
- var getLanguage = (languages) => {
239
- let { language } = navigator;
240
- if (languages.includes(language)) {
241
- return language;
242
- } else if (languages.includes(language = language.slice(0, 2))) {
243
- return language;
244
- } else if (language = languages.find((value) => navigator.languages.includes(value))) {
245
- return language;
246
- }
247
- return languages[0];
248
- };
249
- var createControlledPromise = () => {
250
- const object = {
251
- resolve: null,
252
- reject: null,
253
- promise: null,
254
- cancel: null
255
- };
256
- const init = () => {
257
- const promise = new Promise((resolve, reject) => {
258
- object.reject = reject;
259
- object.resolve = (value) => {
260
- resolve({ cancelled: false, value });
261
- };
262
- object.cancel = () => {
263
- resolve({ cancelled: true, value: null });
264
- init();
265
- };
266
- });
267
- object.promise = promise;
268
- };
269
- return init(), object;
270
- };
271
- var findLastPathItemBeforeItemOfType = (path, name) => {
272
- const item = path.findLast(([_name, _value], i, array) => {
273
- const next = array[i + 1];
274
- return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
275
- });
276
- return item;
277
- };
278
123
  var isBlockStatement = (statement) => {
279
124
  return BLOCK_STATEMENTS.has(statement);
280
125
  };
@@ -287,41 +132,284 @@ var isSkippedDuringRestore = (item) => {
287
132
  var isAudioAction = (action) => {
288
133
  return AUDIO_ACTIONS.has(action);
289
134
  };
290
- var noop = () => {
291
- };
292
135
  var isAction = (element) => {
293
136
  return Array.isArray(element) && isString(element[0]);
294
137
  };
295
- var flatActions = (item) => {
296
- return item.flatMap((data) => {
297
- const type = data[0];
298
- if (Array.isArray(type)) return flatActions(data);
299
- return [data];
300
- });
301
- };
302
- var flattenStory = (story) => {
303
- const entries = Object.entries(story).map(([name, items]) => {
304
- return [name, flatActions(items)];
305
- });
306
- return Object.fromEntries(entries);
138
+ var isImageAsset = (asset2) => {
139
+ return isString(asset2) && isCSSImageURL(asset2);
307
140
  };
308
- var isExitImpossible = (path) => {
309
- const blockStatements = path.filter(([item]) => isBlockStatement(item));
310
- const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
311
- if (blockStatements.length === 0 && blockExitStatements.length === 0) {
312
- return true;
313
- }
314
- if (blockStatements.length > blockExitStatements.length) {
315
- return false;
316
- }
317
- return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
141
+ var isBlockingAction = (action) => {
142
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
143
+ };
144
+ var isAsset = (suspect) => {
145
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
146
+ };
147
+
148
+ // src/utilities/match-action.ts
149
+ var matchAction = (callbacks, values) => {
150
+ const { getContext, onBeforeActionCall, push, forward } = callbacks;
151
+ return (action, props, { ctx, data }) => {
152
+ const context = typeof ctx === "string" ? getContext(ctx) : ctx;
153
+ onBeforeActionCall({
154
+ action,
155
+ props,
156
+ ctx: context
157
+ });
158
+ return values[action](
159
+ {
160
+ ctx: context,
161
+ data,
162
+ push() {
163
+ if (context.meta.preview) return;
164
+ push(context);
165
+ },
166
+ forward() {
167
+ if (context.meta.preview) return;
168
+ forward(context);
169
+ }
170
+ },
171
+ props
172
+ );
173
+ };
174
+ };
175
+
176
+ // src/utilities/ungrupped.ts
177
+ import { memoize } from "es-toolkit/function";
178
+ import { DEV } from "esm-env";
179
+ var getLanguage = (languages) => {
180
+ let { language } = navigator;
181
+ if (languages.includes(language)) {
182
+ return language;
183
+ } else if (languages.includes(language = language.slice(0, 2))) {
184
+ return language;
185
+ } else if (language = languages.find((value) => navigator.languages.includes(value))) {
186
+ return language;
187
+ }
188
+ return languages[0];
189
+ };
190
+ var noop = () => {
191
+ };
192
+ var mapSet = (set2, fn) => {
193
+ return [...set2].map(fn);
194
+ };
195
+ var capitalize = (str) => {
196
+ return str[0].toUpperCase() + str.slice(1);
197
+ };
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;
210
+ };
211
+ var handleAudioAsset = (asset2) => {
212
+ if (DEV && isAsset(asset2) && asset2.type !== "audio") {
213
+ throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
214
+ }
215
+ return unwrapAsset(asset2);
216
+ };
217
+ var handleImageAsset = (asset2) => {
218
+ if (DEV && isAsset(asset2) && asset2.type !== "image") {
219
+ throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
220
+ }
221
+ return unwrapAsset(asset2);
222
+ };
223
+ var getCharactersData = (characters) => {
224
+ const entries = Object.entries(characters);
225
+ const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
226
+ return Object.fromEntries(mapped);
227
+ };
228
+ var toArray = (target) => {
229
+ return Array.isArray(target) ? target : [target];
230
+ };
231
+ var getLanguageFromStore = (store2) => {
232
+ return store2.get().meta[0];
233
+ };
234
+ var getVolumeFromStore = (store2) => {
235
+ const { meta } = store2.get();
236
+ return {
237
+ music: meta[2],
238
+ sound: meta[3],
239
+ voice: meta[4]
240
+ };
241
+ };
242
+
243
+ // src/utilities/actions-processing.ts
244
+ var isExitImpossible = (path) => {
245
+ const blockStatements = path.filter(([item]) => isBlockStatement(item));
246
+ const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
247
+ if (blockStatements.length === 0 && blockExitStatements.length === 0) {
248
+ return true;
249
+ }
250
+ if (blockStatements.length > blockExitStatements.length) {
251
+ return false;
252
+ }
253
+ return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
254
+ };
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;
281
+ };
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;
318
406
  };
319
407
  var getOppositeAction = (action) => {
320
408
  const MAP = {
321
- "showCharacter": "hideCharacter",
322
- "playSound": "stopSound",
323
- "playMusic": "stopMusic",
324
- "voice": "stopVoice"
409
+ showCharacter: "hideCharacter",
410
+ playSound: "stopSound",
411
+ playMusic: "stopMusic",
412
+ voice: "stopVoice"
325
413
  };
326
414
  return MAP[action];
327
415
  };
@@ -421,7 +509,7 @@ var createQueueProcessor = (queue, options) => {
421
509
  const c1 = fn;
422
510
  const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
423
511
  const isIdenticalByReference = c0 === c1;
424
- return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
512
+ return isIdenticalID || isIdenticalByReference || String(c0) === String(c1);
425
513
  });
426
514
  if (notLatest) continue;
427
515
  } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
@@ -488,53 +576,34 @@ var createQueueProcessor = (queue, options) => {
488
576
  }
489
577
  };
490
578
  };
491
- var getStack = memoize2(
492
- (_) => {
493
- return [];
494
- },
495
- {
496
- cache: STACK_MAP,
497
- getCacheKey: (ctx) => ctx.id
498
- }
499
- );
500
- var createUseStackFunction = (renderer) => {
501
- const useStack = (context) => {
502
- const ctx = typeof context === "string" ? renderer.getContext(context) : context;
503
- const stack = getStack(ctx);
504
- return {
505
- get previous() {
506
- return stack.previous;
507
- },
508
- get value() {
509
- return stack.at(-1);
510
- },
511
- set value(value) {
512
- stack[stack.length - 1] = value;
513
- },
514
- back() {
515
- if (stack.length > 1) {
516
- stack.previous = stack.pop();
517
- ctx.meta.goingBack = true;
518
- }
519
- },
520
- push(value) {
521
- stack.push(value);
522
- },
523
- clear() {
524
- stack.previous = void 0;
525
- stack.length = 0;
526
- stack.length = 1;
527
- }
528
- };
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
529
587
  };
530
- return useStack;
531
- };
532
- var mapSet = (set2, fn) => {
533
- return [...set2].map(fn);
534
- };
535
- var isImageAsset = (asset2) => {
536
- 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;
537
602
  };
603
+
604
+ // src/utilities/resources.ts
605
+ import { memoize as memoize2 } from "es-toolkit/function";
606
+ import { DEV as DEV2 } from "esm-env";
538
607
  var getUrlFileExtension = (address) => {
539
608
  try {
540
609
  const { pathname } = new URL(address, location.href);
@@ -561,7 +630,7 @@ var fetchContentType = async (url, request) => {
561
630
  };
562
631
  var getResourseType = memoize2(
563
632
  async ({ url, request }) => {
564
- if (!isCSSImage(url)) {
633
+ if (!isCSSImageURL(url)) {
565
634
  return "other";
566
635
  }
567
636
  const extension = getUrlFileExtension(url);
@@ -584,313 +653,88 @@ var getResourseType = memoize2(
584
653
  getCacheKey: ({ url }) => url
585
654
  }
586
655
  );
587
- var capitalize = (str2) => {
588
- return str2[0].toUpperCase() + str2.slice(1);
589
- };
590
- var getIntlLanguageDisplayName = memoize2((lang) => {
591
- try {
592
- const intl = new Intl.DisplayNames([lang], {
593
- type: "language"
594
- });
595
- return intl.of(lang) || lang;
596
- } catch {
597
- return lang;
598
- }
599
- });
600
- var createReferFunction = (story) => {
601
- const refer = (path) => {
602
- let current = story;
603
- let precurrent = story;
604
- const blocks = [];
605
- for (const [type, val] of path) {
606
- if (type === "jump") {
607
- precurrent = story;
608
- current = current[val];
609
- } else if (type === null) {
610
- precurrent = current;
611
- current = current[val];
612
- } else if (type === "choice") {
613
- blocks.push(precurrent);
614
- current = current[val + 1][1];
615
- } else if (type === "condition") {
616
- blocks.push(precurrent);
617
- current = current[2][val];
618
- } else if (type === "block") {
619
- blocks.push(precurrent);
620
- current = story[val];
621
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
622
- current = blocks.pop();
623
- }
624
- }
625
- return current;
626
- };
627
- return refer;
628
- };
629
- var exitPath = ({ path, refer, onExitImpossible }) => {
630
- const last = path.at(-1);
631
- const ignore = [];
632
- let wasExitImpossible = false;
633
- if (!isAction(refer(path))) {
634
- if (last && isNull(last[0]) && isNumber(last[1])) {
635
- last[1]--;
636
- } else {
637
- path.pop();
638
- }
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 [];
670
+ },
671
+ {
672
+ cache: STACK_MAP,
673
+ getCacheKey: (ctx) => ctx.id
639
674
  }
640
- if (isExitImpossible(path)) {
641
- const referred = refer(path);
642
- if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
643
- onExitImpossible?.();
644
- }
645
- wasExitImpossible = true;
675
+ );
676
+ var createUseStackFunction = (renderer) => {
677
+ const useStack = (context) => {
678
+ const ctx = typeof context === "string" ? renderer.getContext(context) : context;
679
+ const stack = getStack(ctx);
646
680
  return {
647
- exitImpossible: wasExitImpossible
648
- };
649
- }
650
- for (let i = path.length - 1; i > 0; i--) {
651
- const [name] = path[i];
652
- if (isBlockExitStatement(name)) {
653
- ignore.push(name);
654
- }
655
- if (!isBlockStatement(name)) continue;
656
- if (ignore.at(-1)?.startsWith(name)) {
657
- ignore.pop();
658
- continue;
659
- }
660
- path.push([`${name}:exit`]);
661
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
662
- if (prev) path.push([null, prev[1] + 1]);
663
- if (!isAction(refer(path))) {
664
- path.pop();
665
- continue;
666
- }
667
- break;
668
- }
669
- return {
670
- exitImpossible: wasExitImpossible
671
- };
672
- };
673
- var nextPath = (path) => {
674
- const last = path.at(-1);
675
- if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
676
- last[1]++;
677
- } else {
678
- path.push([null, 0]);
679
- }
680
- return path;
681
- };
682
- var isBlockingAction = (action) => {
683
- return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
684
- };
685
- var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
686
- const collection = [];
687
- let action = refer(path);
688
- while (true) {
689
- if (action == void 0) {
690
- const { exitImpossible } = exitPath({
691
- path,
692
- refer
693
- });
694
- if (exitImpossible) {
695
- break;
696
- }
697
- }
698
- if (!action) {
699
- break;
700
- }
701
- if (isBlockingAction(action)) {
702
- const [name, ...props] = action;
703
- if (name === "choice") {
704
- const choiceProps = props;
705
- for (let i = 0; i < choiceProps.length; i++) {
706
- const branchContent = choiceProps[i];
707
- if (!Array.isArray(branchContent)) continue;
708
- const virtualPath = clone(path);
709
- virtualPath.push(["choice", i], [null, 0]);
710
- const innerActions = collectActionsBeforeBlockingAction({
711
- path: virtualPath,
712
- refer,
713
- clone
714
- });
715
- collection.push(...innerActions);
716
- }
717
- } else if (name === "condition") {
718
- const conditionProps = props;
719
- const conditions = Object.keys(conditionProps[1]);
720
- for (const condition of conditions) {
721
- const virtualPath = clone(path);
722
- virtualPath.push(["condition", condition], [null, 0]);
723
- const innerActions = collectActionsBeforeBlockingAction({
724
- path: virtualPath,
725
- refer,
726
- clone
727
- });
728
- collection.push(...innerActions);
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;
729
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;
730
703
  }
731
- break;
732
- }
733
- collection.push(action);
734
- if (action[0] === "jump") {
735
- path = [["jump", action[1]], [null, 0]];
736
- } else if (action[0] == "block") {
737
- path.push(["block", action[1]], [null, 0]);
738
- } else {
739
- nextPath(path);
740
- }
741
- action = refer(path);
742
- }
743
- return collection;
744
- };
745
- var unwrapAsset = (asset2) => {
746
- return isAsset(asset2) ? asset2.source : asset2;
747
- };
748
- var handleAudioAsset = (asset2) => {
749
- if (DEV2 && isAsset(asset2) && asset2.type !== "audio") {
750
- throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
751
- }
752
- return unwrapAsset(asset2);
753
- };
754
- var handleImageAsset = (asset2) => {
755
- if (DEV2 && isAsset(asset2) && asset2.type !== "image") {
756
- throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
757
- }
758
- return unwrapAsset(asset2);
759
- };
760
- var getCharactersData = (characters) => {
761
- const entries = Object.entries(characters);
762
- const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
763
- return Object.fromEntries(mapped);
764
- };
765
- var toArray = (target) => {
766
- return Array.isArray(target) ? target : [target];
767
- };
768
- var getLanguageFromStore = (store2) => {
769
- return store2.get().meta[0];
770
- };
771
- var getVolumeFromStore = (store2) => {
772
- const { meta } = store2.get();
773
- return {
774
- music: meta[2],
775
- sound: meta[3],
776
- voice: meta[4]
777
- };
778
- };
779
-
780
- // src/novely.ts
781
- import { throttle } from "es-toolkit/function";
782
- import { merge as deepmerge } from "es-toolkit/object";
783
- import { dequal } from "dequal/lite";
784
-
785
- // src/store.ts
786
- var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
787
- const subscribe = (cb) => {
788
- subscribers.add(cb), cb(current);
789
- return () => {
790
- subscribers.delete(cb);
791
704
  };
792
705
  };
793
- const push = (value) => {
794
- for (const cb of subscribers) cb(value);
795
- };
796
- const update = (fn) => {
797
- push(current = fn(current));
798
- };
799
- const set2 = (val) => {
800
- update(() => val);
801
- };
802
- const get = () => {
803
- return current;
804
- };
805
- return { subscribe, update, set: set2, get };
706
+ return useStack;
806
707
  };
807
708
 
808
- // ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/full/index.mjs
809
- function set(obj, key, val) {
810
- if (typeof val.value === "object") val.value = klona(val.value);
811
- if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") {
812
- Object.defineProperty(obj, key, val);
813
- } else obj[key] = val.value;
814
- }
815
- function klona(x) {
816
- if (typeof x !== "object") return x;
817
- var i = 0, k, list, tmp, str2 = Object.prototype.toString.call(x);
818
- if (str2 === "[object Object]") {
819
- tmp = Object.create(x.__proto__ || null);
820
- } else if (str2 === "[object Array]") {
821
- tmp = Array(x.length);
822
- } else if (str2 === "[object Set]") {
823
- tmp = /* @__PURE__ */ new Set();
824
- x.forEach(function(val) {
825
- tmp.add(klona(val));
826
- });
827
- } else if (str2 === "[object Map]") {
828
- tmp = /* @__PURE__ */ new Map();
829
- x.forEach(function(val, key) {
830
- tmp.set(klona(key), klona(val));
831
- });
832
- } else if (str2 === "[object Date]") {
833
- tmp = /* @__PURE__ */ new Date(+x);
834
- } else if (str2 === "[object RegExp]") {
835
- tmp = new RegExp(x.source, x.flags);
836
- } else if (str2 === "[object DataView]") {
837
- tmp = new x.constructor(klona(x.buffer));
838
- } else if (str2 === "[object ArrayBuffer]") {
839
- tmp = x.slice(0);
840
- } else if (str2.slice(-6) === "Array]") {
841
- tmp = new x.constructor(x);
842
- }
843
- if (tmp) {
844
- for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
845
- set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
846
- }
847
- for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
848
- if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue;
849
- set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
850
- }
851
- }
852
- return tmp || x;
853
- }
854
-
855
- // src/translation.ts
856
- var RGX = /{{(.*?)}}/g;
857
- var split = (input, delimeters) => {
858
- const output = [];
859
- for (const delimeter of delimeters) {
860
- if (!input) break;
861
- const [start, end] = input.split(delimeter, 2);
862
- output.push(start);
863
- input = end;
864
- }
865
- output.push(input);
866
- return output;
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
+ });
867
716
  };
868
- var flattenAllowedContent = (c, state) => {
869
- if (Array.isArray(c)) {
870
- return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
871
- }
872
- if (typeof c === "function") {
873
- return flattenAllowedContent(c(state), state);
874
- }
875
- return c;
717
+ var flatStory = (story) => {
718
+ const entries = Object.entries(story).map(([name, items]) => {
719
+ return [name, flatActions(items)];
720
+ });
721
+ return Object.fromEntries(entries);
876
722
  };
877
- var replace = (input, data, pluralization, actions, pr) => {
878
- return input.replaceAll(RGX, (x, key, y) => {
879
- x = 0;
880
- y = data;
881
- const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
882
- if (!pathstr) {
883
- return "";
884
- }
885
- const path = pathstr.split(".");
886
- while (y && x < path.length) y = y[path[x++]];
887
- if (plural && pluralization && y && pr) {
888
- y = pluralization[plural][pr.select(y)];
723
+
724
+ // src/browser.ts
725
+ var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
726
+ if (typeof document === "undefined") return noop;
727
+ const onVisibilityChange = () => {
728
+ if (document.visibilityState === "hidden") {
729
+ onChange();
889
730
  }
890
- const actionHandler = actions && action ? actions[action] : void 0;
891
- if (actionHandler) y = actionHandler(y);
892
- return y == null ? "" : y;
893
- });
731
+ };
732
+ addEventListener("visibilitychange", onVisibilityChange);
733
+ addEventListener("beforeunload", onChange);
734
+ return () => {
735
+ removeEventListener("visibilitychange", onVisibilityChange);
736
+ removeEventListener("beforeunload", onChange);
737
+ };
894
738
  };
895
739
 
896
740
  // src/custom-action.ts
@@ -933,13 +777,15 @@ var handleCustomAction = (ctx, fn, { lang, state, setMountElement, setClear, rem
933
777
  };
934
778
  };
935
779
  const clear = (func) => {
936
- setClear(holder.cleanup = () => {
937
- func();
938
- holder.node = null;
939
- holder.cleanup = noop;
940
- setMountElement(null);
941
- setClear(noop);
942
- });
780
+ setClear(
781
+ holder.cleanup = () => {
782
+ func();
783
+ holder.node = null;
784
+ holder.cleanup = noop;
785
+ setMountElement(null);
786
+ setClear(noop);
787
+ }
788
+ );
943
789
  };
944
790
  const data = (updatedData) => {
945
791
  if (updatedData) {
@@ -952,76 +798,40 @@ var handleCustomAction = (ctx, fn, { lang, state, setMountElement, setClear, rem
952
798
  renderersRemove();
953
799
  };
954
800
  const stack = getStack2(ctx);
955
- const getPath = () => {
956
- return stack.value[0];
957
- };
958
- return fn({
959
- flags,
960
- lang,
961
- state,
962
- data,
963
- clear,
964
- remove,
965
- rendererContext: ctx,
966
- getDomNodes,
967
- getPath,
968
- contextKey: ctx.id
969
- });
970
- };
971
-
972
- // src/storage.ts
973
- var localStorageStorage = (options) => {
974
- return {
975
- async get() {
976
- const fallback = { saves: [], data: {}, meta: [] };
977
- try {
978
- const value = localStorage.getItem(options.key);
979
- return value ? JSON.parse(value) : fallback;
980
- } catch {
981
- return fallback;
982
- }
983
- },
984
- async set(data) {
985
- try {
986
- localStorage.setItem(options.key, JSON.stringify(data));
987
- } catch {
988
- }
989
- }
990
- };
991
- };
992
-
993
- // src/browser.ts
994
- var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
995
- if (typeof document === "undefined") return noop;
996
- const onVisibilityChange = () => {
997
- if (document.visibilityState === "hidden") {
998
- onChange();
999
- }
1000
- };
1001
- addEventListener("visibilitychange", onVisibilityChange);
1002
- addEventListener("beforeunload", onChange);
1003
- return () => {
1004
- removeEventListener("visibilitychange", onVisibilityChange);
1005
- removeEventListener("beforeunload", onChange);
801
+ const getPath = () => {
802
+ return stack.value[0];
1006
803
  };
804
+ return fn({
805
+ flags,
806
+ lang,
807
+ state,
808
+ data,
809
+ clear,
810
+ remove,
811
+ rendererContext: ctx,
812
+ getDomNodes,
813
+ getPath,
814
+ contextKey: ctx.id
815
+ });
1007
816
  };
1008
817
 
1009
- // src/novely.ts
1010
- import pLimit from "p-limit";
1011
- import { DEV as DEV3 } from "esm-env";
1012
-
1013
818
  // src/preloading.ts
1014
819
  var ACTION_NAME_TO_VOLUME_MAP = {
1015
- "playMusic": "music",
1016
- "playSound": "sound",
1017
- "voice": "voice"
820
+ playMusic: "music",
821
+ playSound: "sound",
822
+ voice: "voice"
1018
823
  };
1019
824
  var enqueueAssetForPreloading = (asset2) => {
1020
825
  if (!PRELOADED_ASSETS.has(asset2)) {
1021
826
  ASSETS_TO_PRELOAD.add(asset2);
1022
827
  }
1023
828
  };
1024
- var handleAssetsPreloading = async ({ request, limiter, preloadAudioBlocking, preloadImageBlocking }) => {
829
+ var handleAssetsPreloading = async ({
830
+ request,
831
+ limiter,
832
+ preloadAudioBlocking,
833
+ preloadImageBlocking
834
+ }) => {
1025
835
  const list = mapSet(ASSETS_TO_PRELOAD, (asset2) => {
1026
836
  return limiter(async () => {
1027
837
  const type = await getResourseType({
@@ -1096,9 +906,9 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
1096
906
  }
1097
907
  return;
1098
908
  }
1099
- if (action === "custom" && props[0].assets && props[0].assets.length > 0) {
909
+ if (action === "custom" && props[0].assets) {
1100
910
  for (const asset2 of props[0].assets) {
1101
- handle(asset2);
911
+ isAsset(asset2) ? handle(asset2.source) : handle(asset2);
1102
912
  }
1103
913
  return;
1104
914
  }
@@ -1106,10 +916,95 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
1106
916
  for (let i = 1; i < props.length; i++) {
1107
917
  const data = props[i];
1108
918
  if (Array.isArray(data)) {
1109
- handle(handleImageAsset(data[4]));
919
+ handle(handleImageAsset(data[5]));
920
+ }
921
+ }
922
+ }
923
+ };
924
+
925
+ // src/storage.ts
926
+ var localStorageStorage = (options) => {
927
+ return {
928
+ async get() {
929
+ const fallback = { saves: [], data: {}, meta: [] };
930
+ try {
931
+ const value = localStorage.getItem(options.key);
932
+ return value ? JSON.parse(value) : fallback;
933
+ } catch {
934
+ return fallback;
935
+ }
936
+ },
937
+ async set(data) {
938
+ try {
939
+ localStorage.setItem(options.key, JSON.stringify(data));
940
+ } catch {
1110
941
  }
1111
942
  }
943
+ };
944
+ };
945
+
946
+ // src/store.ts
947
+ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
948
+ const subscribe = (cb) => {
949
+ subscribers.add(cb), cb(current);
950
+ return () => {
951
+ subscribers.delete(cb);
952
+ };
953
+ };
954
+ const push = (value) => {
955
+ for (const cb of subscribers) cb(value);
956
+ };
957
+ const update = (fn) => {
958
+ push(current = fn(current));
959
+ };
960
+ const set2 = (val) => {
961
+ update(() => val);
962
+ };
963
+ const get = () => {
964
+ return current;
965
+ };
966
+ return { subscribe, update, set: set2, get };
967
+ };
968
+
969
+ // src/translation.ts
970
+ var RGX = /{{(.*?)}}/g;
971
+ var split = (input, delimeters) => {
972
+ const output = [];
973
+ for (const delimeter of delimeters) {
974
+ if (!input) break;
975
+ const [start, end] = input.split(delimeter, 2);
976
+ output.push(start);
977
+ input = end;
978
+ }
979
+ output.push(input);
980
+ return output;
981
+ };
982
+ var flattenAllowedContent = (c, state) => {
983
+ if (Array.isArray(c)) {
984
+ return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
985
+ }
986
+ if (typeof c === "function") {
987
+ return flattenAllowedContent(c(state), state);
1112
988
  }
989
+ return c;
990
+ };
991
+ var replace = (input, data, pluralization, actions, pr) => {
992
+ return input.replaceAll(RGX, (x, key, y) => {
993
+ x = 0;
994
+ y = data;
995
+ const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
996
+ if (!pathstr) {
997
+ return "";
998
+ }
999
+ const path = pathstr.split(".");
1000
+ while (y && x < path.length) y = y[path[x++]];
1001
+ if (plural && pluralization && y && pr) {
1002
+ y = pluralization[plural][pr.select(y)];
1003
+ }
1004
+ const actionHandler = actions && action ? actions[action] : void 0;
1005
+ if (actionHandler) y = actionHandler(y);
1006
+ return y == null ? "" : y;
1007
+ });
1113
1008
  };
1114
1009
 
1115
1010
  // src/novely.ts
@@ -1150,7 +1045,7 @@ var novely = ({
1150
1045
  };
1151
1046
  const scriptBase = async (part) => {
1152
1047
  if (destroyed) return;
1153
- Object.assign(story, flattenStory(part));
1048
+ Object.assign(story, flatStory(part));
1154
1049
  let loadingIsShown = false;
1155
1050
  if (!initialScreenWasShown) {
1156
1051
  renderer.ui.showLoading();
@@ -1229,7 +1124,8 @@ var novely = ({
1229
1124
  [null, 0]
1230
1125
  ],
1231
1126
  state,
1232
- [intime(Date.now()), "auto"]
1127
+ [intime(Date.now()), "auto"],
1128
+ []
1233
1129
  ];
1234
1130
  };
1235
1131
  const getLanguageWithoutParameters = () => {
@@ -1238,7 +1134,9 @@ var novely = ({
1238
1134
  return language;
1239
1135
  }
1240
1136
  if (DEV3) {
1241
- throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
1137
+ throw new Error(
1138
+ `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1139
+ );
1242
1140
  }
1243
1141
  throw 0;
1244
1142
  };
@@ -1354,7 +1252,9 @@ var novely = ({
1354
1252
  const restore = async (save2) => {
1355
1253
  if (isEmpty(story)) {
1356
1254
  if (DEV3) {
1357
- throw new Error("Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]");
1255
+ throw new Error(
1256
+ "Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]"
1257
+ );
1358
1258
  }
1359
1259
  return;
1360
1260
  }
@@ -1374,7 +1274,10 @@ var novely = ({
1374
1274
  const [path] = stack.value = latest;
1375
1275
  renderer.ui.showScreen("game");
1376
1276
  const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
1377
- const { run, keep: { keep, characters: characters2, audio: audio2 } } = createQueueProcessor(queue, {
1277
+ const {
1278
+ run,
1279
+ keep: { keep, characters: characters2, audio: audio2 }
1280
+ } = createQueueProcessor(queue, {
1378
1281
  skip,
1379
1282
  skipPreserve
1380
1283
  });
@@ -1424,7 +1327,9 @@ var novely = ({
1424
1327
  const isSaved = () => {
1425
1328
  const { saves } = storageData.get();
1426
1329
  const [currentPath, currentData] = stack.value;
1427
- return saves.some(([path, data2, [date, type2]]) => type2 === "manual" && times.has(date) && dequal(path, currentPath) && dequal(data2, currentData));
1330
+ return saves.some(
1331
+ ([path, data2, [date, type2]]) => type2 === "manual" && times.has(date) && dequal(path, currentPath) && dequal(data2, currentData)
1332
+ );
1428
1333
  };
1429
1334
  if (interacted > 1 && !force && askBeforeExit && !isSaved()) {
1430
1335
  renderer.ui.showExitPrompt();
@@ -1520,7 +1425,9 @@ var novely = ({
1520
1425
  const getLanguageDisplayName = (lang) => {
1521
1426
  const language = translation[lang];
1522
1427
  if (DEV3 && !language) {
1523
- throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
1428
+ throw new Error(
1429
+ `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1430
+ );
1524
1431
  }
1525
1432
  return capitalize(language.nameOverride || getIntlLanguageDisplayName(lang));
1526
1433
  };
@@ -1539,6 +1446,61 @@ var novely = ({
1539
1446
  const getCharacterAssets = (character, emotion) => {
1540
1447
  return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1541
1448
  };
1449
+ const getCharacterName = (character) => {
1450
+ const c = character;
1451
+ const cs = characters;
1452
+ const [lang] = storageData.get().meta;
1453
+ if (c && c in cs) {
1454
+ const block = cs[c].name;
1455
+ if (typeof block === "string") {
1456
+ return block;
1457
+ }
1458
+ if (lang in block) {
1459
+ return block[lang];
1460
+ }
1461
+ }
1462
+ return String(c) || "";
1463
+ };
1464
+ const getDialogOverview = () => {
1465
+ const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1466
+ const stateSnapshots = save2[3];
1467
+ const { queue } = getActionsFromPath(story, save2[0], false);
1468
+ const [lang] = storageData.get().meta;
1469
+ const dialogItem = [];
1470
+ for (let p = 0, i = 0; i < queue.length; i++) {
1471
+ const action2 = queue[i];
1472
+ if (action2[0] === "dialog") {
1473
+ const [_, name, text] = action2;
1474
+ let voice = void 0;
1475
+ for (let j = i - 1; j > p; j--) {
1476
+ const action3 = queue[j];
1477
+ if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1478
+ if (action3[0] === "stopVoice") break;
1479
+ if (action3[0] === "voice") {
1480
+ voice = action3[1];
1481
+ break;
1482
+ }
1483
+ }
1484
+ dialogItem.push({
1485
+ name,
1486
+ text,
1487
+ voice
1488
+ });
1489
+ p = i;
1490
+ }
1491
+ }
1492
+ const entries = dialogItem.map(({ name, text, voice }, i) => {
1493
+ const state = stateSnapshots[i];
1494
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1495
+ name = name ? getCharacterName(name) : "";
1496
+ return {
1497
+ name: templateReplace(name, state),
1498
+ text: templateReplace(text, state),
1499
+ voice: audioSource ? handleAudioAsset(audioSource) : ""
1500
+ };
1501
+ });
1502
+ return entries;
1503
+ };
1542
1504
  const renderer = createRenderer({
1543
1505
  mainContextKey: MAIN_CONTEXT_KEY,
1544
1506
  characters: getCharactersData(characters),
@@ -1560,6 +1522,7 @@ var novely = ({
1560
1522
  getLanguageDisplayName,
1561
1523
  getCharacterColor,
1562
1524
  getCharacterAssets,
1525
+ getDialogOverview,
1563
1526
  getResourseType: getResourseTypeForRenderer
1564
1527
  });
1565
1528
  const useStack = createUseStackFunction(renderer);
@@ -1622,17 +1585,19 @@ var novely = ({
1622
1585
  }
1623
1586
  };
1624
1587
  const match = matchAction(matchActionOptions, {
1625
- wait({ ctx, push }, [time]) {
1588
+ wait({ ctx, data: data2, push }, [time]) {
1626
1589
  if (ctx.meta.restoring) return;
1627
- setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
1590
+ setTimeout(push, isFunction(time) ? time(data2) : time);
1628
1591
  },
1629
1592
  showBackground({ ctx, push }, [background]) {
1630
1593
  if (isString(background) || isAsset(background)) {
1631
1594
  ctx.background({
1632
- "all": handleImageAsset(background)
1595
+ all: handleImageAsset(background)
1633
1596
  });
1634
1597
  } else {
1635
- ctx.background(Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)])));
1598
+ ctx.background(
1599
+ Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)]))
1600
+ );
1636
1601
  }
1637
1602
  push();
1638
1603
  },
@@ -1692,29 +1657,13 @@ var novely = ({
1692
1657
  ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
1693
1658
  },
1694
1659
  dialog({ ctx, data: data2, forward }, [character, content, emotion]) {
1695
- const name = (() => {
1696
- const c = character;
1697
- const cs = characters;
1698
- const [lang] = storageData.get().meta;
1699
- if (c && c in cs) {
1700
- const block = cs[c].name;
1701
- if (typeof block === "string") {
1702
- return block;
1703
- }
1704
- if (lang in block) {
1705
- return block[lang];
1706
- }
1707
- }
1708
- return c || "";
1709
- })();
1660
+ const name = getCharacterName(character);
1661
+ const stack = useStack(ctx);
1662
+ if (!ctx.meta.restoring && !ctx.meta.goingBack) {
1663
+ stack.value[3].push(clone(data2));
1664
+ }
1710
1665
  ctx.clearBlockingActions("dialog");
1711
- ctx.dialog(
1712
- templateReplace(content, data2),
1713
- templateReplace(name, data2),
1714
- character,
1715
- emotion,
1716
- forward
1717
- );
1666
+ ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
1718
1667
  },
1719
1668
  function({ ctx, push }, [fn]) {
1720
1669
  const { restoring, goingBack, preview: preview2 } = ctx.meta;
@@ -1739,19 +1688,19 @@ var novely = ({
1739
1688
  const transformedChoices = choices.map(([content, _children, active, visible, onSelect, image]) => {
1740
1689
  const active$ = store(false);
1741
1690
  const visible$ = store(false);
1742
- const update = () => {
1743
- const lang = getLanguageFromStore(storageData);
1744
- const state = getStateAtCtx(ctx);
1745
- const activeValue = !active || active({
1746
- lang,
1747
- state
1748
- });
1749
- const visibleValue = !visible || visible({
1691
+ const lang = getLanguageFromStore(storageData);
1692
+ const getCheckValue = (fn) => {
1693
+ if (!fn) {
1694
+ return true;
1695
+ }
1696
+ return fn({
1750
1697
  lang,
1751
- state
1698
+ state: getStateAtCtx(ctx)
1752
1699
  });
1753
- active$.set(activeValue);
1754
- visible$.set(visibleValue);
1700
+ };
1701
+ const update = () => {
1702
+ active$.set(getCheckValue(active));
1703
+ visible$.set(getCheckValue(visible));
1755
1704
  };
1756
1705
  update();
1757
1706
  const onSelectGuarded = onSelect || noop;
@@ -1764,7 +1713,9 @@ var novely = ({
1764
1713
  return [templateReplace(content, data2), active$, visible$, onSelectWrapped, imageValue];
1765
1714
  });
1766
1715
  if (DEV3 && transformedChoices.length === 0) {
1767
- throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
1716
+ throw new Error(
1717
+ `Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`
1718
+ );
1768
1719
  }
1769
1720
  ctx.clearBlockingActions("choice");
1770
1721
  ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
@@ -1793,6 +1744,7 @@ var novely = ({
1793
1744
  ["jump", scene],
1794
1745
  [null, -1]
1795
1746
  ];
1747
+ stack.value[3] = [];
1796
1748
  match("clear", [], {
1797
1749
  ctx,
1798
1750
  data: data2
@@ -1807,19 +1759,19 @@ var novely = ({
1807
1759
  push
1808
1760
  );
1809
1761
  },
1810
- condition({ ctx }, [condition, variants]) {
1762
+ condition({ ctx, data: data2 }, [condition, variants]) {
1811
1763
  if (DEV3 && Object.values(variants).length === 0) {
1812
1764
  throw new Error(`Attempt to use Condition action with empty variants object`);
1813
1765
  }
1814
1766
  if (!ctx.meta.restoring) {
1815
- const val = String(condition(getStateAtCtx(ctx)));
1767
+ const val = String(condition(data2));
1816
1768
  if (DEV3 && !variants[val]) {
1817
1769
  throw new Error(`Attempt to go to unknown variant "${val}"`);
1818
1770
  }
1819
1771
  if (DEV3 && variants[val].length === 0) {
1820
1772
  throw new Error(`Attempt to go to empty variant "${val}"`);
1821
1773
  }
1822
- const stack = useStack(MAIN_CONTEXT_KEY);
1774
+ const stack = useStack(ctx);
1823
1775
  stack.value[0].push(["condition", val], [null, 0]);
1824
1776
  render(ctx);
1825
1777
  }
@@ -1830,12 +1782,7 @@ var novely = ({
1830
1782
  },
1831
1783
  input({ ctx, data: data2, forward }, [question, onInput, setup]) {
1832
1784
  ctx.clearBlockingActions("input");
1833
- ctx.input(
1834
- templateReplace(question, data2),
1835
- onInput,
1836
- setup || noop,
1837
- forward
1838
- );
1785
+ ctx.input(templateReplace(question, data2), onInput, setup || noop, forward);
1839
1786
  },
1840
1787
  custom({ ctx, push }, [fn]) {
1841
1788
  if (fn.requireUserAction) {
@@ -1875,7 +1822,9 @@ var novely = ({
1875
1822
  animateCharacter({ ctx, push }, [character, className]) {
1876
1823
  const classes = className.split(" ");
1877
1824
  if (DEV3 && classes.length === 0) {
1878
- throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
1825
+ throw new Error(
1826
+ "Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]"
1827
+ );
1879
1828
  }
1880
1829
  if (ctx.meta.preview) return;
1881
1830
  ctx.character(character).animate(classes);
@@ -1909,7 +1858,9 @@ var novely = ({
1909
1858
  },
1910
1859
  preload({ ctx, push }, [source]) {
1911
1860
  if (DEV3 && preloadAssets !== "lazy") {
1912
- console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
1861
+ console.error(
1862
+ `You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`
1863
+ );
1913
1864
  }
1914
1865
  if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1915
1866
  PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
@@ -1932,22 +1883,23 @@ var novely = ({
1932
1883
  });
1933
1884
  const render = (ctx) => {
1934
1885
  const stack = useStack(ctx);
1935
- const referred = refer(stack.value[0]);
1886
+ const [path, state] = stack.value;
1887
+ const referred = refer(path);
1936
1888
  if (isAction(referred)) {
1937
1889
  const [action2, ...props] = referred;
1938
1890
  match(action2, props, {
1939
1891
  ctx,
1940
- data: stack.value[1]
1892
+ data: state
1941
1893
  });
1942
1894
  } else if (Object.values(story).some((branch) => branch === referred)) {
1943
1895
  match("end", [], {
1944
1896
  ctx,
1945
- data: stack.value[1]
1897
+ data: state
1946
1898
  });
1947
1899
  } else {
1948
1900
  match("exit", [], {
1949
1901
  ctx,
1950
- data: stack.value[1]
1902
+ data: state
1951
1903
  });
1952
1904
  }
1953
1905
  };
@@ -1955,21 +1907,15 @@ var novely = ({
1955
1907
  interacted = value ? interacted + 1 : 0;
1956
1908
  };
1957
1909
  const templateReplace = (content, values) => {
1958
- const { data: data2, meta: [lang] } = storageData.get();
1910
+ const {
1911
+ data: data2,
1912
+ meta: [lang]
1913
+ } = storageData.get();
1959
1914
  const obj = values || data2;
1960
- const str2 = flattenAllowedContent(
1961
- !isFunction(content) && !isString(content) ? content[lang] : content,
1962
- obj
1963
- );
1915
+ const str = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1964
1916
  const t2 = translation[lang];
1965
1917
  const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
1966
- return replace(
1967
- str2,
1968
- obj,
1969
- t2.plural,
1970
- t2.actions,
1971
- pluralRules
1972
- );
1918
+ return replace(str, obj, t2.plural, t2.actions, pluralRules);
1973
1919
  };
1974
1920
  const data = (value) => {
1975
1921
  const _data = storageData.get().data;
@@ -1993,7 +1939,9 @@ var novely = ({
1993
1939
  const setStorageData = (data2) => {
1994
1940
  if (destroyed) {
1995
1941
  if (DEV3) {
1996
- throw new Error(`function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`);
1942
+ throw new Error(
1943
+ `function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`
1944
+ );
1997
1945
  }
1998
1946
  return;
1999
1947
  }
@@ -2053,7 +2001,7 @@ var novely = ({
2053
2001
  * @example
2054
2002
  * ```ts
2055
2003
  * import type { ConditionParams, StateFunction } from '@novely/core';
2056
- *
2004
+ *
2057
2005
  * const conditionCheck = (state: StateFunction<ConditionParams<typeof engine.typeEssintials>>) => {
2058
2006
  * return state.age >= 18;
2059
2007
  * }
@@ -2164,7 +2112,9 @@ var RU = {
2164
2112
  CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2165
2113
  MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2166
2114
  SoundVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0437\u0432\u0443\u043A\u043E\u0432",
2167
- VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438"
2115
+ VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2116
+ Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2117
+ DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2168
2118
  };
2169
2119
  var EN = {
2170
2120
  NewGame: "New Game",
@@ -2200,7 +2150,9 @@ var EN = {
2200
2150
  CloseMenu: "Close menu",
2201
2151
  MusicVolume: "Music volume",
2202
2152
  SoundVolume: "Sound volume",
2203
- VoiceVolume: "Voice volume"
2153
+ VoiceVolume: "Voice volume",
2154
+ Close: "Close",
2155
+ DialogOverview: "Dialog Overview"
2204
2156
  };
2205
2157
  var KK = {
2206
2158
  NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
@@ -2236,7 +2188,9 @@ var KK = {
2236
2188
  CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2237
2189
  MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2238
2190
  SoundVolume: "\u0414\u044B\u0431\u044B\u0441\u0442\u0430\u0440\u0434\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2239
- VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456"
2191
+ VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2192
+ Close: "\u0416\u0430\u0431\u0443",
2193
+ DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2240
2194
  };
2241
2195
  var JP = {
2242
2196
  NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
@@ -2272,7 +2226,137 @@ var JP = {
2272
2226
  CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2273
2227
  MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2274
2228
  SoundVolume: "\u97F3\u91CF",
2275
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF"
2229
+ VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2230
+ Close: "\u9589\u3058\u308B",
2231
+ DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2232
+ };
2233
+
2234
+ // src/asset.ts
2235
+ import { memoize as memoize4, once } from "es-toolkit/function";
2236
+ import { DEV as DEV4 } from "esm-env";
2237
+
2238
+ // src/audio-codecs.ts
2239
+ var cut = (str) => str.replace(/^no$/, "");
2240
+ var audio = new Audio();
2241
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
2242
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2243
+ var supportsMap = {
2244
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2245
+ mpeg: canPlay("audio/mpeg;"),
2246
+ opus: canPlay('audio/ogg; codecs="opus"'),
2247
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
2248
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
2249
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2250
+ aac: canPlay("audio/aac;"),
2251
+ caf: canPlay("audio/x-caf;"),
2252
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2253
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2254
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2255
+ weba: canPlay('audio/webm; codecs="vorbis"'),
2256
+ webm: canPlay('audio/webm; codecs="vorbis"'),
2257
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
2258
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2259
+ };
2260
+
2261
+ // src/image-formats.ts
2262
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2263
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2264
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2265
+ var supportsFormat = (source) => {
2266
+ const { promise, resolve } = Promise.withResolvers();
2267
+ const img = Object.assign(document.createElement("img"), {
2268
+ src: source
2269
+ });
2270
+ img.onload = img.onerror = () => {
2271
+ resolve(img.height === 2);
2272
+ };
2273
+ return promise;
2274
+ };
2275
+ var supportsMap2 = {
2276
+ avif: false,
2277
+ jxl: false,
2278
+ webp: false
2279
+ };
2280
+ var formatsMap = {
2281
+ avif,
2282
+ jxl,
2283
+ webp
2284
+ };
2285
+ var loadImageFormatsSupport = async () => {
2286
+ const promises = [];
2287
+ for (const [format, source] of Object.entries(formatsMap)) {
2288
+ const promise = supportsFormat(source).then((supported) => {
2289
+ supportsMap2[format] = supported;
2290
+ });
2291
+ promises.push(promise);
2292
+ }
2293
+ await Promise.all(promises);
2294
+ };
2295
+ loadImageFormatsSupport();
2296
+
2297
+ // src/asset.ts
2298
+ var getType = memoize4(
2299
+ (extensions) => {
2300
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2301
+ return "audio";
2302
+ }
2303
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2304
+ return "image";
2305
+ }
2306
+ throw extensions;
2307
+ },
2308
+ {
2309
+ getCacheKey: (extensions) => extensions.join("~")
2310
+ }
2311
+ );
2312
+ var SUPPORT_MAPS = {
2313
+ image: supportsMap2,
2314
+ audio: supportsMap
2315
+ };
2316
+ var assetPrivate = memoize4(
2317
+ (variants) => {
2318
+ if (DEV4 && variants.length === 0) {
2319
+ throw new Error(`Attempt to use "asset" function without arguments`);
2320
+ }
2321
+ const map = {};
2322
+ const extensions = [];
2323
+ for (const v of variants) {
2324
+ const e = getUrlFileExtension(v);
2325
+ map[e] = v;
2326
+ extensions.push(e);
2327
+ }
2328
+ const type = getType(extensions);
2329
+ const getSource = once(() => {
2330
+ const support = SUPPORT_MAPS[type];
2331
+ for (const extension of extensions) {
2332
+ if (extension in support) {
2333
+ if (support[extension]) {
2334
+ return map[extension];
2335
+ }
2336
+ } else {
2337
+ return map[extension];
2338
+ }
2339
+ }
2340
+ if (DEV4) {
2341
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2342
+ }
2343
+ return "";
2344
+ });
2345
+ return {
2346
+ get source() {
2347
+ return getSource();
2348
+ },
2349
+ get type() {
2350
+ return type;
2351
+ }
2352
+ };
2353
+ },
2354
+ {
2355
+ getCacheKey: (variants) => variants.join("~")
2356
+ }
2357
+ );
2358
+ var asset = (...variants) => {
2359
+ return assetPrivate(variants);
2276
2360
  };
2277
2361
  export {
2278
2362
  EN,