@novely/core 0.34.0 → 0.36.0-beta.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.d.ts CHANGED
@@ -75,6 +75,20 @@ interface CharacterHandle {
75
75
  animate: (timeout: number, classes: string[]) => void;
76
76
  emotions: Record<string, HTMLImageElement[]>;
77
77
  }
78
+ type CustomActionHandle = {
79
+ /**
80
+ * Function to remove custom action from screen (and from your state if any completely)
81
+ */
82
+ remove: () => void;
83
+ /**
84
+ * Function that will give action root (element which you should add to the screen because custom actions rendered into that element)
85
+ */
86
+ setMountElement: (mountElement: null | HTMLDivElement) => void;
87
+ /**
88
+ * Function that will give you clean function provided by custom action.
89
+ */
90
+ setClear: (clear: () => void) => void;
91
+ };
78
92
  type AudioHandle = {
79
93
  stop: () => void;
80
94
  pause: () => void;
@@ -93,8 +107,7 @@ type Context = {
93
107
  music: Set<string>;
94
108
  sounds: Set<string>;
95
109
  }, resolve: () => void) => void;
96
- custom: (fn: CustomHandler<Lang, State>, push: () => void) => Thenable<void>;
97
- clearCustom: (fn: CustomHandler<Lang, State>) => void;
110
+ custom: (fn: CustomHandler<Lang, State>) => CustomActionHandle;
98
111
  /**
99
112
  * Clears all mentioned actions except for preserved one
100
113
  * @param preserve Action that should not be cleared
@@ -213,6 +226,7 @@ type RendererInit<$Language extends Lang, $Characters extends Record<string, Cha
213
226
  preview: (save: Save<State>, name: string) => Promise<void>;
214
227
  removeContext: (name: string) => void;
215
228
  getStateFunction: (context: string) => StateFunction<State>;
229
+ clearCustomAction: (ctx: Context, customAction: CustomHandler) => void;
216
230
  getLanguageDisplayName: (lang: Lang) => string;
217
231
  };
218
232
 
@@ -438,7 +452,7 @@ interface NovelyInit<$Language extends Lang, $Characters extends Record<string,
438
452
  /**
439
453
  * @default "lazy"
440
454
  */
441
- preloadAssets?: 'lazy' | 'blocking';
455
+ preloadAssets?: 'lazy' | 'blocking' | 'automatic';
442
456
  /**
443
457
  * Fetching function
444
458
  */
@@ -499,36 +513,59 @@ type FunctionableValue<T> = T | (() => T);
499
513
  type CustomHandlerGetResultDataFunction = (data?: Record<string, unknown>) => Record<string, unknown>;
500
514
  type CustomHandlerGetResult<I extends boolean> = {
501
515
  /**
502
- * Remove's custom handler instance
516
+ * Element for the custom action to be rendered into
503
517
  */
504
- remove: () => void;
518
+ element: I extends true ? HTMLDivElement : null;
505
519
  /**
506
- * Данные
520
+ * Root node
507
521
  */
508
- data: CustomHandlerGetResultDataFunction;
522
+ root: HTMLElement;
523
+ };
524
+ type CustomHandlerFunctionGetFn = <I extends boolean = true>(insert?: I) => CustomHandlerGetResult<I>;
525
+ type CustomHandlerFunctionParameters<L extends string, S extends State> = {
509
526
  /**
510
- * Элемент слоя
527
+ * Returns:
528
+ * - Root where entire novely is mounted
529
+ * - Element in which custom action could be mounted
530
+ *
531
+ * @example
532
+ * ```ts
533
+ * // pass `true` to insert element to the DOM
534
+ * const { root, element } = getDomNodes(true);
535
+ * ```
511
536
  */
512
- element: I extends true ? HTMLDivElement : null;
537
+ getDomNodes: CustomHandlerFunctionGetFn;
513
538
  /**
514
- * Корневой элемент Novely
539
+ * Renderer Context
515
540
  */
516
- root: HTMLElement;
541
+ rendererContext: Context;
517
542
  /**
518
- * Устанавливает обработчик очистки
543
+ * Function to work with custom action's state
544
+ */
545
+ data: CustomHandlerGetResultDataFunction;
546
+ /**
547
+ * Function to set cleanup handler
519
548
  */
520
549
  clear: (fn: () => void) => void;
521
- __internals: {
522
- ctx: Context;
523
- };
524
- };
525
- type CustomHandlerFunctionGetFn = <I extends boolean = true>(insert?: I) => CustomHandlerGetResult<I>;
526
- type CustomHandlerFunctionParameters<L extends string, S extends State> = {
527
- get: CustomHandlerFunctionGetFn;
550
+ /**
551
+ * Remove's custom handler instance
552
+ */
553
+ remove: () => void;
554
+ /**
555
+ * Context's state function
556
+ */
528
557
  state: StateFunction<S>;
529
- restoring: boolean;
530
- goingBack: boolean;
531
- preview: boolean;
558
+ /**
559
+ * Game flags (aka game states)
560
+ */
561
+ flags: {
562
+ restoring: boolean;
563
+ goingBack: boolean;
564
+ preview: boolean;
565
+ };
566
+ /**
567
+ * Game language
568
+ */
532
569
  lang: L;
533
570
  };
534
571
  type CustomHandlerFunction<L extends string, S extends State> = (parameters: CustomHandlerFunctionParameters<L, S>) => Thenable<void>;
@@ -799,4 +836,4 @@ declare const novely: <$Language extends string, $Characters extends Record<stri
799
836
  */
800
837
  declare const extendAction: <Part0 extends Record<string, (...args: any[]) => ValidAction>, Part1 extends Record<string, (...args: any[]) => ValidAction>>(base: Part0, extension: Part1) => Readonly<Assign<Part0, Part1>>;
801
838
 
802
- export { type ActionInputOnInputMeta, type ActionInputSetup, type ActionInputSetupCleanup, type ActionProxy, type AllowedContent, type AudioHandle, type BackgroundImage, type BaseTranslationStrings, type Character, type CharacterHandle, type ConditionParams, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type Data, type DeepPartial, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, type InputHandler, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type State, type StateFunction, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypeEssentials, type TypewriterSpeed, type ValidAction, extendAction, localStorageStorage, novely };
839
+ export { type ActionInputOnInputMeta, type ActionInputSetup, type ActionInputSetupCleanup, type ActionProxy, type AllowedContent, type AudioHandle, type BackgroundImage, type BaseTranslationStrings, type Character, type CharacterHandle, type ConditionParams, type Context, type CoreData, type CustomActionHandle, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type Data, type DeepPartial, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, type InputHandler, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type State, type StateFunction, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypeEssentials, type TypewriterSpeed, type ValidAction, extendAction, localStorageStorage, novely };
@@ -210,6 +210,7 @@ var Novely = (() => {
210
210
 
211
211
  // src/shared.ts
212
212
  var STACK_MAP = /* @__PURE__ */ new Map();
213
+ var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
213
214
  var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
214
215
 
215
216
  // ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
@@ -244,9 +245,16 @@ var Novely = (() => {
244
245
  }
245
246
 
246
247
  // src/utils.ts
247
- var matchAction = ({ getContext, push, forward }, values) => {
248
+ var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
248
249
  return (action, props, { ctx, data }) => {
249
250
  const context = typeof ctx === "string" ? getContext(ctx) : ctx;
251
+ if (action !== "say") {
252
+ onBeforeActionCall({
253
+ action,
254
+ props,
255
+ ctx: context
256
+ });
257
+ }
250
258
  return values[action]({
251
259
  ctx: context,
252
260
  data,
@@ -411,7 +419,7 @@ var Novely = (() => {
411
419
  var getActionsFromPath = (story, path, filter) => {
412
420
  let current = story;
413
421
  let precurrent;
414
- let ignoreNested = false;
422
+ let ignoreNestedBefore = null;
415
423
  let index = 0;
416
424
  let skipPreserve = void 0;
417
425
  const skip = /* @__PURE__ */ new Set();
@@ -432,11 +440,11 @@ var Novely = (() => {
432
440
  if (isNumber(val)) {
433
441
  index++;
434
442
  let startIndex = 0;
435
- if (ignoreNested) {
436
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), "block");
443
+ if (ignoreNestedBefore) {
444
+ const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), ignoreNestedBefore);
437
445
  if (prev) {
438
446
  startIndex = prev[1];
439
- ignoreNested = false;
447
+ ignoreNestedBefore = null;
440
448
  }
441
449
  }
442
450
  for (let i = startIndex; i <= val; i++) {
@@ -471,7 +479,7 @@ var Novely = (() => {
471
479
  current = story[val];
472
480
  } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
473
481
  current = blocks.pop();
474
- ignoreNested = true;
482
+ ignoreNestedBefore = type.slice(0, -5);
475
483
  }
476
484
  }
477
485
  return {
@@ -655,6 +663,9 @@ var Novely = (() => {
655
663
  }
656
664
  };
657
665
  var getResourseType = async (request, url) => {
666
+ if (!isCSSImage(url)) {
667
+ return "other";
668
+ }
658
669
  const extension = getUrlFileExtension(url);
659
670
  if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
660
671
  return "audio";
@@ -768,6 +779,68 @@ var Novely = (() => {
768
779
  }
769
780
  return path;
770
781
  };
782
+ var isBlockingAction = (action) => {
783
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
784
+ };
785
+ var collectActionsBeforeBlockingAction = ({ path, refer }) => {
786
+ const collection = [];
787
+ let action = refer(path);
788
+ while (true) {
789
+ if (action == void 0) {
790
+ const { exitImpossible } = exitPath({
791
+ path,
792
+ refer
793
+ });
794
+ if (exitImpossible) {
795
+ break;
796
+ }
797
+ }
798
+ if (!action) {
799
+ break;
800
+ }
801
+ if (isBlockingAction(action)) {
802
+ const [name, ...props] = action;
803
+ if (name === "choice") {
804
+ const choiceProps = props;
805
+ for (let i = 0; i < choiceProps.length; i++) {
806
+ const branchContent = choiceProps[i];
807
+ if (!Array.isArray(branchContent))
808
+ continue;
809
+ const virtualPath = klona(path);
810
+ virtualPath.push(["choice", i], [null, 0]);
811
+ const innerActions = collectActionsBeforeBlockingAction({
812
+ path: virtualPath,
813
+ refer
814
+ });
815
+ collection.push(...innerActions);
816
+ }
817
+ } else if (name === "condition") {
818
+ const conditionProps = props;
819
+ const conditions = Object.keys(conditionProps[1]);
820
+ for (const condition of conditions) {
821
+ const virtualPath = klona(path);
822
+ virtualPath.push(["condition", condition], [null, 0]);
823
+ const innerActions = collectActionsBeforeBlockingAction({
824
+ path: virtualPath,
825
+ refer
826
+ });
827
+ collection.push(...innerActions);
828
+ }
829
+ }
830
+ break;
831
+ }
832
+ collection.push(action);
833
+ if (action[0] === "jump") {
834
+ path = [["jump", action[1]], [null, 0]];
835
+ } else if (action[0] == "block") {
836
+ path.push(["block", action[1]], [null, 0]);
837
+ } else {
838
+ nextPath(path);
839
+ }
840
+ action = refer(path);
841
+ }
842
+ return collection;
843
+ };
771
844
 
772
845
  // ../../node_modules/.pnpm/dequal@2.0.3/node_modules/dequal/lite/index.mjs
773
846
  var has = Object.prototype.hasOwnProperty;
@@ -943,6 +1016,76 @@ var Novely = (() => {
943
1016
  });
944
1017
  };
945
1018
 
1019
+ // src/custom-action.ts
1020
+ var createCustomActionNode = (id) => {
1021
+ const div = document.createElement("div");
1022
+ div.setAttribute("data-id", id);
1023
+ return div;
1024
+ };
1025
+ var getCustomActionHolder = (ctx, fn) => {
1026
+ const cached = CUSTOM_ACTION_MAP.get(ctx.id + fn.key);
1027
+ if (cached) {
1028
+ return cached;
1029
+ }
1030
+ const holder = {
1031
+ cleanup: noop,
1032
+ node: null,
1033
+ fn,
1034
+ localData: {}
1035
+ };
1036
+ CUSTOM_ACTION_MAP.set(ctx.id + fn.key, holder);
1037
+ return holder;
1038
+ };
1039
+ var handleCustomAction = (ctx, fn, { lang, state, setMountElement, setClear, remove: renderersRemove }) => {
1040
+ const holder = getCustomActionHolder(ctx, fn);
1041
+ const flags = {
1042
+ ...ctx.meta
1043
+ };
1044
+ const getDomNodes = (insert = true) => {
1045
+ if (holder.node || !insert) {
1046
+ return {
1047
+ element: holder.node,
1048
+ root: ctx.root
1049
+ };
1050
+ }
1051
+ holder.node = insert ? createCustomActionNode(fn.key) : null;
1052
+ setMountElement(holder.node);
1053
+ return {
1054
+ element: holder.node,
1055
+ root: ctx.root
1056
+ };
1057
+ };
1058
+ const clear = (func) => {
1059
+ setClear(holder.cleanup = () => {
1060
+ func();
1061
+ holder.node = null;
1062
+ holder.cleanup = noop;
1063
+ setMountElement(null);
1064
+ setClear(noop);
1065
+ });
1066
+ };
1067
+ const data = (updatedData) => {
1068
+ if (updatedData) {
1069
+ return holder.localData = updatedData;
1070
+ }
1071
+ return holder.localData;
1072
+ };
1073
+ const remove = () => {
1074
+ holder.cleanup();
1075
+ renderersRemove();
1076
+ };
1077
+ return fn({
1078
+ flags,
1079
+ lang,
1080
+ state,
1081
+ data,
1082
+ clear,
1083
+ remove,
1084
+ rendererContext: ctx,
1085
+ getDomNodes
1086
+ });
1087
+ };
1088
+
946
1089
  // src/storage.ts
947
1090
  var localStorageStorage = (options) => {
948
1091
  return {
@@ -1016,6 +1159,26 @@ var Novely = (() => {
1016
1159
  const intime = (value) => {
1017
1160
  return times.add(value), value;
1018
1161
  };
1162
+ const handleAssetsPreloading = async () => {
1163
+ const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
1164
+ const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
1165
+ return limitAssetsDownload(async () => {
1166
+ const type = await getResourseType(request, asset);
1167
+ switch (type) {
1168
+ case "audio": {
1169
+ await preloadAudioBlocking(asset);
1170
+ break;
1171
+ }
1172
+ case "image": {
1173
+ await preloadImageBlocking(asset);
1174
+ break;
1175
+ }
1176
+ }
1177
+ });
1178
+ });
1179
+ await Promise.allSettled(list);
1180
+ ASSETS_TO_PRELOAD.clear();
1181
+ };
1019
1182
  const scriptBase = async (part) => {
1020
1183
  Object.assign(story, flattenStory(part));
1021
1184
  let loadingIsShown = false;
@@ -1027,31 +1190,14 @@ var Novely = (() => {
1027
1190
  if (!loadingIsShown) {
1028
1191
  renderer.ui.showLoading();
1029
1192
  }
1030
- const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
1031
- const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
1032
- return limitAssetsDownload(async () => {
1033
- const type = await getResourseType(request, asset);
1034
- switch (type) {
1035
- case "audio": {
1036
- await preloadAudioBlocking(asset);
1037
- break;
1038
- }
1039
- case "image": {
1040
- await preloadImageBlocking(asset);
1041
- break;
1042
- }
1043
- }
1044
- });
1045
- });
1046
- await Promise.allSettled(list);
1193
+ await handleAssetsPreloading();
1047
1194
  }
1048
- ASSETS_TO_PRELOAD.clear();
1049
1195
  await dataLoaded.promise;
1050
1196
  renderer.ui.hideLoading();
1051
1197
  if (!initialScreenWasShown) {
1052
1198
  initialScreenWasShown = true;
1053
1199
  if (initialScreen === "game") {
1054
- restore();
1200
+ restore(void 0);
1055
1201
  } else {
1056
1202
  renderer.ui.showScreen(initialScreen);
1057
1203
  }
@@ -1060,6 +1206,33 @@ var Novely = (() => {
1060
1206
  const script = (part) => {
1061
1207
  return limitScript(() => scriptBase(part));
1062
1208
  };
1209
+ const checkAndAddToPreloadingList = (action2, props) => {
1210
+ if (action2 === "showBackground") {
1211
+ if (isImageAsset(props[0])) {
1212
+ ASSETS_TO_PRELOAD.add(props[0]);
1213
+ }
1214
+ if (props[0] && typeof props[0] === "object") {
1215
+ for (const value of Object.values(props[0])) {
1216
+ if (!isImageAsset(value))
1217
+ continue;
1218
+ ASSETS_TO_PRELOAD.add(value);
1219
+ }
1220
+ }
1221
+ }
1222
+ if (isAudioAction(action2) && isString(props[0])) {
1223
+ ASSETS_TO_PRELOAD.add(props[0]);
1224
+ }
1225
+ if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
1226
+ const images = characters[props[0]].emotions[props[1]];
1227
+ if (Array.isArray(images)) {
1228
+ for (const asset of images) {
1229
+ ASSETS_TO_PRELOAD.add(asset);
1230
+ }
1231
+ } else {
1232
+ ASSETS_TO_PRELOAD.add(images);
1233
+ }
1234
+ }
1235
+ };
1063
1236
  const action = new Proxy({}, {
1064
1237
  get(_, action2) {
1065
1238
  if (action2 in renderer.actions) {
@@ -1067,31 +1240,7 @@ var Novely = (() => {
1067
1240
  }
1068
1241
  return (...props) => {
1069
1242
  if (preloadAssets === "blocking") {
1070
- if (action2 === "showBackground") {
1071
- if (isImageAsset(props[0])) {
1072
- ASSETS_TO_PRELOAD.add(props[0]);
1073
- }
1074
- if (props[0] && typeof props[0] === "object") {
1075
- for (const value of Object.values(props[0])) {
1076
- if (!isImageAsset(value))
1077
- continue;
1078
- ASSETS_TO_PRELOAD.add(value);
1079
- }
1080
- }
1081
- }
1082
- if (isAudioAction(action2) && isString(props[0])) {
1083
- ASSETS_TO_PRELOAD.add(props[0]);
1084
- }
1085
- if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
1086
- const images = characters[props[0]].emotions[props[1]];
1087
- if (Array.isArray(images)) {
1088
- for (const asset of images) {
1089
- ASSETS_TO_PRELOAD.add(asset);
1090
- }
1091
- } else {
1092
- ASSETS_TO_PRELOAD.add(images);
1093
- }
1094
- }
1243
+ checkAndAddToPreloadingList(action2, props);
1095
1244
  }
1096
1245
  return [action2, ...props];
1097
1246
  };
@@ -1218,7 +1367,12 @@ var Novely = (() => {
1218
1367
  return prev.saves.push(save2), prev;
1219
1368
  });
1220
1369
  }
1221
- restore(save2);
1370
+ const context = renderer.getContext(MAIN_CONTEXT_KEY);
1371
+ const stack = useStack(context);
1372
+ stack.value = save2;
1373
+ context.meta.restoring = context.meta.goingBack = false;
1374
+ renderer.ui.showScreen("game");
1375
+ render(context);
1222
1376
  };
1223
1377
  const set = (save2, ctx) => {
1224
1378
  const stack = useStack(ctx || MAIN_CONTEXT_KEY);
@@ -1263,7 +1417,7 @@ var Novely = (() => {
1263
1417
  }
1264
1418
  const [action2, fn] = element;
1265
1419
  if (action2 === "custom") {
1266
- context.clearCustom(fn);
1420
+ getCustomActionHolder(context, fn).cleanup();
1267
1421
  }
1268
1422
  }
1269
1423
  }
@@ -1273,8 +1427,8 @@ var Novely = (() => {
1273
1427
  data: latest[1]
1274
1428
  });
1275
1429
  }
1276
- const lastQueueItem = queue.at(-1) || [];
1277
- const lastQueueItemRequiresUserAction = isSkippedDuringRestore(lastQueueItem[0]) || isUserRequiredAction(lastQueueItem);
1430
+ const lastQueueItem = queue.at(-1);
1431
+ const lastQueueItemRequiresUserAction = lastQueueItem && isBlockingAction(lastQueueItem);
1278
1432
  await run((item) => {
1279
1433
  if (!latest)
1280
1434
  return;
@@ -1388,6 +1542,9 @@ var Novely = (() => {
1388
1542
  }
1389
1543
  return capitalize(language.nameOverride || getIntlLanguageDisplayName(lang));
1390
1544
  };
1545
+ const clearCustomAction = (ctx, fn) => {
1546
+ getCustomActionHolder(ctx, fn).cleanup();
1547
+ };
1391
1548
  const renderer = createRenderer({
1392
1549
  mainContextKey: MAIN_CONTEXT_KEY,
1393
1550
  characters,
@@ -1401,6 +1558,7 @@ var Novely = (() => {
1401
1558
  preview,
1402
1559
  removeContext,
1403
1560
  getStateFunction,
1561
+ clearCustomAction,
1404
1562
  languages,
1405
1563
  storageData,
1406
1564
  coreData,
@@ -1437,6 +1595,40 @@ var Novely = (() => {
1437
1595
  matchActionOptions.push(ctx);
1438
1596
  if (!ctx.meta.preview)
1439
1597
  interactivity(true);
1598
+ },
1599
+ onBeforeActionCall({ action: action2, props, ctx }) {
1600
+ if (preloadAssets !== "automatic")
1601
+ return;
1602
+ if (ctx.meta.preview || ctx.meta.restoring)
1603
+ return;
1604
+ if (!isBlockingAction([action2, ...props]))
1605
+ return;
1606
+ try {
1607
+ const collection = collectActionsBeforeBlockingAction({
1608
+ path: nextPath(klona(useStack(ctx).value[0])),
1609
+ refer
1610
+ });
1611
+ for (const [action3, ...props2] of collection) {
1612
+ checkAndAddToPreloadingList(action3, props2);
1613
+ }
1614
+ const { preloadAudioBlocking, preloadImage } = renderer.misc;
1615
+ ASSETS_TO_PRELOAD.forEach(async (asset) => {
1616
+ ASSETS_TO_PRELOAD.delete(asset);
1617
+ const type = await getResourseType(request, asset);
1618
+ switch (type) {
1619
+ case "audio": {
1620
+ preloadAudioBlocking(asset);
1621
+ break;
1622
+ }
1623
+ case "image": {
1624
+ preloadImage(asset);
1625
+ break;
1626
+ }
1627
+ }
1628
+ });
1629
+ } catch (cause) {
1630
+ console.error(cause);
1631
+ }
1440
1632
  }
1441
1633
  };
1442
1634
  const match = matchAction(matchActionOptions, {
@@ -1644,19 +1836,31 @@ var Novely = (() => {
1644
1836
  forward
1645
1837
  );
1646
1838
  },
1647
- custom({ ctx, push }, [handler]) {
1648
- if (handler.requireUserAction) {
1839
+ custom({ ctx, push }, [fn]) {
1840
+ if (fn.requireUserAction) {
1649
1841
  ctx.clearBlockingActions(void 0);
1650
1842
  }
1651
- const result = ctx.custom(handler, () => {
1652
- if (ctx.meta.restoring)
1653
- return;
1654
- if (handler.requireUserAction && !ctx.meta.preview) {
1843
+ const state = getStateFunction(ctx);
1844
+ const lang = storageData.get().meta[0];
1845
+ const result = handleCustomAction(ctx, fn, {
1846
+ ...ctx.custom(fn),
1847
+ state,
1848
+ lang
1849
+ });
1850
+ const next2 = () => {
1851
+ if (fn.requireUserAction && !ctx.meta.preview) {
1655
1852
  enmemory(ctx);
1656
1853
  interactivity(true);
1657
1854
  }
1658
1855
  push();
1659
- });
1856
+ };
1857
+ if (!ctx.meta.restoring) {
1858
+ if (isPromise(result)) {
1859
+ result.then(next2);
1860
+ } else {
1861
+ next2();
1862
+ }
1863
+ }
1660
1864
  return result;
1661
1865
  },
1662
1866
  vibrate({ ctx, push }, pattern) {
@@ -1706,6 +1910,9 @@ var Novely = (() => {
1706
1910
  render(ctx);
1707
1911
  },
1708
1912
  preload({ ctx, push }, [source]) {
1913
+ if (DEV && preloadAssets !== "lazy") {
1914
+ console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
1915
+ }
1709
1916
  if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1710
1917
  PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
1711
1918
  }