@momoyu-ink/kit 0.5.0 → 0.5.1

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/lib.cjs CHANGED
@@ -855,6 +855,138 @@ function useFadeOut(outTime, pause = false, onFinished) {
855
855
 
856
856
  // src/hooks/useScenario.ts
857
857
  var import_react4 = require("react");
858
+ var currentScenarioSession = null;
859
+ var scenarioSessionId = 0;
860
+ var scenarioLifecycleQueue = Promise.resolve();
861
+ function normalizeStories(stories) {
862
+ return stories.filter((story) => Boolean(story));
863
+ }
864
+ function createScenarioSessionKey(config) {
865
+ return JSON.stringify({
866
+ stories: config.stories,
867
+ startName: config.startName ?? null,
868
+ entryName: config.entryName ?? null,
869
+ goNextOnLoad: config.goNextOnLoad
870
+ });
871
+ }
872
+ function isCurrentScenarioSession(session) {
873
+ return currentScenarioSession?.id === session.id && !session.disposed;
874
+ }
875
+ function enqueueScenarioLifecycleTask(task) {
876
+ const nextTask = scenarioLifecycleQueue.then(task, task);
877
+ scenarioLifecycleQueue = nextTask.catch(() => {
878
+ });
879
+ return nextTask;
880
+ }
881
+ function clearScenarioDisposeTimer(session) {
882
+ if (session.disposeTimer !== null) {
883
+ clearTimeout(session.disposeTimer);
884
+ session.disposeTimer = null;
885
+ }
886
+ }
887
+ async function terminateCurrentScenario() {
888
+ try {
889
+ await executePluginCommand("scenario", {
890
+ subCommand: "terminateStory"
891
+ });
892
+ } catch (error) {
893
+ console.error("Failed to terminate scenario:", error);
894
+ }
895
+ }
896
+ async function startScenarioSession(session, config) {
897
+ for (const story of config.stories) {
898
+ if (!isCurrentScenarioSession(session)) return;
899
+ try {
900
+ await executePluginCommand("scenario", {
901
+ subCommand: "addStory",
902
+ name: story
903
+ });
904
+ } catch (error) {
905
+ console.error("Failed to load scenario:", error);
906
+ }
907
+ }
908
+ if (!isCurrentScenarioSession(session)) return;
909
+ try {
910
+ if (config.startName) {
911
+ await executePluginCommand("scenario", {
912
+ subCommand: "startStory",
913
+ name: config.startName,
914
+ entry: config.entryName
915
+ });
916
+ }
917
+ if (!isCurrentScenarioSession(session)) return;
918
+ if (config.goNextOnLoad) {
919
+ await nextLine();
920
+ }
921
+ } catch (error) {
922
+ console.error(`Failed to start scenario ${config.startName}:`, error);
923
+ }
924
+ }
925
+ function ensureScenarioSessionStarted(session, config) {
926
+ if (session.started || session.startPromise !== null) {
927
+ return;
928
+ }
929
+ const startPromise = enqueueScenarioLifecycleTask(async () => {
930
+ if (!isCurrentScenarioSession(session)) return;
931
+ await startScenarioSession(session, config);
932
+ if (isCurrentScenarioSession(session)) {
933
+ session.started = true;
934
+ }
935
+ });
936
+ session.startPromise = startPromise;
937
+ void startPromise.finally(() => {
938
+ if (session.startPromise === startPromise) {
939
+ session.startPromise = null;
940
+ }
941
+ });
942
+ }
943
+ function disposeScenarioSession(session, delayMs) {
944
+ clearScenarioDisposeTimer(session);
945
+ session.disposeTimer = setTimeout(() => {
946
+ if (!isCurrentScenarioSession(session) || session.refCount > 0) {
947
+ return;
948
+ }
949
+ session.disposeTimer = null;
950
+ session.disposed = true;
951
+ currentScenarioSession = null;
952
+ void enqueueScenarioLifecycleTask(terminateCurrentScenario);
953
+ }, delayMs);
954
+ }
955
+ function acquireScenarioSession(config) {
956
+ const key = createScenarioSessionKey(config);
957
+ if (currentScenarioSession !== null && currentScenarioSession.key !== key) {
958
+ const previousSession = currentScenarioSession;
959
+ clearScenarioDisposeTimer(previousSession);
960
+ previousSession.disposed = true;
961
+ currentScenarioSession = null;
962
+ void enqueueScenarioLifecycleTask(terminateCurrentScenario);
963
+ }
964
+ if (currentScenarioSession === null) {
965
+ currentScenarioSession = {
966
+ id: ++scenarioSessionId,
967
+ key,
968
+ refCount: 0,
969
+ started: false,
970
+ startPromise: null,
971
+ disposeTimer: null,
972
+ disposed: false
973
+ };
974
+ }
975
+ clearScenarioDisposeTimer(currentScenarioSession);
976
+ currentScenarioSession.refCount += 1;
977
+ ensureScenarioSessionStarted(currentScenarioSession, config);
978
+ return currentScenarioSession;
979
+ }
980
+ function releaseScenarioSession(session) {
981
+ if (!isCurrentScenarioSession(session)) {
982
+ return;
983
+ }
984
+ session.refCount = Math.max(0, session.refCount - 1);
985
+ if (session.refCount > 0) {
986
+ return;
987
+ }
988
+ disposeScenarioSession(session, 0);
989
+ }
858
990
  function nextLine() {
859
991
  return executePluginCommand("scenario", { subCommand: "nextLine" });
860
992
  }
@@ -863,39 +995,14 @@ function setWaiting(time, skippable) {
863
995
  }
864
996
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
865
997
  (0, import_react4.useEffect)(() => {
866
- const loadScenario = async () => {
867
- for (const story of stories) {
868
- if (!story) continue;
869
- try {
870
- await executePluginCommand("scenario", {
871
- subCommand: "addStory",
872
- name: story
873
- });
874
- } catch (error) {
875
- console.error(`Failed to load scenario:`, error);
876
- }
877
- }
878
- };
879
- void loadScenario().then(async () => {
880
- try {
881
- if (startName) {
882
- await executePluginCommand("scenario", {
883
- subCommand: "startStory",
884
- name: startName,
885
- entry: entryName
886
- });
887
- }
888
- if (goNextOnLoad) {
889
- await nextLine();
890
- }
891
- } catch (error) {
892
- console.error(`Failed to start scenario ${startName}:`, error);
893
- }
998
+ const session = acquireScenarioSession({
999
+ stories: normalizeStories(stories),
1000
+ startName,
1001
+ entryName,
1002
+ goNextOnLoad
894
1003
  });
895
1004
  return () => {
896
- executePluginCommand("scenario", {
897
- subCommand: "terminateStory"
898
- });
1005
+ releaseScenarioSession(session);
899
1006
  };
900
1007
  }, [stories, startName, entryName, goNextOnLoad]);
901
1008
  }
package/dist/lib.d.mts CHANGED
@@ -516,6 +516,7 @@ declare function nextLine(): any;
516
516
  declare function setWaiting(time: number, skippable: boolean): void;
517
517
  /**
518
518
  * Custom hook to manage scenario lifecycle (load, start, terminate).
519
+ * The underlying session is resilient to transient remounts such as Fast Refresh.
519
520
  *
520
521
  * @param {string[]} stories - An array of story names to load.
521
522
  * @param {string} [startName] - The name of the story to start.
package/dist/lib.d.ts CHANGED
@@ -516,6 +516,7 @@ declare function nextLine(): any;
516
516
  declare function setWaiting(time: number, skippable: boolean): void;
517
517
  /**
518
518
  * Custom hook to manage scenario lifecycle (load, start, terminate).
519
+ * The underlying session is resilient to transient remounts such as Fast Refresh.
519
520
  *
520
521
  * @param {string[]} stories - An array of story names to load.
521
522
  * @param {string} [startName] - The name of the story to start.
package/dist/lib.mjs CHANGED
@@ -843,6 +843,138 @@ function useFadeOut(outTime, pause = false, onFinished) {
843
843
 
844
844
  // src/hooks/useScenario.ts
845
845
  import { useEffect } from "react";
846
+ var currentScenarioSession = null;
847
+ var scenarioSessionId = 0;
848
+ var scenarioLifecycleQueue = Promise.resolve();
849
+ function normalizeStories(stories) {
850
+ return stories.filter((story) => Boolean(story));
851
+ }
852
+ function createScenarioSessionKey(config) {
853
+ return JSON.stringify({
854
+ stories: config.stories,
855
+ startName: config.startName ?? null,
856
+ entryName: config.entryName ?? null,
857
+ goNextOnLoad: config.goNextOnLoad
858
+ });
859
+ }
860
+ function isCurrentScenarioSession(session) {
861
+ return currentScenarioSession?.id === session.id && !session.disposed;
862
+ }
863
+ function enqueueScenarioLifecycleTask(task) {
864
+ const nextTask = scenarioLifecycleQueue.then(task, task);
865
+ scenarioLifecycleQueue = nextTask.catch(() => {
866
+ });
867
+ return nextTask;
868
+ }
869
+ function clearScenarioDisposeTimer(session) {
870
+ if (session.disposeTimer !== null) {
871
+ clearTimeout(session.disposeTimer);
872
+ session.disposeTimer = null;
873
+ }
874
+ }
875
+ async function terminateCurrentScenario() {
876
+ try {
877
+ await executePluginCommand("scenario", {
878
+ subCommand: "terminateStory"
879
+ });
880
+ } catch (error) {
881
+ console.error("Failed to terminate scenario:", error);
882
+ }
883
+ }
884
+ async function startScenarioSession(session, config) {
885
+ for (const story of config.stories) {
886
+ if (!isCurrentScenarioSession(session)) return;
887
+ try {
888
+ await executePluginCommand("scenario", {
889
+ subCommand: "addStory",
890
+ name: story
891
+ });
892
+ } catch (error) {
893
+ console.error("Failed to load scenario:", error);
894
+ }
895
+ }
896
+ if (!isCurrentScenarioSession(session)) return;
897
+ try {
898
+ if (config.startName) {
899
+ await executePluginCommand("scenario", {
900
+ subCommand: "startStory",
901
+ name: config.startName,
902
+ entry: config.entryName
903
+ });
904
+ }
905
+ if (!isCurrentScenarioSession(session)) return;
906
+ if (config.goNextOnLoad) {
907
+ await nextLine();
908
+ }
909
+ } catch (error) {
910
+ console.error(`Failed to start scenario ${config.startName}:`, error);
911
+ }
912
+ }
913
+ function ensureScenarioSessionStarted(session, config) {
914
+ if (session.started || session.startPromise !== null) {
915
+ return;
916
+ }
917
+ const startPromise = enqueueScenarioLifecycleTask(async () => {
918
+ if (!isCurrentScenarioSession(session)) return;
919
+ await startScenarioSession(session, config);
920
+ if (isCurrentScenarioSession(session)) {
921
+ session.started = true;
922
+ }
923
+ });
924
+ session.startPromise = startPromise;
925
+ void startPromise.finally(() => {
926
+ if (session.startPromise === startPromise) {
927
+ session.startPromise = null;
928
+ }
929
+ });
930
+ }
931
+ function disposeScenarioSession(session, delayMs) {
932
+ clearScenarioDisposeTimer(session);
933
+ session.disposeTimer = setTimeout(() => {
934
+ if (!isCurrentScenarioSession(session) || session.refCount > 0) {
935
+ return;
936
+ }
937
+ session.disposeTimer = null;
938
+ session.disposed = true;
939
+ currentScenarioSession = null;
940
+ void enqueueScenarioLifecycleTask(terminateCurrentScenario);
941
+ }, delayMs);
942
+ }
943
+ function acquireScenarioSession(config) {
944
+ const key = createScenarioSessionKey(config);
945
+ if (currentScenarioSession !== null && currentScenarioSession.key !== key) {
946
+ const previousSession = currentScenarioSession;
947
+ clearScenarioDisposeTimer(previousSession);
948
+ previousSession.disposed = true;
949
+ currentScenarioSession = null;
950
+ void enqueueScenarioLifecycleTask(terminateCurrentScenario);
951
+ }
952
+ if (currentScenarioSession === null) {
953
+ currentScenarioSession = {
954
+ id: ++scenarioSessionId,
955
+ key,
956
+ refCount: 0,
957
+ started: false,
958
+ startPromise: null,
959
+ disposeTimer: null,
960
+ disposed: false
961
+ };
962
+ }
963
+ clearScenarioDisposeTimer(currentScenarioSession);
964
+ currentScenarioSession.refCount += 1;
965
+ ensureScenarioSessionStarted(currentScenarioSession, config);
966
+ return currentScenarioSession;
967
+ }
968
+ function releaseScenarioSession(session) {
969
+ if (!isCurrentScenarioSession(session)) {
970
+ return;
971
+ }
972
+ session.refCount = Math.max(0, session.refCount - 1);
973
+ if (session.refCount > 0) {
974
+ return;
975
+ }
976
+ disposeScenarioSession(session, 0);
977
+ }
846
978
  function nextLine() {
847
979
  return executePluginCommand("scenario", { subCommand: "nextLine" });
848
980
  }
@@ -851,39 +983,14 @@ function setWaiting(time, skippable) {
851
983
  }
852
984
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
853
985
  useEffect(() => {
854
- const loadScenario = async () => {
855
- for (const story of stories) {
856
- if (!story) continue;
857
- try {
858
- await executePluginCommand("scenario", {
859
- subCommand: "addStory",
860
- name: story
861
- });
862
- } catch (error) {
863
- console.error(`Failed to load scenario:`, error);
864
- }
865
- }
866
- };
867
- void loadScenario().then(async () => {
868
- try {
869
- if (startName) {
870
- await executePluginCommand("scenario", {
871
- subCommand: "startStory",
872
- name: startName,
873
- entry: entryName
874
- });
875
- }
876
- if (goNextOnLoad) {
877
- await nextLine();
878
- }
879
- } catch (error) {
880
- console.error(`Failed to start scenario ${startName}:`, error);
881
- }
986
+ const session = acquireScenarioSession({
987
+ stories: normalizeStories(stories),
988
+ startName,
989
+ entryName,
990
+ goNextOnLoad
882
991
  });
883
992
  return () => {
884
- executePluginCommand("scenario", {
885
- subCommand: "terminateStory"
886
- });
993
+ releaseScenarioSession(session);
887
994
  };
888
995
  }, [stories, startName, entryName, goNextOnLoad]);
889
996
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momoyu-ink/kit",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Software Development Kit of Moyu Visual Novel Engine",
5
5
  "main": "dist/lib.cjs",
6
6
  "module": "dist/lib.mjs",