@momoyu-ink/kit 0.5.4 → 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 +861 -48
- package/dist/lib.d.mts +149 -50
- package/dist/lib.d.ts +149 -50
- package/dist/lib.mjs +852 -28
- 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
|
|
|
@@ -212,7 +223,8 @@ var hostConfig = {
|
|
|
212
223
|
* @required
|
|
213
224
|
*/
|
|
214
225
|
createInstance(type, props, _root, _hostContext, _internalInstanceHandle) {
|
|
215
|
-
const
|
|
226
|
+
const { ref, ...restProps } = props;
|
|
227
|
+
const node = Node.create(props.label ?? "", type, restProps);
|
|
216
228
|
return node;
|
|
217
229
|
},
|
|
218
230
|
/**
|
|
@@ -282,7 +294,7 @@ var hostConfig = {
|
|
|
282
294
|
},
|
|
283
295
|
commitUpdate(instance, _type, prevProps, nextProps, _internalHandle) {
|
|
284
296
|
const { label: _oldLabel, children: _oldChildren, ...oldProps } = prevProps;
|
|
285
|
-
const { label, children, ...newProps } = nextProps;
|
|
297
|
+
const { label, children, ref, ...newProps } = nextProps;
|
|
286
298
|
const changedProps = {};
|
|
287
299
|
for (const key in newProps) {
|
|
288
300
|
if (newProps[key] !== oldProps[key]) {
|
|
@@ -449,7 +461,7 @@ var globalVariables = new Proxy(
|
|
|
449
461
|
}
|
|
450
462
|
);
|
|
451
463
|
function safeEval(code, sandbox) {
|
|
452
|
-
const
|
|
464
|
+
const proxy4 = new Proxy(sandbox, {
|
|
453
465
|
has(target, key) {
|
|
454
466
|
if (typeof key === "string" && JS_GLOBAL_KEYS.has(key)) {
|
|
455
467
|
return false;
|
|
@@ -457,7 +469,7 @@ function safeEval(code, sandbox) {
|
|
|
457
469
|
return key in target || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
458
470
|
},
|
|
459
471
|
get(target, key) {
|
|
460
|
-
if (key === "window" || key === "globalThis") return
|
|
472
|
+
if (key === "window" || key === "globalThis") return proxy4;
|
|
461
473
|
if (key === "ARCHIVE") return archiveVariables;
|
|
462
474
|
if (key === "GLOBAL") return globalVariables;
|
|
463
475
|
if (key === Symbol.unscopables) return void 0;
|
|
@@ -468,7 +480,7 @@ function safeEval(code, sandbox) {
|
|
|
468
480
|
return true;
|
|
469
481
|
}
|
|
470
482
|
});
|
|
471
|
-
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(
|
|
483
|
+
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(proxy4)(proxy4);
|
|
472
484
|
}
|
|
473
485
|
globalThis.__moyu_eval_sandbox = (code) => {
|
|
474
486
|
try {
|
|
@@ -650,11 +662,11 @@ function getStageSize() {
|
|
|
650
662
|
}
|
|
651
663
|
|
|
652
664
|
// src/components/navigation.tsx
|
|
653
|
-
var import_react2 =
|
|
654
|
-
var import_react3 = require("react");
|
|
665
|
+
var import_react2 = require("react");
|
|
655
666
|
var import_valtio = require("valtio");
|
|
656
|
-
var
|
|
657
|
-
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);
|
|
658
670
|
var overlayIdCounter = 0;
|
|
659
671
|
var generateOverlayId = () => {
|
|
660
672
|
return `overlay-${++overlayIdCounter}-${Date.now()}`;
|
|
@@ -708,6 +720,9 @@ function createStackNavigator(options) {
|
|
|
708
720
|
delete navigationState.pageParams[page];
|
|
709
721
|
}
|
|
710
722
|
},
|
|
723
|
+
hasPage: (page) => {
|
|
724
|
+
return normalizedPages[page] !== void 0;
|
|
725
|
+
},
|
|
711
726
|
goBack: () => {
|
|
712
727
|
console.warn("goBack() not implemented yet");
|
|
713
728
|
},
|
|
@@ -763,11 +778,14 @@ function createStaticNavigation(navigator) {
|
|
|
763
778
|
const { pages, overlays } = navigator._descriptors;
|
|
764
779
|
const pageDescriptor = pages[navState.currentPage];
|
|
765
780
|
const Page = pageDescriptor?.component || (() => null);
|
|
766
|
-
return /* @__PURE__ */
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
+
] });
|
|
771
789
|
};
|
|
772
790
|
}
|
|
773
791
|
function getNavigator() {
|
|
@@ -781,11 +799,629 @@ function useNavigationState() {
|
|
|
781
799
|
return (0, import_valtio.useSnapshot)(getNavigator()._state);
|
|
782
800
|
}
|
|
783
801
|
function useNavigationParams() {
|
|
784
|
-
const overlayParams = (0,
|
|
785
|
-
const pageParams = (0,
|
|
802
|
+
const overlayParams = (0, import_react2.useContext)(OverlayParamsContext);
|
|
803
|
+
const pageParams = (0, import_react2.useContext)(PageParamsContext);
|
|
786
804
|
return overlayParams ?? pageParams ?? {};
|
|
787
805
|
}
|
|
788
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
|
+
|
|
789
1425
|
// src/hooks/useFadeInOut.ts
|
|
790
1426
|
function useFadeInOut(inTime, _keepTime, _outTime, pause = false, onFinished) {
|
|
791
1427
|
const [style, api] = (0, spring_exports.useSpring)(
|
|
@@ -853,7 +1489,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
|
|
|
853
1489
|
}
|
|
854
1490
|
|
|
855
1491
|
// src/hooks/useScenario.ts
|
|
856
|
-
var
|
|
1492
|
+
var import_react3 = require("react");
|
|
857
1493
|
var currentScenarioSession = null;
|
|
858
1494
|
var scenarioSessionId = 0;
|
|
859
1495
|
var scenarioLifecycleQueue = Promise.resolve();
|
|
@@ -888,11 +1524,13 @@ async function terminateCurrentScenario() {
|
|
|
888
1524
|
await executePluginCommand("scenario", {
|
|
889
1525
|
subCommand: "terminateStory"
|
|
890
1526
|
});
|
|
1527
|
+
clearDebugSessionRuntimeState();
|
|
891
1528
|
} catch (error) {
|
|
892
1529
|
console.error("Failed to terminate scenario:", error);
|
|
893
1530
|
}
|
|
894
1531
|
}
|
|
895
1532
|
async function startScenarioSession(session, config) {
|
|
1533
|
+
clearDebugSessionRuntimeState();
|
|
896
1534
|
for (const story of config.stories) {
|
|
897
1535
|
if (!isCurrentScenarioSession(session)) return;
|
|
898
1536
|
try {
|
|
@@ -993,7 +1631,7 @@ function setWaiting(time, skippable) {
|
|
|
993
1631
|
executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
|
|
994
1632
|
}
|
|
995
1633
|
function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
996
|
-
(0,
|
|
1634
|
+
(0, import_react3.useEffect)(() => {
|
|
997
1635
|
const session = acquireScenarioSession({
|
|
998
1636
|
stories: normalizeStories(stories),
|
|
999
1637
|
startName,
|
|
@@ -1007,10 +1645,10 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
|
1007
1645
|
}
|
|
1008
1646
|
|
|
1009
1647
|
// src/hooks/useSoundEffect.ts
|
|
1010
|
-
var
|
|
1648
|
+
var import_react4 = require("react");
|
|
1011
1649
|
var LOADED_SOUNDS = /* @__PURE__ */ new Set();
|
|
1012
1650
|
function useSoundEffect(src) {
|
|
1013
|
-
(0,
|
|
1651
|
+
(0, import_react4.useEffect)(() => {
|
|
1014
1652
|
const loadSound = async () => {
|
|
1015
1653
|
try {
|
|
1016
1654
|
await executePluginCommand("audio", {
|
|
@@ -1045,7 +1683,7 @@ function useSoundEffect(src) {
|
|
|
1045
1683
|
}
|
|
1046
1684
|
};
|
|
1047
1685
|
}, [src]);
|
|
1048
|
-
return (0,
|
|
1686
|
+
return (0, import_react4.useCallback)(() => {
|
|
1049
1687
|
try {
|
|
1050
1688
|
executePluginCommand("audio", {
|
|
1051
1689
|
subCommand: "play",
|
|
@@ -1058,19 +1696,31 @@ function useSoundEffect(src) {
|
|
|
1058
1696
|
}
|
|
1059
1697
|
|
|
1060
1698
|
// src/hooks/useStage.ts
|
|
1061
|
-
var
|
|
1062
|
-
var
|
|
1699
|
+
var import_react5 = require("react");
|
|
1700
|
+
var import_valtio3 = require("valtio");
|
|
1063
1701
|
var SKIP_INTERVAL_MS = 80;
|
|
1702
|
+
var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
|
|
1703
|
+
var DEFAULT_FAST_FORWARD_TIME_LIMIT_MS = 3e3;
|
|
1064
1704
|
var autoRuntime = {
|
|
1065
1705
|
defaultTailMs: 0
|
|
1066
1706
|
};
|
|
1067
|
-
var skipState = (0,
|
|
1068
|
-
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 });
|
|
1069
1711
|
function useIsSkipping() {
|
|
1070
|
-
return (0,
|
|
1712
|
+
return (0, import_valtio3.useSnapshot)(skipState).active;
|
|
1071
1713
|
}
|
|
1072
1714
|
function useIsAutoing() {
|
|
1073
|
-
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;
|
|
1074
1724
|
}
|
|
1075
1725
|
function setDefaultAutoTailMs(ms) {
|
|
1076
1726
|
if (!Number.isFinite(ms)) {
|
|
@@ -1178,6 +1828,101 @@ function createStage() {
|
|
|
1178
1828
|
let _autoBarrierSeq = 0;
|
|
1179
1829
|
let _autoTicketSeq = 0;
|
|
1180
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
|
+
}
|
|
1181
1926
|
function clearAutoResumeTimer() {
|
|
1182
1927
|
if (_autoResumeTimer !== null) {
|
|
1183
1928
|
clearTimeout(_autoResumeTimer);
|
|
@@ -1212,7 +1957,7 @@ function createStage() {
|
|
|
1212
1957
|
if (!autoState.active) return;
|
|
1213
1958
|
if (_autoBarrier === null || _autoBarrier.id !== barrierId) return;
|
|
1214
1959
|
_autoBarrier = null;
|
|
1215
|
-
|
|
1960
|
+
requestNextLine();
|
|
1216
1961
|
}, safeDelayMs);
|
|
1217
1962
|
}
|
|
1218
1963
|
function trySettleAutoBarrier(barrier) {
|
|
@@ -1348,7 +2093,7 @@ function createStage() {
|
|
|
1348
2093
|
return;
|
|
1349
2094
|
}
|
|
1350
2095
|
if (_autoBarrier === null) {
|
|
1351
|
-
|
|
2096
|
+
requestNextLine();
|
|
1352
2097
|
}
|
|
1353
2098
|
}, autoRuntime.defaultTailMs);
|
|
1354
2099
|
}
|
|
@@ -1356,11 +2101,12 @@ function createStage() {
|
|
|
1356
2101
|
if (_skipTimer !== null) clearTimeout(_skipTimer);
|
|
1357
2102
|
_skipTimer = setTimeout(() => {
|
|
1358
2103
|
_skipTimer = null;
|
|
1359
|
-
if (skipState.active)
|
|
2104
|
+
if (skipState.active) requestNextLine();
|
|
1360
2105
|
}, SKIP_INTERVAL_MS);
|
|
1361
2106
|
}
|
|
1362
2107
|
function startSkip() {
|
|
1363
2108
|
if (skipState.active) return;
|
|
2109
|
+
if (fastForwardState.active) return;
|
|
1364
2110
|
if (isAnyBlockerActive()) return;
|
|
1365
2111
|
skipState.active = true;
|
|
1366
2112
|
invokeSkipCallbacks();
|
|
@@ -1372,7 +2118,7 @@ function createStage() {
|
|
|
1372
2118
|
if (_skipTimer !== null) {
|
|
1373
2119
|
clearTimeout(_skipTimer);
|
|
1374
2120
|
_skipTimer = null;
|
|
1375
|
-
|
|
2121
|
+
requestNextLine();
|
|
1376
2122
|
}
|
|
1377
2123
|
}
|
|
1378
2124
|
function isSkipping() {
|
|
@@ -1380,6 +2126,7 @@ function createStage() {
|
|
|
1380
2126
|
}
|
|
1381
2127
|
function startAuto() {
|
|
1382
2128
|
if (autoState.active) return;
|
|
2129
|
+
if (fastForwardState.active) return;
|
|
1383
2130
|
if (isAnyAutoBlockerActive()) return;
|
|
1384
2131
|
autoState.active = true;
|
|
1385
2132
|
tryInterrupt();
|
|
@@ -1397,12 +2144,58 @@ function createStage() {
|
|
|
1397
2144
|
function isAutoing() {
|
|
1398
2145
|
return autoState.active;
|
|
1399
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
|
+
}
|
|
1400
2184
|
function buildControl() {
|
|
1401
|
-
|
|
2185
|
+
const warpActive = warpState.active;
|
|
2186
|
+
let handled = warpActive;
|
|
1402
2187
|
let unskippableFlag = false;
|
|
2188
|
+
const inertRecordId = "__warp__";
|
|
1403
2189
|
const control = {
|
|
1404
2190
|
setWaiting(time, skippable) {
|
|
1405
2191
|
handled = true;
|
|
2192
|
+
if (warpActive) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (fastForwardState.active) {
|
|
2196
|
+
requestNextLine();
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
1406
2199
|
if (skipState.active) {
|
|
1407
2200
|
invokeSkipCallbacks();
|
|
1408
2201
|
scheduleSkipNextLine();
|
|
@@ -1419,6 +2212,13 @@ function createStage() {
|
|
|
1419
2212
|
},
|
|
1420
2213
|
hold() {
|
|
1421
2214
|
handled = true;
|
|
2215
|
+
if (warpActive) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
if (fastForwardState.active) {
|
|
2219
|
+
requestNextLine();
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
1422
2222
|
if (skipState.active && !unskippableFlag) {
|
|
1423
2223
|
if (isAnyBlockerActive()) {
|
|
1424
2224
|
stopSkip();
|
|
@@ -1437,12 +2237,21 @@ function createStage() {
|
|
|
1437
2237
|
},
|
|
1438
2238
|
nextLine() {
|
|
1439
2239
|
handled = true;
|
|
1440
|
-
|
|
2240
|
+
if (warpActive) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
requestNextLine();
|
|
1441
2244
|
},
|
|
1442
2245
|
unskippable() {
|
|
2246
|
+
if (warpActive) {
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
1443
2249
|
unskippableFlag = true;
|
|
1444
2250
|
},
|
|
1445
2251
|
record(meta) {
|
|
2252
|
+
if (warpActive) {
|
|
2253
|
+
return inertRecordId;
|
|
2254
|
+
}
|
|
1446
2255
|
const result = executePluginCommand("scenario", {
|
|
1447
2256
|
subCommand: "record",
|
|
1448
2257
|
meta
|
|
@@ -1481,19 +2290,19 @@ function createStage() {
|
|
|
1481
2290
|
cmd = transformCommand(e);
|
|
1482
2291
|
} catch (err) {
|
|
1483
2292
|
console.error("Failed to transform scenario command:", err);
|
|
1484
|
-
|
|
2293
|
+
requestNextLine();
|
|
1485
2294
|
return;
|
|
1486
2295
|
}
|
|
1487
2296
|
const handler = commandHandlers.get(cmd.command);
|
|
1488
2297
|
if (!handler) {
|
|
1489
2298
|
console.warn(`[Stage] No handler registered for command "${cmd.command}". Auto-advancing.`);
|
|
1490
|
-
|
|
2299
|
+
requestNextLine();
|
|
1491
2300
|
return;
|
|
1492
2301
|
}
|
|
1493
2302
|
const { control, wasHandled } = buildControl();
|
|
1494
2303
|
handler(cmd, control);
|
|
1495
2304
|
if (!wasHandled()) {
|
|
1496
|
-
|
|
2305
|
+
requestNextLine();
|
|
1497
2306
|
}
|
|
1498
2307
|
}
|
|
1499
2308
|
function dispatchTextLine(e) {
|
|
@@ -1502,7 +2311,7 @@ function createStage() {
|
|
|
1502
2311
|
handler(e, control);
|
|
1503
2312
|
}
|
|
1504
2313
|
if (!wasHandled()) {
|
|
1505
|
-
|
|
2314
|
+
requestNextLine();
|
|
1506
2315
|
}
|
|
1507
2316
|
}
|
|
1508
2317
|
function invokeSkipCallbacks() {
|
|
@@ -1550,48 +2359,52 @@ function createStage() {
|
|
|
1550
2359
|
startAuto,
|
|
1551
2360
|
stopAuto,
|
|
1552
2361
|
isAutoing,
|
|
2362
|
+
startFastForward,
|
|
2363
|
+
stopFastForward,
|
|
2364
|
+
isFastForwarding,
|
|
2365
|
+
resetRuntimeState,
|
|
1553
2366
|
bindEvents
|
|
1554
2367
|
};
|
|
1555
2368
|
}
|
|
1556
|
-
var StageContext = (0,
|
|
2369
|
+
var StageContext = (0, import_react5.createContext)(null);
|
|
1557
2370
|
function useStageContext() {
|
|
1558
|
-
const ctx = (0,
|
|
2371
|
+
const ctx = (0, import_react5.useContext)(StageContext);
|
|
1559
2372
|
if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
|
|
1560
2373
|
return ctx;
|
|
1561
2374
|
}
|
|
1562
2375
|
function StageContextProvider({ stage, children }) {
|
|
1563
|
-
(0,
|
|
2376
|
+
(0, import_react5.useEffect)(() => {
|
|
1564
2377
|
return stage.bindEvents();
|
|
1565
2378
|
}, [stage]);
|
|
1566
|
-
return (0,
|
|
2379
|
+
return (0, import_react5.createElement)(StageContext.Provider, { value: stage }, children);
|
|
1567
2380
|
}
|
|
1568
2381
|
function useSkipCallback(callback) {
|
|
1569
2382
|
const stage = useStageContext();
|
|
1570
|
-
(0,
|
|
2383
|
+
(0, import_react5.useEffect)(() => {
|
|
1571
2384
|
return stage.addSkipCallback(callback);
|
|
1572
2385
|
}, [stage, callback]);
|
|
1573
2386
|
}
|
|
1574
2387
|
function useInterruptCallback(callback) {
|
|
1575
2388
|
const stage = useStageContext();
|
|
1576
|
-
(0,
|
|
2389
|
+
(0, import_react5.useEffect)(() => {
|
|
1577
2390
|
return stage.addInterruptCallback(callback);
|
|
1578
2391
|
}, [stage, callback]);
|
|
1579
2392
|
}
|
|
1580
2393
|
function useSkipBlocker(callback) {
|
|
1581
2394
|
const stage = useStageContext();
|
|
1582
|
-
(0,
|
|
2395
|
+
(0, import_react5.useEffect)(() => {
|
|
1583
2396
|
return stage.addSkipBlocker(callback);
|
|
1584
2397
|
}, [stage, callback]);
|
|
1585
2398
|
}
|
|
1586
2399
|
function useAutoBlocker(callback) {
|
|
1587
2400
|
const stage = useStageContext();
|
|
1588
|
-
(0,
|
|
2401
|
+
(0, import_react5.useEffect)(() => {
|
|
1589
2402
|
return stage.addAutoBlocker(callback);
|
|
1590
2403
|
}, [stage, callback]);
|
|
1591
2404
|
}
|
|
1592
2405
|
function useAutoTicket() {
|
|
1593
2406
|
const stage = useStageContext();
|
|
1594
|
-
return (0,
|
|
2407
|
+
return (0, import_react5.useCallback)(
|
|
1595
2408
|
(options) => {
|
|
1596
2409
|
return stage.issueAutoTicket(options);
|
|
1597
2410
|
},
|
|
@@ -1600,7 +2413,7 @@ function useAutoTicket() {
|
|
|
1600
2413
|
}
|
|
1601
2414
|
function useBeforeHandleCommandCallback(callback) {
|
|
1602
2415
|
const stage = useStageContext();
|
|
1603
|
-
(0,
|
|
2416
|
+
(0, import_react5.useEffect)(() => {
|
|
1604
2417
|
return stage.addBeforeHandleCommandCallback(callback);
|
|
1605
2418
|
}, [stage, callback]);
|
|
1606
2419
|
}
|