@novely/core 0.49.0 → 0.51.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,19 +892,104 @@ 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();
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]
936
+ };
937
+ };
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
+
954
+ // src/store.ts
955
+ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
956
+ const subscribe = (cb) => {
957
+ subscribers.add(cb), cb(current);
958
+ return () => {
959
+ subscribers.delete(cb);
960
+ };
961
+ };
962
+ const push = (value) => {
963
+ for (const cb of subscribers) cb(value);
964
+ };
965
+ const update = (fn) => {
966
+ push(current = fn(current));
967
+ };
968
+ const set2 = (val) => {
969
+ update(() => val);
970
+ };
971
+ const get = () => {
972
+ return current;
973
+ };
974
+ return { subscribe, update, set: set2, get };
975
+ };
976
+ var derive = (input, map) => {
977
+ return {
978
+ get: () => map(input.get()),
979
+ subscribe: (subscriber) => {
980
+ return input.subscribe((value) => {
981
+ return subscriber(map(value));
982
+ });
776
983
  }
777
984
  };
778
- addEventListener("visibilitychange", onVisibilityChange);
779
- addEventListener("beforeunload", onChange);
780
- return () => {
781
- removeEventListener("visibilitychange", onVisibilityChange);
782
- removeEventListener("beforeunload", onChange);
985
+ };
986
+ var immutable = (value) => {
987
+ return {
988
+ get: () => value,
989
+ subscribe: (subscriber) => {
990
+ subscriber(value);
991
+ return noop;
992
+ }
783
993
  };
784
994
  };
785
995
 
@@ -810,7 +1020,8 @@ var handleCustomAction = (ctx, fn, {
810
1020
  setClear,
811
1021
  remove: renderersRemove,
812
1022
  getStack: getStack2,
813
- templateReplace
1023
+ templateReplace,
1024
+ paused
814
1025
  }) => {
815
1026
  const holder = getCustomActionHolder(ctx, fn);
816
1027
  const flags = {
@@ -866,7 +1077,8 @@ var handleCustomAction = (ctx, fn, {
866
1077
  rendererContext: ctx,
867
1078
  getDomNodes,
868
1079
  getPath,
869
- contextKey: ctx.id
1080
+ contextKey: ctx.id,
1081
+ paused: flags.preview ? immutable(false) : paused
870
1082
  });
871
1083
  };
872
1084
 
@@ -910,17 +1122,17 @@ var handleAssetsPreloading = async ({
910
1122
  await Promise.allSettled(list);
911
1123
  ASSETS_TO_PRELOAD.clear();
912
1124
  };
913
- var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) => {
1125
+ var huntAssets = ({ volume, lang, characters, action, props, handle }) => {
914
1126
  if (action === "showBackground") {
915
1127
  if (isString(props[0])) {
916
- handle(handleAudioAsset(props[0]));
1128
+ handle(unwrapAudioAsset(props[0]));
917
1129
  }
918
1130
  if (props[0] && typeof props[0] === "object") {
919
1131
  for (const value of Object.values(props[0])) {
920
1132
  if (isImageAsset(value)) {
921
1133
  handle(value);
922
1134
  } else if (isAsset(value)) {
923
- const unwrapped = handleImageAsset(value);
1135
+ const unwrapped = unwrapImageAsset(value);
924
1136
  if (isImageAsset(unwrapped)) {
925
1137
  handle(unwrapped);
926
1138
  }
@@ -937,7 +1149,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
937
1149
  };
938
1150
  if (isAudioAction(action) && isString(props[0])) {
939
1151
  if (getVolumeFor(action) > 0) {
940
- handle(handleAudioAsset(props[0]));
1152
+ handle(unwrapAudioAsset(props[0]));
941
1153
  }
942
1154
  return;
943
1155
  }
@@ -946,10 +1158,8 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
946
1158
  return;
947
1159
  }
948
1160
  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));
1161
+ if (language === lang) {
1162
+ value && handle(unwrapAudioAsset(value));
953
1163
  }
954
1164
  }
955
1165
  return;
@@ -957,7 +1167,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
957
1167
  if (action === "showCharacter" && isString(props[0]) && isString(props[1])) {
958
1168
  const images = toArray(characters[props[0]].emotions[props[1]]);
959
1169
  for (const asset2 of images) {
960
- handle(handleImageAsset(asset2));
1170
+ handle(unwrapImageAsset(asset2));
961
1171
  }
962
1172
  return;
963
1173
  }
@@ -971,7 +1181,7 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
971
1181
  for (let i = 1; i < props.length; i++) {
972
1182
  const data = props[i];
973
1183
  if (Array.isArray(data)) {
974
- handle(handleImageAsset(data[5]));
1184
+ handle(unwrapImageAsset(data[5]));
975
1185
  }
976
1186
  }
977
1187
  }
@@ -998,29 +1208,6 @@ var localStorageStorage = (options) => {
998
1208
  };
999
1209
  };
1000
1210
 
1001
- // src/store.ts
1002
- var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
1003
- const subscribe = (cb) => {
1004
- subscribers.add(cb), cb(current);
1005
- return () => {
1006
- subscribers.delete(cb);
1007
- };
1008
- };
1009
- const push = (value) => {
1010
- for (const cb of subscribers) cb(value);
1011
- };
1012
- const update = (fn) => {
1013
- push(current = fn(current));
1014
- };
1015
- const set2 = (val) => {
1016
- update(() => val);
1017
- };
1018
- const get = () => {
1019
- return current;
1020
- };
1021
- return { subscribe, update, set: set2, get };
1022
- };
1023
-
1024
1211
  // src/translation.ts
1025
1212
  var RGX = /{{(.*?)}}/g;
1026
1213
  var split = (input, delimeters) => {
@@ -1068,9 +1255,7 @@ var VIRTUAL_ACTIONS = ["say"];
1068
1255
  var buildActionObject = ({
1069
1256
  rendererActions,
1070
1257
  nativeActions,
1071
- characters,
1072
- preloadAssets,
1073
- storageData
1258
+ characters
1074
1259
  }) => {
1075
1260
  const allActions = [...nativeActions, ...VIRTUAL_ACTIONS];
1076
1261
  const object = { ...rendererActions };
@@ -1109,23 +1294,68 @@ var buildActionObject = ({
1109
1294
  actions[key] = flatActions(actions[key]);
1110
1295
  }
1111
1296
  }
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
1297
  return [action, ...props];
1124
1298
  };
1125
1299
  }
1126
1300
  return object;
1127
1301
  };
1128
1302
 
1303
+ // src/utilities/dialog-overview.ts
1304
+ var getDialogOverview = async function() {
1305
+ const { value: save } = this.getStack();
1306
+ const stateSnapshots = save[3];
1307
+ if (stateSnapshots.length == 0) {
1308
+ return [];
1309
+ }
1310
+ const { queue } = await getActionsFromPath({
1311
+ story: this.story,
1312
+ path: save[0],
1313
+ filter: false,
1314
+ referGuarded: this.referGuarded
1315
+ });
1316
+ const lang = this.getLanguage();
1317
+ const dialogItems = [];
1318
+ for (let p = 0, a = stateSnapshots.length, i = queue.length - 1; a > 0 && i > 0; i--) {
1319
+ const action = queue[i];
1320
+ if (action[0] === "dialog") {
1321
+ const [_, name, text] = action;
1322
+ let voice = void 0;
1323
+ for (let j = i - 1; j > p && j > 0; j--) {
1324
+ const action2 = queue[j];
1325
+ if (isUserRequiredAction(action2) || isSkippedDuringRestore(action2[0])) break;
1326
+ if (action2[0] === "stopVoice") break;
1327
+ if (action2[0] === "voice") {
1328
+ voice = action2[1];
1329
+ break;
1330
+ }
1331
+ }
1332
+ dialogItems.push({
1333
+ name,
1334
+ text,
1335
+ voice
1336
+ });
1337
+ p = i;
1338
+ a--;
1339
+ }
1340
+ }
1341
+ const entries = dialogItems.reverse().map(({ name, text, voice }, i) => {
1342
+ const state = stateSnapshots[i];
1343
+ const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1344
+ name = name ? this.getCharacterName(name) : "";
1345
+ return {
1346
+ name: this.templateReplace(name, state),
1347
+ text: this.templateReplace(text, state),
1348
+ voice: audioSource ? unwrapAudioAsset(audioSource) : ""
1349
+ };
1350
+ });
1351
+ return entries;
1352
+ };
1353
+
1354
+ // src/utilities/document.ts
1355
+ var setDocumentLanguage = (language) => {
1356
+ document.documentElement.lang = language;
1357
+ };
1358
+
1129
1359
  // src/novely.ts
1130
1360
  var novely = ({
1131
1361
  characters,
@@ -1144,14 +1374,15 @@ var novely = ({
1144
1374
  getLanguage: getLanguage2 = getLanguage,
1145
1375
  overrideLanguage = false,
1146
1376
  askBeforeExit = true,
1147
- preloadAssets = "lazy",
1377
+ preloadAssets = "automatic",
1148
1378
  parallelAssetsDownloadLimit = 15,
1149
1379
  fetch: request = fetch,
1150
1380
  cloneFunction: clone = klona,
1151
1381
  saveOnUnload = true,
1152
1382
  startKey = "start",
1153
1383
  defaultTypewriterSpeed = DEFAULT_TYPEWRITER_SPEED,
1154
- storyOptions = { mode: "static" }
1384
+ storyOptions = { mode: "static" },
1385
+ onLanguageChange
1155
1386
  }) => {
1156
1387
  const languages = Object.keys(translation);
1157
1388
  const limitScript = pLimit(1);
@@ -1165,7 +1396,7 @@ var novely = ({
1165
1396
  storyOptions.preloadSaves ??= 4;
1166
1397
  }
1167
1398
  const storyLoad = storyOptions.mode === "static" ? noop : storyOptions.load;
1168
- const onUnknownSceneHit = memoize4(async (scene) => {
1399
+ const onUnknownSceneHit = memoize5(async (scene) => {
1169
1400
  const part = await storyLoad(scene);
1170
1401
  if (part) {
1171
1402
  await script(part);
@@ -1180,14 +1411,6 @@ var novely = ({
1180
1411
  if (!initialScreenWasShown) {
1181
1412
  renderer.ui.showLoading();
1182
1413
  }
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
1414
  await dataLoaded.promise;
1192
1415
  renderer.ui.hideLoading();
1193
1416
  if (!initialScreenWasShown) {
@@ -1216,6 +1439,7 @@ var novely = ({
1216
1439
  const getLanguageWithoutParameters = () => {
1217
1440
  const language = getLanguage2(languages, getLanguage);
1218
1441
  if (languages.includes(language)) {
1442
+ setDocumentLanguage(language);
1219
1443
  return language;
1220
1444
  }
1221
1445
  if (DEV5) {
@@ -1232,8 +1456,11 @@ var novely = ({
1232
1456
  };
1233
1457
  const storageData = store(initialData);
1234
1458
  const coreData = store({
1235
- dataLoaded: false
1459
+ dataLoaded: false,
1460
+ paused: false,
1461
+ focused: document.visibilityState === "visible"
1236
1462
  });
1463
+ const paused = derive(coreData, (s) => s.paused || !s.focused);
1237
1464
  const onDataLoadedPromise = async ({ cancelled }) => {
1238
1465
  if (cancelled) {
1239
1466
  dataLoaded.promise.then(onDataLoadedPromise);
@@ -1246,11 +1473,7 @@ var novely = ({
1246
1473
  referGuarded(path);
1247
1474
  }
1248
1475
  };
1249
- if (preloadAssets === "blocking") {
1250
- await preload();
1251
- } else {
1252
- void preload();
1253
- }
1476
+ preload();
1254
1477
  coreData.update((data2) => {
1255
1478
  data2.dataLoaded = true;
1256
1479
  return data2;
@@ -1265,13 +1488,12 @@ var novely = ({
1265
1488
  }
1266
1489
  storage.set(data2);
1267
1490
  };
1268
- const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
1269
- const throttledEmergencyOnStorageDataChange = throttle(() => {
1270
- if (saveOnUnload === true || saveOnUnload === "prod" && !DEV5) {
1271
- onStorageDataChange(storageData.get());
1272
- }
1273
- }, 10);
1491
+ const throttledShortOnStorageDataChange = throttle(() => onStorageDataChange(storageData.get()), 10);
1492
+ const throttledOnStorageDataChange = throttle(throttledShortOnStorageDataChange, throttleTimeout);
1274
1493
  storageData.subscribe(throttledOnStorageDataChange);
1494
+ if (saveOnUnload === true || saveOnUnload === "prod" && !DEV5) {
1495
+ addEventListener("beforeunload", throttledShortOnStorageDataChange);
1496
+ }
1275
1497
  const getStoredData = async () => {
1276
1498
  let stored = await storage.get();
1277
1499
  for (const migration of migrations) {
@@ -1295,9 +1517,6 @@ var novely = ({
1295
1517
  };
1296
1518
  storageDelay.then(getStoredData);
1297
1519
  const initial = getDefaultSave(clone(defaultState));
1298
- const unsubscribeFromBrowserVisibilityChange = setupBrowserVisibilityChangeListeners({
1299
- onChange: throttledEmergencyOnStorageDataChange
1300
- });
1301
1520
  const save = (type) => {
1302
1521
  if (!coreData.get().dataLoaded) return;
1303
1522
  if (!autosaves && type === "auto") return;
@@ -1514,7 +1733,6 @@ var novely = ({
1514
1733
  huntAssets({
1515
1734
  action: action2,
1516
1735
  props,
1517
- mode: preloadAssets,
1518
1736
  characters,
1519
1737
  lang: getLanguageFromStore(storageData),
1520
1738
  volume: getVolumeFromStore(storageData),
@@ -1561,7 +1779,7 @@ var novely = ({
1561
1779
  const clearCustomAction = (ctx, fn) => {
1562
1780
  getCustomActionHolder(ctx, fn).cleanup();
1563
1781
  };
1564
- const getResourseTypeForRenderer = (url) => {
1782
+ const getResourseTypeWrapper = (url) => {
1565
1783
  return getResourseType({
1566
1784
  url,
1567
1785
  request
@@ -1571,12 +1789,12 @@ var novely = ({
1571
1789
  return c in characters ? characters[c].color : "#000000";
1572
1790
  };
1573
1791
  const getCharacterAssets = (character, emotion) => {
1574
- return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1792
+ return toArray(characters[character].emotions[emotion]).map(unwrapImageAsset);
1575
1793
  };
1576
1794
  const getCharacterName = (character) => {
1577
1795
  const c = character;
1578
1796
  const cs = characters;
1579
- const [lang] = storageData.get().meta;
1797
+ const lang = getLanguageFromStore(storageData);
1580
1798
  if (c && c in cs) {
1581
1799
  const block = cs[c].name;
1582
1800
  if (typeof block === "string") {
@@ -1586,56 +1804,19 @@ var novely = ({
1586
1804
  return block[lang];
1587
1805
  }
1588
1806
  }
1589
- return String(c) || "";
1807
+ return String(c);
1590
1808
  };
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--;
1809
+ const setLanguage = (lang) => {
1810
+ storageData.update((prev) => {
1811
+ if (languages.includes(lang)) {
1812
+ prev.meta[0] = lang;
1626
1813
  }
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
- };
1814
+ if (lang === prev.meta[0]) {
1815
+ setDocumentLanguage(lang);
1816
+ onLanguageChange?.(lang);
1817
+ }
1818
+ return prev;
1637
1819
  });
1638
- return entries;
1639
1820
  };
1640
1821
  const renderer = createRenderer({
1641
1822
  mainContextKey: MAIN_CONTEXT_KEY,
@@ -1658,8 +1839,16 @@ var novely = ({
1658
1839
  getLanguageDisplayName,
1659
1840
  getCharacterColor,
1660
1841
  getCharacterAssets,
1661
- getDialogOverview,
1662
- getResourseType: getResourseTypeForRenderer
1842
+ getDialogOverview: getDialogOverview.bind({
1843
+ referGuarded,
1844
+ story,
1845
+ getCharacterName,
1846
+ getLanguage: () => getLanguageFromStore(storageData),
1847
+ getStack: () => useStack(MAIN_CONTEXT_KEY),
1848
+ templateReplace: (...args) => templateReplace(...args)
1849
+ }),
1850
+ getResourseType: getResourseTypeWrapper,
1851
+ setLanguage
1663
1852
  });
1664
1853
  const useStack = createUseStackFunction(renderer);
1665
1854
  useStack(MAIN_CONTEXT_KEY).push(initial);
@@ -1703,7 +1892,6 @@ var novely = ({
1703
1892
  huntAssets({
1704
1893
  action: action3,
1705
1894
  props: props2,
1706
- mode: preloadAssets,
1707
1895
  characters,
1708
1896
  lang: getLanguageFromStore(storageData),
1709
1897
  volume: getVolumeFromStore(storageData),
@@ -1728,47 +1916,47 @@ var novely = ({
1728
1916
  showBackground({ ctx, push }, [background]) {
1729
1917
  if (isString(background) || isAsset(background)) {
1730
1918
  ctx.background({
1731
- all: handleImageAsset(background)
1919
+ all: unwrapImageAsset(background)
1732
1920
  });
1733
1921
  } else {
1734
1922
  ctx.background(
1735
- Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)]))
1923
+ Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, unwrapImageAsset(asset2)]))
1736
1924
  );
1737
1925
  }
1738
1926
  push();
1739
1927
  },
1740
1928
  playMusic({ ctx, push }, [source]) {
1741
- ctx.audio.music(handleAudioAsset(source), "music").play(true);
1929
+ ctx.audio.music(unwrapAudioAsset(source), paused, "music").play(true);
1742
1930
  push();
1743
1931
  },
1744
1932
  pauseMusic({ ctx, push }, [source]) {
1745
- ctx.audio.music(handleAudioAsset(source), "music").pause();
1933
+ ctx.audio.music(unwrapAudioAsset(source), paused, "music").pause();
1746
1934
  push();
1747
1935
  },
1748
1936
  stopMusic({ ctx, push }, [source]) {
1749
- ctx.audio.music(handleAudioAsset(source), "music").stop();
1937
+ ctx.audio.music(unwrapAudioAsset(source), paused, "music").stop();
1750
1938
  push();
1751
1939
  },
1752
1940
  playSound({ ctx, push }, [source, loop]) {
1753
- ctx.audio.music(handleAudioAsset(source), "sound").play(loop || false);
1941
+ ctx.audio.music(unwrapAudioAsset(source), paused, "sound").play(loop || false);
1754
1942
  push();
1755
1943
  },
1756
1944
  pauseSound({ ctx, push }, [source]) {
1757
- ctx.audio.music(handleAudioAsset(source), "sound").pause();
1945
+ ctx.audio.music(unwrapAudioAsset(source), paused, "sound").pause();
1758
1946
  push();
1759
1947
  },
1760
1948
  stopSound({ ctx, push }, [source]) {
1761
- ctx.audio.music(handleAudioAsset(source), "sound").stop();
1949
+ ctx.audio.music(unwrapAudioAsset(source), paused, "sound").stop();
1762
1950
  push();
1763
1951
  },
1764
1952
  voice({ ctx, push }, [source]) {
1765
- const [lang] = storageData.get().meta;
1953
+ const lang = getLanguageFromStore(storageData);
1766
1954
  const audioSource = isString(source) ? source : isAsset(source) ? source : source[lang];
1767
1955
  if (!audioSource) {
1768
1956
  push();
1769
1957
  return;
1770
1958
  }
1771
- ctx.audio.voice(handleAudioAsset(audioSource));
1959
+ ctx.audio.voice(unwrapAudioAsset(audioSource), paused);
1772
1960
  push();
1773
1961
  },
1774
1962
  stopVoice({ ctx, push }) {
@@ -1845,7 +2033,7 @@ var novely = ({
1845
2033
  recompute: update
1846
2034
  });
1847
2035
  };
1848
- const imageValue = image ? handleImageAsset(image) : "";
2036
+ const imageValue = image ? unwrapImageAsset(image) : "";
1849
2037
  return [templateReplace(content, data2), active$, visible$, onSelectWrapped, imageValue];
1850
2038
  });
1851
2039
  if (DEV5 && transformedChoices.length === 0) {
@@ -1925,6 +2113,7 @@ var novely = ({
1925
2113
  state,
1926
2114
  lang,
1927
2115
  getStack: useStack,
2116
+ paused,
1928
2117
  templateReplace
1929
2118
  });
1930
2119
  const next2 = () => {
@@ -1990,11 +2179,28 @@ var novely = ({
1990
2179
  preload({ ctx, push }, [source]) {
1991
2180
  if (DEV5 && preloadAssets !== "lazy") {
1992
2181
  console.error(
1993
- `You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`
2182
+ `You do not need a preload action because "preloadAssets" strategy was set to "${preloadAssets}"`
1994
2183
  );
2184
+ push();
2185
+ return;
1995
2186
  }
1996
- if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1997
- PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
2187
+ const src = unwrapAsset(source);
2188
+ if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(src)) {
2189
+ const process = async () => {
2190
+ const type = isAsset(source) ? source.type : await getResourseTypeWrapper(src);
2191
+ if (type === "image") {
2192
+ renderer.misc.preloadAudioBlocking(src);
2193
+ } else if (type === "audio") {
2194
+ renderer.misc.preloadImage(src);
2195
+ } else {
2196
+ if (DEV5) {
2197
+ console.error(`Preload error: Unknown type of the following resource: `, source);
2198
+ }
2199
+ return;
2200
+ }
2201
+ PRELOADED_ASSETS.add(src);
2202
+ };
2203
+ process();
1998
2204
  }
1999
2205
  push();
2000
2206
  },
@@ -2015,9 +2221,7 @@ var novely = ({
2015
2221
  const action = buildActionObject({
2016
2222
  rendererActions: renderer.actions,
2017
2223
  nativeActions,
2018
- characters,
2019
- preloadAssets,
2020
- storageData
2224
+ characters
2021
2225
  });
2022
2226
  const render = async (ctx) => {
2023
2227
  const stack = useStack(ctx);
@@ -2072,12 +2276,6 @@ var novely = ({
2072
2276
  });
2073
2277
  return void 0;
2074
2278
  };
2075
- const typeEssentials = {
2076
- l: null,
2077
- s: null,
2078
- d: null,
2079
- c: null
2080
- };
2081
2279
  const getCurrentStorageData = () => {
2082
2280
  return coreData.get().dataLoaded ? clone(storageData.get()) : null;
2083
2281
  };
@@ -2143,7 +2341,7 @@ var novely = ({
2143
2341
  data,
2144
2342
  /**
2145
2343
  * Used in combination with type utilities
2146
- *
2344
+ * @deprecated Use `engine.types` instead
2147
2345
  * @example
2148
2346
  * ```ts
2149
2347
  * import type { ConditionParams, StateFunction } from '@novely/core';
@@ -2153,7 +2351,21 @@ var novely = ({
2153
2351
  * }
2154
2352
  * ```
2155
2353
  */
2156
- typeEssentials,
2354
+ typeEssentials: {},
2355
+ /**
2356
+ * Used in combination with type utilities
2357
+ * @example
2358
+ * ```ts
2359
+ * import type { TypesFromEngine, ConditionParams, StateFunction } from '@novely/core';
2360
+ *
2361
+ * type Types = TypesFromEngine<typeof engine>;
2362
+ *
2363
+ * const conditionCheck = (state: StateFunction<ConditionParams<Types>>) => {
2364
+ * return state.age >= 18;
2365
+ * }
2366
+ * ```
2367
+ */
2368
+ types: null,
2157
2369
  /**
2158
2370
  * Replaces content inside {{braces}} using global data
2159
2371
  * @example
@@ -2180,9 +2392,10 @@ var novely = ({
2180
2392
  * Data updates still will work in case Novely already was loaded
2181
2393
  */
2182
2394
  destroy() {
2395
+ if (destroyed) return;
2183
2396
  dataLoaded.cancel();
2184
2397
  UIInstance.unmount();
2185
- unsubscribeFromBrowserVisibilityChange();
2398
+ removeEventListener("beforeunload", throttledShortOnStorageDataChange);
2186
2399
  destroyed = true;
2187
2400
  },
2188
2401
  /**
@@ -2210,7 +2423,44 @@ var novely = ({
2210
2423
  * }
2211
2424
  * ```
2212
2425
  */
2213
- setStorageData
2426
+ setStorageData,
2427
+ /**
2428
+ * Function to control paused state. Custom Actions are provided with `paused` store they can subscribe to.
2429
+ * This function will notify Custom Actions. Pause state can be used when showing ads.
2430
+ * @example
2431
+ * ```ts
2432
+ * sdk.on('pause' () => engine.setPaused(true));
2433
+ * sdk.on('resume', () => engine.setPaused(false));
2434
+ * ```
2435
+ */
2436
+ setPaused: (paused2) => {
2437
+ coreData.update((prev) => {
2438
+ prev.paused = paused2;
2439
+ return prev;
2440
+ });
2441
+ },
2442
+ /**
2443
+ * Function to control focused state. It will affect `paused` store passed to Custom Actions.
2444
+ * This function can be used to pause game when it's not focused.
2445
+ * @example
2446
+ * ```ts
2447
+ * import { pauseOnBlur } from '@novely/core';
2448
+ *
2449
+ * // Will subscribe to blur/focus events and call `setFocused`
2450
+ * pauseOnBlur(engine);
2451
+ *
2452
+ * // OR
2453
+ *
2454
+ * sdk.on('focus' () => engine.setFocused(true));
2455
+ * sdk.on('blur', () => engine.setFocused(false));
2456
+ * ```
2457
+ */
2458
+ setFocused: (focused) => {
2459
+ coreData.update((prev) => {
2460
+ prev.focused = focused;
2461
+ return prev;
2462
+ });
2463
+ }
2214
2464
  };
2215
2465
  };
2216
2466
 
@@ -2224,42 +2474,42 @@ var extendAction = (base, extension) => {
2224
2474
 
2225
2475
  // src/translations.ts
2226
2476
  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"
2477
+ NewGame: "Новая игра",
2478
+ HomeScreen: "Главный экран",
2479
+ ToTheGame: "К игре",
2480
+ Language: "Язык",
2481
+ NoSaves: "Сохранений нет",
2482
+ LoadSave: "Загрузить",
2483
+ Saves: "Сохранения",
2484
+ Settings: "Настройки",
2485
+ Sumbit: "Подтвердить",
2486
+ GoBack: "Назад",
2487
+ DoSave: "Сохранение",
2488
+ Auto: "Авто",
2489
+ Stop: "Стоп",
2490
+ Exit: "Выход",
2491
+ Automatic: "Автоматическое",
2492
+ Manual: "Ручное",
2493
+ Remove: "Удалить",
2494
+ LoadASaveFrom: "Загрузить сохранение от",
2495
+ DeleteASaveFrom: "Удалить сохранение от",
2496
+ TextSpeed: "Скорость текста",
2497
+ TextSpeedSlow: "Медленная",
2498
+ TextSpeedMedium: "Средняя",
2499
+ TextSpeedFast: "Быстрая",
2500
+ TextSpeedAuto: "Автоматическая",
2501
+ CompleteText: "Завершить текст",
2502
+ GoForward: "Перейти вперёд",
2503
+ ExitDialogWarning: "Вы уверены, что хотите выйти? Прогресс будет сохранён.",
2504
+ ExitDialogExit: "Выйти",
2505
+ ExitDialogBack: "Вернуться в игру",
2506
+ OpenMenu: "Открыть меню",
2507
+ CloseMenu: "Закрыть меню",
2508
+ MusicVolume: "Громкость музыки",
2509
+ SoundVolume: "Громкость звуков",
2510
+ VoiceVolume: "Громкость речи",
2511
+ Close: "Закрыть",
2512
+ DialogOverview: "Обзор диалога"
2263
2513
  };
2264
2514
  var EN = {
2265
2515
  NewGame: "New Game",
@@ -2299,244 +2549,53 @@ var EN = {
2299
2549
  Close: "Close",
2300
2550
  DialogOverview: "Dialog Overview"
2301
2551
  };
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
2552
 
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);
2553
+ // src/browser-events.ts
2554
+ var BLUR_HANDLERS = /* @__PURE__ */ new Set();
2555
+ var FOCUS_HANDLERS = /* @__PURE__ */ new Set();
2556
+ var registerEventListeners = (listeners) => {
2557
+ BLUR_HANDLERS.add(listeners.blur);
2558
+ FOCUS_HANDLERS.add(listeners.focus);
2559
+ return () => {
2560
+ BLUR_HANDLERS.delete(listeners.blur);
2561
+ FOCUS_HANDLERS.delete(listeners.focus);
2417
2562
  };
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
2563
  };
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";
2564
+ addEventListener("focus", function(event) {
2565
+ for (const handler of FOCUS_HANDLERS) {
2566
+ try {
2567
+ handler.call(this.document, event);
2568
+ } catch {
2448
2569
  }
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
2570
  }
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);
2571
+ });
2572
+ addEventListener("blur", function(event) {
2573
+ for (const handler of BLUR_HANDLERS) {
2574
+ try {
2575
+ handler.call(this.document, event);
2576
+ } catch {
2473
2577
  }
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
2578
  }
2524
- const asset2 = {
2525
- type: "audio",
2526
- source,
2527
- id: generateRandomId()
2579
+ });
2580
+ var pauseOnBlur = (engine) => {
2581
+ return {
2582
+ unsubscribe: registerEventListeners({
2583
+ focus: () => {
2584
+ engine.setFocused(true);
2585
+ },
2586
+ blur: () => {
2587
+ engine.setFocused(false);
2588
+ }
2589
+ })
2528
2590
  };
2529
- assetPrivate.cache.set(source, asset2);
2530
- return asset2;
2531
2591
  };
2532
2592
  export {
2533
2593
  EN,
2534
- JP,
2535
- KK,
2536
2594
  RU,
2537
2595
  asset,
2538
2596
  extendAction,
2539
2597
  localStorageStorage,
2540
- novely
2598
+ novely,
2599
+ pauseOnBlur
2541
2600
  };
2542
2601
  //# sourceMappingURL=index.js.map