@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.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
|
|
|
@@ -397,8 +408,35 @@ function createRoot(options) {
|
|
|
397
408
|
|
|
398
409
|
// src/sandbox.ts
|
|
399
410
|
var JS_GLOBAL_KEYS = new Set(Object.getOwnPropertyNames(globalThis));
|
|
400
|
-
var JS_GLOBAL_KEYS_EXCLUDED = /* @__PURE__ */ new Set(["window", "globalThis", "ARCHIVE", "GLOBAL"]);
|
|
401
|
-
JS_GLOBAL_KEYS_EXCLUDED.forEach((key) =>
|
|
411
|
+
var JS_GLOBAL_KEYS_EXCLUDED = /* @__PURE__ */ new Set(["window", "globalThis", "LOCAL", "ARCHIVE", "GLOBAL"]);
|
|
412
|
+
JS_GLOBAL_KEYS_EXCLUDED.forEach((key) => {
|
|
413
|
+
JS_GLOBAL_KEYS.delete(key);
|
|
414
|
+
});
|
|
415
|
+
var localVariables = new Proxy(
|
|
416
|
+
{},
|
|
417
|
+
{
|
|
418
|
+
get(_, key) {
|
|
419
|
+
if (typeof key === "string") {
|
|
420
|
+
return executePluginCommand("scenario", {
|
|
421
|
+
subCommand: "getLocalVariable",
|
|
422
|
+
name: key
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
return void 0;
|
|
426
|
+
},
|
|
427
|
+
set(_, key, value) {
|
|
428
|
+
if (typeof key === "string") {
|
|
429
|
+
executePluginCommand("scenario", {
|
|
430
|
+
subCommand: "setLocalVariable",
|
|
431
|
+
name: key,
|
|
432
|
+
value
|
|
433
|
+
});
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
);
|
|
402
440
|
var archiveVariables = new Proxy(
|
|
403
441
|
{},
|
|
404
442
|
{
|
|
@@ -450,15 +488,16 @@ var globalVariables = new Proxy(
|
|
|
450
488
|
}
|
|
451
489
|
);
|
|
452
490
|
function safeEval(code, sandbox) {
|
|
453
|
-
const
|
|
491
|
+
const proxy4 = new Proxy(sandbox, {
|
|
454
492
|
has(target, key) {
|
|
455
493
|
if (typeof key === "string" && JS_GLOBAL_KEYS.has(key)) {
|
|
456
494
|
return false;
|
|
457
495
|
}
|
|
458
|
-
return key in target || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
496
|
+
return key in target || key === "LOCAL" || key === "ARCHIVE" || key === "GLOBAL" || key === "window" || key === "globalThis";
|
|
459
497
|
},
|
|
460
498
|
get(target, key) {
|
|
461
|
-
if (key === "window" || key === "globalThis") return
|
|
499
|
+
if (key === "window" || key === "globalThis") return proxy4;
|
|
500
|
+
if (key === "LOCAL") return localVariables;
|
|
462
501
|
if (key === "ARCHIVE") return archiveVariables;
|
|
463
502
|
if (key === "GLOBAL") return globalVariables;
|
|
464
503
|
if (key === Symbol.unscopables) return void 0;
|
|
@@ -469,7 +508,7 @@ function safeEval(code, sandbox) {
|
|
|
469
508
|
return true;
|
|
470
509
|
}
|
|
471
510
|
});
|
|
472
|
-
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(
|
|
511
|
+
return new Function("sandbox", `with(sandbox) { return (${code}) }`).bind(proxy4)(proxy4);
|
|
473
512
|
}
|
|
474
513
|
globalThis.__moyu_eval_sandbox = (code) => {
|
|
475
514
|
try {
|
|
@@ -651,11 +690,11 @@ function getStageSize() {
|
|
|
651
690
|
}
|
|
652
691
|
|
|
653
692
|
// src/components/navigation.tsx
|
|
654
|
-
var import_react2 =
|
|
655
|
-
var import_react3 = require("react");
|
|
693
|
+
var import_react2 = require("react");
|
|
656
694
|
var import_valtio = require("valtio");
|
|
657
|
-
var
|
|
658
|
-
var
|
|
695
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
696
|
+
var PageParamsContext = (0, import_react2.createContext)(void 0);
|
|
697
|
+
var OverlayParamsContext = (0, import_react2.createContext)(void 0);
|
|
659
698
|
var overlayIdCounter = 0;
|
|
660
699
|
var generateOverlayId = () => {
|
|
661
700
|
return `overlay-${++overlayIdCounter}-${Date.now()}`;
|
|
@@ -709,6 +748,9 @@ function createStackNavigator(options) {
|
|
|
709
748
|
delete navigationState.pageParams[page];
|
|
710
749
|
}
|
|
711
750
|
},
|
|
751
|
+
hasPage: (page) => {
|
|
752
|
+
return normalizedPages[page] !== void 0;
|
|
753
|
+
},
|
|
712
754
|
goBack: () => {
|
|
713
755
|
console.warn("goBack() not implemented yet");
|
|
714
756
|
},
|
|
@@ -764,11 +806,14 @@ function createStaticNavigation(navigator) {
|
|
|
764
806
|
const { pages, overlays } = navigator._descriptors;
|
|
765
807
|
const pageDescriptor = pages[navState.currentPage];
|
|
766
808
|
const Page = pageDescriptor?.component || (() => null);
|
|
767
|
-
return /* @__PURE__ */
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageParamsContext.Provider, { value: navState.pageParams[navState.currentPage], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Page, {}) }),
|
|
811
|
+
navState.overlayStack.map((overlay) => {
|
|
812
|
+
const overlayDescriptor = overlays[overlay.type];
|
|
813
|
+
const OverlayComponent = overlayDescriptor?.component || (() => null);
|
|
814
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayComponent, {}) }, overlay.id);
|
|
815
|
+
})
|
|
816
|
+
] });
|
|
772
817
|
};
|
|
773
818
|
}
|
|
774
819
|
function getNavigator() {
|
|
@@ -782,11 +827,629 @@ function useNavigationState() {
|
|
|
782
827
|
return (0, import_valtio.useSnapshot)(getNavigator()._state);
|
|
783
828
|
}
|
|
784
829
|
function useNavigationParams() {
|
|
785
|
-
const overlayParams = (0,
|
|
786
|
-
const pageParams = (0,
|
|
830
|
+
const overlayParams = (0, import_react2.useContext)(OverlayParamsContext);
|
|
831
|
+
const pageParams = (0, import_react2.useContext)(PageParamsContext);
|
|
787
832
|
return overlayParams ?? pageParams ?? {};
|
|
788
833
|
}
|
|
789
834
|
|
|
835
|
+
// src/debug/session.ts
|
|
836
|
+
var import_valtio2 = require("valtio");
|
|
837
|
+
var debugState = (0, import_valtio2.proxy)({
|
|
838
|
+
active: false,
|
|
839
|
+
checkpoints: {},
|
|
840
|
+
currentCursor: null,
|
|
841
|
+
restoring: false,
|
|
842
|
+
lastError: null
|
|
843
|
+
});
|
|
844
|
+
var appStateAdapter = null;
|
|
845
|
+
var currentConfig = {
|
|
846
|
+
onMarkerEnter: () => {
|
|
847
|
+
},
|
|
848
|
+
onError: () => {
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
var disposeMarkerListener = null;
|
|
852
|
+
var disposeFinishedListener = null;
|
|
853
|
+
var disposeWarpFinishedListener = null;
|
|
854
|
+
var debugOperationQueue = Promise.resolve();
|
|
855
|
+
var activeFastForward = null;
|
|
856
|
+
var activeWarp = null;
|
|
857
|
+
function enqueueDebugOperation(task) {
|
|
858
|
+
const nextTask = debugOperationQueue.then(task, task);
|
|
859
|
+
debugOperationQueue = nextTask.then(
|
|
860
|
+
() => void 0,
|
|
861
|
+
() => void 0
|
|
862
|
+
);
|
|
863
|
+
return nextTask;
|
|
864
|
+
}
|
|
865
|
+
function executeScenarioCommand(payload) {
|
|
866
|
+
return Promise.resolve(executePluginCommand("scenario", payload));
|
|
867
|
+
}
|
|
868
|
+
function toErrorMessage(error) {
|
|
869
|
+
if (error instanceof Error) {
|
|
870
|
+
return error.message;
|
|
871
|
+
}
|
|
872
|
+
return String(error);
|
|
873
|
+
}
|
|
874
|
+
function isPromiseLike(value) {
|
|
875
|
+
return !!value && typeof value.then === "function";
|
|
876
|
+
}
|
|
877
|
+
function reportDebugSessionError(error) {
|
|
878
|
+
debugState.lastError = toErrorMessage(error);
|
|
879
|
+
void Promise.resolve(currentConfig.onError(error));
|
|
880
|
+
}
|
|
881
|
+
function clearLocalRuntimeState() {
|
|
882
|
+
debugState.checkpoints = {};
|
|
883
|
+
debugState.currentCursor = null;
|
|
884
|
+
debugState.restoring = false;
|
|
885
|
+
debugState.lastError = null;
|
|
886
|
+
}
|
|
887
|
+
async function doSettleActiveSeek(op, clearSlot, error) {
|
|
888
|
+
if (!op) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
clearSlot();
|
|
892
|
+
try {
|
|
893
|
+
await appStateAdapter?.exitFastForwardMode?.();
|
|
894
|
+
} catch (exitError) {
|
|
895
|
+
if (error === void 0) {
|
|
896
|
+
error = exitError;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (error === void 0) {
|
|
900
|
+
op.resolve();
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
op.reject(error);
|
|
904
|
+
}
|
|
905
|
+
async function settleActiveFastForward(error) {
|
|
906
|
+
await doSettleActiveSeek(activeFastForward, () => {
|
|
907
|
+
activeFastForward = null;
|
|
908
|
+
}, error);
|
|
909
|
+
}
|
|
910
|
+
async function settleActiveWarp(error) {
|
|
911
|
+
await doSettleActiveSeek(activeWarp, () => {
|
|
912
|
+
activeWarp = null;
|
|
913
|
+
}, error);
|
|
914
|
+
}
|
|
915
|
+
async function clearRemoteCheckpoints() {
|
|
916
|
+
await executeScenarioCommand({ subCommand: "clearCheckpoints" });
|
|
917
|
+
}
|
|
918
|
+
async function syncLocalWarpCheckpoint(targetMarkerId) {
|
|
919
|
+
const cursor = await executeScenarioCommand({
|
|
920
|
+
subCommand: "getExecutionCursor"
|
|
921
|
+
});
|
|
922
|
+
const checkpointCursor = cursor ? {
|
|
923
|
+
story: cursor.story,
|
|
924
|
+
paragraph: cursor.paragraph,
|
|
925
|
+
markerId: targetMarkerId
|
|
926
|
+
} : debugState.currentCursor ? {
|
|
927
|
+
story: debugState.currentCursor.story,
|
|
928
|
+
paragraph: debugState.currentCursor.paragraph,
|
|
929
|
+
markerId: targetMarkerId
|
|
930
|
+
} : null;
|
|
931
|
+
if (!checkpointCursor) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
const checkpoint = {
|
|
935
|
+
cursor: checkpointCursor,
|
|
936
|
+
appState: appStateAdapter ? await appStateAdapter.capture() : void 0
|
|
937
|
+
};
|
|
938
|
+
debugState.currentCursor = checkpointCursor;
|
|
939
|
+
debugState.checkpoints[targetMarkerId] = checkpoint;
|
|
940
|
+
await currentConfig.onMarkerEnter(checkpoint);
|
|
941
|
+
}
|
|
942
|
+
function captureCheckpointAppStateSync() {
|
|
943
|
+
if (!appStateAdapter) {
|
|
944
|
+
return void 0;
|
|
945
|
+
}
|
|
946
|
+
const appState = appStateAdapter.capture();
|
|
947
|
+
if (isPromiseLike(appState)) {
|
|
948
|
+
throw new Error("Async app state capture is not supported for debug checkpoints");
|
|
949
|
+
}
|
|
950
|
+
return appState;
|
|
951
|
+
}
|
|
952
|
+
function notifyMarkerEnter(checkpoint) {
|
|
953
|
+
try {
|
|
954
|
+
const result = currentConfig.onMarkerEnter(checkpoint);
|
|
955
|
+
if (isPromiseLike(result)) {
|
|
956
|
+
void result.catch(reportDebugSessionError);
|
|
957
|
+
}
|
|
958
|
+
} catch (error) {
|
|
959
|
+
reportDebugSessionError(error);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
async function prepareJumpStart(markerId) {
|
|
963
|
+
const checkpoint = debugState.checkpoints[markerId];
|
|
964
|
+
if (checkpoint) {
|
|
965
|
+
const restored2 = await executeScenarioCommand({
|
|
966
|
+
subCommand: "restoreCheckpoint",
|
|
967
|
+
key: markerId
|
|
968
|
+
});
|
|
969
|
+
if (!restored2) {
|
|
970
|
+
throw new Error(`Failed to restore checkpoint ${markerId}`);
|
|
971
|
+
}
|
|
972
|
+
if (appStateAdapter) {
|
|
973
|
+
await appStateAdapter.restore(checkpoint.appState);
|
|
974
|
+
}
|
|
975
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
const lookup = await executeScenarioCommand({
|
|
979
|
+
subCommand: "getFastForwardCheckpoint",
|
|
980
|
+
key: markerId
|
|
981
|
+
});
|
|
982
|
+
if (!lookup.markerExists) {
|
|
983
|
+
throw new Error(`Marker ${markerId} is not available in the current story`);
|
|
984
|
+
}
|
|
985
|
+
const startCheckpointKey = lookup.checkpointKey ?? null;
|
|
986
|
+
if (!startCheckpointKey) {
|
|
987
|
+
if (!appStateAdapter?.restartCurrentStoryFromHead) {
|
|
988
|
+
throw new Error("Restarting the current story from head is not supported by the app state adapter");
|
|
989
|
+
}
|
|
990
|
+
await appStateAdapter.restartCurrentStoryFromHead();
|
|
991
|
+
debugState.checkpoints = {};
|
|
992
|
+
debugState.currentCursor = null;
|
|
993
|
+
return true;
|
|
994
|
+
}
|
|
995
|
+
const startCheckpoint = debugState.checkpoints[startCheckpointKey];
|
|
996
|
+
if (!startCheckpoint) {
|
|
997
|
+
throw new Error(`Local checkpoint ${startCheckpointKey} not found`);
|
|
998
|
+
}
|
|
999
|
+
const restored = await executeScenarioCommand({
|
|
1000
|
+
subCommand: "restoreCheckpoint",
|
|
1001
|
+
key: startCheckpointKey
|
|
1002
|
+
});
|
|
1003
|
+
if (!restored) {
|
|
1004
|
+
throw new Error(`Failed to restore checkpoint ${startCheckpointKey}`);
|
|
1005
|
+
}
|
|
1006
|
+
if (appStateAdapter) {
|
|
1007
|
+
await appStateAdapter.restore(startCheckpoint.appState);
|
|
1008
|
+
}
|
|
1009
|
+
debugState.currentCursor = startCheckpoint.cursor;
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
function handleMarkerEnter(marker) {
|
|
1013
|
+
const checkpoint = {
|
|
1014
|
+
cursor: {
|
|
1015
|
+
story: marker.story,
|
|
1016
|
+
paragraph: marker.paragraph,
|
|
1017
|
+
markerId: marker.markerId
|
|
1018
|
+
},
|
|
1019
|
+
appState: captureCheckpointAppStateSync()
|
|
1020
|
+
};
|
|
1021
|
+
debugState.currentCursor = checkpoint.cursor;
|
|
1022
|
+
debugState.checkpoints[marker.markerId] = checkpoint;
|
|
1023
|
+
notifyMarkerEnter(checkpoint);
|
|
1024
|
+
if (activeFastForward?.targetMarkerId === marker.markerId) {
|
|
1025
|
+
void settleActiveFastForward();
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
async function runFastForwardToMarker(markerId) {
|
|
1029
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1030
|
+
throw new Error("Fast-forward mode is not supported by the app state adapter");
|
|
1031
|
+
}
|
|
1032
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1033
|
+
throw new Error("A debug seek operation is already active");
|
|
1034
|
+
}
|
|
1035
|
+
await appStateAdapter.enterFastForwardMode({
|
|
1036
|
+
onAbort: (error) => {
|
|
1037
|
+
void settleActiveFastForward(error);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
const completion = new Promise((resolve, reject) => {
|
|
1041
|
+
activeFastForward = {
|
|
1042
|
+
targetMarkerId: markerId,
|
|
1043
|
+
resolve,
|
|
1044
|
+
reject
|
|
1045
|
+
};
|
|
1046
|
+
});
|
|
1047
|
+
try {
|
|
1048
|
+
await executeScenarioCommand({ subCommand: "nextLine" });
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
await settleActiveFastForward(error);
|
|
1051
|
+
}
|
|
1052
|
+
await completion;
|
|
1053
|
+
}
|
|
1054
|
+
async function runWarp(options) {
|
|
1055
|
+
if (!appStateAdapter?.enterFastForwardMode || !appStateAdapter?.exitFastForwardMode) {
|
|
1056
|
+
throw new Error("Warp mode is not supported by the app state adapter");
|
|
1057
|
+
}
|
|
1058
|
+
if (activeFastForward !== null || activeWarp !== null) {
|
|
1059
|
+
throw new Error("A debug seek operation is already active");
|
|
1060
|
+
}
|
|
1061
|
+
const boundary = options.boundary ?? "before";
|
|
1062
|
+
await appStateAdapter.enterFastForwardMode({ warp: true });
|
|
1063
|
+
const completion = new Promise((resolve, reject) => {
|
|
1064
|
+
activeWarp = {
|
|
1065
|
+
targetMarkerId: options.markerId,
|
|
1066
|
+
resolve,
|
|
1067
|
+
reject
|
|
1068
|
+
};
|
|
1069
|
+
});
|
|
1070
|
+
try {
|
|
1071
|
+
await executeScenarioCommand({
|
|
1072
|
+
subCommand: "warp",
|
|
1073
|
+
markerId: options.markerId,
|
|
1074
|
+
boundary
|
|
1075
|
+
});
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
await settleActiveWarp(error);
|
|
1078
|
+
}
|
|
1079
|
+
await completion;
|
|
1080
|
+
}
|
|
1081
|
+
var debugSessionController = {
|
|
1082
|
+
async restoreCheckpoint(markerId) {
|
|
1083
|
+
debugState.restoring = true;
|
|
1084
|
+
debugState.lastError = null;
|
|
1085
|
+
try {
|
|
1086
|
+
const needsFastForward = await enqueueDebugOperation(async () => prepareJumpStart(markerId));
|
|
1087
|
+
if (needsFastForward) {
|
|
1088
|
+
await runFastForwardToMarker(markerId);
|
|
1089
|
+
}
|
|
1090
|
+
return true;
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
debugState.lastError = toErrorMessage(error);
|
|
1093
|
+
throw error;
|
|
1094
|
+
} finally {
|
|
1095
|
+
debugState.restoring = false;
|
|
1096
|
+
}
|
|
1097
|
+
},
|
|
1098
|
+
async warp(options) {
|
|
1099
|
+
debugState.restoring = true;
|
|
1100
|
+
debugState.lastError = null;
|
|
1101
|
+
try {
|
|
1102
|
+
const needsWarp = await enqueueDebugOperation(async () => prepareJumpStart(options.markerId));
|
|
1103
|
+
if (needsWarp) {
|
|
1104
|
+
await runWarp(options);
|
|
1105
|
+
}
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
debugState.lastError = toErrorMessage(error);
|
|
1108
|
+
throw error;
|
|
1109
|
+
} finally {
|
|
1110
|
+
debugState.restoring = false;
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
async switchPage(page, params) {
|
|
1114
|
+
return enqueueDebugOperation(async () => {
|
|
1115
|
+
debugState.lastError = null;
|
|
1116
|
+
try {
|
|
1117
|
+
if (!appStateAdapter?.switchPage) {
|
|
1118
|
+
throw new Error("Page switching is not supported by the app state adapter");
|
|
1119
|
+
}
|
|
1120
|
+
await appStateAdapter.switchPage(page, params);
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
debugState.lastError = toErrorMessage(error);
|
|
1123
|
+
throw error;
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
function registerAppStateAdapter(adapter) {
|
|
1129
|
+
appStateAdapter = adapter;
|
|
1130
|
+
}
|
|
1131
|
+
function getAppStateAdapter() {
|
|
1132
|
+
return appStateAdapter;
|
|
1133
|
+
}
|
|
1134
|
+
async function startDebugSession(config = {}) {
|
|
1135
|
+
await stopDebugSession();
|
|
1136
|
+
currentConfig = {
|
|
1137
|
+
onMarkerEnter: config.onMarkerEnter ?? (() => {
|
|
1138
|
+
}),
|
|
1139
|
+
onError: config.onError ?? (() => {
|
|
1140
|
+
})
|
|
1141
|
+
};
|
|
1142
|
+
clearLocalRuntimeState();
|
|
1143
|
+
debugState.active = true;
|
|
1144
|
+
disposeMarkerListener = addEventListener("scenariomarkerenter", (event) => {
|
|
1145
|
+
const marker = event;
|
|
1146
|
+
try {
|
|
1147
|
+
handleMarkerEnter(marker);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
reportDebugSessionError(error);
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
disposeFinishedListener = addEventListener("scenariofinished", () => {
|
|
1153
|
+
debugState.currentCursor = null;
|
|
1154
|
+
if (activeFastForward) {
|
|
1155
|
+
void settleActiveFastForward(
|
|
1156
|
+
new Error(`Marker ${activeFastForward.targetMarkerId} was not reached before the scenario finished`)
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
if (activeWarp) {
|
|
1160
|
+
const targetMarkerId = activeWarp.targetMarkerId;
|
|
1161
|
+
void settleActiveWarp(new Error(`Marker ${targetMarkerId} was not reached before the scenario finished`));
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
disposeWarpFinishedListener = addEventListener("scenariowarpfinished", () => {
|
|
1165
|
+
const warp = activeWarp;
|
|
1166
|
+
if (!warp) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
void enqueueDebugOperation(async () => {
|
|
1170
|
+
try {
|
|
1171
|
+
await syncLocalWarpCheckpoint(warp.targetMarkerId);
|
|
1172
|
+
await settleActiveWarp();
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
debugState.lastError = toErrorMessage(error);
|
|
1175
|
+
await settleActiveWarp(error);
|
|
1176
|
+
await currentConfig.onError(error);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
1180
|
+
try {
|
|
1181
|
+
await clearRemoteCheckpoints();
|
|
1182
|
+
debugState.currentCursor = await executeScenarioCommand({
|
|
1183
|
+
subCommand: "getExecutionCursor"
|
|
1184
|
+
});
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
debugState.active = false;
|
|
1187
|
+
clearLocalRuntimeState();
|
|
1188
|
+
disposeMarkerListener?.();
|
|
1189
|
+
disposeFinishedListener?.();
|
|
1190
|
+
disposeWarpFinishedListener?.();
|
|
1191
|
+
disposeMarkerListener = null;
|
|
1192
|
+
disposeFinishedListener = null;
|
|
1193
|
+
disposeWarpFinishedListener = null;
|
|
1194
|
+
await currentConfig.onError(error);
|
|
1195
|
+
throw error;
|
|
1196
|
+
}
|
|
1197
|
+
return debugSessionController;
|
|
1198
|
+
}
|
|
1199
|
+
async function stopDebugSession() {
|
|
1200
|
+
disposeMarkerListener?.();
|
|
1201
|
+
disposeFinishedListener?.();
|
|
1202
|
+
disposeWarpFinishedListener?.();
|
|
1203
|
+
disposeMarkerListener = null;
|
|
1204
|
+
disposeFinishedListener = null;
|
|
1205
|
+
disposeWarpFinishedListener = null;
|
|
1206
|
+
await settleActiveFastForward(new Error("Debug session stopped"));
|
|
1207
|
+
await settleActiveWarp(new Error("Debug session stopped"));
|
|
1208
|
+
if (debugState.active) {
|
|
1209
|
+
await enqueueDebugOperation(async () => {
|
|
1210
|
+
try {
|
|
1211
|
+
await clearRemoteCheckpoints();
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
debugState.lastError = toErrorMessage(error);
|
|
1214
|
+
await currentConfig.onError(error);
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
debugState.active = false;
|
|
1219
|
+
clearLocalRuntimeState();
|
|
1220
|
+
}
|
|
1221
|
+
function clearDebugSessionRuntimeState() {
|
|
1222
|
+
clearLocalRuntimeState();
|
|
1223
|
+
void settleActiveFastForward(new Error("Debug session runtime state cleared"));
|
|
1224
|
+
void settleActiveWarp(new Error("Debug session runtime state cleared"));
|
|
1225
|
+
if (!debugState.active) {
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
void enqueueDebugOperation(async () => {
|
|
1229
|
+
try {
|
|
1230
|
+
await clearRemoteCheckpoints();
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
debugState.lastError = toErrorMessage(error);
|
|
1233
|
+
await currentConfig.onError(error);
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
function useDebugSession() {
|
|
1238
|
+
const snapshot = (0, import_valtio2.useSnapshot)(debugState);
|
|
1239
|
+
return {
|
|
1240
|
+
...snapshot,
|
|
1241
|
+
startDebugSession,
|
|
1242
|
+
stopDebugSession,
|
|
1243
|
+
restoreCheckpoint: debugSessionController.restoreCheckpoint,
|
|
1244
|
+
warp: debugSessionController.warp,
|
|
1245
|
+
switchPage: debugSessionController.switchPage
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/debug/runtime.ts
|
|
1250
|
+
var currentConnection = null;
|
|
1251
|
+
var currentController = null;
|
|
1252
|
+
var bufferedRequestMessages = [];
|
|
1253
|
+
function getRuntimeWebSocket() {
|
|
1254
|
+
return globalThis.WebSocket ?? null;
|
|
1255
|
+
}
|
|
1256
|
+
function isJumpRequestMessage(message) {
|
|
1257
|
+
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");
|
|
1258
|
+
}
|
|
1259
|
+
function isRouteRequestMessage(message) {
|
|
1260
|
+
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));
|
|
1261
|
+
}
|
|
1262
|
+
function sendRuntimeDebugMessage(socket, message) {
|
|
1263
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1264
|
+
if (!RuntimeWebSocket || socket.readyState !== RuntimeWebSocket.OPEN) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
socket.send(JSON.stringify(message));
|
|
1268
|
+
}
|
|
1269
|
+
function clearBufferedRequestMessages() {
|
|
1270
|
+
bufferedRequestMessages = [];
|
|
1271
|
+
}
|
|
1272
|
+
function bufferRequestMessage(message) {
|
|
1273
|
+
bufferedRequestMessages.push(message);
|
|
1274
|
+
}
|
|
1275
|
+
function flushBufferedRequestMessages(socket, sessionId) {
|
|
1276
|
+
const messages = bufferedRequestMessages;
|
|
1277
|
+
bufferedRequestMessages = [];
|
|
1278
|
+
for (const message of messages) {
|
|
1279
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
function handleRuntimeDebugRequest(socket, sessionId, message) {
|
|
1283
|
+
const controller = currentController;
|
|
1284
|
+
if (!controller) {
|
|
1285
|
+
bufferRequestMessage(message);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (isJumpRequestMessage(message)) {
|
|
1289
|
+
const runJump = message.strategy === "warp" ? controller.warp({
|
|
1290
|
+
markerId: message.markerId,
|
|
1291
|
+
boundary: message.boundary
|
|
1292
|
+
}).then(() => true) : controller.restoreCheckpoint(message.markerId);
|
|
1293
|
+
void runJump.then((restored) => {
|
|
1294
|
+
if (!restored) {
|
|
1295
|
+
sendRuntimeDebugMessage(socket, {
|
|
1296
|
+
type: "jump:error",
|
|
1297
|
+
sessionId,
|
|
1298
|
+
requestId: message.requestId,
|
|
1299
|
+
markerId: message.markerId,
|
|
1300
|
+
error: "Checkpoint not found"
|
|
1301
|
+
});
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
sendRuntimeDebugMessage(socket, {
|
|
1305
|
+
type: "jump:done",
|
|
1306
|
+
sessionId,
|
|
1307
|
+
requestId: message.requestId,
|
|
1308
|
+
markerId: message.markerId
|
|
1309
|
+
});
|
|
1310
|
+
}).catch((error) => {
|
|
1311
|
+
sendRuntimeDebugMessage(socket, {
|
|
1312
|
+
type: "jump:error",
|
|
1313
|
+
sessionId,
|
|
1314
|
+
requestId: message.requestId,
|
|
1315
|
+
markerId: message.markerId,
|
|
1316
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1317
|
+
});
|
|
1318
|
+
});
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
void controller.switchPage(message.page, message.params).then(() => {
|
|
1322
|
+
sendRuntimeDebugMessage(socket, {
|
|
1323
|
+
type: "route:done",
|
|
1324
|
+
sessionId,
|
|
1325
|
+
requestId: message.requestId,
|
|
1326
|
+
page: message.page
|
|
1327
|
+
});
|
|
1328
|
+
}).catch((error) => {
|
|
1329
|
+
sendRuntimeDebugMessage(socket, {
|
|
1330
|
+
type: "route:error",
|
|
1331
|
+
sessionId,
|
|
1332
|
+
requestId: message.requestId,
|
|
1333
|
+
page: message.page,
|
|
1334
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
async function readDebugParams() {
|
|
1339
|
+
const rawParams = await executePluginCommand("system", {
|
|
1340
|
+
subCommand: "getParams"
|
|
1341
|
+
});
|
|
1342
|
+
if (!rawParams) {
|
|
1343
|
+
return {};
|
|
1344
|
+
}
|
|
1345
|
+
try {
|
|
1346
|
+
const parsed = JSON.parse(rawParams);
|
|
1347
|
+
return {
|
|
1348
|
+
debug: parsed.debug === true,
|
|
1349
|
+
debugSessionId: typeof parsed.debugSessionId === "string" ? parsed.debugSessionId : void 0,
|
|
1350
|
+
debugWsUrl: typeof parsed.debugWsUrl === "string" ? parsed.debugWsUrl : void 0
|
|
1351
|
+
};
|
|
1352
|
+
} catch {
|
|
1353
|
+
return {};
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
async function stopRuntimeDebugSession() {
|
|
1357
|
+
const connection = currentConnection;
|
|
1358
|
+
currentConnection = null;
|
|
1359
|
+
currentController = null;
|
|
1360
|
+
clearBufferedRequestMessages();
|
|
1361
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1362
|
+
await stopDebugSession();
|
|
1363
|
+
if (!connection) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
if (RuntimeWebSocket && (connection.socket.readyState === RuntimeWebSocket.OPEN || connection.socket.readyState === RuntimeWebSocket.CONNECTING)) {
|
|
1367
|
+
connection.socket.close();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
async function startRuntimeDebugSession() {
|
|
1371
|
+
await stopRuntimeDebugSession();
|
|
1372
|
+
const params = await readDebugParams();
|
|
1373
|
+
if (params.debug !== true || !params.debugSessionId || !params.debugWsUrl) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
const RuntimeWebSocket = getRuntimeWebSocket();
|
|
1377
|
+
if (!RuntimeWebSocket) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const socket = new RuntimeWebSocket(params.debugWsUrl);
|
|
1381
|
+
const sessionId = params.debugSessionId;
|
|
1382
|
+
currentConnection = { sessionId, socket };
|
|
1383
|
+
socket.onopen = () => {
|
|
1384
|
+
if (currentConnection?.socket !== socket) {
|
|
1385
|
+
socket.close();
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
void startDebugSession({
|
|
1389
|
+
onMarkerEnter(checkpoint) {
|
|
1390
|
+
if (!checkpoint.cursor.markerId) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
sendRuntimeDebugMessage(socket, {
|
|
1394
|
+
type: "marker:enter",
|
|
1395
|
+
sessionId,
|
|
1396
|
+
markerId: checkpoint.cursor.markerId,
|
|
1397
|
+
story: checkpoint.cursor.story,
|
|
1398
|
+
paragraph: checkpoint.cursor.paragraph
|
|
1399
|
+
});
|
|
1400
|
+
},
|
|
1401
|
+
onError(error) {
|
|
1402
|
+
console.error("[debug] runtime debug session error:", error);
|
|
1403
|
+
}
|
|
1404
|
+
}).then((controller) => {
|
|
1405
|
+
currentController = controller;
|
|
1406
|
+
flushBufferedRequestMessages(socket, sessionId);
|
|
1407
|
+
}).catch((error) => {
|
|
1408
|
+
console.error("[debug] failed to start runtime debug session:", error);
|
|
1409
|
+
socket.close();
|
|
1410
|
+
});
|
|
1411
|
+
};
|
|
1412
|
+
socket.onclose = () => {
|
|
1413
|
+
if (currentConnection?.socket !== socket) {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
currentConnection = null;
|
|
1417
|
+
currentController = null;
|
|
1418
|
+
clearBufferedRequestMessages();
|
|
1419
|
+
void stopDebugSession();
|
|
1420
|
+
};
|
|
1421
|
+
socket.onerror = (event) => {
|
|
1422
|
+
if (currentConnection?.socket !== socket) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
console.error("[debug] runtime websocket error:", event);
|
|
1426
|
+
};
|
|
1427
|
+
socket.onmessage = (event) => {
|
|
1428
|
+
if (currentConnection?.socket !== socket) {
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
try {
|
|
1432
|
+
const message = JSON.parse(String(event.data));
|
|
1433
|
+
if (message.sessionId && message.sessionId !== sessionId) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (isJumpRequestMessage(message)) {
|
|
1437
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
if (isRouteRequestMessage(message)) {
|
|
1441
|
+
handleRuntimeDebugRequest(socket, sessionId, message);
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
if (message.type && message.type !== "session:init") {
|
|
1445
|
+
console.debug("[debug] runtime received message:", message);
|
|
1446
|
+
}
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
console.error("[debug] failed to parse runtime debug message:", error);
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
|
|
790
1453
|
// src/hooks/useFadeInOut.ts
|
|
791
1454
|
function useFadeInOut(inTime, _keepTime, _outTime, pause = false, onFinished) {
|
|
792
1455
|
const [style, api] = (0, spring_exports.useSpring)(
|
|
@@ -854,7 +1517,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
|
|
|
854
1517
|
}
|
|
855
1518
|
|
|
856
1519
|
// src/hooks/useScenario.ts
|
|
857
|
-
var
|
|
1520
|
+
var import_react3 = require("react");
|
|
858
1521
|
var currentScenarioSession = null;
|
|
859
1522
|
var scenarioSessionId = 0;
|
|
860
1523
|
var scenarioLifecycleQueue = Promise.resolve();
|
|
@@ -889,11 +1552,13 @@ async function terminateCurrentScenario() {
|
|
|
889
1552
|
await executePluginCommand("scenario", {
|
|
890
1553
|
subCommand: "terminateStory"
|
|
891
1554
|
});
|
|
1555
|
+
clearDebugSessionRuntimeState();
|
|
892
1556
|
} catch (error) {
|
|
893
1557
|
console.error("Failed to terminate scenario:", error);
|
|
894
1558
|
}
|
|
895
1559
|
}
|
|
896
1560
|
async function startScenarioSession(session, config) {
|
|
1561
|
+
clearDebugSessionRuntimeState();
|
|
897
1562
|
for (const story of config.stories) {
|
|
898
1563
|
if (!isCurrentScenarioSession(session)) return;
|
|
899
1564
|
try {
|
|
@@ -994,7 +1659,7 @@ function setWaiting(time, skippable) {
|
|
|
994
1659
|
executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
|
|
995
1660
|
}
|
|
996
1661
|
function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
997
|
-
(0,
|
|
1662
|
+
(0, import_react3.useEffect)(() => {
|
|
998
1663
|
const session = acquireScenarioSession({
|
|
999
1664
|
stories: normalizeStories(stories),
|
|
1000
1665
|
startName,
|
|
@@ -1008,10 +1673,10 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
|
|
|
1008
1673
|
}
|
|
1009
1674
|
|
|
1010
1675
|
// src/hooks/useSoundEffect.ts
|
|
1011
|
-
var
|
|
1676
|
+
var import_react4 = require("react");
|
|
1012
1677
|
var LOADED_SOUNDS = /* @__PURE__ */ new Set();
|
|
1013
1678
|
function useSoundEffect(src) {
|
|
1014
|
-
(0,
|
|
1679
|
+
(0, import_react4.useEffect)(() => {
|
|
1015
1680
|
const loadSound = async () => {
|
|
1016
1681
|
try {
|
|
1017
1682
|
await executePluginCommand("audio", {
|
|
@@ -1046,7 +1711,7 @@ function useSoundEffect(src) {
|
|
|
1046
1711
|
}
|
|
1047
1712
|
};
|
|
1048
1713
|
}, [src]);
|
|
1049
|
-
return (0,
|
|
1714
|
+
return (0, import_react4.useCallback)(() => {
|
|
1050
1715
|
try {
|
|
1051
1716
|
executePluginCommand("audio", {
|
|
1052
1717
|
subCommand: "play",
|
|
@@ -1059,19 +1724,31 @@ function useSoundEffect(src) {
|
|
|
1059
1724
|
}
|
|
1060
1725
|
|
|
1061
1726
|
// src/hooks/useStage.ts
|
|
1062
|
-
var
|
|
1063
|
-
var
|
|
1727
|
+
var import_react5 = require("react");
|
|
1728
|
+
var import_valtio3 = require("valtio");
|
|
1064
1729
|
var SKIP_INTERVAL_MS = 80;
|
|
1730
|
+
var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
|
|
1731
|
+
var DEFAULT_FAST_FORWARD_TIME_LIMIT_MS = 3e3;
|
|
1065
1732
|
var autoRuntime = {
|
|
1066
1733
|
defaultTailMs: 0
|
|
1067
1734
|
};
|
|
1068
|
-
var skipState = (0,
|
|
1069
|
-
var autoState = (0,
|
|
1735
|
+
var skipState = (0, import_valtio3.proxy)({ active: false });
|
|
1736
|
+
var autoState = (0, import_valtio3.proxy)({ active: false });
|
|
1737
|
+
var fastForwardState = (0, import_valtio3.proxy)({ active: false });
|
|
1738
|
+
var warpState = (0, import_valtio3.proxy)({ active: false });
|
|
1070
1739
|
function useIsSkipping() {
|
|
1071
|
-
return (0,
|
|
1740
|
+
return (0, import_valtio3.useSnapshot)(skipState).active;
|
|
1072
1741
|
}
|
|
1073
1742
|
function useIsAutoing() {
|
|
1074
|
-
return (0,
|
|
1743
|
+
return (0, import_valtio3.useSnapshot)(autoState).active;
|
|
1744
|
+
}
|
|
1745
|
+
function useIsSeeking() {
|
|
1746
|
+
return (0, import_valtio3.useSnapshot)(fastForwardState).active;
|
|
1747
|
+
}
|
|
1748
|
+
function getSeekingType() {
|
|
1749
|
+
if (warpState.active) return "warp";
|
|
1750
|
+
if (fastForwardState.active) return "fast-forward";
|
|
1751
|
+
return null;
|
|
1075
1752
|
}
|
|
1076
1753
|
function setDefaultAutoTailMs(ms) {
|
|
1077
1754
|
if (!Number.isFinite(ms)) {
|
|
@@ -1179,6 +1856,101 @@ function createStage() {
|
|
|
1179
1856
|
let _autoBarrierSeq = 0;
|
|
1180
1857
|
let _autoTicketSeq = 0;
|
|
1181
1858
|
const _pendingAutoTickets = /* @__PURE__ */ new Map();
|
|
1859
|
+
let _fastForwardRuntime = null;
|
|
1860
|
+
let _managedAdvanceInFlight = false;
|
|
1861
|
+
let _managedAdvancePending = false;
|
|
1862
|
+
function resetManagedAdvanceState() {
|
|
1863
|
+
_managedAdvanceInFlight = false;
|
|
1864
|
+
_managedAdvancePending = false;
|
|
1865
|
+
}
|
|
1866
|
+
function clearFastForwardRuntime() {
|
|
1867
|
+
_fastForwardRuntime = null;
|
|
1868
|
+
resetManagedAdvanceState();
|
|
1869
|
+
}
|
|
1870
|
+
function stopFastForwardInternal() {
|
|
1871
|
+
fastForwardState.active = false;
|
|
1872
|
+
warpState.active = false;
|
|
1873
|
+
clearFastForwardRuntime();
|
|
1874
|
+
}
|
|
1875
|
+
function abortFastForward(error) {
|
|
1876
|
+
const runtime = _fastForwardRuntime;
|
|
1877
|
+
stopFastForwardInternal();
|
|
1878
|
+
if (!runtime?.onAbort) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
try {
|
|
1882
|
+
runtime.onAbort(error);
|
|
1883
|
+
} catch (abortError) {
|
|
1884
|
+
console.error("Error in fast-forward abort callback:", abortError);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function canAdvanceFastForward() {
|
|
1888
|
+
if (!fastForwardState.active || _fastForwardRuntime === null) {
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
const elapsedMs = Date.now() - _fastForwardRuntime.startedAt;
|
|
1892
|
+
if (elapsedMs > _fastForwardRuntime.timeLimitMs) {
|
|
1893
|
+
abortFastForward(new Error(`Fast forward exceeded time limit (${_fastForwardRuntime.timeLimitMs}ms)`));
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
_fastForwardRuntime.stepCount += 1;
|
|
1897
|
+
if (_fastForwardRuntime.stepCount > _fastForwardRuntime.stepLimit) {
|
|
1898
|
+
abortFastForward(new Error(`Fast forward exceeded step limit (${_fastForwardRuntime.stepLimit})`));
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
function normalizeAdvanceError(error) {
|
|
1904
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1905
|
+
}
|
|
1906
|
+
function isManagedAdvanceActive() {
|
|
1907
|
+
return fastForwardState.active || skipState.active || autoState.active;
|
|
1908
|
+
}
|
|
1909
|
+
function canScheduleManagedAdvance() {
|
|
1910
|
+
if (fastForwardState.active) {
|
|
1911
|
+
return canAdvanceFastForward();
|
|
1912
|
+
}
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1915
|
+
function handleManagedAdvanceError(error) {
|
|
1916
|
+
console.error("Failed to advance scenario:", error);
|
|
1917
|
+
if (fastForwardState.active) {
|
|
1918
|
+
abortFastForward(normalizeAdvanceError(error));
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
function scheduleManagedAdvance() {
|
|
1922
|
+
if (!isManagedAdvanceActive()) {
|
|
1923
|
+
_managedAdvancePending = false;
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
if (_managedAdvanceInFlight) {
|
|
1927
|
+
_managedAdvancePending = true;
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
if (!canScheduleManagedAdvance()) {
|
|
1931
|
+
_managedAdvancePending = false;
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
_managedAdvanceInFlight = true;
|
|
1935
|
+
_managedAdvancePending = false;
|
|
1936
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError).finally(() => {
|
|
1937
|
+
_managedAdvanceInFlight = false;
|
|
1938
|
+
if (!isManagedAdvanceActive()) {
|
|
1939
|
+
_managedAdvancePending = false;
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
if (_managedAdvancePending) {
|
|
1943
|
+
scheduleManagedAdvance();
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
function requestNextLine() {
|
|
1948
|
+
if (isManagedAdvanceActive()) {
|
|
1949
|
+
scheduleManagedAdvance();
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
|
|
1953
|
+
}
|
|
1182
1954
|
function clearAutoResumeTimer() {
|
|
1183
1955
|
if (_autoResumeTimer !== null) {
|
|
1184
1956
|
clearTimeout(_autoResumeTimer);
|
|
@@ -1213,7 +1985,7 @@ function createStage() {
|
|
|
1213
1985
|
if (!autoState.active) return;
|
|
1214
1986
|
if (_autoBarrier === null || _autoBarrier.id !== barrierId) return;
|
|
1215
1987
|
_autoBarrier = null;
|
|
1216
|
-
|
|
1988
|
+
requestNextLine();
|
|
1217
1989
|
}, safeDelayMs);
|
|
1218
1990
|
}
|
|
1219
1991
|
function trySettleAutoBarrier(barrier) {
|
|
@@ -1349,7 +2121,7 @@ function createStage() {
|
|
|
1349
2121
|
return;
|
|
1350
2122
|
}
|
|
1351
2123
|
if (_autoBarrier === null) {
|
|
1352
|
-
|
|
2124
|
+
requestNextLine();
|
|
1353
2125
|
}
|
|
1354
2126
|
}, autoRuntime.defaultTailMs);
|
|
1355
2127
|
}
|
|
@@ -1357,11 +2129,12 @@ function createStage() {
|
|
|
1357
2129
|
if (_skipTimer !== null) clearTimeout(_skipTimer);
|
|
1358
2130
|
_skipTimer = setTimeout(() => {
|
|
1359
2131
|
_skipTimer = null;
|
|
1360
|
-
if (skipState.active)
|
|
2132
|
+
if (skipState.active) requestNextLine();
|
|
1361
2133
|
}, SKIP_INTERVAL_MS);
|
|
1362
2134
|
}
|
|
1363
2135
|
function startSkip() {
|
|
1364
2136
|
if (skipState.active) return;
|
|
2137
|
+
if (fastForwardState.active) return;
|
|
1365
2138
|
if (isAnyBlockerActive()) return;
|
|
1366
2139
|
skipState.active = true;
|
|
1367
2140
|
invokeSkipCallbacks();
|
|
@@ -1373,7 +2146,7 @@ function createStage() {
|
|
|
1373
2146
|
if (_skipTimer !== null) {
|
|
1374
2147
|
clearTimeout(_skipTimer);
|
|
1375
2148
|
_skipTimer = null;
|
|
1376
|
-
|
|
2149
|
+
requestNextLine();
|
|
1377
2150
|
}
|
|
1378
2151
|
}
|
|
1379
2152
|
function isSkipping() {
|
|
@@ -1381,6 +2154,7 @@ function createStage() {
|
|
|
1381
2154
|
}
|
|
1382
2155
|
function startAuto() {
|
|
1383
2156
|
if (autoState.active) return;
|
|
2157
|
+
if (fastForwardState.active) return;
|
|
1384
2158
|
if (isAnyAutoBlockerActive()) return;
|
|
1385
2159
|
autoState.active = true;
|
|
1386
2160
|
tryInterrupt();
|
|
@@ -1398,12 +2172,58 @@ function createStage() {
|
|
|
1398
2172
|
function isAutoing() {
|
|
1399
2173
|
return autoState.active;
|
|
1400
2174
|
}
|
|
2175
|
+
function startFastForward(options) {
|
|
2176
|
+
if (fastForwardState.active) return;
|
|
2177
|
+
resetRuntimeState();
|
|
2178
|
+
_fastForwardRuntime = {
|
|
2179
|
+
stepLimit: Math.max(1, options?.stepLimit ?? DEFAULT_FAST_FORWARD_STEP_LIMIT),
|
|
2180
|
+
timeLimitMs: Math.max(1, options?.timeLimitMs ?? DEFAULT_FAST_FORWARD_TIME_LIMIT_MS),
|
|
2181
|
+
stepCount: 0,
|
|
2182
|
+
startedAt: Date.now(),
|
|
2183
|
+
onAbort: options?.onAbort,
|
|
2184
|
+
warp: options?.warp === true
|
|
2185
|
+
};
|
|
2186
|
+
fastForwardState.active = true;
|
|
2187
|
+
warpState.active = _fastForwardRuntime.warp;
|
|
2188
|
+
}
|
|
2189
|
+
function stopFastForward() {
|
|
2190
|
+
stopFastForwardInternal();
|
|
2191
|
+
}
|
|
2192
|
+
function isFastForwarding() {
|
|
2193
|
+
return fastForwardState.active;
|
|
2194
|
+
}
|
|
2195
|
+
function resetRuntimeState() {
|
|
2196
|
+
skipState.active = false;
|
|
2197
|
+
autoState.active = false;
|
|
2198
|
+
fastForwardState.active = false;
|
|
2199
|
+
warpState.active = false;
|
|
2200
|
+
clearFastForwardRuntime();
|
|
2201
|
+
if (_skipTimer !== null) {
|
|
2202
|
+
clearTimeout(_skipTimer);
|
|
2203
|
+
_skipTimer = null;
|
|
2204
|
+
}
|
|
2205
|
+
if (_autoTimer !== null) {
|
|
2206
|
+
clearTimeout(_autoTimer);
|
|
2207
|
+
_autoTimer = null;
|
|
2208
|
+
}
|
|
2209
|
+
clearAutoBarrier();
|
|
2210
|
+
clearPendingAutoTickets();
|
|
2211
|
+
}
|
|
1401
2212
|
function buildControl() {
|
|
1402
|
-
|
|
2213
|
+
const warpActive = warpState.active;
|
|
2214
|
+
let handled = warpActive;
|
|
1403
2215
|
let unskippableFlag = false;
|
|
2216
|
+
const inertRecordId = "__warp__";
|
|
1404
2217
|
const control = {
|
|
1405
2218
|
setWaiting(time, skippable) {
|
|
1406
2219
|
handled = true;
|
|
2220
|
+
if (warpActive) {
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
if (fastForwardState.active) {
|
|
2224
|
+
requestNextLine();
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
1407
2227
|
if (skipState.active) {
|
|
1408
2228
|
invokeSkipCallbacks();
|
|
1409
2229
|
scheduleSkipNextLine();
|
|
@@ -1420,6 +2240,13 @@ function createStage() {
|
|
|
1420
2240
|
},
|
|
1421
2241
|
hold() {
|
|
1422
2242
|
handled = true;
|
|
2243
|
+
if (warpActive) {
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
if (fastForwardState.active) {
|
|
2247
|
+
requestNextLine();
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
1423
2250
|
if (skipState.active && !unskippableFlag) {
|
|
1424
2251
|
if (isAnyBlockerActive()) {
|
|
1425
2252
|
stopSkip();
|
|
@@ -1438,12 +2265,21 @@ function createStage() {
|
|
|
1438
2265
|
},
|
|
1439
2266
|
nextLine() {
|
|
1440
2267
|
handled = true;
|
|
1441
|
-
|
|
2268
|
+
if (warpActive) {
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
requestNextLine();
|
|
1442
2272
|
},
|
|
1443
2273
|
unskippable() {
|
|
2274
|
+
if (warpActive) {
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
1444
2277
|
unskippableFlag = true;
|
|
1445
2278
|
},
|
|
1446
2279
|
record(meta) {
|
|
2280
|
+
if (warpActive) {
|
|
2281
|
+
return inertRecordId;
|
|
2282
|
+
}
|
|
1447
2283
|
const result = executePluginCommand("scenario", {
|
|
1448
2284
|
subCommand: "record",
|
|
1449
2285
|
meta
|
|
@@ -1482,19 +2318,19 @@ function createStage() {
|
|
|
1482
2318
|
cmd = transformCommand(e);
|
|
1483
2319
|
} catch (err) {
|
|
1484
2320
|
console.error("Failed to transform scenario command:", err);
|
|
1485
|
-
|
|
2321
|
+
requestNextLine();
|
|
1486
2322
|
return;
|
|
1487
2323
|
}
|
|
1488
2324
|
const handler = commandHandlers.get(cmd.command);
|
|
1489
2325
|
if (!handler) {
|
|
1490
2326
|
console.warn(`[Stage] No handler registered for command "${cmd.command}". Auto-advancing.`);
|
|
1491
|
-
|
|
2327
|
+
requestNextLine();
|
|
1492
2328
|
return;
|
|
1493
2329
|
}
|
|
1494
2330
|
const { control, wasHandled } = buildControl();
|
|
1495
2331
|
handler(cmd, control);
|
|
1496
2332
|
if (!wasHandled()) {
|
|
1497
|
-
|
|
2333
|
+
requestNextLine();
|
|
1498
2334
|
}
|
|
1499
2335
|
}
|
|
1500
2336
|
function dispatchTextLine(e) {
|
|
@@ -1503,7 +2339,7 @@ function createStage() {
|
|
|
1503
2339
|
handler(e, control);
|
|
1504
2340
|
}
|
|
1505
2341
|
if (!wasHandled()) {
|
|
1506
|
-
|
|
2342
|
+
requestNextLine();
|
|
1507
2343
|
}
|
|
1508
2344
|
}
|
|
1509
2345
|
function invokeSkipCallbacks() {
|
|
@@ -1551,48 +2387,52 @@ function createStage() {
|
|
|
1551
2387
|
startAuto,
|
|
1552
2388
|
stopAuto,
|
|
1553
2389
|
isAutoing,
|
|
2390
|
+
startFastForward,
|
|
2391
|
+
stopFastForward,
|
|
2392
|
+
isFastForwarding,
|
|
2393
|
+
resetRuntimeState,
|
|
1554
2394
|
bindEvents
|
|
1555
2395
|
};
|
|
1556
2396
|
}
|
|
1557
|
-
var StageContext = (0,
|
|
2397
|
+
var StageContext = (0, import_react5.createContext)(null);
|
|
1558
2398
|
function useStageContext() {
|
|
1559
|
-
const ctx = (0,
|
|
2399
|
+
const ctx = (0, import_react5.useContext)(StageContext);
|
|
1560
2400
|
if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
|
|
1561
2401
|
return ctx;
|
|
1562
2402
|
}
|
|
1563
2403
|
function StageContextProvider({ stage, children }) {
|
|
1564
|
-
(0,
|
|
2404
|
+
(0, import_react5.useEffect)(() => {
|
|
1565
2405
|
return stage.bindEvents();
|
|
1566
2406
|
}, [stage]);
|
|
1567
|
-
return (0,
|
|
2407
|
+
return (0, import_react5.createElement)(StageContext.Provider, { value: stage }, children);
|
|
1568
2408
|
}
|
|
1569
2409
|
function useSkipCallback(callback) {
|
|
1570
2410
|
const stage = useStageContext();
|
|
1571
|
-
(0,
|
|
2411
|
+
(0, import_react5.useEffect)(() => {
|
|
1572
2412
|
return stage.addSkipCallback(callback);
|
|
1573
2413
|
}, [stage, callback]);
|
|
1574
2414
|
}
|
|
1575
2415
|
function useInterruptCallback(callback) {
|
|
1576
2416
|
const stage = useStageContext();
|
|
1577
|
-
(0,
|
|
2417
|
+
(0, import_react5.useEffect)(() => {
|
|
1578
2418
|
return stage.addInterruptCallback(callback);
|
|
1579
2419
|
}, [stage, callback]);
|
|
1580
2420
|
}
|
|
1581
2421
|
function useSkipBlocker(callback) {
|
|
1582
2422
|
const stage = useStageContext();
|
|
1583
|
-
(0,
|
|
2423
|
+
(0, import_react5.useEffect)(() => {
|
|
1584
2424
|
return stage.addSkipBlocker(callback);
|
|
1585
2425
|
}, [stage, callback]);
|
|
1586
2426
|
}
|
|
1587
2427
|
function useAutoBlocker(callback) {
|
|
1588
2428
|
const stage = useStageContext();
|
|
1589
|
-
(0,
|
|
2429
|
+
(0, import_react5.useEffect)(() => {
|
|
1590
2430
|
return stage.addAutoBlocker(callback);
|
|
1591
2431
|
}, [stage, callback]);
|
|
1592
2432
|
}
|
|
1593
2433
|
function useAutoTicket() {
|
|
1594
2434
|
const stage = useStageContext();
|
|
1595
|
-
return (0,
|
|
2435
|
+
return (0, import_react5.useCallback)(
|
|
1596
2436
|
(options) => {
|
|
1597
2437
|
return stage.issueAutoTicket(options);
|
|
1598
2438
|
},
|
|
@@ -1601,7 +2441,7 @@ function useAutoTicket() {
|
|
|
1601
2441
|
}
|
|
1602
2442
|
function useBeforeHandleCommandCallback(callback) {
|
|
1603
2443
|
const stage = useStageContext();
|
|
1604
|
-
(0,
|
|
2444
|
+
(0, import_react5.useEffect)(() => {
|
|
1605
2445
|
return stage.addBeforeHandleCommandCallback(callback);
|
|
1606
2446
|
}, [stage, callback]);
|
|
1607
2447
|
}
|