@novely/core 0.49.0 → 0.50.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,6 +1,31 @@
1
+ // ../../node_modules/.pnpm/dequal@2.0.3/node_modules/dequal/lite/index.mjs
2
+ var has = Object.prototype.hasOwnProperty;
3
+ function dequal(foo, bar) {
4
+ var ctor, len;
5
+ if (foo === bar) return true;
6
+ if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
7
+ if (ctor === Date) return foo.getTime() === bar.getTime();
8
+ if (ctor === RegExp) return foo.toString() === bar.toString();
9
+ if (ctor === Array) {
10
+ if ((len = foo.length) === bar.length) {
11
+ while (len-- && dequal(foo[len], bar[len])) ;
12
+ }
13
+ return len === -1;
14
+ }
15
+ if (!ctor || typeof foo === "object") {
16
+ len = 0;
17
+ for (ctor in foo) {
18
+ if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
19
+ if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
20
+ }
21
+ return Object.keys(bar).length === len;
22
+ }
23
+ }
24
+ return foo !== foo && bar !== bar;
25
+ }
26
+
1
27
  // src/novely.ts
2
- import { dequal } from "dequal/lite";
3
- import { memoize as memoize4, throttle } from "es-toolkit/function";
28
+ import { memoize as memoize5, throttle } from "es-toolkit/function";
4
29
  import { merge as deepmerge } from "es-toolkit/object";
5
30
  import { DEV as DEV5 } from "esm-env";
6
31
 
@@ -94,6 +119,12 @@ var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
94
119
  ]);
95
120
  var MAIN_CONTEXT_KEY = "$MAIN";
96
121
 
122
+ // src/shared.ts
123
+ var STACK_MAP = /* @__PURE__ */ new Map();
124
+ var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
125
+ var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
126
+ var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
127
+
97
128
  // src/utilities/assertions.ts
98
129
  var isNumber = (val) => {
99
130
  return typeof val === "number";
@@ -177,72 +208,174 @@ var matchAction = (callbacks, values) => {
177
208
  };
178
209
  };
179
210
 
180
- // src/utilities/ungrupped.ts
181
- import { memoize } from "es-toolkit/function";
211
+ // src/asset.ts
212
+ import { memoize, once } from "es-toolkit/function";
182
213
  import { DEV } from "esm-env";
183
- var getLanguage = (languages) => {
184
- let { language } = navigator;
185
- if (languages.includes(language)) {
186
- return language;
187
- } else if (languages.includes(language = language.slice(0, 2))) {
188
- return language;
189
- } else if (language = languages.find((value) => navigator.languages.includes(value))) {
190
- return language;
191
- }
192
- return languages[0];
214
+
215
+ // src/audio-codecs.ts
216
+ var cut = (str) => str.replace(/^no$/, "");
217
+ var audio = new Audio();
218
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
219
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
220
+ var supportsMap = {
221
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
222
+ mpeg: canPlay("audio/mpeg;"),
223
+ opus: canPlay('audio/ogg; codecs="opus"'),
224
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
225
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
226
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
227
+ aac: canPlay("audio/aac;"),
228
+ caf: canPlay("audio/x-caf;"),
229
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
230
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
231
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
232
+ weba: canPlay('audio/webm; codecs="vorbis"'),
233
+ webm: canPlay('audio/webm; codecs="vorbis"'),
234
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
235
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
193
236
  };
194
- var noop = () => {
237
+
238
+ // src/image-formats.ts
239
+ var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
240
+ var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
241
+ var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
242
+ var supportsFormat = (source) => {
243
+ const { promise, resolve } = Promise.withResolvers();
244
+ const img = Object.assign(document.createElement("img"), {
245
+ src: source
246
+ });
247
+ img.onload = img.onerror = () => {
248
+ resolve(img.height === 2);
249
+ };
250
+ return promise;
195
251
  };
196
- var mapSet = (set2, fn) => {
197
- return [...set2].map(fn);
252
+ var supportsMap2 = {
253
+ avif: false,
254
+ jxl: false,
255
+ webp: false
198
256
  };
199
- var capitalize = (str) => {
200
- return str[0].toUpperCase() + str.slice(1);
257
+ var formatsMap = {
258
+ avif,
259
+ jxl,
260
+ webp
201
261
  };
202
- var getIntlLanguageDisplayName = memoize((lang) => {
203
- try {
204
- const intl = new Intl.DisplayNames([lang], {
205
- type: "language"
262
+ var loadImageFormatsSupport = async () => {
263
+ const promises = [];
264
+ for (const [format, source] of Object.entries(formatsMap)) {
265
+ const promise = supportsFormat(source).then((supported) => {
266
+ supportsMap2[format] = supported;
206
267
  });
207
- return intl.of(lang) || lang;
208
- } catch {
209
- return lang;
268
+ promises.push(promise);
210
269
  }
211
- });
270
+ await Promise.all(promises);
271
+ };
272
+ loadImageFormatsSupport();
273
+
274
+ // src/asset.ts
275
+ var generateRandomId = () => Math.random().toString(36);
276
+ var getType = memoize(
277
+ (extensions) => {
278
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
279
+ return "audio";
280
+ }
281
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
282
+ return "image";
283
+ }
284
+ throw extensions;
285
+ },
286
+ {
287
+ getCacheKey: (extensions) => extensions.join("~")
288
+ }
289
+ );
290
+ var SUPPORT_MAPS = {
291
+ image: supportsMap2,
292
+ audio: supportsMap
293
+ };
294
+ var assetPrivate = memoize(
295
+ (variants) => {
296
+ if (DEV && variants.length === 0) {
297
+ throw new Error(`Attempt to use "asset" function without arguments`);
298
+ }
299
+ const map = {};
300
+ const extensions = [];
301
+ for (const v of variants) {
302
+ const e = getUrlFileExtension(v);
303
+ map[e] = v;
304
+ extensions.push(e);
305
+ }
306
+ const type = getType(extensions);
307
+ const getSource = once(() => {
308
+ const support = SUPPORT_MAPS[type];
309
+ for (const extension of extensions) {
310
+ if (extension in support) {
311
+ if (support[extension]) {
312
+ return map[extension];
313
+ }
314
+ } else {
315
+ return map[extension];
316
+ }
317
+ }
318
+ if (DEV) {
319
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
320
+ }
321
+ return "";
322
+ });
323
+ return {
324
+ get source() {
325
+ return getSource();
326
+ },
327
+ get type() {
328
+ return type;
329
+ },
330
+ id: generateRandomId()
331
+ };
332
+ },
333
+ {
334
+ getCacheKey: (variants) => variants.join("~")
335
+ }
336
+ );
337
+ var asset = (...variants) => {
338
+ return assetPrivate(variants);
339
+ };
340
+ asset.image = (source) => {
341
+ if (assetPrivate.cache.has(source)) {
342
+ return assetPrivate.cache.get(source);
343
+ }
344
+ const asset2 = {
345
+ type: "image",
346
+ source,
347
+ id: generateRandomId()
348
+ };
349
+ assetPrivate.cache.set(source, asset2);
350
+ return asset2;
351
+ };
352
+ asset.audio = (source) => {
353
+ if (assetPrivate.cache.has(source)) {
354
+ return assetPrivate.cache.get(source);
355
+ }
356
+ const asset2 = {
357
+ type: "audio",
358
+ source,
359
+ id: generateRandomId()
360
+ };
361
+ assetPrivate.cache.set(source, asset2);
362
+ return asset2;
363
+ };
212
364
  var unwrapAsset = (asset2) => {
213
365
  return isAsset(asset2) ? asset2.source : asset2;
214
366
  };
215
- var handleAudioAsset = (asset2) => {
367
+ var unwrapAudioAsset = (asset2) => {
216
368
  if (DEV && isAsset(asset2) && asset2.type !== "audio") {
217
369
  throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
218
370
  }
219
371
  return unwrapAsset(asset2);
220
372
  };
221
- var handleImageAsset = (asset2) => {
373
+ var unwrapImageAsset = (asset2) => {
222
374
  if (DEV && isAsset(asset2) && asset2.type !== "image") {
223
375
  throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
224
376
  }
225
377
  return unwrapAsset(asset2);
226
378
  };
227
- var getCharactersData = (characters) => {
228
- const entries = Object.entries(characters);
229
- const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
230
- return Object.fromEntries(mapped);
231
- };
232
- var toArray = (target) => {
233
- return Array.isArray(target) ? target : [target];
234
- };
235
- var getLanguageFromStore = (store2) => {
236
- return store2.get().meta[0];
237
- };
238
- var getVolumeFromStore = (store2) => {
239
- const { meta } = store2.get();
240
- return {
241
- music: meta[2],
242
- sound: meta[3],
243
- voice: meta[4]
244
- };
245
- };
246
379
 
247
380
  // src/utilities/actions-processing.ts
248
381
  import { DEV as DEV2 } from "esm-env";
@@ -704,14 +837,6 @@ var getResourseType = memoize2(
704
837
 
705
838
  // src/utilities/stack.ts
706
839
  import { memoize as memoize3 } from "es-toolkit/function";
707
-
708
- // src/shared.ts
709
- var STACK_MAP = /* @__PURE__ */ new Map();
710
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
711
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
712
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
713
-
714
- // src/utilities/stack.ts
715
840
  var getStack = memoize3(
716
841
  (_) => {
717
842
  return [];
@@ -767,22 +892,65 @@ var flatStory = (story) => {
767
892
  return story;
768
893
  };
769
894
 
770
- // src/browser.ts
771
- var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
772
- if (typeof document === "undefined") return noop;
773
- const onVisibilityChange = () => {
774
- if (document.visibilityState === "hidden") {
775
- onChange();
776
- }
777
- };
778
- addEventListener("visibilitychange", onVisibilityChange);
779
- addEventListener("beforeunload", onChange);
780
- return () => {
781
- removeEventListener("visibilitychange", onVisibilityChange);
782
- removeEventListener("beforeunload", onChange);
895
+ // src/utilities/internationalization.ts
896
+ import { memoize as memoize4 } from "es-toolkit/function";
897
+ var getLanguage = (languages) => {
898
+ let { language } = navigator;
899
+ if (languages.includes(language)) {
900
+ return language;
901
+ } else if (languages.includes(language = language.slice(0, 2))) {
902
+ return language;
903
+ } else if (language = languages.find((value) => navigator.languages.includes(value))) {
904
+ return language;
905
+ }
906
+ return languages[0];
907
+ };
908
+ var getIntlLanguageDisplayName = memoize4((lang) => {
909
+ try {
910
+ const intl = new Intl.DisplayNames([lang], {
911
+ type: "language"
912
+ });
913
+ return intl.of(lang) || lang;
914
+ } catch {
915
+ return lang;
916
+ }
917
+ });
918
+ var capitalize = (str) => {
919
+ return str[0].toUpperCase() + str.slice(1);
920
+ };
921
+
922
+ // src/utilities/functions.ts
923
+ var noop = () => {
924
+ };
925
+
926
+ // src/utilities/store.ts
927
+ var getLanguageFromStore = (store2) => {
928
+ return store2.get().meta[0];
929
+ };
930
+ var getVolumeFromStore = (store2) => {
931
+ const { meta } = store2.get();
932
+ return {
933
+ music: meta[2],
934
+ sound: meta[3],
935
+ voice: meta[4]
783
936
  };
784
937
  };
785
938
 
939
+ // src/utilities/array.ts
940
+ var mapSet = (set2, fn) => {
941
+ return [...set2].map(fn);
942
+ };
943
+ var toArray = (target) => {
944
+ return Array.isArray(target) ? target : [target];
945
+ };
946
+
947
+ // src/utilities/else.ts
948
+ var getCharactersData = (characters) => {
949
+ const entries = Object.entries(characters);
950
+ const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
951
+ return Object.fromEntries(mapped);
952
+ };
953
+
786
954
  // src/custom-action.ts
787
955
  var createCustomActionNode = (id) => {
788
956
  const div = document.createElement("div");
@@ -910,17 +1078,17 @@ var handleAssetsPreloading = async ({
910
1078
  await Promise.allSettled(list);
911
1079
  ASSETS_TO_PRELOAD.clear();
912
1080
  };
913
- var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) => {
1081
+ var huntAssets = ({ volume, lang, characters, action, props, handle }) => {
914
1082
  if (action === "showBackground") {
915
1083
  if (isString(props[0])) {
916
- handle(handleAudioAsset(props[0]));
1084
+ handle(unwrapAudioAsset(props[0]));
917
1085
  }
918
1086
  if (props[0] && typeof props[0] === "object") {
919
1087
  for (const value of Object.values(props[0])) {
920
1088
  if (isImageAsset(value)) {
921
1089
  handle(value);
922
1090
  } else if (isAsset(value)) {
923
- const unwrapped = handleImageAsset(value);
1091
+ const unwrapped = unwrapImageAsset(value);
924
1092
  if (isImageAsset(unwrapped)) {
925
1093
  handle(unwrapped);
926
1094
  }
@@ -937,7 +1105,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
937
1105
  };
938
1106
  if (isAudioAction(action) && isString(props[0])) {
939
1107
  if (getVolumeFor(action) > 0) {
940
- handle(handleAudioAsset(props[0]));
1108
+ handle(unwrapAudioAsset(props[0]));
941
1109
  }
942
1110
  return;
943
1111
  }
@@ -946,10 +1114,8 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
946
1114
  return;
947
1115
  }
948
1116
  for (const [language, value] of Object.entries(props[0])) {
949
- if (mode === "blocking") {
950
- value && handle(handleAudioAsset(value));
951
- } else if (language === lang) {
952
- value && handle(handleAudioAsset(value));
1117
+ if (language === lang) {
1118
+ value && handle(unwrapAudioAsset(value));
953
1119
  }
954
1120
  }
955
1121
  return;
@@ -957,7 +1123,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
957
1123
  if (action === "showCharacter" && isString(props[0]) && isString(props[1])) {
958
1124
  const images = toArray(characters[props[0]].emotions[props[1]]);
959
1125
  for (const asset2 of images) {
960
- handle(handleImageAsset(asset2));
1126
+ handle(unwrapImageAsset(asset2));
961
1127
  }
962
1128
  return;
963
1129
  }
@@ -971,7 +1137,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
971
1137
  for (let i = 1; i < props.length; i++) {
972
1138
  const data = props[i];
973
1139
  if (Array.isArray(data)) {
974
- handle(handleImageAsset(data[5]));
1140
+ handle(unwrapImageAsset(data[5]));
975
1141
  }
976
1142
  }
977
1143
  }
@@ -1068,9 +1234,7 @@ var VIRTUAL_ACTIONS = ["say"];
1068
1234
  var buildActionObject = ({
1069
1235
  rendererActions,
1070
1236
  nativeActions,
1071
- characters,
1072
- preloadAssets,
1073
- storageData
1237
+ characters
1074
1238
  }) => {
1075
1239
  const allActions = [...nativeActions, ...VIRTUAL_ACTIONS];
1076
1240
  const object = { ...rendererActions };
@@ -1109,23 +1273,63 @@ var buildActionObject = ({
1109
1273
  actions[key] = flatActions(actions[key]);
1110
1274
  }
1111
1275
  }
1112
- if (preloadAssets === "blocking") {
1113
- huntAssets({
1114
- action,
1115
- props,
1116
- mode: preloadAssets,
1117
- characters,
1118
- lang: getLanguageFromStore(storageData),
1119
- volume: getVolumeFromStore(storageData),
1120
- handle: enqueueAssetForPreloading
1121
- });
1122
- }
1123
1276
  return [action, ...props];
1124
1277
  };
1125
1278
  }
1126
1279
  return object;
1127
1280
  };
1128
1281
 
1282
+ // src/utilities/dialog-overview.ts
1283
+ var getDialogOverview = async function() {
1284
+ const { value: save } = this.getStack();
1285
+ const stateSnapshots = save[3];
1286
+ if (stateSnapshots.length == 0) {
1287
+ return [];
1288
+ }
1289
+ const { queue } = await getActionsFromPath({
1290
+ story: this.story,
1291
+ path: save[0],
1292
+ filter: false,
1293
+ referGuarded: this.referGuarded
1294
+ });
1295
+ const lang = this.getLanguage();
1296
+ const dialogItems = [];
1297
+ for (let p = 0, a = stateSnapshots.length, i = queue.length - 1; a > 0 && i > 0; i--) {
1298
+ const action = queue[i];
1299
+ if (action[0] === "dialog") {
1300
+ const [_, name, text] = action;
1301
+ let voice = void 0;
1302
+ for (let j = i - 1; j > p && j > 0; j--) {
1303
+ const action2 = queue[j];
1304
+ if (isUserRequiredAction(action2) || isSkippedDuringRestore(action2[0])) break;
1305
+ if (action2[0] === "stopVoice") break;
1306
+ if (action2[0] === "voice") {
1307
+ voice = action2[1];
1308
+ break;
1309
+ }
1310
+ }
1311
+ dialogItems.push({
1312
+ name,
1313
+ text,
1314
+ voice
1315
+ });
1316
+ p = i;
1317
+ a--;
1318
+ }
1319
+ }
1320
+ const entries = dialogItems.reverse().map(({ name, text, voice }, i) => {
1321
+ const state = stateSnapshots[i];
1322
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1323
+ name = name ? this.getCharacterName(name) : "";
1324
+ return {
1325
+ name: this.templateReplace(name, state),
1326
+ text: this.templateReplace(text, state),
1327
+ voice: audioSource ? unwrapAudioAsset(audioSource) : ""
1328
+ };
1329
+ });
1330
+ return entries;
1331
+ };
1332
+
1129
1333
  // src/novely.ts
1130
1334
  var novely = ({
1131
1335
  characters,
@@ -1144,7 +1348,7 @@ var novely = ({
1144
1348
  getLanguage: getLanguage2 = getLanguage,
1145
1349
  overrideLanguage = false,
1146
1350
  askBeforeExit = true,
1147
- preloadAssets = "lazy",
1351
+ preloadAssets = "automatic",
1148
1352
  parallelAssetsDownloadLimit = 15,
1149
1353
  fetch: request = fetch,
1150
1354
  cloneFunction: clone = klona,
@@ -1165,7 +1369,7 @@ var novely = ({
1165
1369
  storyOptions.preloadSaves ??= 4;
1166
1370
  }
1167
1371
  const storyLoad = storyOptions.mode === "static" ? noop : storyOptions.load;
1168
- const onUnknownSceneHit = memoize4(async (scene) => {
1372
+ const onUnknownSceneHit = memoize5(async (scene) => {
1169
1373
  const part = await storyLoad(scene);
1170
1374
  if (part) {
1171
1375
  await script(part);
@@ -1180,14 +1384,6 @@ var novely = ({
1180
1384
  if (!initialScreenWasShown) {
1181
1385
  renderer.ui.showLoading();
1182
1386
  }
1183
- if (preloadAssets === "blocking" && ASSETS_TO_PRELOAD.size > 0) {
1184
- renderer.ui.showLoading();
1185
- await handleAssetsPreloading({
1186
- ...renderer.misc,
1187
- limiter: limitAssetsDownload,
1188
- request
1189
- });
1190
- }
1191
1387
  await dataLoaded.promise;
1192
1388
  renderer.ui.hideLoading();
1193
1389
  if (!initialScreenWasShown) {
@@ -1246,11 +1442,7 @@ var novely = ({
1246
1442
  referGuarded(path);
1247
1443
  }
1248
1444
  };
1249
- if (preloadAssets === "blocking") {
1250
- await preload();
1251
- } else {
1252
- void preload();
1253
- }
1445
+ preload();
1254
1446
  coreData.update((data2) => {
1255
1447
  data2.dataLoaded = true;
1256
1448
  return data2;
@@ -1265,13 +1457,12 @@ var novely = ({
1265
1457
  }
1266
1458
  storage.set(data2);
1267
1459
  };
1268
- const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
1269
- const throttledEmergencyOnStorageDataChange = throttle(() => {
1270
- if (saveOnUnload === true || saveOnUnload === "prod" && !DEV5) {
1271
- onStorageDataChange(storageData.get());
1272
- }
1273
- }, 10);
1460
+ const throttledShortOnStorageDataChange = throttle(() => onStorageDataChange(storageData.get()), 10);
1461
+ const throttledOnStorageDataChange = throttle(throttledShortOnStorageDataChange, throttleTimeout);
1274
1462
  storageData.subscribe(throttledOnStorageDataChange);
1463
+ if (saveOnUnload === true || saveOnUnload === "prod" && !DEV5) {
1464
+ addEventListener("beforeunload", throttledShortOnStorageDataChange);
1465
+ }
1275
1466
  const getStoredData = async () => {
1276
1467
  let stored = await storage.get();
1277
1468
  for (const migration of migrations) {
@@ -1295,9 +1486,6 @@ var novely = ({
1295
1486
  };
1296
1487
  storageDelay.then(getStoredData);
1297
1488
  const initial = getDefaultSave(clone(defaultState));
1298
- const unsubscribeFromBrowserVisibilityChange = setupBrowserVisibilityChangeListeners({
1299
- onChange: throttledEmergencyOnStorageDataChange
1300
- });
1301
1489
  const save = (type) => {
1302
1490
  if (!coreData.get().dataLoaded) return;
1303
1491
  if (!autosaves && type === "auto") return;
@@ -1514,7 +1702,6 @@ var novely = ({
1514
1702
  huntAssets({
1515
1703
  action: action2,
1516
1704
  props,
1517
- mode: preloadAssets,
1518
1705
  characters,
1519
1706
  lang: getLanguageFromStore(storageData),
1520
1707
  volume: getVolumeFromStore(storageData),
@@ -1561,7 +1748,7 @@ var novely = ({
1561
1748
  const clearCustomAction = (ctx, fn) => {
1562
1749
  getCustomActionHolder(ctx, fn).cleanup();
1563
1750
  };
1564
- const getResourseTypeForRenderer = (url) => {
1751
+ const getResourseTypeWrapper = (url) => {
1565
1752
  return getResourseType({
1566
1753
  url,
1567
1754
  request
@@ -1571,12 +1758,12 @@ var novely = ({
1571
1758
  return c in characters ? characters[c].color : "#000000";
1572
1759
  };
1573
1760
  const getCharacterAssets = (character, emotion) => {
1574
- return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1761
+ return toArray(characters[character].emotions[emotion]).map(unwrapImageAsset);
1575
1762
  };
1576
1763
  const getCharacterName = (character) => {
1577
1764
  const c = character;
1578
1765
  const cs = characters;
1579
- const [lang] = storageData.get().meta;
1766
+ const lang = getLanguageFromStore(storageData);
1580
1767
  if (c && c in cs) {
1581
1768
  const block = cs[c].name;
1582
1769
  if (typeof block === "string") {
@@ -1586,56 +1773,7 @@ var novely = ({
1586
1773
  return block[lang];
1587
1774
  }
1588
1775
  }
1589
- return String(c) || "";
1590
- };
1591
- const getDialogOverview = async () => {
1592
- const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1593
- const stateSnapshots = save2[3];
1594
- if (stateSnapshots.length == 0) {
1595
- return [];
1596
- }
1597
- const { queue } = await getActionsFromPath({
1598
- story,
1599
- path: save2[0],
1600
- filter: false,
1601
- referGuarded
1602
- });
1603
- const [lang] = storageData.get().meta;
1604
- const dialogItems = [];
1605
- for (let p = 0, a = stateSnapshots.length, i = queue.length - 1; a > 0 && i > 0; i--) {
1606
- const action2 = queue[i];
1607
- if (action2[0] === "dialog") {
1608
- const [_, name, text] = action2;
1609
- let voice = void 0;
1610
- for (let j = i - 1; j > p && j > 0; j--) {
1611
- const action3 = queue[j];
1612
- if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1613
- if (action3[0] === "stopVoice") break;
1614
- if (action3[0] === "voice") {
1615
- voice = action3[1];
1616
- break;
1617
- }
1618
- }
1619
- dialogItems.push({
1620
- name,
1621
- text,
1622
- voice
1623
- });
1624
- p = i;
1625
- a--;
1626
- }
1627
- }
1628
- const entries = dialogItems.reverse().map(({ name, text, voice }, i) => {
1629
- const state = stateSnapshots[i];
1630
- const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1631
- name = name ? getCharacterName(name) : "";
1632
- return {
1633
- name: templateReplace(name, state),
1634
- text: templateReplace(text, state),
1635
- voice: audioSource ? handleAudioAsset(audioSource) : ""
1636
- };
1637
- });
1638
- return entries;
1776
+ return String(c);
1639
1777
  };
1640
1778
  const renderer = createRenderer({
1641
1779
  mainContextKey: MAIN_CONTEXT_KEY,
@@ -1658,8 +1796,15 @@ var novely = ({
1658
1796
  getLanguageDisplayName,
1659
1797
  getCharacterColor,
1660
1798
  getCharacterAssets,
1661
- getDialogOverview,
1662
- getResourseType: getResourseTypeForRenderer
1799
+ getDialogOverview: getDialogOverview.bind({
1800
+ referGuarded,
1801
+ story,
1802
+ getCharacterName,
1803
+ getLanguage: () => getLanguageFromStore(storageData),
1804
+ getStack: () => useStack(MAIN_CONTEXT_KEY),
1805
+ templateReplace: (...args) => templateReplace(...args)
1806
+ }),
1807
+ getResourseType: getResourseTypeWrapper
1663
1808
  });
1664
1809
  const useStack = createUseStackFunction(renderer);
1665
1810
  useStack(MAIN_CONTEXT_KEY).push(initial);
@@ -1703,7 +1848,6 @@ var novely = ({
1703
1848
  huntAssets({
1704
1849
  action: action3,
1705
1850
  props: props2,
1706
- mode: preloadAssets,
1707
1851
  characters,
1708
1852
  lang: getLanguageFromStore(storageData),
1709
1853
  volume: getVolumeFromStore(storageData),
@@ -1728,47 +1872,47 @@ var novely = ({
1728
1872
  showBackground({ ctx, push }, [background]) {
1729
1873
  if (isString(background) || isAsset(background)) {
1730
1874
  ctx.background({
1731
- all: handleImageAsset(background)
1875
+ all: unwrapImageAsset(background)
1732
1876
  });
1733
1877
  } else {
1734
1878
  ctx.background(
1735
- Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)]))
1879
+ Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, unwrapImageAsset(asset2)]))
1736
1880
  );
1737
1881
  }
1738
1882
  push();
1739
1883
  },
1740
1884
  playMusic({ ctx, push }, [source]) {
1741
- ctx.audio.music(handleAudioAsset(source), "music").play(true);
1885
+ ctx.audio.music(unwrapAudioAsset(source), "music").play(true);
1742
1886
  push();
1743
1887
  },
1744
1888
  pauseMusic({ ctx, push }, [source]) {
1745
- ctx.audio.music(handleAudioAsset(source), "music").pause();
1889
+ ctx.audio.music(unwrapAudioAsset(source), "music").pause();
1746
1890
  push();
1747
1891
  },
1748
1892
  stopMusic({ ctx, push }, [source]) {
1749
- ctx.audio.music(handleAudioAsset(source), "music").stop();
1893
+ ctx.audio.music(unwrapAudioAsset(source), "music").stop();
1750
1894
  push();
1751
1895
  },
1752
1896
  playSound({ ctx, push }, [source, loop]) {
1753
- ctx.audio.music(handleAudioAsset(source), "sound").play(loop || false);
1897
+ ctx.audio.music(unwrapAudioAsset(source), "sound").play(loop || false);
1754
1898
  push();
1755
1899
  },
1756
1900
  pauseSound({ ctx, push }, [source]) {
1757
- ctx.audio.music(handleAudioAsset(source), "sound").pause();
1901
+ ctx.audio.music(unwrapAudioAsset(source), "sound").pause();
1758
1902
  push();
1759
1903
  },
1760
1904
  stopSound({ ctx, push }, [source]) {
1761
- ctx.audio.music(handleAudioAsset(source), "sound").stop();
1905
+ ctx.audio.music(unwrapAudioAsset(source), "sound").stop();
1762
1906
  push();
1763
1907
  },
1764
1908
  voice({ ctx, push }, [source]) {
1765
- const [lang] = storageData.get().meta;
1909
+ const lang = getLanguageFromStore(storageData);
1766
1910
  const audioSource = isString(source) ? source : isAsset(source) ? source : source[lang];
1767
1911
  if (!audioSource) {
1768
1912
  push();
1769
1913
  return;
1770
1914
  }
1771
- ctx.audio.voice(handleAudioAsset(audioSource));
1915
+ ctx.audio.voice(unwrapAudioAsset(audioSource));
1772
1916
  push();
1773
1917
  },
1774
1918
  stopVoice({ ctx, push }) {
@@ -1845,7 +1989,7 @@ var novely = ({
1845
1989
  recompute: update
1846
1990
  });
1847
1991
  };
1848
- const imageValue = image ? handleImageAsset(image) : "";
1992
+ const imageValue = image ? unwrapImageAsset(image) : "";
1849
1993
  return [templateReplace(content, data2), active$, visible$, onSelectWrapped, imageValue];
1850
1994
  });
1851
1995
  if (DEV5 && transformedChoices.length === 0) {
@@ -1990,11 +2134,28 @@ var novely = ({
1990
2134
  preload({ ctx, push }, [source]) {
1991
2135
  if (DEV5 && preloadAssets !== "lazy") {
1992
2136
  console.error(
1993
- `You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`
2137
+ `You do not need a preload action because "preloadAssets" strategy was set to "${preloadAssets}"`
1994
2138
  );
2139
+ push();
2140
+ return;
1995
2141
  }
1996
- if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1997
- PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
2142
+ const src = unwrapAsset(source);
2143
+ if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(src)) {
2144
+ const process = async () => {
2145
+ const type = isAsset(source) ? source.type : await getResourseTypeWrapper(src);
2146
+ if (type === "image") {
2147
+ renderer.misc.preloadAudioBlocking(src);
2148
+ } else if (type === "audio") {
2149
+ renderer.misc.preloadImage(src);
2150
+ } else {
2151
+ if (DEV5) {
2152
+ console.error(`Preload error: Unknown type of the following resource: `, source);
2153
+ }
2154
+ return;
2155
+ }
2156
+ PRELOADED_ASSETS.add(src);
2157
+ };
2158
+ process();
1998
2159
  }
1999
2160
  push();
2000
2161
  },
@@ -2015,9 +2176,7 @@ var novely = ({
2015
2176
  const action = buildActionObject({
2016
2177
  rendererActions: renderer.actions,
2017
2178
  nativeActions,
2018
- characters,
2019
- preloadAssets,
2020
- storageData
2179
+ characters
2021
2180
  });
2022
2181
  const render = async (ctx) => {
2023
2182
  const stack = useStack(ctx);
@@ -2072,12 +2231,6 @@ var novely = ({
2072
2231
  });
2073
2232
  return void 0;
2074
2233
  };
2075
- const typeEssentials = {
2076
- l: null,
2077
- s: null,
2078
- d: null,
2079
- c: null
2080
- };
2081
2234
  const getCurrentStorageData = () => {
2082
2235
  return coreData.get().dataLoaded ? clone(storageData.get()) : null;
2083
2236
  };
@@ -2143,7 +2296,7 @@ var novely = ({
2143
2296
  data,
2144
2297
  /**
2145
2298
  * Used in combination with type utilities
2146
- *
2299
+ * @deprecated Use `engine.types` instead
2147
2300
  * @example
2148
2301
  * ```ts
2149
2302
  * import type { ConditionParams, StateFunction } from '@novely/core';
@@ -2153,7 +2306,21 @@ var novely = ({
2153
2306
  * }
2154
2307
  * ```
2155
2308
  */
2156
- typeEssentials,
2309
+ typeEssentials: {},
2310
+ /**
2311
+ * Used in combination with type utilities
2312
+ * @example
2313
+ * ```ts
2314
+ * import type { TypesFromEngine, ConditionParams, StateFunction } from '@novely/core';
2315
+ *
2316
+ * type Types = TypesFromEngine<typeof engine>;
2317
+ *
2318
+ * const conditionCheck = (state: StateFunction<ConditionParams<Types>>) => {
2319
+ * return state.age >= 18;
2320
+ * }
2321
+ * ```
2322
+ */
2323
+ types: null,
2157
2324
  /**
2158
2325
  * Replaces content inside {{braces}} using global data
2159
2326
  * @example
@@ -2182,7 +2349,7 @@ var novely = ({
2182
2349
  destroy() {
2183
2350
  dataLoaded.cancel();
2184
2351
  UIInstance.unmount();
2185
- unsubscribeFromBrowserVisibilityChange();
2352
+ removeEventListener("beforeunload", throttledShortOnStorageDataChange);
2186
2353
  destroyed = true;
2187
2354
  },
2188
2355
  /**
@@ -2224,42 +2391,42 @@ var extendAction = (base, extension) => {
2224
2391
 
2225
2392
  // src/translations.ts
2226
2393
  var RU = {
2227
- NewGame: "\u041D\u043E\u0432\u0430\u044F \u0438\u0433\u0440\u0430",
2228
- HomeScreen: "\u0413\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D",
2229
- ToTheGame: "\u041A \u0438\u0433\u0440\u0435",
2230
- Language: "\u042F\u0437\u044B\u043A",
2231
- NoSaves: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0439 \u043D\u0435\u0442",
2232
- LoadSave: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
2233
- Saves: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F",
2234
- Settings: "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438",
2235
- Sumbit: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C",
2236
- GoBack: "\u041D\u0430\u0437\u0430\u0434",
2237
- DoSave: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435",
2238
- Auto: "\u0410\u0432\u0442\u043E",
2239
- Stop: "\u0421\u0442\u043E\u043F",
2240
- Exit: "\u0412\u044B\u0445\u043E\u0434",
2241
- Automatic: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0435",
2242
- Manual: "\u0420\u0443\u0447\u043D\u043E\u0435",
2243
- Remove: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
2244
- LoadASaveFrom: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043E\u0442",
2245
- DeleteASaveFrom: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043E\u0442",
2246
- TextSpeed: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u0442\u0435\u043A\u0441\u0442\u0430",
2247
- TextSpeedSlow: "\u041C\u0435\u0434\u043B\u0435\u043D\u043D\u0430\u044F",
2248
- TextSpeedMedium: "\u0421\u0440\u0435\u0434\u043D\u044F\u044F",
2249
- TextSpeedFast: "\u0411\u044B\u0441\u0442\u0440\u0430\u044F",
2250
- TextSpeedAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F",
2251
- CompleteText: "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0442\u0435\u043A\u0441\u0442",
2252
- GoForward: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u0432\u043F\u0435\u0440\u0451\u0434",
2253
- ExitDialogWarning: "\u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0432\u044B\u0439\u0442\u0438? \u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D.",
2254
- ExitDialogExit: "\u0412\u044B\u0439\u0442\u0438",
2255
- ExitDialogBack: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u0432 \u0438\u0433\u0440\u0443",
2256
- OpenMenu: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2257
- CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2258
- MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2259
- SoundVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0437\u0432\u0443\u043A\u043E\u0432",
2260
- VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2261
- Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2262
- DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2394
+ NewGame: "Новая игра",
2395
+ HomeScreen: "Главный экран",
2396
+ ToTheGame: "К игре",
2397
+ Language: "Язык",
2398
+ NoSaves: "Сохранений нет",
2399
+ LoadSave: "Загрузить",
2400
+ Saves: "Сохранения",
2401
+ Settings: "Настройки",
2402
+ Sumbit: "Подтвердить",
2403
+ GoBack: "Назад",
2404
+ DoSave: "Сохранение",
2405
+ Auto: "Авто",
2406
+ Stop: "Стоп",
2407
+ Exit: "Выход",
2408
+ Automatic: "Автоматическое",
2409
+ Manual: "Ручное",
2410
+ Remove: "Удалить",
2411
+ LoadASaveFrom: "Загрузить сохранение от",
2412
+ DeleteASaveFrom: "Удалить сохранение от",
2413
+ TextSpeed: "Скорость текста",
2414
+ TextSpeedSlow: "Медленная",
2415
+ TextSpeedMedium: "Средняя",
2416
+ TextSpeedFast: "Быстрая",
2417
+ TextSpeedAuto: "Автоматическая",
2418
+ CompleteText: "Завершить текст",
2419
+ GoForward: "Перейти вперёд",
2420
+ ExitDialogWarning: "Вы уверены, что хотите выйти? Прогресс будет сохранён.",
2421
+ ExitDialogExit: "Выйти",
2422
+ ExitDialogBack: "Вернуться в игру",
2423
+ OpenMenu: "Открыть меню",
2424
+ CloseMenu: "Закрыть меню",
2425
+ MusicVolume: "Громкость музыки",
2426
+ SoundVolume: "Громкость звуков",
2427
+ VoiceVolume: "Громкость речи",
2428
+ Close: "Закрыть",
2429
+ DialogOverview: "Обзор диалога"
2263
2430
  };
2264
2431
  var EN = {
2265
2432
  NewGame: "New Game",
@@ -2299,240 +2466,8 @@ var EN = {
2299
2466
  Close: "Close",
2300
2467
  DialogOverview: "Dialog Overview"
2301
2468
  };
2302
- var KK = {
2303
- NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
2304
- HomeScreen: "\u041D\u0435\u0433\u0456\u0437\u0433\u0456 \u044D\u043A\u0440\u0430\u043D",
2305
- ToTheGame: "\u041E\u0439\u044B\u043D\u0493\u0430",
2306
- Language: "\u0422\u0456\u043B",
2307
- NoSaves: "\u0421\u0430\u049B\u0442\u0430\u0443 \u0436\u043E\u049B",
2308
- LoadSave: "\u0416\u04AF\u043A\u0442\u0435\u0443",
2309
- Saves: "\u0421\u0430\u049B\u0442\u0430\u0443",
2310
- Settings: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043B\u0435\u0440",
2311
- Sumbit: "\u0420\u0430\u0441\u0442\u0430\u0443",
2312
- GoBack: "\u0410\u0440\u0442\u049B\u0430",
2313
- DoSave: "\u0421\u0430\u049B\u0442\u0430\u0443",
2314
- Auto: "\u0410\u0432\u0442\u043E",
2315
- Stop: "\u0422\u043E\u049B\u0442\u0430",
2316
- Exit: "\u0428\u044B\u0493\u0443",
2317
- Automatic: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0442\u044B",
2318
- Manual: "\u049A\u043E\u043B\u043C\u0435\u043D",
2319
- Remove: "\u0416\u043E\u044E",
2320
- LoadASaveFrom: "\u0421\u0430\u049B\u0442\u0430\u0443\u0434\u044B \u0436\u04AF\u043A\u0442\u0435\u0443",
2321
- DeleteASaveFrom: "\u0421\u0430\u049B\u0442\u0430\u0443\u0434\u044B \u0436\u043E\u044E",
2322
- TextSpeed: "\u041C\u04D9\u0442\u0456\u043D \u0416\u044B\u043B\u0434\u0430\u043C\u0434\u044B\u0493\u044B",
2323
- TextSpeedSlow: "\u0411\u0430\u044F\u0443",
2324
- TextSpeedMedium: "\u041E\u0440\u0442\u0430\u0448\u0430",
2325
- TextSpeedFast: "\u0416\u044B\u043B\u0434\u0430\u043C",
2326
- TextSpeedAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0442\u044B",
2327
- CompleteText: "\u041C\u04D9\u0442\u0456\u043D\u0434\u0456 \u0430\u044F\u049B\u0442\u0430\u0443",
2328
- GoForward: "\u0410\u043B\u0493\u0430 \u0436\u044B\u043B\u0436\u0443",
2329
- ExitDialogWarning: "\u0421\u0456\u0437 \u0448\u044B\u049B\u049B\u044B\u04A3\u044B\u0437 \u043A\u0435\u043B\u0435\u0442\u0456\u043D\u0456\u043D\u0435 \u0441\u0435\u043D\u0456\u043C\u0434\u0456\u0441\u0456\u0437 \u0431\u0435? \u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u0441\u0430\u049B\u0442\u0430\u043B\u0430\u0434\u044B",
2330
- ExitDialogExit: "\u0428\u044B\u0493\u0443",
2331
- ExitDialogBack: "\u041E\u0439\u044B\u043D\u0493\u0430 \u043E\u0440\u0430\u043B\u0443",
2332
- OpenMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0430\u0448\u044B\u04A3\u044B\u0437",
2333
- CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2334
- MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2335
- SoundVolume: "\u0414\u044B\u0431\u044B\u0441\u0442\u0430\u0440\u0434\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2336
- VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2337
- Close: "\u0416\u0430\u0431\u0443",
2338
- DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2339
- };
2340
- var JP = {
2341
- NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
2342
- HomeScreen: "\u30DB\u30FC\u30E0\u753B\u9762",
2343
- ToTheGame: "\u300C\u30B2\u30FC\u30E0\u306B\u623B\u308B\u300D",
2344
- Language: "\u8A00\u8A9E",
2345
- NoSaves: "\u30CE\u30FC\u30BB\u30FC\u30D6",
2346
- LoadSave: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9",
2347
- Saves: "\u4FDD\u5B58",
2348
- Settings: "\u8A2D\u5B9A",
2349
- Sumbit: "\u78BA\u8A8D",
2350
- GoBack: "\u300C\u623B\u308B\u300D",
2351
- DoSave: "\u4FDD\u5B58",
2352
- Auto: "\u30AA\u30FC\u30C8",
2353
- Stop: "\u6B62\u307E\u308C",
2354
- Exit: "\u51FA\u53E3",
2355
- Automatic: "\u81EA\u52D5",
2356
- Manual: "\u30DE\u30CB\u30E5\u30A2\u30EB",
2357
- Remove: "\u524A\u9664",
2358
- LoadASaveFrom: "\u30ED\u30FC\u30C9\u30BB\u30FC\u30D6\u304B\u3089",
2359
- DeleteASaveFrom: "\u304B\u3089\u4FDD\u5B58\u3092\u524A\u9664",
2360
- TextSpeed: "\u30C6\u30AD\u30B9\u30C8\u30B9\u30D4\u30FC\u30C9",
2361
- TextSpeedSlow: "\u300C\u9045\u3044\u300D",
2362
- TextSpeedMedium: "\u30DF\u30C7\u30A3\u30A2\u30E0",
2363
- TextSpeedFast: "\u300C\u901F\u3044\u300D",
2364
- TextSpeedAuto: "\u81EA\u52D5",
2365
- CompleteText: "\u30C6\u30AD\u30B9\u30C8\u3092\u5B8C\u6210\u3055\u305B\u308B",
2366
- GoForward: "\u5148\u306B\u884C\u304F",
2367
- ExitDialogWarning: "\u672C\u5F53\u306B\u7D42\u4E86\u3057\u307E\u3059\u304B\uFF1F\u9032\u884C\u72B6\u6CC1\u306F\u4FDD\u5B58\u3055\u308C\u307E\u3059",
2368
- ExitDialogExit: "\u7D42\u4E86",
2369
- ExitDialogBack: "\u30B2\u30FC\u30E0\u306B\u623B\u308B",
2370
- OpenMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u958B\u304F",
2371
- CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2372
- MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2373
- SoundVolume: "\u97F3\u91CF",
2374
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2375
- Close: "\u9589\u3058\u308B",
2376
- DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2377
- };
2378
-
2379
- // src/asset.ts
2380
- import { memoize as memoize5, once } from "es-toolkit/function";
2381
- import { DEV as DEV6 } from "esm-env";
2382
-
2383
- // src/audio-codecs.ts
2384
- var cut = (str) => str.replace(/^no$/, "");
2385
- var audio = new Audio();
2386
- var canPlay = (type) => !!cut(audio.canPlayType(type));
2387
- var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2388
- var supportsMap = {
2389
- mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2390
- mpeg: canPlay("audio/mpeg;"),
2391
- opus: canPlay('audio/ogg; codecs="opus"'),
2392
- ogg: canPlay('audio/ogg; codecs="vorbis"'),
2393
- oga: canPlay('audio/ogg; codecs="vorbis"'),
2394
- wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2395
- aac: canPlay("audio/aac;"),
2396
- caf: canPlay("audio/x-caf;"),
2397
- m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2398
- m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2399
- mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2400
- weba: canPlay('audio/webm; codecs="vorbis"'),
2401
- webm: canPlay('audio/webm; codecs="vorbis"'),
2402
- dolby: canPlay('audio/mp4; codecs="ec-3"'),
2403
- flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2404
- };
2405
-
2406
- // src/image-formats.ts
2407
- var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2408
- var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2409
- var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2410
- var supportsFormat = (source) => {
2411
- const { promise, resolve } = Promise.withResolvers();
2412
- const img = Object.assign(document.createElement("img"), {
2413
- src: source
2414
- });
2415
- img.onload = img.onerror = () => {
2416
- resolve(img.height === 2);
2417
- };
2418
- return promise;
2419
- };
2420
- var supportsMap2 = {
2421
- avif: false,
2422
- jxl: false,
2423
- webp: false
2424
- };
2425
- var formatsMap = {
2426
- avif,
2427
- jxl,
2428
- webp
2429
- };
2430
- var loadImageFormatsSupport = async () => {
2431
- const promises = [];
2432
- for (const [format, source] of Object.entries(formatsMap)) {
2433
- const promise = supportsFormat(source).then((supported) => {
2434
- supportsMap2[format] = supported;
2435
- });
2436
- promises.push(promise);
2437
- }
2438
- await Promise.all(promises);
2439
- };
2440
- loadImageFormatsSupport();
2441
-
2442
- // src/asset.ts
2443
- var generateRandomId = () => Math.random().toString(36);
2444
- var getType = memoize5(
2445
- (extensions) => {
2446
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2447
- return "audio";
2448
- }
2449
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2450
- return "image";
2451
- }
2452
- throw extensions;
2453
- },
2454
- {
2455
- getCacheKey: (extensions) => extensions.join("~")
2456
- }
2457
- );
2458
- var SUPPORT_MAPS = {
2459
- image: supportsMap2,
2460
- audio: supportsMap
2461
- };
2462
- var assetPrivate = memoize5(
2463
- (variants) => {
2464
- if (DEV6 && variants.length === 0) {
2465
- throw new Error(`Attempt to use "asset" function without arguments`);
2466
- }
2467
- const map = {};
2468
- const extensions = [];
2469
- for (const v of variants) {
2470
- const e = getUrlFileExtension(v);
2471
- map[e] = v;
2472
- extensions.push(e);
2473
- }
2474
- const type = getType(extensions);
2475
- const getSource = once(() => {
2476
- const support = SUPPORT_MAPS[type];
2477
- for (const extension of extensions) {
2478
- if (extension in support) {
2479
- if (support[extension]) {
2480
- return map[extension];
2481
- }
2482
- } else {
2483
- return map[extension];
2484
- }
2485
- }
2486
- if (DEV6) {
2487
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2488
- }
2489
- return "";
2490
- });
2491
- return {
2492
- get source() {
2493
- return getSource();
2494
- },
2495
- get type() {
2496
- return type;
2497
- },
2498
- id: generateRandomId()
2499
- };
2500
- },
2501
- {
2502
- getCacheKey: (variants) => variants.join("~")
2503
- }
2504
- );
2505
- var asset = (...variants) => {
2506
- return assetPrivate(variants);
2507
- };
2508
- asset.image = (source) => {
2509
- if (assetPrivate.cache.has(source)) {
2510
- return assetPrivate.cache.get(source);
2511
- }
2512
- const asset2 = {
2513
- type: "image",
2514
- source,
2515
- id: generateRandomId()
2516
- };
2517
- assetPrivate.cache.set(source, asset2);
2518
- return asset2;
2519
- };
2520
- asset.audio = (source) => {
2521
- if (assetPrivate.cache.has(source)) {
2522
- return assetPrivate.cache.get(source);
2523
- }
2524
- const asset2 = {
2525
- type: "audio",
2526
- source,
2527
- id: generateRandomId()
2528
- };
2529
- assetPrivate.cache.set(source, asset2);
2530
- return asset2;
2531
- };
2532
2469
  export {
2533
2470
  EN,
2534
- JP,
2535
- KK,
2536
2471
  RU,
2537
2472
  asset,
2538
2473
  extendAction,