@momoyu-ink/kit 0.5.5 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{jsx-dev-runtime-BKsP0hXU.d.mts → jsx-dev-runtime-CFlTWFj5.d.mts} +30 -0
- package/dist/{jsx-dev-runtime-BKsP0hXU.d.ts → jsx-dev-runtime-CFlTWFj5.d.ts} +30 -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 +889 -49
- package/dist/lib.d.mts +149 -50
- package/dist/lib.d.ts +149 -50
- package/dist/lib.mjs +880 -29
- package/package.json +1 -1
package/dist/lib.mjs
CHANGED
|
@@ -33,23 +33,33 @@ __export(lib_exports, {
|
|
|
33
33
|
createStaticNavigation: () => createStaticNavigation,
|
|
34
34
|
executeNodeCommand: () => executeNodeCommand,
|
|
35
35
|
executePluginCommand: () => executePluginCommand,
|
|
36
|
+
fastForwardState: () => fastForwardState,
|
|
37
|
+
getAppStateAdapter: () => getAppStateAdapter,
|
|
36
38
|
getNavigator: () => getNavigator,
|
|
39
|
+
getSeekingType: () => getSeekingType,
|
|
37
40
|
getStageSize: () => getStageSize,
|
|
38
41
|
globalEventListeners: () => globalEventListeners,
|
|
39
42
|
globalRequestAnimationFrameListeners: () => globalRequestAnimationFrameListeners,
|
|
40
43
|
moyu: () => moyu_exports,
|
|
41
44
|
nextLine: () => nextLine,
|
|
45
|
+
registerAppStateAdapter: () => registerAppStateAdapter,
|
|
42
46
|
setDefaultAutoTailMs: () => setDefaultAutoTailMs,
|
|
43
47
|
setWaiting: () => setWaiting,
|
|
44
48
|
skipState: () => skipState,
|
|
49
|
+
startDebugSession: () => startDebugSession,
|
|
50
|
+
startRuntimeDebugSession: () => startRuntimeDebugSession,
|
|
51
|
+
stopDebugSession: () => stopDebugSession,
|
|
52
|
+
stopRuntimeDebugSession: () => stopRuntimeDebugSession,
|
|
45
53
|
useAutoBlocker: () => useAutoBlocker,
|
|
46
54
|
useAutoTicket: () => useAutoTicket,
|
|
47
55
|
useBeforeHandleCommandCallback: () => useBeforeHandleCommandCallback,
|
|
56
|
+
useDebugSession: () => useDebugSession,
|
|
48
57
|
useFadeIn: () => useFadeIn,
|
|
49
58
|
useFadeInOut: () => useFadeInOut,
|
|
50
59
|
useFadeOut: () => useFadeOut,
|
|
51
60
|
useInterruptCallback: () => useInterruptCallback,
|
|
52
61
|
useIsAutoing: () => useIsAutoing,
|
|
62
|
+
useIsSeeking: () => useIsSeeking,
|
|
53
63
|
useIsSkipping: () => useIsSkipping,
|
|
54
64
|
useNavigation: () => useNavigation,
|
|
55
65
|
useNavigationParams: () => useNavigationParams,
|
|
@@ -58,7 +68,8 @@ __export(lib_exports, {
|
|
|
58
68
|
useSkipBlocker: () => useSkipBlocker,
|
|
59
69
|
useSkipCallback: () => useSkipCallback,
|
|
60
70
|
useSoundEffect: () => useSoundEffect,
|
|
61
|
-
useStageContext: () => useStageContext
|
|
71
|
+
useStageContext: () => useStageContext,
|
|
72
|
+
warpState: () => warpState
|
|
62
73
|
});
|
|
63
74
|
|
|
64
75
|
// src/react.ts
|
|
@@ -384,8 +395,35 @@ function createRoot(options) {
|
|
|
384
395
|
|
|
385
396
|
// src/sandbox.ts
|
|
386
397
|
var JS_GLOBAL_KEYS = new Set(Object.getOwnPropertyNames(globalThis));
|
|
387
|
-
var JS_GLOBAL_KEYS_EXCLUDED = /* @__PURE__ */ new Set(["window", "globalThis", "ARCHIVE", "GLOBAL"]);
|
|
388
|
-
JS_GLOBAL_KEYS_EXCLUDED.forEach((key) =>
|
|
398
|
+
var JS_GLOBAL_KEYS_EXCLUDED = /* @__PURE__ */ new Set(["window", "globalThis", "LOCAL", "ARCHIVE", "GLOBAL"]);
|
|
399
|
+
JS_GLOBAL_KEYS_EXCLUDED.forEach((key) => {
|
|
400
|
+
JS_GLOBAL_KEYS.delete(key);
|
|
401
|
+
});
|
|
402
|
+
var localVariables = new Proxy(
|
|
403
|
+
{},
|
|
404
|
+
{
|
|
405
|
+
get(_, key) {
|
|
406
|
+
if (typeof key === "string") {
|
|
407
|
+
return executePluginCommand("scenario", {
|
|
408
|
+
subCommand: "getLocalVariable",
|
|
409
|
+
name: key
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return void 0;
|
|
413
|
+
},
|
|
414
|
+
set(_, key, value) {
|
|
415
|
+
if (typeof key === "string") {
|
|
416
|
+
executePluginCommand("scenario", {
|
|
417
|
+
subCommand: "setLocalVariable",
|
|
418
|
+
name: key,
|
|
419
|
+
value
|
|
420
|
+
});
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
);
|
|
389
427
|
var archiveVariables = new Proxy(
|
|
390
428
|
{},
|
|
391
429
|
{
|
|
@@ -437,15 +475,16 @@ var globalVariables = new Proxy(
|
|
|
437
475
|
}
|
|
438
476
|
);
|
|
439
477
|
function safeEval(code, sandbox) {
|
|
440
|
-
const
|
|
478
|
+
const proxy4 = new Proxy(sandbox, {
|
|
441
479
|
has(target, key) {
|
|
442
480
|
if (typeof key === "string" && JS_GLOBAL_KEYS.has(key)) {
|
|
443
481
|
return false;
|
|
444
482
|
}
|
|
445
|
-
return key in target || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
483
|
+
return key in target || key === "LOCAL" || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
446
484
|
},
|
|
447
485
|
get(target, key) {
|
|
448
|
-
if (key === "window" || key === "globalThis") return
|
|
486
|
+
if (key === "window" || key === "globalThis") return proxy4;
|
|
487
|
+
if (key === "LOCAL") return localVariables;
|
|
449
488
|
if (key === "ARCHIVE") return archiveVariables;
|
|
450
489
|
if (key === "GLOBAL") return globalVariables;
|
|
451
490
|
if (key === Symbol.unscopables) return void 0;
|
|
@@ -456,7 +495,7 @@ function safeEval(code, sandbox) {
|
|
|
456
495
|
return true;
|
|
457
496
|
}
|
|
458
497
|
});
|
|
459
|
-
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(
|
|
498
|
+
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(proxy4)(proxy4);
|
|
460
499
|
}
|
|
461
500
|
globalThis.__moyu_eval_sandbox = (code) => {
|
|
462
501
|
try {
|
|
@@ -639,9 +678,9 @@ function getStageSize() {
|
|
|
639
678
|
}
|
|
640
679
|
|
|
641
680
|
// src/components/navigation.tsx
|
|
642
|
-
import React2 from "react";
|
|
643
681
|
import { createContext, useContext } from "react";
|
|
644
682
|
import { proxy, useSnapshot } from "valtio";
|
|
683
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
645
684
|
var PageParamsContext = createContext(void 0);
|
|
646
685
|
var OverlayParamsContext = createContext(void 0);
|
|
647
686
|
var overlayIdCounter = 0;
|
|
@@ -697,6 +736,9 @@ function createStackNavigator(options) {
|
|
|
697
736
|
delete navigationState.pageParams[page];
|
|
698
737
|
}
|
|
699
738
|
},
|
|
739
|
+
hasPage: (page) => {
|
|
740
|
+
return normalizedPages[page] !== void 0;
|
|
741
|
+
},
|
|
700
742
|
goBack: () => {
|
|
701
743
|
console.warn("goBack() not implemented yet");
|
|
702
744
|
},
|
|
@@ -752,11 +794,14 @@ function createStaticNavigation(navigator) {
|
|
|
752
794
|
const { pages, overlays } = navigator._descriptors;
|
|
753
795
|
const pageDescriptor = pages[navState.currentPage];
|
|
754
796
|
const Page = pageDescriptor?.component || (() => null);
|
|
755
|
-
return /* @__PURE__ */
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
797
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
798
|
+
/* @__PURE__ */ jsx(PageParamsContext.Provider, { value: navState.pageParams[navState.currentPage], children: /* @__PURE__ */ jsx(Page, {}) }),
|
|
799
|
+
navState.overlayStack.map((overlay) => {
|
|
800
|
+
const overlayDescriptor = overlays[overlay.type];
|
|
801
|
+
const OverlayComponent = overlayDescriptor?.component || (() => null);
|
|
802
|
+
return /* @__PURE__ */ jsx(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ jsx(OverlayComponent, {}) }, overlay.id);
|
|
803
|
+
})
|
|
804
|
+
] });
|
|
760
805
|
};
|
|
761
806
|
}
|
|
762
807
|
function getNavigator() {
|
|
@@ -775,6 +820,624 @@ function useNavigationParams() {
|
|
|
775
820
|
return overlayParams ?? pageParams ?? {};
|
|
776
821
|
}
|
|
777
822
|
|
|
823
|
+
// src/debug/session.ts
|
|
824
|
+
import { useSnapshot as useSnapshot2, proxy as proxy2 } from "valtio";
|
|
825
|
+
var debugState = proxy2({
|
|
826
|
+
active: false,
|
|
827
|
+
checkpoints: {},
|
|
828
|
+
currentCursor: null,
|
|
829
|
+
restoring: false,
|
|
830
|
+
lastError: null
|
|
831
|
+
});
|
|
832
|
+
var appStateAdapter = null;
|
|
833
|
+
var currentConfig = {
|
|
834
|
+
onMarkerEnter: () => {
|
|
835
|
+
},
|
|
836
|
+
onError: () => {
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
var disposeMarkerListener = null;
|
|
840
|
+
var disposeFinishedListener = null;
|
|
841
|
+
var disposeWarpFinishedListener = null;
|
|
842
|
+
var debugOperationQueue = Promise.resolve();
|
|
843
|
+
var activeFastForward = null;
|
|
844
|
+
var activeWarp = null;
|
|
845
|
+
function enqueueDebugOperation(task) {
|
|
846
|
+
const nextTask = debugOperationQueue.then(task, task);
|
|
847
|
+
debugOperationQueue = nextTask.then(
|
|
848
|
+
() => void 0,
|
|
849
|
+
() => void 0
|
|
850
|
+
);
|
|
851
|
+
return nextTask;
|
|
852
|
+
}
|
|
853
|
+
function executeScenarioCommand(payload) {
|
|
854
|
+
return Promise.resolve(executePluginCommand("scenario", payload));
|
|
855
|
+
}
|
|
856
|
+
function toErrorMessage(error) {
|
|
857
|
+
if (error instanceof Error) {
|
|
858
|
+
return error.message;
|
|
859
|
+
}
|
|
860
|
+
return String(error);
|
|
861
|
+
}
|
|
862
|
+
function isPromiseLike(value) {
|
|
863
|
+
return !!value && typeof value.then === "function";
|
|
864
|
+
}
|
|
865
|
+
function reportDebugSessionError(error) {
|
|
866
|
+
debugState.lastError = toErrorMessage(error);
|
|
867
|
+
void Promise.resolve(currentConfig.onError(error));
|
|
868
|
+
}
|
|
869
|
+
function clearLocalRuntimeState() {
|
|
870
|
+
debugState.checkpoints = {};
|
|
871
|
+
debugState.currentCursor = null;
|
|
872
|
+
debugState.restoring = false;
|
|
873
|
+
debugState.lastError = null;
|
|
874
|
+
}
|
|
875
|
+
async function doSettleActiveSeek(op, clearSlot, error) {
|
|
876
|
+
if (!op) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
clearSlot();
|
|
880
|
+
try {
|
|
881
|
+
await appStateAdapter?.exitFastForwardMode?.();
|
|
882
|
+
} catch (exitError) {
|
|
883
|
+
if (error === void 0) {
|
|
884
|
+
error = exitError;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (error === void 0) {
|
|
888
|
+
op.resolve();
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
op.reject(error);
|
|
892
|
+
}
|
|
893
|
+
async function settleActiveFastForward(error) {
|
|
894
|
+
await doSettleActiveSeek(activeFastForward, () => {
|
|
895
|
+
activeFastForward = null;
|
|
896
|
+
}, error);
|
|
897
|
+
}
|
|
898
|
+
async function settleActiveWarp(error) {
|
|
899
|
+
await doSettleActiveSeek(activeWarp, () => {
|
|
900
|
+
activeWarp = null;
|
|
901
|
+
}, error);
|
|
902
|
+
}
|
|
903
|
+
async function clearRemoteCheckpoints() {
|
|
904
|
+
await executeScenarioCommand({ subCommand: "clearCheckpoints" });
|
|
905
|
+
}
|
|
906
|
+
async function syncLocalWarpCheckpoint(targetMarkerId) {
|
|
907
|
+
const cursor = await executeScenarioCommand({
|
|
908
|
+
subCommand: "getExecutionCursor"
|
|
909
|
+
});
|
|
910
|
+
const checkpointCursor = cursor ? {
|
|
911
|
+
story: cursor.story,
|
|
912
|
+
paragraph: cursor.paragraph,
|
|
913
|
+
markerId: targetMarkerId
|
|
914
|
+
} : debugState.currentCursor ? {
|
|
915
|
+
story: debugState.currentCursor.story,
|
|
916
|
+
paragraph: debugState.currentCursor.paragraph,
|
|
917
|
+
markerId: targetMarkerId
|
|
918
|
+
} : null;
|
|
919
|
+
if (!checkpointCursor) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const checkpoint = {
|
|
923
|
+
cursor: checkpointCursor,
|
|
924
|
+
appState: appStateAdapter ? await appStateAdapter.capture() : void 0
|
|
925
|
+
};
|
|
926
|
+
debugState.currentCursor = checkpointCursor;
|
|
927
|
+
debugState.checkpoints[targetMarkerId] = checkpoint;
|
|
928
|
+
await currentConfig.onMarkerEnter(checkpoint);
|
|
929
|
+
}
|
|
930
|
+
function captureCheckpointAppStateSync() {
|
|
931
|
+
if (!appStateAdapter) {
|
|
932
|
+
return void 0;
|
|
933
|
+
}
|
|
934
|
+
const appState = appStateAdapter.capture();
|
|
935
|
+
if (isPromiseLike(appState)) {
|
|
936
|
+
throw new Error("Async app state capture is not supported for debug checkpoints");
|
|
937
|
+
}
|
|
938
|
+
return appState;
|
|
939
|
+
}
|
|
940
|
+
function notifyMarkerEnter(checkpoint) {
|
|
941
|
+
try {
|
|
942
|
+
const result = currentConfig.onMarkerEnter(checkpoint);
|
|
943
|
+
if (isPromiseLike(result)) {
|
|
944
|
+
void result.catch(reportDebugSessionError);
|
|
945
|
+
}
|
|
946
|
+
} catch (error) {
|
|
947
|
+
reportDebugSessionError(error);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
async function prepareJumpStart(markerId) {
|
|
951
|
+
const checkpoint = debugState.checkpoints[markerId];
|
|
952
|
+
if (checkpoint) {
|
|
953
|
+
const restored2 = await executeScenarioCommand({
|
|
954
|
+
subCommand: "restoreCheckpoint",
|
|
955
|
+
key: markerId
|
|
956
|
+
});
|
|
957
|
+
if (!restored2) {
|
|
958
|
+
throw new Error(`Failed to restore checkpoint ${markerId}`);
|
|
959
|
+
}
|
|
960
|
+
if (appStateAdapter) {
|
|
961
|
+
await appStateAdapter.restore(checkpoint.appState);
|
|
962
|
+
}
|
|
963
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
const lookup = await executeScenarioCommand({
|
|
967
|
+
subCommand: "getFastForwardCheckpoint",
|
|
968
|
+
key: markerId
|
|
969
|
+
});
|
|
970
|
+
if (!lookup.markerExists) {
|
|
971
|
+
throw new Error(`Marker ${markerId} is not available in the current story`);
|
|
972
|
+
}
|
|
973
|
+
const startCheckpointKey = lookup.checkpointKey ?? null;
|
|
974
|
+
if (!startCheckpointKey) {
|
|
975
|
+
if (!appStateAdapter?.restartCurrentStoryFromHead) {
|
|
976
|
+
throw new Error("Restarting the current story from head is not supported by the app state adapter");
|
|
977
|
+
}
|
|
978
|
+
await appStateAdapter.restartCurrentStoryFromHead();
|
|
979
|
+
debugState.checkpoints = {};
|
|
980
|
+
debugState.currentCursor = null;
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
const startCheckpoint = debugState.checkpoints[startCheckpointKey];
|
|
984
|
+
if (!startCheckpoint) {
|
|
985
|
+
throw new Error(`Local checkpoint ${startCheckpointKey} not found`);
|
|
986
|
+
}
|
|
987
|
+
const restored = await executeScenarioCommand({
|
|
988
|
+
subCommand: "restoreCheckpoint",
|
|
989
|
+
key: startCheckpointKey
|
|
990
|
+
});
|
|
991
|
+
if (!restored) {
|
|
992
|
+
throw new Error(`Failed to restore checkpoint ${startCheckpointKey}`);
|
|
993
|
+
}
|
|
994
|
+
if (appStateAdapter) {
|
|
995
|
+
await appStateAdapter.restore(startCheckpoint.appState);
|
|
996
|
+
}
|
|
997
|
+
debugState.currentCursor = startCheckpoint.cursor;
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
function handleMarkerEnter(marker) {
|
|
1001
|
+
const checkpoint = {
|
|
1002
|
+
cursor: {
|
|
1003
|
+
story: marker.story,
|
|
1004
|
+
paragraph: marker.paragraph,
|
|
1005
|
+
markerId: marker.markerId
|
|
1006
|
+
},
|
|
1007
|
+
appState: captureCheckpointAppStateSync()
|
|
1008
|
+
};
|
|
1009
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
1010
|
+
debugState.checkpoints[marker.markerId] = checkpoint;
|
|
1011
|
+
notifyMarkerEnter(checkpoint);
|
|
1012
|
+
if (activeFastForward?.targetMarkerId === marker.markerId) {
|
|
1013
|
+
void settleActiveFastForward();
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
async function runFastForwardToMarker(markerId) {
|
|
1017
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1018
|
+
throw new Error("Fast-forward mode is not supported by the app state adapter");
|
|
1019
|
+
}
|
|
1020
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1021
|
+
throw new Error("A debug seek operation is already active");
|
|
1022
|
+
}
|
|
1023
|
+
await appStateAdapter.enterFastForwardMode({
|
|
1024
|
+
onAbort: (error) => {
|
|
1025
|
+
void settleActiveFastForward(error);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
const completion = new Promise((resolve, reject) => {
|
|
1029
|
+
activeFastForward = {
|
|
1030
|
+
targetMarkerId: markerId,
|
|
1031
|
+
resolve,
|
|
1032
|
+
reject
|
|
1033
|
+
};
|
|
1034
|
+
});
|
|
1035
|
+
try {
|
|
1036
|
+
await executeScenarioCommand({ subCommand: "nextLine" });
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
await settleActiveFastForward(error);
|
|
1039
|
+
}
|
|
1040
|
+
await completion;
|
|
1041
|
+
}
|
|
1042
|
+
async function runWarp(options) {
|
|
1043
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1044
|
+
throw new Error("Warp mode is not supported by the app state adapter");
|
|
1045
|
+
}
|
|
1046
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1047
|
+
throw new Error("A debug seek operation is already active");
|
|
1048
|
+
}
|
|
1049
|
+
const boundary = options.boundary ?? "before";
|
|
1050
|
+
await appStateAdapter.enterFastForwardMode({ warp: true });
|
|
1051
|
+
const completion = new Promise((resolve, reject) => {
|
|
1052
|
+
activeWarp = {
|
|
1053
|
+
targetMarkerId: options.markerId,
|
|
1054
|
+
resolve,
|
|
1055
|
+
reject
|
|
1056
|
+
};
|
|
1057
|
+
});
|
|
1058
|
+
try {
|
|
1059
|
+
await executeScenarioCommand({
|
|
1060
|
+
subCommand: "warp",
|
|
1061
|
+
markerId: options.markerId,
|
|
1062
|
+
boundary
|
|
1063
|
+
});
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
await settleActiveWarp(error);
|
|
1066
|
+
}
|
|
1067
|
+
await completion;
|
|
1068
|
+
}
|
|
1069
|
+
var debugSessionController = {
|
|
1070
|
+
async restoreCheckpoint(markerId) {
|
|
1071
|
+
debugState.restoring = true;
|
|
1072
|
+
debugState.lastError = null;
|
|
1073
|
+
try {
|
|
1074
|
+
const needsFastForward = await enqueueDebugOperation(async () => prepareJumpStart(markerId));
|
|
1075
|
+
if (needsFastForward) {
|
|
1076
|
+
await runFastForwardToMarker(markerId);
|
|
1077
|
+
}
|
|
1078
|
+
return true;
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
debugState.lastError = toErrorMessage(error);
|
|
1081
|
+
throw error;
|
|
1082
|
+
} finally {
|
|
1083
|
+
debugState.restoring = false;
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
async warp(options) {
|
|
1087
|
+
debugState.restoring = true;
|
|
1088
|
+
debugState.lastError = null;
|
|
1089
|
+
try {
|
|
1090
|
+
const needsWarp = await enqueueDebugOperation(async () => prepareJumpStart(options.markerId));
|
|
1091
|
+
if (needsWarp) {
|
|
1092
|
+
await runWarp(options);
|
|
1093
|
+
}
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
debugState.lastError = toErrorMessage(error);
|
|
1096
|
+
throw error;
|
|
1097
|
+
} finally {
|
|
1098
|
+
debugState.restoring = false;
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
async switchPage(page, params) {
|
|
1102
|
+
return enqueueDebugOperation(async () => {
|
|
1103
|
+
debugState.lastError = null;
|
|
1104
|
+
try {
|
|
1105
|
+
if (!appStateAdapter?.switchPage) {
|
|
1106
|
+
throw new Error("Page switching is not supported by the app state adapter");
|
|
1107
|
+
}
|
|
1108
|
+
await appStateAdapter.switchPage(page, params);
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
debugState.lastError = toErrorMessage(error);
|
|
1111
|
+
throw error;
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
function registerAppStateAdapter(adapter) {
|
|
1117
|
+
appStateAdapter = adapter;
|
|
1118
|
+
}
|
|
1119
|
+
function getAppStateAdapter() {
|
|
1120
|
+
return appStateAdapter;
|
|
1121
|
+
}
|
|
1122
|
+
async function startDebugSession(config = {}) {
|
|
1123
|
+
await stopDebugSession();
|
|
1124
|
+
currentConfig = {
|
|
1125
|
+
onMarkerEnter: config.onMarkerEnter ?? (() => {
|
|
1126
|
+
}),
|
|
1127
|
+
onError: config.onError ?? (() => {
|
|
1128
|
+
})
|
|
1129
|
+
};
|
|
1130
|
+
clearLocalRuntimeState();
|
|
1131
|
+
debugState.active = true;
|
|
1132
|
+
disposeMarkerListener = addEventListener("scenariomarkerenter", (event) => {
|
|
1133
|
+
const marker = event;
|
|
1134
|
+
try {
|
|
1135
|
+
handleMarkerEnter(marker);
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
reportDebugSessionError(error);
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
disposeFinishedListener = addEventListener("scenariofinished", () => {
|
|
1141
|
+
debugState.currentCursor = null;
|
|
1142
|
+
if (activeFastForward) {
|
|
1143
|
+
void settleActiveFastForward(
|
|
1144
|
+
new Error(`Marker ${activeFastForward.targetMarkerId} was not reached before the scenario finished`)
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
if (activeWarp) {
|
|
1148
|
+
const targetMarkerId = activeWarp.targetMarkerId;
|
|
1149
|
+
void settleActiveWarp(new Error(`Marker ${targetMarkerId} was not reached before the scenario finished`));
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
disposeWarpFinishedListener = addEventListener("scenariowarpfinished", () => {
|
|
1153
|
+
const warp = activeWarp;
|
|
1154
|
+
if (!warp) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
void enqueueDebugOperation(async () => {
|
|
1158
|
+
try {
|
|
1159
|
+
await syncLocalWarpCheckpoint(warp.targetMarkerId);
|
|
1160
|
+
await settleActiveWarp();
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
debugState.lastError = toErrorMessage(error);
|
|
1163
|
+
await settleActiveWarp(error);
|
|
1164
|
+
await currentConfig.onError(error);
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
try {
|
|
1169
|
+
await clearRemoteCheckpoints();
|
|
1170
|
+
debugState.currentCursor = await executeScenarioCommand({
|
|
1171
|
+
subCommand: "getExecutionCursor"
|
|
1172
|
+
});
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
debugState.active = false;
|
|
1175
|
+
clearLocalRuntimeState();
|
|
1176
|
+
disposeMarkerListener?.();
|
|
1177
|
+
disposeFinishedListener?.();
|
|
1178
|
+
disposeWarpFinishedListener?.();
|
|
1179
|
+
disposeMarkerListener = null;
|
|
1180
|
+
disposeFinishedListener = null;
|
|
1181
|
+
disposeWarpFinishedListener = null;
|
|
1182
|
+
await currentConfig.onError(error);
|
|
1183
|
+
throw error;
|
|
1184
|
+
}
|
|
1185
|
+
return debugSessionController;
|
|
1186
|
+
}
|
|
1187
|
+
async function stopDebugSession() {
|
|
1188
|
+
disposeMarkerListener?.();
|
|
1189
|
+
disposeFinishedListener?.();
|
|
1190
|
+
disposeWarpFinishedListener?.();
|
|
1191
|
+
disposeMarkerListener = null;
|
|
1192
|
+
disposeFinishedListener = null;
|
|
1193
|
+
disposeWarpFinishedListener = null;
|
|
1194
|
+
await settleActiveFastForward(new Error("Debug session stopped"));
|
|
1195
|
+
await settleActiveWarp(new Error("Debug session stopped"));
|
|
1196
|
+
if (debugState.active) {
|
|
1197
|
+
await enqueueDebugOperation(async () => {
|
|
1198
|
+
try {
|
|
1199
|
+
await clearRemoteCheckpoints();
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
debugState.lastError = toErrorMessage(error);
|
|
1202
|
+
await currentConfig.onError(error);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
debugState.active = false;
|
|
1207
|
+
clearLocalRuntimeState();
|
|
1208
|
+
}
|
|
1209
|
+
function clearDebugSessionRuntimeState() {
|
|
1210
|
+
clearLocalRuntimeState();
|
|
1211
|
+
void settleActiveFastForward(new Error("Debug session runtime state cleared"));
|
|
1212
|
+
void settleActiveWarp(new Error("Debug session runtime state cleared"));
|
|
1213
|
+
if (!debugState.active) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
void enqueueDebugOperation(async () => {
|
|
1217
|
+
try {
|
|
1218
|
+
await clearRemoteCheckpoints();
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
debugState.lastError = toErrorMessage(error);
|
|
1221
|
+
await currentConfig.onError(error);
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
function useDebugSession() {
|
|
1226
|
+
const snapshot = useSnapshot2(debugState);
|
|
1227
|
+
return {
|
|
1228
|
+
...snapshot,
|
|
1229
|
+
startDebugSession,
|
|
1230
|
+
stopDebugSession,
|
|
1231
|
+
restoreCheckpoint: debugSessionController.restoreCheckpoint,
|
|
1232
|
+
warp: debugSessionController.warp,
|
|
1233
|
+
switchPage: debugSessionController.switchPage
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// src/debug/runtime.ts
|
|
1238
|
+
var currentConnection = null;
|
|
1239
|
+
var currentController = null;
|
|
1240
|
+
var bufferedRequestMessages = [];
|
|
1241
|
+
function getRuntimeWebSocket() {
|
|
1242
|
+
return globalThis.WebSocket ?? null;
|
|
1243
|
+
}
|
|
1244
|
+
function isJumpRequestMessage(message) {
|
|
1245
|
+
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");
|
|
1246
|
+
}
|
|
1247
|
+
function isRouteRequestMessage(message) {
|
|
1248
|
+
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));
|
|
1249
|
+
}
|
|
1250
|
+
function sendRuntimeDebugMessage(socket, message) {
|
|
1251
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1252
|
+
if (!RuntimeWebSocket || socket.readyState !== RuntimeWebSocket.OPEN) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
socket.send(JSON.stringify(message));
|
|
1256
|
+
}
|
|
1257
|
+
function clearBufferedRequestMessages() {
|
|
1258
|
+
bufferedRequestMessages = [];
|
|
1259
|
+
}
|
|
1260
|
+
function bufferRequestMessage(message) {
|
|
1261
|
+
bufferedRequestMessages.push(message);
|
|
1262
|
+
}
|
|
1263
|
+
function flushBufferedRequestMessages(socket, sessionId) {
|
|
1264
|
+
const messages = bufferedRequestMessages;
|
|
1265
|
+
bufferedRequestMessages = [];
|
|
1266
|
+
for (const message of messages) {
|
|
1267
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function handleRuntimeDebugRequest(socket, sessionId, message) {
|
|
1271
|
+
const controller = currentController;
|
|
1272
|
+
if (!controller) {
|
|
1273
|
+
bufferRequestMessage(message);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (isJumpRequestMessage(message)) {
|
|
1277
|
+
const runJump = message.strategy === "warp" ? controller.warp({
|
|
1278
|
+
markerId: message.markerId,
|
|
1279
|
+
boundary: message.boundary
|
|
1280
|
+
}).then(() => true) : controller.restoreCheckpoint(message.markerId);
|
|
1281
|
+
void runJump.then((restored) => {
|
|
1282
|
+
if (!restored) {
|
|
1283
|
+
sendRuntimeDebugMessage(socket, {
|
|
1284
|
+
type: "jump:error",
|
|
1285
|
+
sessionId,
|
|
1286
|
+
requestId: message.requestId,
|
|
1287
|
+
markerId: message.markerId,
|
|
1288
|
+
error: "Checkpoint not found"
|
|
1289
|
+
});
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
sendRuntimeDebugMessage(socket, {
|
|
1293
|
+
type: "jump:done",
|
|
1294
|
+
sessionId,
|
|
1295
|
+
requestId: message.requestId,
|
|
1296
|
+
markerId: message.markerId
|
|
1297
|
+
});
|
|
1298
|
+
}).catch((error) => {
|
|
1299
|
+
sendRuntimeDebugMessage(socket, {
|
|
1300
|
+
type: "jump:error",
|
|
1301
|
+
sessionId,
|
|
1302
|
+
requestId: message.requestId,
|
|
1303
|
+
markerId: message.markerId,
|
|
1304
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1305
|
+
});
|
|
1306
|
+
});
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
void controller.switchPage(message.page, message.params).then(() => {
|
|
1310
|
+
sendRuntimeDebugMessage(socket, {
|
|
1311
|
+
type: "route:done",
|
|
1312
|
+
sessionId,
|
|
1313
|
+
requestId: message.requestId,
|
|
1314
|
+
page: message.page
|
|
1315
|
+
});
|
|
1316
|
+
}).catch((error) => {
|
|
1317
|
+
sendRuntimeDebugMessage(socket, {
|
|
1318
|
+
type: "route:error",
|
|
1319
|
+
sessionId,
|
|
1320
|
+
requestId: message.requestId,
|
|
1321
|
+
page: message.page,
|
|
1322
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1323
|
+
});
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
async function readDebugParams() {
|
|
1327
|
+
const rawParams = await executePluginCommand("system", {
|
|
1328
|
+
subCommand: "getParams"
|
|
1329
|
+
});
|
|
1330
|
+
if (!rawParams) {
|
|
1331
|
+
return {};
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
const parsed = JSON.parse(rawParams);
|
|
1335
|
+
return {
|
|
1336
|
+
debug: parsed.debug === true,
|
|
1337
|
+
debugSessionId: typeof parsed.debugSessionId === "string" ? parsed.debugSessionId : void 0,
|
|
1338
|
+
debugWsUrl: typeof parsed.debugWsUrl === "string" ? parsed.debugWsUrl : void 0
|
|
1339
|
+
};
|
|
1340
|
+
} catch {
|
|
1341
|
+
return {};
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
async function stopRuntimeDebugSession() {
|
|
1345
|
+
const connection = currentConnection;
|
|
1346
|
+
currentConnection = null;
|
|
1347
|
+
currentController = null;
|
|
1348
|
+
clearBufferedRequestMessages();
|
|
1349
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1350
|
+
await stopDebugSession();
|
|
1351
|
+
if (!connection) {
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (RuntimeWebSocket && (connection.socket.readyState === RuntimeWebSocket.OPEN || connection.socket.readyState === RuntimeWebSocket.CONNECTING)) {
|
|
1355
|
+
connection.socket.close();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
async function startRuntimeDebugSession() {
|
|
1359
|
+
await stopRuntimeDebugSession();
|
|
1360
|
+
const params = await readDebugParams();
|
|
1361
|
+
if (params.debug !== true || !params.debugSessionId || !params.debugWsUrl) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1365
|
+
if (!RuntimeWebSocket) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
const socket = new RuntimeWebSocket(params.debugWsUrl);
|
|
1369
|
+
const sessionId = params.debugSessionId;
|
|
1370
|
+
currentConnection = { sessionId, socket };
|
|
1371
|
+
socket.onopen = () => {
|
|
1372
|
+
if (currentConnection?.socket !== socket) {
|
|
1373
|
+
socket.close();
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
void startDebugSession({
|
|
1377
|
+
onMarkerEnter(checkpoint) {
|
|
1378
|
+
if (!checkpoint.cursor.markerId) {
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
sendRuntimeDebugMessage(socket, {
|
|
1382
|
+
type: "marker:enter",
|
|
1383
|
+
sessionId,
|
|
1384
|
+
markerId: checkpoint.cursor.markerId,
|
|
1385
|
+
story: checkpoint.cursor.story,
|
|
1386
|
+
paragraph: checkpoint.cursor.paragraph
|
|
1387
|
+
});
|
|
1388
|
+
},
|
|
1389
|
+
onError(error) {
|
|
1390
|
+
console.error("[debug] runtime debug session error:", error);
|
|
1391
|
+
}
|
|
1392
|
+
}).then((controller) => {
|
|
1393
|
+
currentController = controller;
|
|
1394
|
+
flushBufferedRequestMessages(socket, sessionId);
|
|
1395
|
+
}).catch((error) => {
|
|
1396
|
+
console.error("[debug] failed to start runtime debug session:", error);
|
|
1397
|
+
socket.close();
|
|
1398
|
+
});
|
|
1399
|
+
};
|
|
1400
|
+
socket.onclose = () => {
|
|
1401
|
+
if (currentConnection?.socket !== socket) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
currentConnection = null;
|
|
1405
|
+
currentController = null;
|
|
1406
|
+
clearBufferedRequestMessages();
|
|
1407
|
+
void stopDebugSession();
|
|
1408
|
+
};
|
|
1409
|
+
socket.onerror = (event) => {
|
|
1410
|
+
if (currentConnection?.socket !== socket) {
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
console.error("[debug] runtime websocket error:", event);
|
|
1414
|
+
};
|
|
1415
|
+
socket.onmessage = (event) => {
|
|
1416
|
+
if (currentConnection?.socket !== socket) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
try {
|
|
1420
|
+
const message = JSON.parse(String(event.data));
|
|
1421
|
+
if (message.sessionId && message.sessionId !== sessionId) {
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
if (isJumpRequestMessage(message)) {
|
|
1425
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
if (isRouteRequestMessage(message)) {
|
|
1429
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
if (message.type && message.type !== "session:init") {
|
|
1433
|
+
console.debug("[debug] runtime received message:", message);
|
|
1434
|
+
}
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
console.error("[debug] failed to parse runtime debug message:", error);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
|
|
778
1441
|
// src/hooks/useFadeInOut.ts
|
|
779
1442
|
function useFadeInOut(inTime, _keepTime, _outTime, pause = false, onFinished) {
|
|
780
1443
|
const [style, api] = (0, spring_exports.useSpring)(
|
|
@@ -877,11 +1540,13 @@ async function terminateCurrentScenario() {
|
|
|
877
1540
|
await executePluginCommand("scenario", {
|
|
878
1541
|
subCommand: "terminateStory"
|
|
879
1542
|
});
|
|
1543
|
+
clearDebugSessionRuntimeState();
|
|
880
1544
|
} catch (error) {
|
|
881
1545
|
console.error("Failed to terminate scenario:", error);
|
|
882
1546
|
}
|
|
883
1547
|
}
|
|
884
1548
|
async function startScenarioSession(session, config) {
|
|
1549
|
+
clearDebugSessionRuntimeState();
|
|
885
1550
|
for (const story of config.stories) {
|
|
886
1551
|
if (!isCurrentScenarioSession(session)) return;
|
|
887
1552
|
try {
|
|
@@ -1048,18 +1713,30 @@ function useSoundEffect(src) {
|
|
|
1048
1713
|
|
|
1049
1714
|
// src/hooks/useStage.ts
|
|
1050
1715
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, createElement, useCallback as useCallback2 } from "react";
|
|
1051
|
-
import { proxy as
|
|
1716
|
+
import { proxy as proxy3, useSnapshot as useSnapshot3 } from "valtio";
|
|
1052
1717
|
var SKIP_INTERVAL_MS = 80;
|
|
1718
|
+
var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
|
|
1719
|
+
var DEFAULT_FAST_FORWARD_TIME_LIMIT_MS = 3e3;
|
|
1053
1720
|
var autoRuntime = {
|
|
1054
1721
|
defaultTailMs: 0
|
|
1055
1722
|
};
|
|
1056
|
-
var skipState =
|
|
1057
|
-
var autoState =
|
|
1723
|
+
var skipState = proxy3({ active: false });
|
|
1724
|
+
var autoState = proxy3({ active: false });
|
|
1725
|
+
var fastForwardState = proxy3({ active: false });
|
|
1726
|
+
var warpState = proxy3({ active: false });
|
|
1058
1727
|
function useIsSkipping() {
|
|
1059
|
-
return
|
|
1728
|
+
return useSnapshot3(skipState).active;
|
|
1060
1729
|
}
|
|
1061
1730
|
function useIsAutoing() {
|
|
1062
|
-
return
|
|
1731
|
+
return useSnapshot3(autoState).active;
|
|
1732
|
+
}
|
|
1733
|
+
function useIsSeeking() {
|
|
1734
|
+
return useSnapshot3(fastForwardState).active;
|
|
1735
|
+
}
|
|
1736
|
+
function getSeekingType() {
|
|
1737
|
+
if (warpState.active) return "warp";
|
|
1738
|
+
if (fastForwardState.active) return "fast-forward";
|
|
1739
|
+
return null;
|
|
1063
1740
|
}
|
|
1064
1741
|
function setDefaultAutoTailMs(ms) {
|
|
1065
1742
|
if (!Number.isFinite(ms)) {
|
|
@@ -1167,6 +1844,101 @@ function createStage() {
|
|
|
1167
1844
|
let _autoBarrierSeq = 0;
|
|
1168
1845
|
let _autoTicketSeq = 0;
|
|
1169
1846
|
const _pendingAutoTickets = /* @__PURE__ */ new Map();
|
|
1847
|
+
let _fastForwardRuntime = null;
|
|
1848
|
+
let _managedAdvanceInFlight = false;
|
|
1849
|
+
let _managedAdvancePending = false;
|
|
1850
|
+
function resetManagedAdvanceState() {
|
|
1851
|
+
_managedAdvanceInFlight = false;
|
|
1852
|
+
_managedAdvancePending = false;
|
|
1853
|
+
}
|
|
1854
|
+
function clearFastForwardRuntime() {
|
|
1855
|
+
_fastForwardRuntime = null;
|
|
1856
|
+
resetManagedAdvanceState();
|
|
1857
|
+
}
|
|
1858
|
+
function stopFastForwardInternal() {
|
|
1859
|
+
fastForwardState.active = false;
|
|
1860
|
+
warpState.active = false;
|
|
1861
|
+
clearFastForwardRuntime();
|
|
1862
|
+
}
|
|
1863
|
+
function abortFastForward(error) {
|
|
1864
|
+
const runtime = _fastForwardRuntime;
|
|
1865
|
+
stopFastForwardInternal();
|
|
1866
|
+
if (!runtime?.onAbort) {
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
try {
|
|
1870
|
+
runtime.onAbort(error);
|
|
1871
|
+
} catch (abortError) {
|
|
1872
|
+
console.error("Error in fast-forward abort callback:", abortError);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
function canAdvanceFastForward() {
|
|
1876
|
+
if (!fastForwardState.active || _fastForwardRuntime === null) {
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
const elapsedMs = Date.now() - _fastForwardRuntime.startedAt;
|
|
1880
|
+
if (elapsedMs > _fastForwardRuntime.timeLimitMs) {
|
|
1881
|
+
abortFastForward(new Error(`Fast forward exceeded time limit (${_fastForwardRuntime.timeLimitMs}ms)`));
|
|
1882
|
+
return false;
|
|
1883
|
+
}
|
|
1884
|
+
_fastForwardRuntime.stepCount += 1;
|
|
1885
|
+
if (_fastForwardRuntime.stepCount > _fastForwardRuntime.stepLimit) {
|
|
1886
|
+
abortFastForward(new Error(`Fast forward exceeded step limit (${_fastForwardRuntime.stepLimit})`));
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
function normalizeAdvanceError(error) {
|
|
1892
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1893
|
+
}
|
|
1894
|
+
function isManagedAdvanceActive() {
|
|
1895
|
+
return fastForwardState.active || skipState.active || autoState.active;
|
|
1896
|
+
}
|
|
1897
|
+
function canScheduleManagedAdvance() {
|
|
1898
|
+
if (fastForwardState.active) {
|
|
1899
|
+
return canAdvanceFastForward();
|
|
1900
|
+
}
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
function handleManagedAdvanceError(error) {
|
|
1904
|
+
console.error("Failed to advance scenario:", error);
|
|
1905
|
+
if (fastForwardState.active) {
|
|
1906
|
+
abortFastForward(normalizeAdvanceError(error));
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function scheduleManagedAdvance() {
|
|
1910
|
+
if (!isManagedAdvanceActive()) {
|
|
1911
|
+
_managedAdvancePending = false;
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (_managedAdvanceInFlight) {
|
|
1915
|
+
_managedAdvancePending = true;
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
if (!canScheduleManagedAdvance()) {
|
|
1919
|
+
_managedAdvancePending = false;
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
_managedAdvanceInFlight = true;
|
|
1923
|
+
_managedAdvancePending = false;
|
|
1924
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError).finally(() => {
|
|
1925
|
+
_managedAdvanceInFlight = false;
|
|
1926
|
+
if (!isManagedAdvanceActive()) {
|
|
1927
|
+
_managedAdvancePending = false;
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
if (_managedAdvancePending) {
|
|
1931
|
+
scheduleManagedAdvance();
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
function requestNextLine() {
|
|
1936
|
+
if (isManagedAdvanceActive()) {
|
|
1937
|
+
scheduleManagedAdvance();
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
|
|
1941
|
+
}
|
|
1170
1942
|
function clearAutoResumeTimer() {
|
|
1171
1943
|
if (_autoResumeTimer !== null) {
|
|
1172
1944
|
clearTimeout(_autoResumeTimer);
|
|
@@ -1201,7 +1973,7 @@ function createStage() {
|
|
|
1201
1973
|
if (!autoState.active) return;
|
|
1202
1974
|
if (_autoBarrier === null || _autoBarrier.id !== barrierId) return;
|
|
1203
1975
|
_autoBarrier = null;
|
|
1204
|
-
|
|
1976
|
+
requestNextLine();
|
|
1205
1977
|
}, safeDelayMs);
|
|
1206
1978
|
}
|
|
1207
1979
|
function trySettleAutoBarrier(barrier) {
|
|
@@ -1337,7 +2109,7 @@ function createStage() {
|
|
|
1337
2109
|
return;
|
|
1338
2110
|
}
|
|
1339
2111
|
if (_autoBarrier === null) {
|
|
1340
|
-
|
|
2112
|
+
requestNextLine();
|
|
1341
2113
|
}
|
|
1342
2114
|
}, autoRuntime.defaultTailMs);
|
|
1343
2115
|
}
|
|
@@ -1345,11 +2117,12 @@ function createStage() {
|
|
|
1345
2117
|
if (_skipTimer !== null) clearTimeout(_skipTimer);
|
|
1346
2118
|
_skipTimer = setTimeout(() => {
|
|
1347
2119
|
_skipTimer = null;
|
|
1348
|
-
if (skipState.active)
|
|
2120
|
+
if (skipState.active) requestNextLine();
|
|
1349
2121
|
}, SKIP_INTERVAL_MS);
|
|
1350
2122
|
}
|
|
1351
2123
|
function startSkip() {
|
|
1352
2124
|
if (skipState.active) return;
|
|
2125
|
+
if (fastForwardState.active) return;
|
|
1353
2126
|
if (isAnyBlockerActive()) return;
|
|
1354
2127
|
skipState.active = true;
|
|
1355
2128
|
invokeSkipCallbacks();
|
|
@@ -1361,7 +2134,7 @@ function createStage() {
|
|
|
1361
2134
|
if (_skipTimer !== null) {
|
|
1362
2135
|
clearTimeout(_skipTimer);
|
|
1363
2136
|
_skipTimer = null;
|
|
1364
|
-
|
|
2137
|
+
requestNextLine();
|
|
1365
2138
|
}
|
|
1366
2139
|
}
|
|
1367
2140
|
function isSkipping() {
|
|
@@ -1369,6 +2142,7 @@ function createStage() {
|
|
|
1369
2142
|
}
|
|
1370
2143
|
function startAuto() {
|
|
1371
2144
|
if (autoState.active) return;
|
|
2145
|
+
if (fastForwardState.active) return;
|
|
1372
2146
|
if (isAnyAutoBlockerActive()) return;
|
|
1373
2147
|
autoState.active = true;
|
|
1374
2148
|
tryInterrupt();
|
|
@@ -1386,12 +2160,58 @@ function createStage() {
|
|
|
1386
2160
|
function isAutoing() {
|
|
1387
2161
|
return autoState.active;
|
|
1388
2162
|
}
|
|
2163
|
+
function startFastForward(options) {
|
|
2164
|
+
if (fastForwardState.active) return;
|
|
2165
|
+
resetRuntimeState();
|
|
2166
|
+
_fastForwardRuntime = {
|
|
2167
|
+
stepLimit: Math.max(1, options?.stepLimit ?? DEFAULT_FAST_FORWARD_STEP_LIMIT),
|
|
2168
|
+
timeLimitMs: Math.max(1, options?.timeLimitMs ?? DEFAULT_FAST_FORWARD_TIME_LIMIT_MS),
|
|
2169
|
+
stepCount: 0,
|
|
2170
|
+
startedAt: Date.now(),
|
|
2171
|
+
onAbort: options?.onAbort,
|
|
2172
|
+
warp: options?.warp === true
|
|
2173
|
+
};
|
|
2174
|
+
fastForwardState.active = true;
|
|
2175
|
+
warpState.active = _fastForwardRuntime.warp;
|
|
2176
|
+
}
|
|
2177
|
+
function stopFastForward() {
|
|
2178
|
+
stopFastForwardInternal();
|
|
2179
|
+
}
|
|
2180
|
+
function isFastForwarding() {
|
|
2181
|
+
return fastForwardState.active;
|
|
2182
|
+
}
|
|
2183
|
+
function resetRuntimeState() {
|
|
2184
|
+
skipState.active = false;
|
|
2185
|
+
autoState.active = false;
|
|
2186
|
+
fastForwardState.active = false;
|
|
2187
|
+
warpState.active = false;
|
|
2188
|
+
clearFastForwardRuntime();
|
|
2189
|
+
if (_skipTimer !== null) {
|
|
2190
|
+
clearTimeout(_skipTimer);
|
|
2191
|
+
_skipTimer = null;
|
|
2192
|
+
}
|
|
2193
|
+
if (_autoTimer !== null) {
|
|
2194
|
+
clearTimeout(_autoTimer);
|
|
2195
|
+
_autoTimer = null;
|
|
2196
|
+
}
|
|
2197
|
+
clearAutoBarrier();
|
|
2198
|
+
clearPendingAutoTickets();
|
|
2199
|
+
}
|
|
1389
2200
|
function buildControl() {
|
|
1390
|
-
|
|
2201
|
+
const warpActive = warpState.active;
|
|
2202
|
+
let handled = warpActive;
|
|
1391
2203
|
let unskippableFlag = false;
|
|
2204
|
+
const inertRecordId = "__warp__";
|
|
1392
2205
|
const control = {
|
|
1393
2206
|
setWaiting(time, skippable) {
|
|
1394
2207
|
handled = true;
|
|
2208
|
+
if (warpActive) {
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
if (fastForwardState.active) {
|
|
2212
|
+
requestNextLine();
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
1395
2215
|
if (skipState.active) {
|
|
1396
2216
|
invokeSkipCallbacks();
|
|
1397
2217
|
scheduleSkipNextLine();
|
|
@@ -1408,6 +2228,13 @@ function createStage() {
|
|
|
1408
2228
|
},
|
|
1409
2229
|
hold() {
|
|
1410
2230
|
handled = true;
|
|
2231
|
+
if (warpActive) {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
if (fastForwardState.active) {
|
|
2235
|
+
requestNextLine();
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
1411
2238
|
if (skipState.active && !unskippableFlag) {
|
|
1412
2239
|
if (isAnyBlockerActive()) {
|
|
1413
2240
|
stopSkip();
|
|
@@ -1426,12 +2253,21 @@ function createStage() {
|
|
|
1426
2253
|
},
|
|
1427
2254
|
nextLine() {
|
|
1428
2255
|
handled = true;
|
|
1429
|
-
|
|
2256
|
+
if (warpActive) {
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
requestNextLine();
|
|
1430
2260
|
},
|
|
1431
2261
|
unskippable() {
|
|
2262
|
+
if (warpActive) {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
1432
2265
|
unskippableFlag = true;
|
|
1433
2266
|
},
|
|
1434
2267
|
record(meta) {
|
|
2268
|
+
if (warpActive) {
|
|
2269
|
+
return inertRecordId;
|
|
2270
|
+
}
|
|
1435
2271
|
const result = executePluginCommand("scenario", {
|
|
1436
2272
|
subCommand: "record",
|
|
1437
2273
|
meta
|
|
@@ -1470,19 +2306,19 @@ function createStage() {
|
|
|
1470
2306
|
cmd = transformCommand(e);
|
|
1471
2307
|
} catch (err) {
|
|
1472
2308
|
console.error("Failed to transform scenario command:", err);
|
|
1473
|
-
|
|
2309
|
+
requestNextLine();
|
|
1474
2310
|
return;
|
|
1475
2311
|
}
|
|
1476
2312
|
const handler = commandHandlers.get(cmd.command);
|
|
1477
2313
|
if (!handler) {
|
|
1478
2314
|
console.warn(`[Stage] No handler registered for command "${cmd.command}". Auto-advancing.`);
|
|
1479
|
-
|
|
2315
|
+
requestNextLine();
|
|
1480
2316
|
return;
|
|
1481
2317
|
}
|
|
1482
2318
|
const { control, wasHandled } = buildControl();
|
|
1483
2319
|
handler(cmd, control);
|
|
1484
2320
|
if (!wasHandled()) {
|
|
1485
|
-
|
|
2321
|
+
requestNextLine();
|
|
1486
2322
|
}
|
|
1487
2323
|
}
|
|
1488
2324
|
function dispatchTextLine(e) {
|
|
@@ -1491,7 +2327,7 @@ function createStage() {
|
|
|
1491
2327
|
handler(e, control);
|
|
1492
2328
|
}
|
|
1493
2329
|
if (!wasHandled()) {
|
|
1494
|
-
|
|
2330
|
+
requestNextLine();
|
|
1495
2331
|
}
|
|
1496
2332
|
}
|
|
1497
2333
|
function invokeSkipCallbacks() {
|
|
@@ -1539,6 +2375,10 @@ function createStage() {
|
|
|
1539
2375
|
startAuto,
|
|
1540
2376
|
stopAuto,
|
|
1541
2377
|
isAutoing,
|
|
2378
|
+
startFastForward,
|
|
2379
|
+
stopFastForward,
|
|
2380
|
+
isFastForwarding,
|
|
2381
|
+
resetRuntimeState,
|
|
1542
2382
|
bindEvents
|
|
1543
2383
|
};
|
|
1544
2384
|
}
|
|
@@ -1611,23 +2451,33 @@ export {
|
|
|
1611
2451
|
createStaticNavigation,
|
|
1612
2452
|
executeNodeCommand,
|
|
1613
2453
|
executePluginCommand,
|
|
2454
|
+
fastForwardState,
|
|
2455
|
+
getAppStateAdapter,
|
|
1614
2456
|
getNavigator,
|
|
2457
|
+
getSeekingType,
|
|
1615
2458
|
getStageSize,
|
|
1616
2459
|
globalEventListeners,
|
|
1617
2460
|
globalRequestAnimationFrameListeners,
|
|
1618
2461
|
moyu_exports as moyu,
|
|
1619
2462
|
nextLine,
|
|
2463
|
+
registerAppStateAdapter,
|
|
1620
2464
|
setDefaultAutoTailMs,
|
|
1621
2465
|
setWaiting,
|
|
1622
2466
|
skipState,
|
|
2467
|
+
startDebugSession,
|
|
2468
|
+
startRuntimeDebugSession,
|
|
2469
|
+
stopDebugSession,
|
|
2470
|
+
stopRuntimeDebugSession,
|
|
1623
2471
|
useAutoBlocker,
|
|
1624
2472
|
useAutoTicket,
|
|
1625
2473
|
useBeforeHandleCommandCallback,
|
|
2474
|
+
useDebugSession,
|
|
1626
2475
|
useFadeIn,
|
|
1627
2476
|
useFadeInOut,
|
|
1628
2477
|
useFadeOut,
|
|
1629
2478
|
useInterruptCallback,
|
|
1630
2479
|
useIsAutoing,
|
|
2480
|
+
useIsSeeking,
|
|
1631
2481
|
useIsSkipping,
|
|
1632
2482
|
useNavigation,
|
|
1633
2483
|
useNavigationParams,
|
|
@@ -1636,5 +2486,6 @@ export {
|
|
|
1636
2486
|
useSkipBlocker,
|
|
1637
2487
|
useSkipCallback,
|
|
1638
2488
|
useSoundEffect,
|
|
1639
|
-
useStageContext
|
|
2489
|
+
useStageContext,
|
|
2490
|
+
warpState
|
|
1640
2491
|
};
|