@momoyu-ink/kit 0.5.5 → 0.6.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/{jsx-dev-runtime-BKsP0hXU.d.mts → jsx-dev-runtime-B5T-ZMWr.d.mts} +16 -0
- package/dist/{jsx-dev-runtime-BKsP0hXU.d.ts → jsx-dev-runtime-B5T-ZMWr.d.ts} +16 -0
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/lib.cjs +858 -46
- package/dist/lib.d.mts +149 -50
- package/dist/lib.d.ts +149 -50
- package/dist/lib.mjs +849 -26
- package/package.json +1 -1
package/dist/lib.cjs
CHANGED
|
@@ -45,23 +45,33 @@ __export(lib_exports, {
|
|
|
45
45
|
createStaticNavigation: () => createStaticNavigation,
|
|
46
46
|
executeNodeCommand: () => executeNodeCommand,
|
|
47
47
|
executePluginCommand: () => executePluginCommand,
|
|
48
|
+
fastForwardState: () => fastForwardState,
|
|
49
|
+
getAppStateAdapter: () => getAppStateAdapter,
|
|
48
50
|
getNavigator: () => getNavigator,
|
|
51
|
+
getSeekingType: () => getSeekingType,
|
|
49
52
|
getStageSize: () => getStageSize,
|
|
50
53
|
globalEventListeners: () => globalEventListeners,
|
|
51
54
|
globalRequestAnimationFrameListeners: () => globalRequestAnimationFrameListeners,
|
|
52
55
|
moyu: () => moyu_exports,
|
|
53
56
|
nextLine: () => nextLine,
|
|
57
|
+
registerAppStateAdapter: () => registerAppStateAdapter,
|
|
54
58
|
setDefaultAutoTailMs: () => setDefaultAutoTailMs,
|
|
55
59
|
setWaiting: () => setWaiting,
|
|
56
60
|
skipState: () => skipState,
|
|
61
|
+
startDebugSession: () => startDebugSession,
|
|
62
|
+
startRuntimeDebugSession: () => startRuntimeDebugSession,
|
|
63
|
+
stopDebugSession: () => stopDebugSession,
|
|
64
|
+
stopRuntimeDebugSession: () => stopRuntimeDebugSession,
|
|
57
65
|
useAutoBlocker: () => useAutoBlocker,
|
|
58
66
|
useAutoTicket: () => useAutoTicket,
|
|
59
67
|
useBeforeHandleCommandCallback: () => useBeforeHandleCommandCallback,
|
|
68
|
+
useDebugSession: () => useDebugSession,
|
|
60
69
|
useFadeIn: () => useFadeIn,
|
|
61
70
|
useFadeInOut: () => useFadeInOut,
|
|
62
71
|
useFadeOut: () => useFadeOut,
|
|
63
72
|
useInterruptCallback: () => useInterruptCallback,
|
|
64
73
|
useIsAutoing: () => useIsAutoing,
|
|
74
|
+
useIsSeeking: () => useIsSeeking,
|
|
65
75
|
useIsSkipping: () => useIsSkipping,
|
|
66
76
|
useNavigation: () => useNavigation,
|
|
67
77
|
useNavigationParams: () => useNavigationParams,
|
|
@@ -70,7 +80,8 @@ __export(lib_exports, {
|
|
|
70
80
|
useSkipBlocker: () => useSkipBlocker,
|
|
71
81
|
useSkipCallback: () => useSkipCallback,
|
|
72
82
|
useSoundEffect: () => useSoundEffect,
|
|
73
|
-
useStageContext: () => useStageContext
|
|
83
|
+
useStageContext: () => useStageContext,
|
|
84
|
+
warpState: () => warpState
|
|
74
85
|
});
|
|
75
86
|
module.exports = __toCommonJS(lib_exports);
|
|
76
87
|
|
|
@@ -450,7 +461,7 @@ var globalVariables = new Proxy(
|
|
|
450
461
|
}
|
|
451
462
|
);
|
|
452
463
|
function safeEval(code, sandbox) {
|
|
453
|
-
const
|
|
464
|
+
const proxy4 = new Proxy(sandbox, {
|
|
454
465
|
has(target, key) {
|
|
455
466
|
if (typeof key === "string" && JS_GLOBAL_KEYS.has(key)) {
|
|
456
467
|
return false;
|
|
@@ -458,7 +469,7 @@ function safeEval(code, sandbox) {
|
|
|
458
469
|
return key in target || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
459
470
|
},
|
|
460
471
|
get(target, key) {
|
|
461
|
-
if (key === "window" || key === "globalThis") return
|
|
472
|
+
if (key === "window" || key === "globalThis") return proxy4;
|
|
462
473
|
if (key === "ARCHIVE") return archiveVariables;
|
|
463
474
|
if (key === "GLOBAL") return globalVariables;
|
|
464
475
|
if (key === Symbol.unscopables) return void 0;
|
|
@@ -469,7 +480,7 @@ function safeEval(code, sandbox) {
|
|
|
469
480
|
return true;
|
|
470
481
|
}
|
|
471
482
|
});
|
|
472
|
-
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(
|
|
483
|
+
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(proxy4)(proxy4);
|
|
473
484
|
}
|
|
474
485
|
globalThis.__moyu_eval_sandbox = (code) => {
|
|
475
486
|
try {
|
|
@@ -651,11 +662,11 @@ function getStageSize() {
|
|
|
651
662
|
}
|
|
652
663
|
|
|
653
664
|
// src/components/navigation.tsx
|
|
654
|
-
var import_react2 =
|
|
655
|
-
var import_react3 = require("react");
|
|
665
|
+
var import_react2 = require("react");
|
|
656
666
|
var import_valtio = require("valtio");
|
|
657
|
-
var
|
|
658
|
-
var
|
|
667
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
668
|
+
var PageParamsContext = (0, import_react2.createContext)(void 0);
|
|
669
|
+
var OverlayParamsContext = (0, import_react2.createContext)(void 0);
|
|
659
670
|
var overlayIdCounter = 0;
|
|
660
671
|
var generateOverlayId = () => {
|
|
661
672
|
return `overlay-${++overlayIdCounter}-${Date.now()}`;
|
|
@@ -709,6 +720,9 @@ function createStackNavigator(options) {
|
|
|
709
720
|
delete navigationState.pageParams[page];
|
|
710
721
|
}
|
|
711
722
|
},
|
|
723
|
+
hasPage: (page) => {
|
|
724
|
+
return normalizedPages[page] !== void 0;
|
|
725
|
+
},
|
|
712
726
|
goBack: () => {
|
|
713
727
|
console.warn("goBack() not implemented yet");
|
|
714
728
|
},
|
|
@@ -764,11 +778,14 @@ function createStaticNavigation(navigator) {
|
|
|
764
778
|
const { pages, overlays } = navigator._descriptors;
|
|
765
779
|
const pageDescriptor = pages[navState.currentPage];
|
|
766
780
|
const Page = pageDescriptor?.component || (() => null);
|
|
767
|
-
return /* @__PURE__ */
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
782
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageParamsContext.Provider, { value: navState.pageParams[navState.currentPage], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Page, {}) }),
|
|
783
|
+
navState.overlayStack.map((overlay) => {
|
|
784
|
+
const overlayDescriptor = overlays[overlay.type];
|
|
785
|
+
const OverlayComponent = overlayDescriptor?.component || (() => null);
|
|
786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayComponent, {}) }, overlay.id);
|
|
787
|
+
})
|
|
788
|
+
] });
|
|
772
789
|
};
|
|
773
790
|
}
|
|
774
791
|
function getNavigator() {
|
|
@@ -782,11 +799,629 @@ function useNavigationState() {
|
|
|
782
799
|
return (0, import_valtio.useSnapshot)(getNavigator()._state);
|
|
783
800
|
}
|
|
784
801
|
function useNavigationParams() {
|
|
785
|
-
const overlayParams = (0,
|
|
786
|
-
const pageParams = (0,
|
|
802
|
+
const overlayParams = (0, import_react2.useContext)(OverlayParamsContext);
|
|
803
|
+
const pageParams = (0, import_react2.useContext)(PageParamsContext);
|
|
787
804
|
return overlayParams ?? pageParams ?? {};
|
|
788
805
|
}
|
|
789
806
|
|
|
807
|
+
// src/debug/session.ts
|
|
808
|
+
var import_valtio2 = require("valtio");
|
|
809
|
+
var debugState = (0, import_valtio2.proxy)({
|
|
810
|
+
active: false,
|
|
811
|
+
checkpoints: {},
|
|
812
|
+
currentCursor: null,
|
|
813
|
+
restoring: false,
|
|
814
|
+
lastError: null
|
|
815
|
+
});
|
|
816
|
+
var appStateAdapter = null;
|
|
817
|
+
var currentConfig = {
|
|
818
|
+
onMarkerEnter: () => {
|
|
819
|
+
},
|
|
820
|
+
onError: () => {
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
var disposeMarkerListener = null;
|
|
824
|
+
var disposeFinishedListener = null;
|
|
825
|
+
var disposeWarpFinishedListener = null;
|
|
826
|
+
var debugOperationQueue = Promise.resolve();
|
|
827
|
+
var activeFastForward = null;
|
|
828
|
+
var activeWarp = null;
|
|
829
|
+
function enqueueDebugOperation(task) {
|
|
830
|
+
const nextTask = debugOperationQueue.then(task, task);
|
|
831
|
+
debugOperationQueue = nextTask.then(
|
|
832
|
+
() => void 0,
|
|
833
|
+
() => void 0
|
|
834
|
+
);
|
|
835
|
+
return nextTask;
|
|
836
|
+
}
|
|
837
|
+
function executeScenarioCommand(payload) {
|
|
838
|
+
return Promise.resolve(executePluginCommand("scenario", payload));
|
|
839
|
+
}
|
|
840
|
+
function toErrorMessage(error) {
|
|
841
|
+
if (error instanceof Error) {
|
|
842
|
+
return error.message;
|
|
843
|
+
}
|
|
844
|
+
return String(error);
|
|
845
|
+
}
|
|
846
|
+
function isPromiseLike(value) {
|
|
847
|
+
return !!value && typeof value.then === "function";
|
|
848
|
+
}
|
|
849
|
+
function reportDebugSessionError(error) {
|
|
850
|
+
debugState.lastError = toErrorMessage(error);
|
|
851
|
+
void Promise.resolve(currentConfig.onError(error));
|
|
852
|
+
}
|
|
853
|
+
function clearLocalRuntimeState() {
|
|
854
|
+
debugState.checkpoints = {};
|
|
855
|
+
debugState.currentCursor = null;
|
|
856
|
+
debugState.restoring = false;
|
|
857
|
+
debugState.lastError = null;
|
|
858
|
+
}
|
|
859
|
+
async function doSettleActiveSeek(op, clearSlot, error) {
|
|
860
|
+
if (!op) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
clearSlot();
|
|
864
|
+
try {
|
|
865
|
+
await appStateAdapter?.exitFastForwardMode?.();
|
|
866
|
+
} catch (exitError) {
|
|
867
|
+
if (error === void 0) {
|
|
868
|
+
error = exitError;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (error === void 0) {
|
|
872
|
+
op.resolve();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
op.reject(error);
|
|
876
|
+
}
|
|
877
|
+
async function settleActiveFastForward(error) {
|
|
878
|
+
await doSettleActiveSeek(activeFastForward, () => {
|
|
879
|
+
activeFastForward = null;
|
|
880
|
+
}, error);
|
|
881
|
+
}
|
|
882
|
+
async function settleActiveWarp(error) {
|
|
883
|
+
await doSettleActiveSeek(activeWarp, () => {
|
|
884
|
+
activeWarp = null;
|
|
885
|
+
}, error);
|
|
886
|
+
}
|
|
887
|
+
async function clearRemoteCheckpoints() {
|
|
888
|
+
await executeScenarioCommand({ subCommand: "clearCheckpoints" });
|
|
889
|
+
}
|
|
890
|
+
async function syncLocalWarpCheckpoint(targetMarkerId) {
|
|
891
|
+
const cursor = await executeScenarioCommand({
|
|
892
|
+
subCommand: "getExecutionCursor"
|
|
893
|
+
});
|
|
894
|
+
const checkpointCursor = cursor ? {
|
|
895
|
+
story: cursor.story,
|
|
896
|
+
paragraph: cursor.paragraph,
|
|
897
|
+
markerId: targetMarkerId
|
|
898
|
+
} : debugState.currentCursor ? {
|
|
899
|
+
story: debugState.currentCursor.story,
|
|
900
|
+
paragraph: debugState.currentCursor.paragraph,
|
|
901
|
+
markerId: targetMarkerId
|
|
902
|
+
} : null;
|
|
903
|
+
if (!checkpointCursor) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const checkpoint = {
|
|
907
|
+
cursor: checkpointCursor,
|
|
908
|
+
appState: appStateAdapter ? await appStateAdapter.capture() : void 0
|
|
909
|
+
};
|
|
910
|
+
debugState.currentCursor = checkpointCursor;
|
|
911
|
+
debugState.checkpoints[targetMarkerId] = checkpoint;
|
|
912
|
+
await currentConfig.onMarkerEnter(checkpoint);
|
|
913
|
+
}
|
|
914
|
+
function captureCheckpointAppStateSync() {
|
|
915
|
+
if (!appStateAdapter) {
|
|
916
|
+
return void 0;
|
|
917
|
+
}
|
|
918
|
+
const appState = appStateAdapter.capture();
|
|
919
|
+
if (isPromiseLike(appState)) {
|
|
920
|
+
throw new Error("Async app state capture is not supported for debug checkpoints");
|
|
921
|
+
}
|
|
922
|
+
return appState;
|
|
923
|
+
}
|
|
924
|
+
function notifyMarkerEnter(checkpoint) {
|
|
925
|
+
try {
|
|
926
|
+
const result = currentConfig.onMarkerEnter(checkpoint);
|
|
927
|
+
if (isPromiseLike(result)) {
|
|
928
|
+
void result.catch(reportDebugSessionError);
|
|
929
|
+
}
|
|
930
|
+
} catch (error) {
|
|
931
|
+
reportDebugSessionError(error);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
async function prepareJumpStart(markerId) {
|
|
935
|
+
const checkpoint = debugState.checkpoints[markerId];
|
|
936
|
+
if (checkpoint) {
|
|
937
|
+
const restored2 = await executeScenarioCommand({
|
|
938
|
+
subCommand: "restoreCheckpoint",
|
|
939
|
+
key: markerId
|
|
940
|
+
});
|
|
941
|
+
if (!restored2) {
|
|
942
|
+
throw new Error(`Failed to restore checkpoint ${markerId}`);
|
|
943
|
+
}
|
|
944
|
+
if (appStateAdapter) {
|
|
945
|
+
await appStateAdapter.restore(checkpoint.appState);
|
|
946
|
+
}
|
|
947
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
const lookup = await executeScenarioCommand({
|
|
951
|
+
subCommand: "getFastForwardCheckpoint",
|
|
952
|
+
key: markerId
|
|
953
|
+
});
|
|
954
|
+
if (!lookup.markerExists) {
|
|
955
|
+
throw new Error(`Marker ${markerId} is not available in the current story`);
|
|
956
|
+
}
|
|
957
|
+
const startCheckpointKey = lookup.checkpointKey ?? null;
|
|
958
|
+
if (!startCheckpointKey) {
|
|
959
|
+
if (!appStateAdapter?.restartCurrentStoryFromHead) {
|
|
960
|
+
throw new Error("Restarting the current story from head is not supported by the app state adapter");
|
|
961
|
+
}
|
|
962
|
+
await appStateAdapter.restartCurrentStoryFromHead();
|
|
963
|
+
debugState.checkpoints = {};
|
|
964
|
+
debugState.currentCursor = null;
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
const startCheckpoint = debugState.checkpoints[startCheckpointKey];
|
|
968
|
+
if (!startCheckpoint) {
|
|
969
|
+
throw new Error(`Local checkpoint ${startCheckpointKey} not found`);
|
|
970
|
+
}
|
|
971
|
+
const restored = await executeScenarioCommand({
|
|
972
|
+
subCommand: "restoreCheckpoint",
|
|
973
|
+
key: startCheckpointKey
|
|
974
|
+
});
|
|
975
|
+
if (!restored) {
|
|
976
|
+
throw new Error(`Failed to restore checkpoint ${startCheckpointKey}`);
|
|
977
|
+
}
|
|
978
|
+
if (appStateAdapter) {
|
|
979
|
+
await appStateAdapter.restore(startCheckpoint.appState);
|
|
980
|
+
}
|
|
981
|
+
debugState.currentCursor = startCheckpoint.cursor;
|
|
982
|
+
return true;
|
|
983
|
+
}
|
|
984
|
+
function handleMarkerEnter(marker) {
|
|
985
|
+
const checkpoint = {
|
|
986
|
+
cursor: {
|
|
987
|
+
story: marker.story,
|
|
988
|
+
paragraph: marker.paragraph,
|
|
989
|
+
markerId: marker.markerId
|
|
990
|
+
},
|
|
991
|
+
appState: captureCheckpointAppStateSync()
|
|
992
|
+
};
|
|
993
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
994
|
+
debugState.checkpoints[marker.markerId] = checkpoint;
|
|
995
|
+
notifyMarkerEnter(checkpoint);
|
|
996
|
+
if (activeFastForward?.targetMarkerId === marker.markerId) {
|
|
997
|
+
void settleActiveFastForward();
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
async function runFastForwardToMarker(markerId) {
|
|
1001
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1002
|
+
throw new Error("Fast-forward mode is not supported by the app state adapter");
|
|
1003
|
+
}
|
|
1004
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1005
|
+
throw new Error("A debug seek operation is already active");
|
|
1006
|
+
}
|
|
1007
|
+
await appStateAdapter.enterFastForwardMode({
|
|
1008
|
+
onAbort: (error) => {
|
|
1009
|
+
void settleActiveFastForward(error);
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
const completion = new Promise((resolve, reject) => {
|
|
1013
|
+
activeFastForward = {
|
|
1014
|
+
targetMarkerId: markerId,
|
|
1015
|
+
resolve,
|
|
1016
|
+
reject
|
|
1017
|
+
};
|
|
1018
|
+
});
|
|
1019
|
+
try {
|
|
1020
|
+
await executeScenarioCommand({ subCommand: "nextLine" });
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
await settleActiveFastForward(error);
|
|
1023
|
+
}
|
|
1024
|
+
await completion;
|
|
1025
|
+
}
|
|
1026
|
+
async function runWarp(options) {
|
|
1027
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1028
|
+
throw new Error("Warp mode is not supported by the app state adapter");
|
|
1029
|
+
}
|
|
1030
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1031
|
+
throw new Error("A debug seek operation is already active");
|
|
1032
|
+
}
|
|
1033
|
+
const boundary = options.boundary ?? "before";
|
|
1034
|
+
await appStateAdapter.enterFastForwardMode({ warp: true });
|
|
1035
|
+
const completion = new Promise((resolve, reject) => {
|
|
1036
|
+
activeWarp = {
|
|
1037
|
+
targetMarkerId: options.markerId,
|
|
1038
|
+
resolve,
|
|
1039
|
+
reject
|
|
1040
|
+
};
|
|
1041
|
+
});
|
|
1042
|
+
try {
|
|
1043
|
+
await executeScenarioCommand({
|
|
1044
|
+
subCommand: "warp",
|
|
1045
|
+
markerId: options.markerId,
|
|
1046
|
+
boundary
|
|
1047
|
+
});
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
await settleActiveWarp(error);
|
|
1050
|
+
}
|
|
1051
|
+
await completion;
|
|
1052
|
+
}
|
|
1053
|
+
var debugSessionController = {
|
|
1054
|
+
async restoreCheckpoint(markerId) {
|
|
1055
|
+
debugState.restoring = true;
|
|
1056
|
+
debugState.lastError = null;
|
|
1057
|
+
try {
|
|
1058
|
+
const needsFastForward = await enqueueDebugOperation(async () => prepareJumpStart(markerId));
|
|
1059
|
+
if (needsFastForward) {
|
|
1060
|
+
await runFastForwardToMarker(markerId);
|
|
1061
|
+
}
|
|
1062
|
+
return true;
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
debugState.lastError = toErrorMessage(error);
|
|
1065
|
+
throw error;
|
|
1066
|
+
} finally {
|
|
1067
|
+
debugState.restoring = false;
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
async warp(options) {
|
|
1071
|
+
debugState.restoring = true;
|
|
1072
|
+
debugState.lastError = null;
|
|
1073
|
+
try {
|
|
1074
|
+
const needsWarp = await enqueueDebugOperation(async () => prepareJumpStart(options.markerId));
|
|
1075
|
+
if (needsWarp) {
|
|
1076
|
+
await runWarp(options);
|
|
1077
|
+
}
|
|
1078
|
+
} catch (error) {
|
|
1079
|
+
debugState.lastError = toErrorMessage(error);
|
|
1080
|
+
throw error;
|
|
1081
|
+
} finally {
|
|
1082
|
+
debugState.restoring = false;
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
async switchPage(page, params) {
|
|
1086
|
+
return enqueueDebugOperation(async () => {
|
|
1087
|
+
debugState.lastError = null;
|
|
1088
|
+
try {
|
|
1089
|
+
if (!appStateAdapter?.switchPage) {
|
|
1090
|
+
throw new Error("Page switching is not supported by the app state adapter");
|
|
1091
|
+
}
|
|
1092
|
+
await appStateAdapter.switchPage(page, params);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
debugState.lastError = toErrorMessage(error);
|
|
1095
|
+
throw error;
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
function registerAppStateAdapter(adapter) {
|
|
1101
|
+
appStateAdapter = adapter;
|
|
1102
|
+
}
|
|
1103
|
+
function getAppStateAdapter() {
|
|
1104
|
+
return appStateAdapter;
|
|
1105
|
+
}
|
|
1106
|
+
async function startDebugSession(config = {}) {
|
|
1107
|
+
await stopDebugSession();
|
|
1108
|
+
currentConfig = {
|
|
1109
|
+
onMarkerEnter: config.onMarkerEnter ?? (() => {
|
|
1110
|
+
}),
|
|
1111
|
+
onError: config.onError ?? (() => {
|
|
1112
|
+
})
|
|
1113
|
+
};
|
|
1114
|
+
clearLocalRuntimeState();
|
|
1115
|
+
debugState.active = true;
|
|
1116
|
+
disposeMarkerListener = addEventListener("scenariomarkerenter", (event) => {
|
|
1117
|
+
const marker = event;
|
|
1118
|
+
try {
|
|
1119
|
+
handleMarkerEnter(marker);
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
reportDebugSessionError(error);
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
disposeFinishedListener = addEventListener("scenariofinished", () => {
|
|
1125
|
+
debugState.currentCursor = null;
|
|
1126
|
+
if (activeFastForward) {
|
|
1127
|
+
void settleActiveFastForward(
|
|
1128
|
+
new Error(`Marker ${activeFastForward.targetMarkerId} was not reached before the scenario finished`)
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
if (activeWarp) {
|
|
1132
|
+
const targetMarkerId = activeWarp.targetMarkerId;
|
|
1133
|
+
void settleActiveWarp(new Error(`Marker ${targetMarkerId} was not reached before the scenario finished`));
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
disposeWarpFinishedListener = addEventListener("scenariowarpfinished", () => {
|
|
1137
|
+
const warp = activeWarp;
|
|
1138
|
+
if (!warp) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
void enqueueDebugOperation(async () => {
|
|
1142
|
+
try {
|
|
1143
|
+
await syncLocalWarpCheckpoint(warp.targetMarkerId);
|
|
1144
|
+
await settleActiveWarp();
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
debugState.lastError = toErrorMessage(error);
|
|
1147
|
+
await settleActiveWarp(error);
|
|
1148
|
+
await currentConfig.onError(error);
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
try {
|
|
1153
|
+
await clearRemoteCheckpoints();
|
|
1154
|
+
debugState.currentCursor = await executeScenarioCommand({
|
|
1155
|
+
subCommand: "getExecutionCursor"
|
|
1156
|
+
});
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
debugState.active = false;
|
|
1159
|
+
clearLocalRuntimeState();
|
|
1160
|
+
disposeMarkerListener?.();
|
|
1161
|
+
disposeFinishedListener?.();
|
|
1162
|
+
disposeWarpFinishedListener?.();
|
|
1163
|
+
disposeMarkerListener = null;
|
|
1164
|
+
disposeFinishedListener = null;
|
|
1165
|
+
disposeWarpFinishedListener = null;
|
|
1166
|
+
await currentConfig.onError(error);
|
|
1167
|
+
throw error;
|
|
1168
|
+
}
|
|
1169
|
+
return debugSessionController;
|
|
1170
|
+
}
|
|
1171
|
+
async function stopDebugSession() {
|
|
1172
|
+
disposeMarkerListener?.();
|
|
1173
|
+
disposeFinishedListener?.();
|
|
1174
|
+
disposeWarpFinishedListener?.();
|
|
1175
|
+
disposeMarkerListener = null;
|
|
1176
|
+
disposeFinishedListener = null;
|
|
1177
|
+
disposeWarpFinishedListener = null;
|
|
1178
|
+
await settleActiveFastForward(new Error("Debug session stopped"));
|
|
1179
|
+
await settleActiveWarp(new Error("Debug session stopped"));
|
|
1180
|
+
if (debugState.active) {
|
|
1181
|
+
await enqueueDebugOperation(async () => {
|
|
1182
|
+
try {
|
|
1183
|
+
await clearRemoteCheckpoints();
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
debugState.lastError = toErrorMessage(error);
|
|
1186
|
+
await currentConfig.onError(error);
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
debugState.active = false;
|
|
1191
|
+
clearLocalRuntimeState();
|
|
1192
|
+
}
|
|
1193
|
+
function clearDebugSessionRuntimeState() {
|
|
1194
|
+
clearLocalRuntimeState();
|
|
1195
|
+
void settleActiveFastForward(new Error("Debug session runtime state cleared"));
|
|
1196
|
+
void settleActiveWarp(new Error("Debug session runtime state cleared"));
|
|
1197
|
+
if (!debugState.active) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
void enqueueDebugOperation(async () => {
|
|
1201
|
+
try {
|
|
1202
|
+
await clearRemoteCheckpoints();
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
debugState.lastError = toErrorMessage(error);
|
|
1205
|
+
await currentConfig.onError(error);
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
function useDebugSession() {
|
|
1210
|
+
const snapshot = (0, import_valtio2.useSnapshot)(debugState);
|
|
1211
|
+
return {
|
|
1212
|
+
...snapshot,
|
|
1213
|
+
startDebugSession,
|
|
1214
|
+
stopDebugSession,
|
|
1215
|
+
restoreCheckpoint: debugSessionController.restoreCheckpoint,
|
|
1216
|
+
warp: debugSessionController.warp,
|
|
1217
|
+
switchPage: debugSessionController.switchPage
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// src/debug/runtime.ts
|
|
1222
|
+
var currentConnection = null;
|
|
1223
|
+
var currentController = null;
|
|
1224
|
+
var bufferedRequestMessages = [];
|
|
1225
|
+
function getRuntimeWebSocket() {
|
|
1226
|
+
return globalThis.WebSocket ?? null;
|
|
1227
|
+
}
|
|
1228
|
+
function isJumpRequestMessage(message) {
|
|
1229
|
+
return message.type === "jump:request" && typeof message.sessionId === "string" && typeof message.requestId === "number" && typeof message.markerId === "string" && (message.boundary === void 0 || message.boundary === "before" || message.boundary === "after") && (message.strategy === void 0 || message.strategy === "fast-forward" || message.strategy === "warp");
|
|
1230
|
+
}
|
|
1231
|
+
function isRouteRequestMessage(message) {
|
|
1232
|
+
return message.type === "route:request" && typeof message.sessionId === "string" && typeof message.requestId === "number" && typeof message.page === "string" && (message.params === void 0 || typeof message.params === "object" && message.params !== null && !Array.isArray(message.params));
|
|
1233
|
+
}
|
|
1234
|
+
function sendRuntimeDebugMessage(socket, message) {
|
|
1235
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1236
|
+
if (!RuntimeWebSocket || socket.readyState !== RuntimeWebSocket.OPEN) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
socket.send(JSON.stringify(message));
|
|
1240
|
+
}
|
|
1241
|
+
function clearBufferedRequestMessages() {
|
|
1242
|
+
bufferedRequestMessages = [];
|
|
1243
|
+
}
|
|
1244
|
+
function bufferRequestMessage(message) {
|
|
1245
|
+
bufferedRequestMessages.push(message);
|
|
1246
|
+
}
|
|
1247
|
+
function flushBufferedRequestMessages(socket, sessionId) {
|
|
1248
|
+
const messages = bufferedRequestMessages;
|
|
1249
|
+
bufferedRequestMessages = [];
|
|
1250
|
+
for (const message of messages) {
|
|
1251
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
function handleRuntimeDebugRequest(socket, sessionId, message) {
|
|
1255
|
+
const controller = currentController;
|
|
1256
|
+
if (!controller) {
|
|
1257
|
+
bufferRequestMessage(message);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (isJumpRequestMessage(message)) {
|
|
1261
|
+
const runJump = message.strategy === "warp" ? controller.warp({
|
|
1262
|
+
markerId: message.markerId,
|
|
1263
|
+
boundary: message.boundary
|
|
1264
|
+
}).then(() => true) : controller.restoreCheckpoint(message.markerId);
|
|
1265
|
+
void runJump.then((restored) => {
|
|
1266
|
+
if (!restored) {
|
|
1267
|
+
sendRuntimeDebugMessage(socket, {
|
|
1268
|
+
type: "jump:error",
|
|
1269
|
+
sessionId,
|
|
1270
|
+
requestId: message.requestId,
|
|
1271
|
+
markerId: message.markerId,
|
|
1272
|
+
error: "Checkpoint not found"
|
|
1273
|
+
});
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
sendRuntimeDebugMessage(socket, {
|
|
1277
|
+
type: "jump:done",
|
|
1278
|
+
sessionId,
|
|
1279
|
+
requestId: message.requestId,
|
|
1280
|
+
markerId: message.markerId
|
|
1281
|
+
});
|
|
1282
|
+
}).catch((error) => {
|
|
1283
|
+
sendRuntimeDebugMessage(socket, {
|
|
1284
|
+
type: "jump:error",
|
|
1285
|
+
sessionId,
|
|
1286
|
+
requestId: message.requestId,
|
|
1287
|
+
markerId: message.markerId,
|
|
1288
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
void controller.switchPage(message.page, message.params).then(() => {
|
|
1294
|
+
sendRuntimeDebugMessage(socket, {
|
|
1295
|
+
type: "route:done",
|
|
1296
|
+
sessionId,
|
|
1297
|
+
requestId: message.requestId,
|
|
1298
|
+
page: message.page
|
|
1299
|
+
});
|
|
1300
|
+
}).catch((error) => {
|
|
1301
|
+
sendRuntimeDebugMessage(socket, {
|
|
1302
|
+
type: "route:error",
|
|
1303
|
+
sessionId,
|
|
1304
|
+
requestId: message.requestId,
|
|
1305
|
+
page: message.page,
|
|
1306
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
async function readDebugParams() {
|
|
1311
|
+
const rawParams = await executePluginCommand("system", {
|
|
1312
|
+
subCommand: "getParams"
|
|
1313
|
+
});
|
|
1314
|
+
if (!rawParams) {
|
|
1315
|
+
return {};
|
|
1316
|
+
}
|
|
1317
|
+
try {
|
|
1318
|
+
const parsed = JSON.parse(rawParams);
|
|
1319
|
+
return {
|
|
1320
|
+
debug: parsed.debug === true,
|
|
1321
|
+
debugSessionId: typeof parsed.debugSessionId === "string" ? parsed.debugSessionId : void 0,
|
|
1322
|
+
debugWsUrl: typeof parsed.debugWsUrl === "string" ? parsed.debugWsUrl : void 0
|
|
1323
|
+
};
|
|
1324
|
+
} catch {
|
|
1325
|
+
return {};
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
async function stopRuntimeDebugSession() {
|
|
1329
|
+
const connection = currentConnection;
|
|
1330
|
+
currentConnection = null;
|
|
1331
|
+
currentController = null;
|
|
1332
|
+
clearBufferedRequestMessages();
|
|
1333
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1334
|
+
await stopDebugSession();
|
|
1335
|
+
if (!connection) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
if (RuntimeWebSocket && (connection.socket.readyState === RuntimeWebSocket.OPEN || connection.socket.readyState === RuntimeWebSocket.CONNECTING)) {
|
|
1339
|
+
connection.socket.close();
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
async function startRuntimeDebugSession() {
|
|
1343
|
+
await stopRuntimeDebugSession();
|
|
1344
|
+
const params = await readDebugParams();
|
|
1345
|
+
if (params.debug !== true || !params.debugSessionId || !params.debugWsUrl) {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1349
|
+
if (!RuntimeWebSocket) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
const socket = new RuntimeWebSocket(params.debugWsUrl);
|
|
1353
|
+
const sessionId = params.debugSessionId;
|
|
1354
|
+
currentConnection = { sessionId, socket };
|
|
1355
|
+
socket.onopen = () => {
|
|
1356
|
+
if (currentConnection?.socket !== socket) {
|
|
1357
|
+
socket.close();
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
void startDebugSession({
|
|
1361
|
+
onMarkerEnter(checkpoint) {
|
|
1362
|
+
if (!checkpoint.cursor.markerId) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
sendRuntimeDebugMessage(socket, {
|
|
1366
|
+
type: "marker:enter",
|
|
1367
|
+
sessionId,
|
|
1368
|
+
markerId: checkpoint.cursor.markerId,
|
|
1369
|
+
story: checkpoint.cursor.story,
|
|
1370
|
+
paragraph: checkpoint.cursor.paragraph
|
|
1371
|
+
});
|
|
1372
|
+
},
|
|
1373
|
+
onError(error) {
|
|
1374
|
+
console.error("[debug] runtime debug session error:", error);
|
|
1375
|
+
}
|
|
1376
|
+
}).then((controller) => {
|
|
1377
|
+
currentController = controller;
|
|
1378
|
+
flushBufferedRequestMessages(socket, sessionId);
|
|
1379
|
+
}).catch((error) => {
|
|
1380
|
+
console.error("[debug] failed to start runtime debug session:", error);
|
|
1381
|
+
socket.close();
|
|
1382
|
+
});
|
|
1383
|
+
};
|
|
1384
|
+
socket.onclose = () => {
|
|
1385
|
+
if (currentConnection?.socket !== socket) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
currentConnection = null;
|
|
1389
|
+
currentController = null;
|
|
1390
|
+
clearBufferedRequestMessages();
|
|
1391
|
+
void stopDebugSession();
|
|
1392
|
+
};
|
|
1393
|
+
socket.onerror = (event) => {
|
|
1394
|
+
if (currentConnection?.socket !== socket) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
console.error("[debug] runtime websocket error:", event);
|
|
1398
|
+
};
|
|
1399
|
+
socket.onmessage = (event) => {
|
|
1400
|
+
if (currentConnection?.socket !== socket) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
try {
|
|
1404
|
+
const message = JSON.parse(String(event.data));
|
|
1405
|
+
if (message.sessionId && message.sessionId !== sessionId) {
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
if (isJumpRequestMessage(message)) {
|
|
1409
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
if (isRouteRequestMessage(message)) {
|
|
1413
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (message.type && message.type !== "session:init") {
|
|
1417
|
+
console.debug("[debug] runtime received message:", message);
|
|
1418
|
+
}
|
|
1419
|
+
} catch (error) {
|
|
1420
|
+
console.error("[debug] failed to parse runtime debug message:", error);
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
|
|
790
1425
|
// src/hooks/useFadeInOut.ts
|
|
791
1426
|
function useFadeInOut(inTime, _keepTime, _outTime, pause = false, onFinished) {
|
|
792
1427
|
const [style, api] = (0, spring_exports.useSpring)(
|
|
@@ -854,7 +1489,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
|
|
|
854
1489
|
}
|
|
855
1490
|
|
|
856
1491
|
// src/hooks/useScenario.ts
|
|
857
|
-
var
|
|
1492
|
+
var import_react3 = require("react");
|
|
858
1493
|
var currentScenarioSession = null;
|
|
859
1494
|
var scenarioSessionId = 0;
|
|
860
1495
|
var scenarioLifecycleQueue = Promise.resolve();
|
|
@@ -889,11 +1524,13 @@ async function terminateCurrentScenario() {
|
|
|
889
1524
|
await executePluginCommand("scenario", {
|
|
890
1525
|
subCommand: "terminateStory"
|
|
891
1526
|
});
|
|
1527
|
+
clearDebugSessionRuntimeState();
|
|
892
1528
|
} catch (error) {
|
|
893
1529
|
console.error("Failed to terminate scenario:", error);
|
|
894
1530
|
}
|
|
895
1531
|
}
|
|
896
1532
|
async function startScenarioSession(session, config) {
|
|
1533
|
+
clearDebugSessionRuntimeState();
|
|
897
1534
|
for (const story of config.stories) {
|
|
898
1535
|
if (!isCurrentScenarioSession(session)) return;
|
|
899
1536
|
try {
|
|
@@ -994,7 +1631,7 @@ function setWaiting(time, skippable) {
|
|
|
994
1631
|
executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
|
|
995
1632
|
}
|
|
996
1633
|
function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
997
|
-
(0,
|
|
1634
|
+
(0, import_react3.useEffect)(() => {
|
|
998
1635
|
const session = acquireScenarioSession({
|
|
999
1636
|
stories: normalizeStories(stories),
|
|
1000
1637
|
startName,
|
|
@@ -1008,10 +1645,10 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
|
1008
1645
|
}
|
|
1009
1646
|
|
|
1010
1647
|
// src/hooks/useSoundEffect.ts
|
|
1011
|
-
var
|
|
1648
|
+
var import_react4 = require("react");
|
|
1012
1649
|
var LOADED_SOUNDS = /* @__PURE__ */ new Set();
|
|
1013
1650
|
function useSoundEffect(src) {
|
|
1014
|
-
(0,
|
|
1651
|
+
(0, import_react4.useEffect)(() => {
|
|
1015
1652
|
const loadSound = async () => {
|
|
1016
1653
|
try {
|
|
1017
1654
|
await executePluginCommand("audio", {
|
|
@@ -1046,7 +1683,7 @@ function useSoundEffect(src) {
|
|
|
1046
1683
|
}
|
|
1047
1684
|
};
|
|
1048
1685
|
}, [src]);
|
|
1049
|
-
return (0,
|
|
1686
|
+
return (0, import_react4.useCallback)(() => {
|
|
1050
1687
|
try {
|
|
1051
1688
|
executePluginCommand("audio", {
|
|
1052
1689
|
subCommand: "play",
|
|
@@ -1059,19 +1696,31 @@ function useSoundEffect(src) {
|
|
|
1059
1696
|
}
|
|
1060
1697
|
|
|
1061
1698
|
// src/hooks/useStage.ts
|
|
1062
|
-
var
|
|
1063
|
-
var
|
|
1699
|
+
var import_react5 = require("react");
|
|
1700
|
+
var import_valtio3 = require("valtio");
|
|
1064
1701
|
var SKIP_INTERVAL_MS = 80;
|
|
1702
|
+
var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
|
|
1703
|
+
var DEFAULT_FAST_FORWARD_TIME_LIMIT_MS = 3e3;
|
|
1065
1704
|
var autoRuntime = {
|
|
1066
1705
|
defaultTailMs: 0
|
|
1067
1706
|
};
|
|
1068
|
-
var skipState = (0,
|
|
1069
|
-
var autoState = (0,
|
|
1707
|
+
var skipState = (0, import_valtio3.proxy)({ active: false });
|
|
1708
|
+
var autoState = (0, import_valtio3.proxy)({ active: false });
|
|
1709
|
+
var fastForwardState = (0, import_valtio3.proxy)({ active: false });
|
|
1710
|
+
var warpState = (0, import_valtio3.proxy)({ active: false });
|
|
1070
1711
|
function useIsSkipping() {
|
|
1071
|
-
return (0,
|
|
1712
|
+
return (0, import_valtio3.useSnapshot)(skipState).active;
|
|
1072
1713
|
}
|
|
1073
1714
|
function useIsAutoing() {
|
|
1074
|
-
return (0,
|
|
1715
|
+
return (0, import_valtio3.useSnapshot)(autoState).active;
|
|
1716
|
+
}
|
|
1717
|
+
function useIsSeeking() {
|
|
1718
|
+
return (0, import_valtio3.useSnapshot)(fastForwardState).active;
|
|
1719
|
+
}
|
|
1720
|
+
function getSeekingType() {
|
|
1721
|
+
if (warpState.active) return "warp";
|
|
1722
|
+
if (fastForwardState.active) return "fast-forward";
|
|
1723
|
+
return null;
|
|
1075
1724
|
}
|
|
1076
1725
|
function setDefaultAutoTailMs(ms) {
|
|
1077
1726
|
if (!Number.isFinite(ms)) {
|
|
@@ -1179,6 +1828,101 @@ function createStage() {
|
|
|
1179
1828
|
let _autoBarrierSeq = 0;
|
|
1180
1829
|
let _autoTicketSeq = 0;
|
|
1181
1830
|
const _pendingAutoTickets = /* @__PURE__ */ new Map();
|
|
1831
|
+
let _fastForwardRuntime = null;
|
|
1832
|
+
let _managedAdvanceInFlight = false;
|
|
1833
|
+
let _managedAdvancePending = false;
|
|
1834
|
+
function resetManagedAdvanceState() {
|
|
1835
|
+
_managedAdvanceInFlight = false;
|
|
1836
|
+
_managedAdvancePending = false;
|
|
1837
|
+
}
|
|
1838
|
+
function clearFastForwardRuntime() {
|
|
1839
|
+
_fastForwardRuntime = null;
|
|
1840
|
+
resetManagedAdvanceState();
|
|
1841
|
+
}
|
|
1842
|
+
function stopFastForwardInternal() {
|
|
1843
|
+
fastForwardState.active = false;
|
|
1844
|
+
warpState.active = false;
|
|
1845
|
+
clearFastForwardRuntime();
|
|
1846
|
+
}
|
|
1847
|
+
function abortFastForward(error) {
|
|
1848
|
+
const runtime = _fastForwardRuntime;
|
|
1849
|
+
stopFastForwardInternal();
|
|
1850
|
+
if (!runtime?.onAbort) {
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
try {
|
|
1854
|
+
runtime.onAbort(error);
|
|
1855
|
+
} catch (abortError) {
|
|
1856
|
+
console.error("Error in fast-forward abort callback:", abortError);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
function canAdvanceFastForward() {
|
|
1860
|
+
if (!fastForwardState.active || _fastForwardRuntime === null) {
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
const elapsedMs = Date.now() - _fastForwardRuntime.startedAt;
|
|
1864
|
+
if (elapsedMs > _fastForwardRuntime.timeLimitMs) {
|
|
1865
|
+
abortFastForward(new Error(`Fast forward exceeded time limit (${_fastForwardRuntime.timeLimitMs}ms)`));
|
|
1866
|
+
return false;
|
|
1867
|
+
}
|
|
1868
|
+
_fastForwardRuntime.stepCount += 1;
|
|
1869
|
+
if (_fastForwardRuntime.stepCount > _fastForwardRuntime.stepLimit) {
|
|
1870
|
+
abortFastForward(new Error(`Fast forward exceeded step limit (${_fastForwardRuntime.stepLimit})`));
|
|
1871
|
+
return false;
|
|
1872
|
+
}
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
function normalizeAdvanceError(error) {
|
|
1876
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1877
|
+
}
|
|
1878
|
+
function isManagedAdvanceActive() {
|
|
1879
|
+
return fastForwardState.active || skipState.active || autoState.active;
|
|
1880
|
+
}
|
|
1881
|
+
function canScheduleManagedAdvance() {
|
|
1882
|
+
if (fastForwardState.active) {
|
|
1883
|
+
return canAdvanceFastForward();
|
|
1884
|
+
}
|
|
1885
|
+
return true;
|
|
1886
|
+
}
|
|
1887
|
+
function handleManagedAdvanceError(error) {
|
|
1888
|
+
console.error("Failed to advance scenario:", error);
|
|
1889
|
+
if (fastForwardState.active) {
|
|
1890
|
+
abortFastForward(normalizeAdvanceError(error));
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function scheduleManagedAdvance() {
|
|
1894
|
+
if (!isManagedAdvanceActive()) {
|
|
1895
|
+
_managedAdvancePending = false;
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
if (_managedAdvanceInFlight) {
|
|
1899
|
+
_managedAdvancePending = true;
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
if (!canScheduleManagedAdvance()) {
|
|
1903
|
+
_managedAdvancePending = false;
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
_managedAdvanceInFlight = true;
|
|
1907
|
+
_managedAdvancePending = false;
|
|
1908
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError).finally(() => {
|
|
1909
|
+
_managedAdvanceInFlight = false;
|
|
1910
|
+
if (!isManagedAdvanceActive()) {
|
|
1911
|
+
_managedAdvancePending = false;
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (_managedAdvancePending) {
|
|
1915
|
+
scheduleManagedAdvance();
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
function requestNextLine() {
|
|
1920
|
+
if (isManagedAdvanceActive()) {
|
|
1921
|
+
scheduleManagedAdvance();
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
|
|
1925
|
+
}
|
|
1182
1926
|
function clearAutoResumeTimer() {
|
|
1183
1927
|
if (_autoResumeTimer !== null) {
|
|
1184
1928
|
clearTimeout(_autoResumeTimer);
|
|
@@ -1213,7 +1957,7 @@ function createStage() {
|
|
|
1213
1957
|
if (!autoState.active) return;
|
|
1214
1958
|
if (_autoBarrier === null || _autoBarrier.id !== barrierId) return;
|
|
1215
1959
|
_autoBarrier = null;
|
|
1216
|
-
|
|
1960
|
+
requestNextLine();
|
|
1217
1961
|
}, safeDelayMs);
|
|
1218
1962
|
}
|
|
1219
1963
|
function trySettleAutoBarrier(barrier) {
|
|
@@ -1349,7 +2093,7 @@ function createStage() {
|
|
|
1349
2093
|
return;
|
|
1350
2094
|
}
|
|
1351
2095
|
if (_autoBarrier === null) {
|
|
1352
|
-
|
|
2096
|
+
requestNextLine();
|
|
1353
2097
|
}
|
|
1354
2098
|
}, autoRuntime.defaultTailMs);
|
|
1355
2099
|
}
|
|
@@ -1357,11 +2101,12 @@ function createStage() {
|
|
|
1357
2101
|
if (_skipTimer !== null) clearTimeout(_skipTimer);
|
|
1358
2102
|
_skipTimer = setTimeout(() => {
|
|
1359
2103
|
_skipTimer = null;
|
|
1360
|
-
if (skipState.active)
|
|
2104
|
+
if (skipState.active) requestNextLine();
|
|
1361
2105
|
}, SKIP_INTERVAL_MS);
|
|
1362
2106
|
}
|
|
1363
2107
|
function startSkip() {
|
|
1364
2108
|
if (skipState.active) return;
|
|
2109
|
+
if (fastForwardState.active) return;
|
|
1365
2110
|
if (isAnyBlockerActive()) return;
|
|
1366
2111
|
skipState.active = true;
|
|
1367
2112
|
invokeSkipCallbacks();
|
|
@@ -1373,7 +2118,7 @@ function createStage() {
|
|
|
1373
2118
|
if (_skipTimer !== null) {
|
|
1374
2119
|
clearTimeout(_skipTimer);
|
|
1375
2120
|
_skipTimer = null;
|
|
1376
|
-
|
|
2121
|
+
requestNextLine();
|
|
1377
2122
|
}
|
|
1378
2123
|
}
|
|
1379
2124
|
function isSkipping() {
|
|
@@ -1381,6 +2126,7 @@ function createStage() {
|
|
|
1381
2126
|
}
|
|
1382
2127
|
function startAuto() {
|
|
1383
2128
|
if (autoState.active) return;
|
|
2129
|
+
if (fastForwardState.active) return;
|
|
1384
2130
|
if (isAnyAutoBlockerActive()) return;
|
|
1385
2131
|
autoState.active = true;
|
|
1386
2132
|
tryInterrupt();
|
|
@@ -1398,12 +2144,58 @@ function createStage() {
|
|
|
1398
2144
|
function isAutoing() {
|
|
1399
2145
|
return autoState.active;
|
|
1400
2146
|
}
|
|
2147
|
+
function startFastForward(options) {
|
|
2148
|
+
if (fastForwardState.active) return;
|
|
2149
|
+
resetRuntimeState();
|
|
2150
|
+
_fastForwardRuntime = {
|
|
2151
|
+
stepLimit: Math.max(1, options?.stepLimit ?? DEFAULT_FAST_FORWARD_STEP_LIMIT),
|
|
2152
|
+
timeLimitMs: Math.max(1, options?.timeLimitMs ?? DEFAULT_FAST_FORWARD_TIME_LIMIT_MS),
|
|
2153
|
+
stepCount: 0,
|
|
2154
|
+
startedAt: Date.now(),
|
|
2155
|
+
onAbort: options?.onAbort,
|
|
2156
|
+
warp: options?.warp === true
|
|
2157
|
+
};
|
|
2158
|
+
fastForwardState.active = true;
|
|
2159
|
+
warpState.active = _fastForwardRuntime.warp;
|
|
2160
|
+
}
|
|
2161
|
+
function stopFastForward() {
|
|
2162
|
+
stopFastForwardInternal();
|
|
2163
|
+
}
|
|
2164
|
+
function isFastForwarding() {
|
|
2165
|
+
return fastForwardState.active;
|
|
2166
|
+
}
|
|
2167
|
+
function resetRuntimeState() {
|
|
2168
|
+
skipState.active = false;
|
|
2169
|
+
autoState.active = false;
|
|
2170
|
+
fastForwardState.active = false;
|
|
2171
|
+
warpState.active = false;
|
|
2172
|
+
clearFastForwardRuntime();
|
|
2173
|
+
if (_skipTimer !== null) {
|
|
2174
|
+
clearTimeout(_skipTimer);
|
|
2175
|
+
_skipTimer = null;
|
|
2176
|
+
}
|
|
2177
|
+
if (_autoTimer !== null) {
|
|
2178
|
+
clearTimeout(_autoTimer);
|
|
2179
|
+
_autoTimer = null;
|
|
2180
|
+
}
|
|
2181
|
+
clearAutoBarrier();
|
|
2182
|
+
clearPendingAutoTickets();
|
|
2183
|
+
}
|
|
1401
2184
|
function buildControl() {
|
|
1402
|
-
|
|
2185
|
+
const warpActive = warpState.active;
|
|
2186
|
+
let handled = warpActive;
|
|
1403
2187
|
let unskippableFlag = false;
|
|
2188
|
+
const inertRecordId = "__warp__";
|
|
1404
2189
|
const control = {
|
|
1405
2190
|
setWaiting(time, skippable) {
|
|
1406
2191
|
handled = true;
|
|
2192
|
+
if (warpActive) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (fastForwardState.active) {
|
|
2196
|
+
requestNextLine();
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
1407
2199
|
if (skipState.active) {
|
|
1408
2200
|
invokeSkipCallbacks();
|
|
1409
2201
|
scheduleSkipNextLine();
|
|
@@ -1420,6 +2212,13 @@ function createStage() {
|
|
|
1420
2212
|
},
|
|
1421
2213
|
hold() {
|
|
1422
2214
|
handled = true;
|
|
2215
|
+
if (warpActive) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
if (fastForwardState.active) {
|
|
2219
|
+
requestNextLine();
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
1423
2222
|
if (skipState.active && !unskippableFlag) {
|
|
1424
2223
|
if (isAnyBlockerActive()) {
|
|
1425
2224
|
stopSkip();
|
|
@@ -1438,12 +2237,21 @@ function createStage() {
|
|
|
1438
2237
|
},
|
|
1439
2238
|
nextLine() {
|
|
1440
2239
|
handled = true;
|
|
1441
|
-
|
|
2240
|
+
if (warpActive) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
requestNextLine();
|
|
1442
2244
|
},
|
|
1443
2245
|
unskippable() {
|
|
2246
|
+
if (warpActive) {
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
1444
2249
|
unskippableFlag = true;
|
|
1445
2250
|
},
|
|
1446
2251
|
record(meta) {
|
|
2252
|
+
if (warpActive) {
|
|
2253
|
+
return inertRecordId;
|
|
2254
|
+
}
|
|
1447
2255
|
const result = executePluginCommand("scenario", {
|
|
1448
2256
|
subCommand: "record",
|
|
1449
2257
|
meta
|
|
@@ -1482,19 +2290,19 @@ function createStage() {
|
|
|
1482
2290
|
cmd = transformCommand(e);
|
|
1483
2291
|
} catch (err) {
|
|
1484
2292
|
console.error("Failed to transform scenario command:", err);
|
|
1485
|
-
|
|
2293
|
+
requestNextLine();
|
|
1486
2294
|
return;
|
|
1487
2295
|
}
|
|
1488
2296
|
const handler = commandHandlers.get(cmd.command);
|
|
1489
2297
|
if (!handler) {
|
|
1490
2298
|
console.warn(`[Stage] No handler registered for command "${cmd.command}". Auto-advancing.`);
|
|
1491
|
-
|
|
2299
|
+
requestNextLine();
|
|
1492
2300
|
return;
|
|
1493
2301
|
}
|
|
1494
2302
|
const { control, wasHandled } = buildControl();
|
|
1495
2303
|
handler(cmd, control);
|
|
1496
2304
|
if (!wasHandled()) {
|
|
1497
|
-
|
|
2305
|
+
requestNextLine();
|
|
1498
2306
|
}
|
|
1499
2307
|
}
|
|
1500
2308
|
function dispatchTextLine(e) {
|
|
@@ -1503,7 +2311,7 @@ function createStage() {
|
|
|
1503
2311
|
handler(e, control);
|
|
1504
2312
|
}
|
|
1505
2313
|
if (!wasHandled()) {
|
|
1506
|
-
|
|
2314
|
+
requestNextLine();
|
|
1507
2315
|
}
|
|
1508
2316
|
}
|
|
1509
2317
|
function invokeSkipCallbacks() {
|
|
@@ -1551,48 +2359,52 @@ function createStage() {
|
|
|
1551
2359
|
startAuto,
|
|
1552
2360
|
stopAuto,
|
|
1553
2361
|
isAutoing,
|
|
2362
|
+
startFastForward,
|
|
2363
|
+
stopFastForward,
|
|
2364
|
+
isFastForwarding,
|
|
2365
|
+
resetRuntimeState,
|
|
1554
2366
|
bindEvents
|
|
1555
2367
|
};
|
|
1556
2368
|
}
|
|
1557
|
-
var StageContext = (0,
|
|
2369
|
+
var StageContext = (0, import_react5.createContext)(null);
|
|
1558
2370
|
function useStageContext() {
|
|
1559
|
-
const ctx = (0,
|
|
2371
|
+
const ctx = (0, import_react5.useContext)(StageContext);
|
|
1560
2372
|
if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
|
|
1561
2373
|
return ctx;
|
|
1562
2374
|
}
|
|
1563
2375
|
function StageContextProvider({ stage, children }) {
|
|
1564
|
-
(0,
|
|
2376
|
+
(0, import_react5.useEffect)(() => {
|
|
1565
2377
|
return stage.bindEvents();
|
|
1566
2378
|
}, [stage]);
|
|
1567
|
-
return (0,
|
|
2379
|
+
return (0, import_react5.createElement)(StageContext.Provider, { value: stage }, children);
|
|
1568
2380
|
}
|
|
1569
2381
|
function useSkipCallback(callback) {
|
|
1570
2382
|
const stage = useStageContext();
|
|
1571
|
-
(0,
|
|
2383
|
+
(0, import_react5.useEffect)(() => {
|
|
1572
2384
|
return stage.addSkipCallback(callback);
|
|
1573
2385
|
}, [stage, callback]);
|
|
1574
2386
|
}
|
|
1575
2387
|
function useInterruptCallback(callback) {
|
|
1576
2388
|
const stage = useStageContext();
|
|
1577
|
-
(0,
|
|
2389
|
+
(0, import_react5.useEffect)(() => {
|
|
1578
2390
|
return stage.addInterruptCallback(callback);
|
|
1579
2391
|
}, [stage, callback]);
|
|
1580
2392
|
}
|
|
1581
2393
|
function useSkipBlocker(callback) {
|
|
1582
2394
|
const stage = useStageContext();
|
|
1583
|
-
(0,
|
|
2395
|
+
(0, import_react5.useEffect)(() => {
|
|
1584
2396
|
return stage.addSkipBlocker(callback);
|
|
1585
2397
|
}, [stage, callback]);
|
|
1586
2398
|
}
|
|
1587
2399
|
function useAutoBlocker(callback) {
|
|
1588
2400
|
const stage = useStageContext();
|
|
1589
|
-
(0,
|
|
2401
|
+
(0, import_react5.useEffect)(() => {
|
|
1590
2402
|
return stage.addAutoBlocker(callback);
|
|
1591
2403
|
}, [stage, callback]);
|
|
1592
2404
|
}
|
|
1593
2405
|
function useAutoTicket() {
|
|
1594
2406
|
const stage = useStageContext();
|
|
1595
|
-
return (0,
|
|
2407
|
+
return (0, import_react5.useCallback)(
|
|
1596
2408
|
(options) => {
|
|
1597
2409
|
return stage.issueAutoTicket(options);
|
|
1598
2410
|
},
|
|
@@ -1601,7 +2413,7 @@ function useAutoTicket() {
|
|
|
1601
2413
|
}
|
|
1602
2414
|
function useBeforeHandleCommandCallback(callback) {
|
|
1603
2415
|
const stage = useStageContext();
|
|
1604
|
-
(0,
|
|
2416
|
+
(0, import_react5.useEffect)(() => {
|
|
1605
2417
|
return stage.addBeforeHandleCommandCallback(callback);
|
|
1606
2418
|
}, [stage, callback]);
|
|
1607
2419
|
}
|