@novely/core 0.45.1 → 0.45.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,15 +1,91 @@
1
+ // src/novely.ts
2
+ import { dequal } from "dequal/lite";
3
+ import { throttle } from "es-toolkit/function";
4
+ import { merge as deepmerge } from "es-toolkit/object";
5
+ import { DEV as DEV3 } from "esm-env";
6
+
7
+ // ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/full/index.mjs
8
+ function set(obj, key, val) {
9
+ if (typeof val.value === "object") val.value = klona(val.value);
10
+ if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") {
11
+ Object.defineProperty(obj, key, val);
12
+ } else obj[key] = val.value;
13
+ }
14
+ function klona(x) {
15
+ if (typeof x !== "object") return x;
16
+ var i = 0, k, list, tmp, str2 = Object.prototype.toString.call(x);
17
+ if (str2 === "[object Object]") {
18
+ tmp = Object.create(x.__proto__ || null);
19
+ } else if (str2 === "[object Array]") {
20
+ tmp = Array(x.length);
21
+ } else if (str2 === "[object Set]") {
22
+ tmp = /* @__PURE__ */ new Set();
23
+ x.forEach(function(val) {
24
+ tmp.add(klona(val));
25
+ });
26
+ } else if (str2 === "[object Map]") {
27
+ tmp = /* @__PURE__ */ new Map();
28
+ x.forEach(function(val, key) {
29
+ tmp.set(klona(key), klona(val));
30
+ });
31
+ } else if (str2 === "[object Date]") {
32
+ tmp = /* @__PURE__ */ new Date(+x);
33
+ } else if (str2 === "[object RegExp]") {
34
+ tmp = new RegExp(x.source, x.flags);
35
+ } else if (str2 === "[object DataView]") {
36
+ tmp = new x.constructor(klona(x.buffer));
37
+ } else if (str2 === "[object ArrayBuffer]") {
38
+ tmp = x.slice(0);
39
+ } else if (str2.slice(-6) === "Array]") {
40
+ tmp = new x.constructor(x);
41
+ }
42
+ if (tmp) {
43
+ for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
44
+ set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
45
+ }
46
+ for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
47
+ if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue;
48
+ set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
49
+ }
50
+ }
51
+ return tmp || x;
52
+ }
53
+
54
+ // src/novely.ts
55
+ import pLimit from "p-limit";
56
+
57
+ // src/asset.ts
58
+ import { memoize as memoize2, once } from "es-toolkit/function";
59
+ import { DEV as DEV2 } from "esm-env";
60
+
61
+ // src/audio-codecs.ts
62
+ var cut = (str2) => str2.replace(/^no$/, "");
63
+ var audio = new Audio();
64
+ var canPlay = (type) => !!cut(audio.canPlayType(type));
65
+ var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
66
+ var supportsMap = {
67
+ mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
68
+ mpeg: canPlay("audio/mpeg;"),
69
+ opus: canPlay('audio/ogg; codecs="opus"'),
70
+ ogg: canPlay('audio/ogg; codecs="vorbis"'),
71
+ oga: canPlay('audio/ogg; codecs="vorbis"'),
72
+ wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
73
+ aac: canPlay("audio/aac;"),
74
+ caf: canPlay("audio/x-caf;"),
75
+ m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
76
+ m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
77
+ mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
78
+ weba: canPlay('audio/webm; codecs="vorbis"'),
79
+ webm: canPlay('audio/webm; codecs="vorbis"'),
80
+ dolby: canPlay('audio/mp4; codecs="ec-3"'),
81
+ flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
82
+ };
83
+
1
84
  // src/constants.ts
2
85
  var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
3
86
  var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
4
87
  var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
5
- var AUDIO_ACTIONS = /* @__PURE__ */ new Set([
6
- "playMusic",
7
- "stopMusic",
8
- "playSound",
9
- "stopSound",
10
- "voice",
11
- "stopVoice"
12
- ]);
88
+ var AUDIO_ACTIONS = /* @__PURE__ */ new Set(["playMusic", "stopMusic", "playSound", "stopSound", "voice", "stopVoice"]);
13
89
  var EMPTY_SET = /* @__PURE__ */ new Set();
14
90
  var DEFAULT_TYPEWRITER_SPEED = "Medium";
15
91
  var HOWLER_SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set([
@@ -45,42 +121,6 @@ var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
45
121
  ]);
46
122
  var MAIN_CONTEXT_KEY = "$MAIN";
47
123
 
48
- // src/shared.ts
49
- var STACK_MAP = /* @__PURE__ */ new Map();
50
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
51
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
52
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
53
-
54
- // src/utils.ts
55
- import { DEV as DEV2 } from "esm-env";
56
- import { memoize as memoize2 } from "es-toolkit/function";
57
-
58
- // src/asset.ts
59
- import { DEV } from "esm-env";
60
-
61
- // src/audio-codecs.ts
62
- var cut = (str2) => str2.replace(/^no$/, "");
63
- var audio = new Audio();
64
- var canPlay = (type) => !!cut(audio.canPlayType(type));
65
- var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
66
- var supportsMap = {
67
- mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
68
- mpeg: canPlay("audio/mpeg;"),
69
- opus: canPlay('audio/ogg; codecs="opus"'),
70
- ogg: canPlay('audio/ogg; codecs="vorbis"'),
71
- oga: canPlay('audio/ogg; codecs="vorbis"'),
72
- wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
73
- aac: canPlay("audio/aac;"),
74
- caf: canPlay("audio/x-caf;"),
75
- m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
76
- m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
77
- mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
78
- weba: canPlay('audio/webm; codecs="vorbis"'),
79
- webm: canPlay('audio/webm; codecs="vorbis"'),
80
- dolby: canPlay('audio/mp4; codecs="ec-3"'),
81
- flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
82
- };
83
-
84
124
  // src/image-formats.ts
85
125
  var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
86
126
  var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
@@ -117,74 +157,15 @@ var loadImageFormatsSupport = async () => {
117
157
  };
118
158
  loadImageFormatsSupport();
119
159
 
120
- // src/asset.ts
121
- import { memoize, once } from "es-toolkit/function";
122
- var getType = memoize(
123
- (extensions) => {
124
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
125
- return "audio";
126
- }
127
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
128
- return "image";
129
- }
130
- throw extensions;
131
- },
132
- {
133
- getCacheKey: (extensions) => extensions.join("~")
134
- }
135
- );
136
- var SUPPORT_MAPS = {
137
- "image": supportsMap2,
138
- "audio": supportsMap
139
- };
140
- var assetPrivate = memoize(
141
- (variants) => {
142
- if (DEV && variants.length === 0) {
143
- throw new Error(`Attempt to use "asset" function without arguments`);
144
- }
145
- const map = {};
146
- const extensions = [];
147
- for (const v of variants) {
148
- const e = getUrlFileExtension(v);
149
- map[e] = v;
150
- extensions.push(e);
151
- }
152
- const type = getType(extensions);
153
- const getSource = once(() => {
154
- const support = SUPPORT_MAPS[type];
155
- for (const extension of extensions) {
156
- if (extension in support) {
157
- if (support[extension]) {
158
- return map[extension];
159
- }
160
- } else {
161
- return map[extension];
162
- }
163
- }
164
- if (DEV) {
165
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
166
- }
167
- return "";
168
- });
169
- return {
170
- get source() {
171
- return getSource();
172
- },
173
- get type() {
174
- return type;
175
- }
176
- };
177
- },
178
- {
179
- getCacheKey: (variants) => variants.join("~")
180
- }
181
- );
182
- var asset = (...variants) => {
183
- return assetPrivate(variants);
184
- };
185
- var isAsset = (suspect) => {
186
- return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
187
- };
160
+ // src/utils.ts
161
+ import { memoize } from "es-toolkit/function";
162
+ import { DEV } from "esm-env";
163
+
164
+ // src/shared.ts
165
+ var STACK_MAP = /* @__PURE__ */ new Map();
166
+ var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
167
+ var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
168
+ var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
188
169
 
189
170
  // src/utils.ts
190
171
  var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
@@ -195,18 +176,21 @@ var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) =>
195
176
  props,
196
177
  ctx: context
197
178
  });
198
- return values[action]({
199
- ctx: context,
200
- data,
201
- push() {
202
- if (context.meta.preview) return;
203
- push(context);
179
+ return values[action](
180
+ {
181
+ ctx: context,
182
+ data,
183
+ push() {
184
+ if (context.meta.preview) return;
185
+ push(context);
186
+ },
187
+ forward() {
188
+ if (context.meta.preview) return;
189
+ forward(context);
190
+ }
204
191
  },
205
- forward() {
206
- if (context.meta.preview) return;
207
- forward(context);
208
- }
209
- }, props);
192
+ props
193
+ );
210
194
  };
211
195
  };
212
196
  var isNumber = (val) => {
@@ -318,10 +302,10 @@ var isExitImpossible = (path) => {
318
302
  };
319
303
  var getOppositeAction = (action) => {
320
304
  const MAP = {
321
- "showCharacter": "hideCharacter",
322
- "playSound": "stopSound",
323
- "playMusic": "stopMusic",
324
- "voice": "stopVoice"
305
+ showCharacter: "hideCharacter",
306
+ playSound: "stopSound",
307
+ playMusic: "stopMusic",
308
+ voice: "stopVoice"
325
309
  };
326
310
  return MAP[action];
327
311
  };
@@ -488,7 +472,7 @@ var createQueueProcessor = (queue, options) => {
488
472
  }
489
473
  };
490
474
  };
491
- var getStack = memoize2(
475
+ var getStack = memoize(
492
476
  (_) => {
493
477
  return [];
494
478
  },
@@ -540,7 +524,7 @@ var getUrlFileExtension = (address) => {
540
524
  const { pathname } = new URL(address, location.href);
541
525
  return pathname.split(".").at(-1).split("!")[0].split(":")[0];
542
526
  } catch (error) {
543
- if (DEV2) {
527
+ if (DEV) {
544
528
  console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
545
529
  }
546
530
  return "";
@@ -553,13 +537,13 @@ var fetchContentType = async (url, request) => {
553
537
  });
554
538
  return response.headers.get("Content-Type") || "";
555
539
  } catch (error) {
556
- if (DEV2) {
540
+ if (DEV) {
557
541
  console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
558
542
  }
559
543
  return "";
560
544
  }
561
545
  };
562
- var getResourseType = memoize2(
546
+ var getResourseType = memoize(
563
547
  async ({ url, request }) => {
564
548
  if (!isCSSImage(url)) {
565
549
  return "other";
@@ -587,7 +571,7 @@ var getResourseType = memoize2(
587
571
  var capitalize = (str2) => {
588
572
  return str2[0].toUpperCase() + str2.slice(1);
589
573
  };
590
- var getIntlLanguageDisplayName = memoize2((lang) => {
574
+ var getIntlLanguageDisplayName = memoize((lang) => {
591
575
  try {
592
576
  const intl = new Intl.DisplayNames([lang], {
593
577
  type: "language"
@@ -732,7 +716,10 @@ var collectActionsBeforeBlockingAction = ({ path, refer, clone }) => {
732
716
  }
733
717
  collection.push(action);
734
718
  if (action[0] === "jump") {
735
- path = [["jump", action[1]], [null, 0]];
719
+ path = [
720
+ ["jump", action[1]],
721
+ [null, 0]
722
+ ];
736
723
  } else if (action[0] == "block") {
737
724
  path.push(["block", action[1]], [null, 0]);
738
725
  } else {
@@ -746,13 +733,13 @@ var unwrapAsset = (asset2) => {
746
733
  return isAsset(asset2) ? asset2.source : asset2;
747
734
  };
748
735
  var handleAudioAsset = (asset2) => {
749
- if (DEV2 && isAsset(asset2) && asset2.type !== "audio") {
736
+ if (DEV && isAsset(asset2) && asset2.type !== "audio") {
750
737
  throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
751
738
  }
752
739
  return unwrapAsset(asset2);
753
740
  };
754
741
  var handleImageAsset = (asset2) => {
755
- if (DEV2 && isAsset(asset2) && asset2.type !== "image") {
742
+ if (DEV && isAsset(asset2) && asset2.type !== "image") {
756
743
  throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
757
744
  }
758
745
  return unwrapAsset(asset2);
@@ -777,120 +764,88 @@ var getVolumeFromStore = (store2) => {
777
764
  };
778
765
  };
779
766
 
780
- // src/novely.ts
781
- import { throttle } from "es-toolkit/function";
782
- import { merge as deepmerge } from "es-toolkit/object";
783
- import { dequal } from "dequal/lite";
784
-
785
- // src/store.ts
786
- var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
787
- const subscribe = (cb) => {
788
- subscribers.add(cb), cb(current);
789
- return () => {
790
- subscribers.delete(cb);
791
- };
792
- };
793
- const push = (value) => {
794
- for (const cb of subscribers) cb(value);
795
- };
796
- const update = (fn) => {
797
- push(current = fn(current));
798
- };
799
- const set2 = (val) => {
800
- update(() => val);
801
- };
802
- const get = () => {
803
- return current;
804
- };
805
- return { subscribe, update, set: set2, get };
806
- };
807
-
808
- // ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/full/index.mjs
809
- function set(obj, key, val) {
810
- if (typeof val.value === "object") val.value = klona(val.value);
811
- if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") {
812
- Object.defineProperty(obj, key, val);
813
- } else obj[key] = val.value;
814
- }
815
- function klona(x) {
816
- if (typeof x !== "object") return x;
817
- var i = 0, k, list, tmp, str2 = Object.prototype.toString.call(x);
818
- if (str2 === "[object Object]") {
819
- tmp = Object.create(x.__proto__ || null);
820
- } else if (str2 === "[object Array]") {
821
- tmp = Array(x.length);
822
- } else if (str2 === "[object Set]") {
823
- tmp = /* @__PURE__ */ new Set();
824
- x.forEach(function(val) {
825
- tmp.add(klona(val));
826
- });
827
- } else if (str2 === "[object Map]") {
828
- tmp = /* @__PURE__ */ new Map();
829
- x.forEach(function(val, key) {
830
- tmp.set(klona(key), klona(val));
831
- });
832
- } else if (str2 === "[object Date]") {
833
- tmp = /* @__PURE__ */ new Date(+x);
834
- } else if (str2 === "[object RegExp]") {
835
- tmp = new RegExp(x.source, x.flags);
836
- } else if (str2 === "[object DataView]") {
837
- tmp = new x.constructor(klona(x.buffer));
838
- } else if (str2 === "[object ArrayBuffer]") {
839
- tmp = x.slice(0);
840
- } else if (str2.slice(-6) === "Array]") {
841
- tmp = new x.constructor(x);
842
- }
843
- if (tmp) {
844
- for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
845
- set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
767
+ // src/asset.ts
768
+ var getType = memoize2(
769
+ (extensions) => {
770
+ if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
771
+ return "audio";
846
772
  }
847
- for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
848
- if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue;
849
- set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
773
+ if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
774
+ return "image";
850
775
  }
776
+ throw extensions;
777
+ },
778
+ {
779
+ getCacheKey: (extensions) => extensions.join("~")
851
780
  }
852
- return tmp || x;
853
- }
854
-
855
- // src/translation.ts
856
- var RGX = /{{(.*?)}}/g;
857
- var split = (input, delimeters) => {
858
- const output = [];
859
- for (const delimeter of delimeters) {
860
- if (!input) break;
861
- const [start, end] = input.split(delimeter, 2);
862
- output.push(start);
863
- input = end;
864
- }
865
- output.push(input);
866
- return output;
781
+ );
782
+ var SUPPORT_MAPS = {
783
+ image: supportsMap2,
784
+ audio: supportsMap
867
785
  };
868
- var flattenAllowedContent = (c, state) => {
869
- if (Array.isArray(c)) {
870
- return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
871
- }
872
- if (typeof c === "function") {
873
- return flattenAllowedContent(c(state), state);
786
+ var assetPrivate = memoize2(
787
+ (variants) => {
788
+ if (DEV2 && variants.length === 0) {
789
+ throw new Error(`Attempt to use "asset" function without arguments`);
790
+ }
791
+ const map = {};
792
+ const extensions = [];
793
+ for (const v of variants) {
794
+ const e = getUrlFileExtension(v);
795
+ map[e] = v;
796
+ extensions.push(e);
797
+ }
798
+ const type = getType(extensions);
799
+ const getSource = once(() => {
800
+ const support = SUPPORT_MAPS[type];
801
+ for (const extension of extensions) {
802
+ if (extension in support) {
803
+ if (support[extension]) {
804
+ return map[extension];
805
+ }
806
+ } else {
807
+ return map[extension];
808
+ }
809
+ }
810
+ if (DEV2) {
811
+ throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
812
+ }
813
+ return "";
814
+ });
815
+ return {
816
+ get source() {
817
+ return getSource();
818
+ },
819
+ get type() {
820
+ return type;
821
+ }
822
+ };
823
+ },
824
+ {
825
+ getCacheKey: (variants) => variants.join("~")
874
826
  }
875
- return c;
827
+ );
828
+ var asset = (...variants) => {
829
+ return assetPrivate(variants);
876
830
  };
877
- var replace = (input, data, pluralization, actions, pr) => {
878
- return input.replaceAll(RGX, (x, key, y) => {
879
- x = 0;
880
- y = data;
881
- const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
882
- if (!pathstr) {
883
- return "";
884
- }
885
- const path = pathstr.split(".");
886
- while (y && x < path.length) y = y[path[x++]];
887
- if (plural && pluralization && y && pr) {
888
- y = pluralization[plural][pr.select(y)];
831
+ var isAsset = (suspect) => {
832
+ return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
833
+ };
834
+
835
+ // src/browser.ts
836
+ var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
837
+ if (typeof document === "undefined") return noop;
838
+ const onVisibilityChange = () => {
839
+ if (document.visibilityState === "hidden") {
840
+ onChange();
889
841
  }
890
- const actionHandler = actions && action ? actions[action] : void 0;
891
- if (actionHandler) y = actionHandler(y);
892
- return y == null ? "" : y;
893
- });
842
+ };
843
+ addEventListener("visibilitychange", onVisibilityChange);
844
+ addEventListener("beforeunload", onChange);
845
+ return () => {
846
+ removeEventListener("visibilitychange", onVisibilityChange);
847
+ removeEventListener("beforeunload", onChange);
848
+ };
894
849
  };
895
850
 
896
851
  // src/custom-action.ts
@@ -933,13 +888,15 @@ var handleCustomAction = (ctx, fn, { lang, state, setMountElement, setClear, rem
933
888
  };
934
889
  };
935
890
  const clear = (func) => {
936
- setClear(holder.cleanup = () => {
937
- func();
938
- holder.node = null;
939
- holder.cleanup = noop;
940
- setMountElement(null);
941
- setClear(noop);
942
- });
891
+ setClear(
892
+ holder.cleanup = () => {
893
+ func();
894
+ holder.node = null;
895
+ holder.cleanup = noop;
896
+ setMountElement(null);
897
+ setClear(noop);
898
+ }
899
+ );
943
900
  };
944
901
  const data = (updatedData) => {
945
902
  if (updatedData) {
@@ -969,59 +926,23 @@ var handleCustomAction = (ctx, fn, { lang, state, setMountElement, setClear, rem
969
926
  });
970
927
  };
971
928
 
972
- // src/storage.ts
973
- var localStorageStorage = (options) => {
974
- return {
975
- async get() {
976
- const fallback = { saves: [], data: {}, meta: [] };
977
- try {
978
- const value = localStorage.getItem(options.key);
979
- return value ? JSON.parse(value) : fallback;
980
- } catch {
981
- return fallback;
982
- }
983
- },
984
- async set(data) {
985
- try {
986
- localStorage.setItem(options.key, JSON.stringify(data));
987
- } catch {
988
- }
989
- }
990
- };
991
- };
992
-
993
- // src/browser.ts
994
- var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
995
- if (typeof document === "undefined") return noop;
996
- const onVisibilityChange = () => {
997
- if (document.visibilityState === "hidden") {
998
- onChange();
999
- }
1000
- };
1001
- addEventListener("visibilitychange", onVisibilityChange);
1002
- addEventListener("beforeunload", onChange);
1003
- return () => {
1004
- removeEventListener("visibilitychange", onVisibilityChange);
1005
- removeEventListener("beforeunload", onChange);
1006
- };
1007
- };
1008
-
1009
- // src/novely.ts
1010
- import pLimit from "p-limit";
1011
- import { DEV as DEV3 } from "esm-env";
1012
-
1013
929
  // src/preloading.ts
1014
930
  var ACTION_NAME_TO_VOLUME_MAP = {
1015
- "playMusic": "music",
1016
- "playSound": "sound",
1017
- "voice": "voice"
931
+ playMusic: "music",
932
+ playSound: "sound",
933
+ voice: "voice"
1018
934
  };
1019
935
  var enqueueAssetForPreloading = (asset2) => {
1020
936
  if (!PRELOADED_ASSETS.has(asset2)) {
1021
937
  ASSETS_TO_PRELOAD.add(asset2);
1022
938
  }
1023
939
  };
1024
- var handleAssetsPreloading = async ({ request, limiter, preloadAudioBlocking, preloadImageBlocking }) => {
940
+ var handleAssetsPreloading = async ({
941
+ request,
942
+ limiter,
943
+ preloadAudioBlocking,
944
+ preloadImageBlocking
945
+ }) => {
1025
946
  const list = mapSet(ASSETS_TO_PRELOAD, (asset2) => {
1026
947
  return limiter(async () => {
1027
948
  const type = await getResourseType({
@@ -1096,9 +1017,9 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
1096
1017
  }
1097
1018
  return;
1098
1019
  }
1099
- if (action === "custom" && props[0].assets && props[0].assets.length > 0) {
1020
+ if (action === "custom" && props[0].assets) {
1100
1021
  for (const asset2 of props[0].assets) {
1101
- handle(asset2);
1022
+ isAsset(asset2) ? handle(asset2.source) : handle(asset2);
1102
1023
  }
1103
1024
  return;
1104
1025
  }
@@ -1106,12 +1027,97 @@ var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) =>
1106
1027
  for (let i = 1; i < props.length; i++) {
1107
1028
  const data = props[i];
1108
1029
  if (Array.isArray(data)) {
1109
- handle(handleImageAsset(data[4]));
1030
+ handle(handleImageAsset(data[5]));
1110
1031
  }
1111
1032
  }
1112
1033
  }
1113
1034
  };
1114
1035
 
1036
+ // src/storage.ts
1037
+ var localStorageStorage = (options) => {
1038
+ return {
1039
+ async get() {
1040
+ const fallback = { saves: [], data: {}, meta: [] };
1041
+ try {
1042
+ const value = localStorage.getItem(options.key);
1043
+ return value ? JSON.parse(value) : fallback;
1044
+ } catch {
1045
+ return fallback;
1046
+ }
1047
+ },
1048
+ async set(data) {
1049
+ try {
1050
+ localStorage.setItem(options.key, JSON.stringify(data));
1051
+ } catch {
1052
+ }
1053
+ }
1054
+ };
1055
+ };
1056
+
1057
+ // src/store.ts
1058
+ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
1059
+ const subscribe = (cb) => {
1060
+ subscribers.add(cb), cb(current);
1061
+ return () => {
1062
+ subscribers.delete(cb);
1063
+ };
1064
+ };
1065
+ const push = (value) => {
1066
+ for (const cb of subscribers) cb(value);
1067
+ };
1068
+ const update = (fn) => {
1069
+ push(current = fn(current));
1070
+ };
1071
+ const set2 = (val) => {
1072
+ update(() => val);
1073
+ };
1074
+ const get = () => {
1075
+ return current;
1076
+ };
1077
+ return { subscribe, update, set: set2, get };
1078
+ };
1079
+
1080
+ // src/translation.ts
1081
+ var RGX = /{{(.*?)}}/g;
1082
+ var split = (input, delimeters) => {
1083
+ const output = [];
1084
+ for (const delimeter of delimeters) {
1085
+ if (!input) break;
1086
+ const [start, end] = input.split(delimeter, 2);
1087
+ output.push(start);
1088
+ input = end;
1089
+ }
1090
+ output.push(input);
1091
+ return output;
1092
+ };
1093
+ var flattenAllowedContent = (c, state) => {
1094
+ if (Array.isArray(c)) {
1095
+ return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
1096
+ }
1097
+ if (typeof c === "function") {
1098
+ return flattenAllowedContent(c(state), state);
1099
+ }
1100
+ return c;
1101
+ };
1102
+ var replace = (input, data, pluralization, actions, pr) => {
1103
+ return input.replaceAll(RGX, (x, key, y) => {
1104
+ x = 0;
1105
+ y = data;
1106
+ const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
1107
+ if (!pathstr) {
1108
+ return "";
1109
+ }
1110
+ const path = pathstr.split(".");
1111
+ while (y && x < path.length) y = y[path[x++]];
1112
+ if (plural && pluralization && y && pr) {
1113
+ y = pluralization[plural][pr.select(y)];
1114
+ }
1115
+ const actionHandler = actions && action ? actions[action] : void 0;
1116
+ if (actionHandler) y = actionHandler(y);
1117
+ return y == null ? "" : y;
1118
+ });
1119
+ };
1120
+
1115
1121
  // src/novely.ts
1116
1122
  var novely = ({
1117
1123
  characters,
@@ -1238,7 +1244,9 @@ var novely = ({
1238
1244
  return language;
1239
1245
  }
1240
1246
  if (DEV3) {
1241
- throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
1247
+ throw new Error(
1248
+ `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1249
+ );
1242
1250
  }
1243
1251
  throw 0;
1244
1252
  };
@@ -1354,7 +1362,9 @@ var novely = ({
1354
1362
  const restore = async (save2) => {
1355
1363
  if (isEmpty(story)) {
1356
1364
  if (DEV3) {
1357
- throw new Error("Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]");
1365
+ throw new Error(
1366
+ "Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]"
1367
+ );
1358
1368
  }
1359
1369
  return;
1360
1370
  }
@@ -1374,7 +1384,10 @@ var novely = ({
1374
1384
  const [path] = stack.value = latest;
1375
1385
  renderer.ui.showScreen("game");
1376
1386
  const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
1377
- const { run, keep: { keep, characters: characters2, audio: audio2 } } = createQueueProcessor(queue, {
1387
+ const {
1388
+ run,
1389
+ keep: { keep, characters: characters2, audio: audio2 }
1390
+ } = createQueueProcessor(queue, {
1378
1391
  skip,
1379
1392
  skipPreserve
1380
1393
  });
@@ -1424,7 +1437,9 @@ var novely = ({
1424
1437
  const isSaved = () => {
1425
1438
  const { saves } = storageData.get();
1426
1439
  const [currentPath, currentData] = stack.value;
1427
- return saves.some(([path, data2, [date, type2]]) => type2 === "manual" && times.has(date) && dequal(path, currentPath) && dequal(data2, currentData));
1440
+ return saves.some(
1441
+ ([path, data2, [date, type2]]) => type2 === "manual" && times.has(date) && dequal(path, currentPath) && dequal(data2, currentData)
1442
+ );
1428
1443
  };
1429
1444
  if (interacted > 1 && !force && askBeforeExit && !isSaved()) {
1430
1445
  renderer.ui.showExitPrompt();
@@ -1520,7 +1535,9 @@ var novely = ({
1520
1535
  const getLanguageDisplayName = (lang) => {
1521
1536
  const language = translation[lang];
1522
1537
  if (DEV3 && !language) {
1523
- throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
1538
+ throw new Error(
1539
+ `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1540
+ );
1524
1541
  }
1525
1542
  return capitalize(language.nameOverride || getIntlLanguageDisplayName(lang));
1526
1543
  };
@@ -1629,10 +1646,12 @@ var novely = ({
1629
1646
  showBackground({ ctx, push }, [background]) {
1630
1647
  if (isString(background) || isAsset(background)) {
1631
1648
  ctx.background({
1632
- "all": handleImageAsset(background)
1649
+ all: handleImageAsset(background)
1633
1650
  });
1634
1651
  } else {
1635
- ctx.background(Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)])));
1652
+ ctx.background(
1653
+ Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)]))
1654
+ );
1636
1655
  }
1637
1656
  push();
1638
1657
  },
@@ -1708,13 +1727,7 @@ var novely = ({
1708
1727
  return c || "";
1709
1728
  })();
1710
1729
  ctx.clearBlockingActions("dialog");
1711
- ctx.dialog(
1712
- templateReplace(content, data2),
1713
- templateReplace(name, data2),
1714
- character,
1715
- emotion,
1716
- forward
1717
- );
1730
+ ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
1718
1731
  },
1719
1732
  function({ ctx, push }, [fn]) {
1720
1733
  const { restoring, goingBack, preview: preview2 } = ctx.meta;
@@ -1764,7 +1777,9 @@ var novely = ({
1764
1777
  return [templateReplace(content, data2), active$, visible$, onSelectWrapped, imageValue];
1765
1778
  });
1766
1779
  if (DEV3 && transformedChoices.length === 0) {
1767
- throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
1780
+ throw new Error(
1781
+ `Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`
1782
+ );
1768
1783
  }
1769
1784
  ctx.clearBlockingActions("choice");
1770
1785
  ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
@@ -1819,7 +1834,7 @@ var novely = ({
1819
1834
  if (DEV3 && variants[val].length === 0) {
1820
1835
  throw new Error(`Attempt to go to empty variant "${val}"`);
1821
1836
  }
1822
- const stack = useStack(MAIN_CONTEXT_KEY);
1837
+ const stack = useStack(ctx);
1823
1838
  stack.value[0].push(["condition", val], [null, 0]);
1824
1839
  render(ctx);
1825
1840
  }
@@ -1830,12 +1845,7 @@ var novely = ({
1830
1845
  },
1831
1846
  input({ ctx, data: data2, forward }, [question, onInput, setup]) {
1832
1847
  ctx.clearBlockingActions("input");
1833
- ctx.input(
1834
- templateReplace(question, data2),
1835
- onInput,
1836
- setup || noop,
1837
- forward
1838
- );
1848
+ ctx.input(templateReplace(question, data2), onInput, setup || noop, forward);
1839
1849
  },
1840
1850
  custom({ ctx, push }, [fn]) {
1841
1851
  if (fn.requireUserAction) {
@@ -1875,7 +1885,9 @@ var novely = ({
1875
1885
  animateCharacter({ ctx, push }, [character, className]) {
1876
1886
  const classes = className.split(" ");
1877
1887
  if (DEV3 && classes.length === 0) {
1878
- throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
1888
+ throw new Error(
1889
+ "Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]"
1890
+ );
1879
1891
  }
1880
1892
  if (ctx.meta.preview) return;
1881
1893
  ctx.character(character).animate(classes);
@@ -1909,7 +1921,9 @@ var novely = ({
1909
1921
  },
1910
1922
  preload({ ctx, push }, [source]) {
1911
1923
  if (DEV3 && preloadAssets !== "lazy") {
1912
- console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
1924
+ console.error(
1925
+ `You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`
1926
+ );
1913
1927
  }
1914
1928
  if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1915
1929
  PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
@@ -1955,21 +1969,15 @@ var novely = ({
1955
1969
  interacted = value ? interacted + 1 : 0;
1956
1970
  };
1957
1971
  const templateReplace = (content, values) => {
1958
- const { data: data2, meta: [lang] } = storageData.get();
1972
+ const {
1973
+ data: data2,
1974
+ meta: [lang]
1975
+ } = storageData.get();
1959
1976
  const obj = values || data2;
1960
- const str2 = flattenAllowedContent(
1961
- !isFunction(content) && !isString(content) ? content[lang] : content,
1962
- obj
1963
- );
1977
+ const str2 = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
1964
1978
  const t2 = translation[lang];
1965
1979
  const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
1966
- return replace(
1967
- str2,
1968
- obj,
1969
- t2.plural,
1970
- t2.actions,
1971
- pluralRules
1972
- );
1980
+ return replace(str2, obj, t2.plural, t2.actions, pluralRules);
1973
1981
  };
1974
1982
  const data = (value) => {
1975
1983
  const _data = storageData.get().data;
@@ -1993,7 +2001,9 @@ var novely = ({
1993
2001
  const setStorageData = (data2) => {
1994
2002
  if (destroyed) {
1995
2003
  if (DEV3) {
1996
- throw new Error(`function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`);
2004
+ throw new Error(
2005
+ `function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`
2006
+ );
1997
2007
  }
1998
2008
  return;
1999
2009
  }
@@ -2053,7 +2063,7 @@ var novely = ({
2053
2063
  * @example
2054
2064
  * ```ts
2055
2065
  * import type { ConditionParams, StateFunction } from '@novely/core';
2056
- *
2066
+ *
2057
2067
  * const conditionCheck = (state: StateFunction<ConditionParams<typeof engine.typeEssintials>>) => {
2058
2068
  * return state.age >= 18;
2059
2069
  * }