@momoyu-ink/kit 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -134,6 +134,10 @@ type JsonValue = number | string | boolean | Array<JsonValue> | {
134
134
  type ScenarioCommand = {
135
135
  subCommand: 'addStory';
136
136
  name: string;
137
+ } | {
138
+ subCommand: 'replaceStoryData';
139
+ name: string;
140
+ content: string;
137
141
  } | {
138
142
  subCommand: 'removeStory';
139
143
  name: string;
@@ -134,6 +134,10 @@ type JsonValue = number | string | boolean | Array<JsonValue> | {
134
134
  type ScenarioCommand = {
135
135
  subCommand: 'addStory';
136
136
  name: string;
137
+ } | {
138
+ subCommand: 'replaceStoryData';
139
+ name: string;
140
+ content: string;
137
141
  } | {
138
142
  subCommand: 'removeStory';
139
143
  name: string;
@@ -1,4 +1,4 @@
1
1
  export { Fragment, jsxDEV } from 'react/jsx-dev-runtime';
2
- export { J as JSX } from './jsx-dev-runtime-CKclO27C.mjs';
2
+ export { J as JSX } from './jsx-dev-runtime-BCxm528Z.mjs';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
@@ -1,4 +1,4 @@
1
1
  export { Fragment, jsxDEV } from 'react/jsx-dev-runtime';
2
- export { J as JSX } from './jsx-dev-runtime-CKclO27C.js';
2
+ export { J as JSX } from './jsx-dev-runtime-BCxm528Z.js';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
@@ -1,4 +1,4 @@
1
1
  export { Fragment, jsx, jsxs } from 'react/jsx-runtime';
2
- export { J as JSX } from './jsx-dev-runtime-CKclO27C.mjs';
2
+ export { J as JSX } from './jsx-dev-runtime-BCxm528Z.mjs';
3
3
  import 'react/jsx-dev-runtime';
4
4
  import 'react';
@@ -1,4 +1,4 @@
1
1
  export { Fragment, jsx, jsxs } from 'react/jsx-runtime';
2
- export { J as JSX } from './jsx-dev-runtime-CKclO27C.js';
2
+ export { J as JSX } from './jsx-dev-runtime-BCxm528Z.js';
3
3
  import 'react/jsx-dev-runtime';
4
4
  import 'react';
package/dist/lib.cjs CHANGED
@@ -1222,6 +1222,36 @@ async function clearLocalDebugSessionRuntimeState() {
1222
1222
  async function clearRemoteCheckpoints() {
1223
1223
  await executeScenarioCommand({ subCommand: "clearCheckpoints" });
1224
1224
  }
1225
+ function pruneLocalStoryCheckpoints(story, preserved) {
1226
+ debugState.checkpoints = Object.fromEntries(
1227
+ Object.entries(debugState.checkpoints).filter(([key, checkpoint]) => {
1228
+ if (checkpoint.cursor.story !== story) {
1229
+ return true;
1230
+ }
1231
+ return preserved.has(key);
1232
+ })
1233
+ );
1234
+ }
1235
+ async function syncCurrentExecutionCursor(fallbackStory = null) {
1236
+ debugState.currentCursor = await executeScenarioCommand({
1237
+ subCommand: "getExecutionCursor"
1238
+ });
1239
+ debugState.currentStory = debugState.currentCursor?.story ?? fallbackStory;
1240
+ }
1241
+ async function executeRestartBoundary(boundary, story) {
1242
+ if (boundary.kind === "checkpoint") {
1243
+ await debugSessionController.restoreCheckpoint(boundary.checkpointKey, { story });
1244
+ return;
1245
+ }
1246
+ if (!appStateAdapter?.restartStoryFromHead) {
1247
+ throw new Error("Restarting a story from an explicit entry is not supported by the app state adapter");
1248
+ }
1249
+ await appStateAdapter.restartStoryFromHead({
1250
+ story: boundary.story,
1251
+ entry: boundary.kind === "restart-story" ? boundary.entry : boundary.paragraph
1252
+ });
1253
+ await syncCurrentExecutionCursor(boundary.story);
1254
+ }
1225
1255
  async function syncLocalWarpCheckpoint(targetMarkerId) {
1226
1256
  const cursor = await executeScenarioCommand({
1227
1257
  subCommand: "getExecutionCursor"
@@ -1281,15 +1311,17 @@ async function prepareJumpStart(markerId, options) {
1281
1311
  subCommand: "restoreCheckpoint",
1282
1312
  key: markerId
1283
1313
  });
1284
- if (!restored2) {
1285
- throw new Error(`Failed to restore checkpoint ${markerId}`);
1286
- }
1287
- if (appStateAdapter) {
1288
- await appStateAdapter.restore(checkpoint.appState);
1314
+ if (restored2) {
1315
+ if (appStateAdapter) {
1316
+ await appStateAdapter.restore(checkpoint.appState);
1317
+ }
1318
+ debugState.currentCursor = checkpoint.cursor;
1319
+ debugState.currentStory = checkpoint.cursor.story;
1320
+ return false;
1289
1321
  }
1290
- debugState.currentCursor = checkpoint.cursor;
1291
- debugState.currentStory = checkpoint.cursor.story;
1292
- return false;
1322
+ debugState.checkpoints = Object.fromEntries(
1323
+ Object.entries(debugState.checkpoints).filter(([key]) => key !== markerId)
1324
+ );
1293
1325
  }
1294
1326
  const lookup = await executeScenarioCommand({
1295
1327
  subCommand: "getFastForwardCheckpoint",
@@ -1309,7 +1341,8 @@ async function prepareJumpStart(markerId, options) {
1309
1341
  }
1310
1342
  const startCheckpoint = debugState.checkpoints[startCheckpointKey];
1311
1343
  if (!startCheckpoint) {
1312
- throw new Error(`Local checkpoint ${startCheckpointKey} not found`);
1344
+ await restartJumpStoryFromHead(targetStory, currentStory);
1345
+ return true;
1313
1346
  }
1314
1347
  const restored = await executeScenarioCommand({
1315
1348
  subCommand: "restoreCheckpoint",
@@ -1452,6 +1485,46 @@ var debugSessionController = {
1452
1485
  throw error;
1453
1486
  }
1454
1487
  });
1488
+ },
1489
+ async replaceStory(options) {
1490
+ debugState.restoring = true;
1491
+ debugState.lastError = null;
1492
+ try {
1493
+ const outcome = await enqueueDebugOperation(
1494
+ async () => executeScenarioCommand({
1495
+ subCommand: "replaceStoryData",
1496
+ name: options.story,
1497
+ content: options.content
1498
+ })
1499
+ );
1500
+ if (outcome.plan.mode === "invalidate-only") {
1501
+ pruneLocalStoryCheckpoints(options.story, /* @__PURE__ */ new Set());
1502
+ }
1503
+ if (outcome.plan.mode === "reposition") {
1504
+ if (!outcome.plan.boundary) {
1505
+ throw new Error("Story replace outcome is missing an executable boundary");
1506
+ }
1507
+ const seekMode = options.seekMode ?? "warp";
1508
+ const replaceTargetMarkerId = options.targetMarkerId ?? outcome.plan.targetMarkerId;
1509
+ await executeRestartBoundary(outcome.plan.boundary, options.story);
1510
+ if (replaceTargetMarkerId && !(outcome.plan.boundary.kind === "checkpoint" && outcome.plan.boundary.checkpointKey === replaceTargetMarkerId)) {
1511
+ if (seekMode === "warp") {
1512
+ await runWarp({
1513
+ markerId: replaceTargetMarkerId,
1514
+ boundary: "after"
1515
+ });
1516
+ } else if (seekMode === "fast-forward") {
1517
+ await runFastForwardToMarker(replaceTargetMarkerId);
1518
+ }
1519
+ }
1520
+ }
1521
+ return outcome;
1522
+ } catch (error) {
1523
+ debugState.lastError = toErrorMessage(error);
1524
+ throw error;
1525
+ } finally {
1526
+ debugState.restoring = false;
1527
+ }
1455
1528
  }
1456
1529
  };
1457
1530
  function registerAppStateAdapter(adapter) {
@@ -1556,7 +1629,8 @@ function useDebugSession() {
1556
1629
  stopDebugSession,
1557
1630
  restoreCheckpoint: debugSessionController.restoreCheckpoint,
1558
1631
  warp: debugSessionController.warp,
1559
- switchRoute: debugSessionController.switchRoute
1632
+ switchRoute: debugSessionController.switchRoute,
1633
+ replaceStory: debugSessionController.replaceStory
1560
1634
  };
1561
1635
  }
1562
1636
 
@@ -1646,6 +1720,9 @@ function isJumpRequestMessage(message) {
1646
1720
  function isRouteRequestMessage(message) {
1647
1721
  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));
1648
1722
  }
1723
+ function isStoryReplaceMessage(message) {
1724
+ return message.type === "story:replace" && typeof message.sessionId === "string" && typeof message.requestId === "number" && typeof message.story === "string" && typeof message.content === "string" && (message.seekMode === void 0 || message.seekMode === "off" || message.seekMode === "fast-forward" || message.seekMode === "warp") && (message.targetMarkerId === void 0 || typeof message.targetMarkerId === "string");
1725
+ }
1649
1726
  function isUiReplaceMessage(message) {
1650
1727
  return message.type === "ui:replace" && typeof message.sessionId === "string";
1651
1728
  }
@@ -1732,6 +1809,35 @@ function handleRuntimeDebugRequest(socket, sessionId, message) {
1732
1809
  });
1733
1810
  return;
1734
1811
  }
1812
+ if (isStoryReplaceMessage(message)) {
1813
+ setRuntimeDebugVariableEmissionSuspended(true);
1814
+ void controller.replaceStory({
1815
+ story: message.story,
1816
+ content: message.content,
1817
+ seekMode: message.seekMode,
1818
+ targetMarkerId: message.targetMarkerId
1819
+ }).then(async () => {
1820
+ sendRuntimeDebugMessage(socket, {
1821
+ type: "story:replace:done",
1822
+ sessionId,
1823
+ requestId: message.requestId,
1824
+ story: message.story
1825
+ });
1826
+ await syncRuntimeDebugVariablesSnapshotSafe("story-replace-done");
1827
+ }).catch(async (error) => {
1828
+ sendRuntimeDebugMessage(socket, {
1829
+ type: "story:replace:error",
1830
+ sessionId,
1831
+ requestId: message.requestId,
1832
+ story: message.story,
1833
+ error: error instanceof Error ? error.message : String(error)
1834
+ });
1835
+ await syncRuntimeDebugVariablesSnapshotSafe("story-replace-error");
1836
+ }).finally(() => {
1837
+ setRuntimeDebugVariableEmissionSuspended(false);
1838
+ });
1839
+ return;
1840
+ }
1735
1841
  setRuntimeDebugVariableEmissionSuspended(true);
1736
1842
  void controller.switchRoute(message.page, message.params).then(async () => {
1737
1843
  sendRuntimeDebugMessage(socket, {
@@ -1869,6 +1975,10 @@ async function startRuntimeDebugSession() {
1869
1975
  handleRuntimeDebugRequest(socket, sessionId, message);
1870
1976
  return;
1871
1977
  }
1978
+ if (isStoryReplaceMessage(message)) {
1979
+ handleRuntimeDebugRequest(socket, sessionId, message);
1980
+ return;
1981
+ }
1872
1982
  if (isUiReplaceMessage(message)) {
1873
1983
  handleRuntimeDebugRequest(socket, sessionId, message);
1874
1984
  return;
package/dist/lib.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactElement, Component, ForwardRefExoticComponent, CSSProperties, FC, ComponentType } from 'react';
3
- import { M as MouseEvent, a as MoyuEvent, C as Command, b as MaybePromise, J as JSX, c as JsonValue } from './jsx-dev-runtime-CKclO27C.mjs';
4
- export { A as AudioCommand, d as AudioSettings, B as BubbleEvent, D as DetailedMoyuProps, E as EffectParams, G as GamepadCommand, K as KeyboardEvent, e as KeyboardEventKind, f as MakeNullOptional, g as MouseEventKind, h as MoyuAnimationAttributes, i as MoyuBackdropAttributes, j as MoyuClipAttributes, k as MoyuContainerAttributes, l as MoyuEventHandler, m as MoyuFilterAttributes, n as MoyuListenerAttributes, o as MoyuNodeAttributes, p as MoyuSpriteAttributes, q as MoyuTextAttributes, r as MoyuVideoAttributes, N as Node, S as ScenarioCommand, s as SystemCommand, T as TextCommand, t as TouchEvent, u as TouchEventKind, v as Tuple2, w as Tuple3, x as Tuple4, W as WindowState, y as createBubbleEvent, z as getStageSize } from './jsx-dev-runtime-CKclO27C.mjs';
3
+ import { M as MouseEvent, a as MoyuEvent, C as Command, b as MaybePromise, J as JSX, c as JsonValue } from './jsx-dev-runtime-BCxm528Z.mjs';
4
+ export { A as AudioCommand, d as AudioSettings, B as BubbleEvent, D as DetailedMoyuProps, E as EffectParams, G as GamepadCommand, K as KeyboardEvent, e as KeyboardEventKind, f as MakeNullOptional, g as MouseEventKind, h as MoyuAnimationAttributes, i as MoyuBackdropAttributes, j as MoyuClipAttributes, k as MoyuContainerAttributes, l as MoyuEventHandler, m as MoyuFilterAttributes, n as MoyuListenerAttributes, o as MoyuNodeAttributes, p as MoyuSpriteAttributes, q as MoyuTextAttributes, r as MoyuVideoAttributes, N as Node, S as ScenarioCommand, s as SystemCommand, T as TextCommand, t as TouchEvent, u as TouchEventKind, v as Tuple2, w as Tuple3, x as Tuple4, W as WindowState, y as createBubbleEvent, z as getStageSize } from './jsx-dev-runtime-BCxm528Z.mjs';
5
5
  import { FluidValue } from '@react-spring/shared';
6
6
  import { ElementType, AssignableKeys, ComponentPropsWithRef } from '@react-spring/types';
7
7
  import { Snapshot } from 'valtio';
@@ -522,6 +522,34 @@ type ExecutionCursor = {
522
522
  markerId?: string;
523
523
  };
524
524
 
525
+ type ExecutableRestartBoundary = {
526
+ kind: 'checkpoint';
527
+ checkpointKey: string;
528
+ } | {
529
+ kind: 'restart-story';
530
+ story: string;
531
+ entry: string;
532
+ } | {
533
+ kind: 'restart-paragraph';
534
+ story: string;
535
+ paragraph: string;
536
+ };
537
+
538
+ type StoryReplaceMode = 'noop' | 'reposition' | 'invalidate-only';
539
+
540
+ type StoryReplaceExecutionPlan = {
541
+ mode: StoryReplaceMode;
542
+ boundary?: ExecutableRestartBoundary;
543
+ targetMarkerId?: string;
544
+ changedControlFlow: boolean;
545
+ };
546
+
547
+ type StoryReplaceOutcome = {
548
+ story: string;
549
+ currentStoryAffected: boolean;
550
+ plan: StoryReplaceExecutionPlan;
551
+ };
552
+
525
553
  /** Flow control interface passed to every handler. */
526
554
  interface GameControl {
527
555
  /** Timed wait — blocks auto-advance. Engine fires scenariowaitingcancelled on timeout or skip. */
@@ -695,6 +723,12 @@ interface DebugSessionController {
695
723
  restoreCheckpoint(markerId: string, options?: DebugJumpTargetOptions): Promise<boolean>;
696
724
  warp(options: DebugWarpOptions): Promise<void>;
697
725
  switchRoute(routeName: string, params?: Record<string, unknown>): Promise<void>;
726
+ replaceStory(options: {
727
+ story: string;
728
+ content: string;
729
+ seekMode?: 'off' | 'fast-forward' | 'warp';
730
+ targetMarkerId?: string;
731
+ }): Promise<StoryReplaceOutcome>;
698
732
  }
699
733
  interface DebugStoryStartOptions {
700
734
  story: string;
@@ -718,6 +752,12 @@ declare function useDebugSession(): {
718
752
  restoreCheckpoint: (markerId: string, options?: DebugJumpTargetOptions) => Promise<boolean>;
719
753
  warp: (options: DebugWarpOptions) => Promise<void>;
720
754
  switchRoute: (routeName: string, params?: Record<string, unknown>) => Promise<void>;
755
+ replaceStory: (options: {
756
+ story: string;
757
+ content: string;
758
+ seekMode?: "off" | "fast-forward" | "warp";
759
+ targetMarkerId?: string;
760
+ }) => Promise<StoryReplaceOutcome>;
721
761
  active: boolean;
722
762
  checkpoints: {
723
763
  readonly [x: string]: {
package/dist/lib.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactElement, Component, ForwardRefExoticComponent, CSSProperties, FC, ComponentType } from 'react';
3
- import { M as MouseEvent, a as MoyuEvent, C as Command, b as MaybePromise, J as JSX, c as JsonValue } from './jsx-dev-runtime-CKclO27C.js';
4
- export { A as AudioCommand, d as AudioSettings, B as BubbleEvent, D as DetailedMoyuProps, E as EffectParams, G as GamepadCommand, K as KeyboardEvent, e as KeyboardEventKind, f as MakeNullOptional, g as MouseEventKind, h as MoyuAnimationAttributes, i as MoyuBackdropAttributes, j as MoyuClipAttributes, k as MoyuContainerAttributes, l as MoyuEventHandler, m as MoyuFilterAttributes, n as MoyuListenerAttributes, o as MoyuNodeAttributes, p as MoyuSpriteAttributes, q as MoyuTextAttributes, r as MoyuVideoAttributes, N as Node, S as ScenarioCommand, s as SystemCommand, T as TextCommand, t as TouchEvent, u as TouchEventKind, v as Tuple2, w as Tuple3, x as Tuple4, W as WindowState, y as createBubbleEvent, z as getStageSize } from './jsx-dev-runtime-CKclO27C.js';
3
+ import { M as MouseEvent, a as MoyuEvent, C as Command, b as MaybePromise, J as JSX, c as JsonValue } from './jsx-dev-runtime-BCxm528Z.js';
4
+ export { A as AudioCommand, d as AudioSettings, B as BubbleEvent, D as DetailedMoyuProps, E as EffectParams, G as GamepadCommand, K as KeyboardEvent, e as KeyboardEventKind, f as MakeNullOptional, g as MouseEventKind, h as MoyuAnimationAttributes, i as MoyuBackdropAttributes, j as MoyuClipAttributes, k as MoyuContainerAttributes, l as MoyuEventHandler, m as MoyuFilterAttributes, n as MoyuListenerAttributes, o as MoyuNodeAttributes, p as MoyuSpriteAttributes, q as MoyuTextAttributes, r as MoyuVideoAttributes, N as Node, S as ScenarioCommand, s as SystemCommand, T as TextCommand, t as TouchEvent, u as TouchEventKind, v as Tuple2, w as Tuple3, x as Tuple4, W as WindowState, y as createBubbleEvent, z as getStageSize } from './jsx-dev-runtime-BCxm528Z.js';
5
5
  import { FluidValue } from '@react-spring/shared';
6
6
  import { ElementType, AssignableKeys, ComponentPropsWithRef } from '@react-spring/types';
7
7
  import { Snapshot } from 'valtio';
@@ -522,6 +522,34 @@ type ExecutionCursor = {
522
522
  markerId?: string;
523
523
  };
524
524
 
525
+ type ExecutableRestartBoundary = {
526
+ kind: 'checkpoint';
527
+ checkpointKey: string;
528
+ } | {
529
+ kind: 'restart-story';
530
+ story: string;
531
+ entry: string;
532
+ } | {
533
+ kind: 'restart-paragraph';
534
+ story: string;
535
+ paragraph: string;
536
+ };
537
+
538
+ type StoryReplaceMode = 'noop' | 'reposition' | 'invalidate-only';
539
+
540
+ type StoryReplaceExecutionPlan = {
541
+ mode: StoryReplaceMode;
542
+ boundary?: ExecutableRestartBoundary;
543
+ targetMarkerId?: string;
544
+ changedControlFlow: boolean;
545
+ };
546
+
547
+ type StoryReplaceOutcome = {
548
+ story: string;
549
+ currentStoryAffected: boolean;
550
+ plan: StoryReplaceExecutionPlan;
551
+ };
552
+
525
553
  /** Flow control interface passed to every handler. */
526
554
  interface GameControl {
527
555
  /** Timed wait — blocks auto-advance. Engine fires scenariowaitingcancelled on timeout or skip. */
@@ -695,6 +723,12 @@ interface DebugSessionController {
695
723
  restoreCheckpoint(markerId: string, options?: DebugJumpTargetOptions): Promise<boolean>;
696
724
  warp(options: DebugWarpOptions): Promise<void>;
697
725
  switchRoute(routeName: string, params?: Record<string, unknown>): Promise<void>;
726
+ replaceStory(options: {
727
+ story: string;
728
+ content: string;
729
+ seekMode?: 'off' | 'fast-forward' | 'warp';
730
+ targetMarkerId?: string;
731
+ }): Promise<StoryReplaceOutcome>;
698
732
  }
699
733
  interface DebugStoryStartOptions {
700
734
  story: string;
@@ -718,6 +752,12 @@ declare function useDebugSession(): {
718
752
  restoreCheckpoint: (markerId: string, options?: DebugJumpTargetOptions) => Promise<boolean>;
719
753
  warp: (options: DebugWarpOptions) => Promise<void>;
720
754
  switchRoute: (routeName: string, params?: Record<string, unknown>) => Promise<void>;
755
+ replaceStory: (options: {
756
+ story: string;
757
+ content: string;
758
+ seekMode?: "off" | "fast-forward" | "warp";
759
+ targetMarkerId?: string;
760
+ }) => Promise<StoryReplaceOutcome>;
721
761
  active: boolean;
722
762
  checkpoints: {
723
763
  readonly [x: string]: {
package/dist/lib.mjs CHANGED
@@ -1210,6 +1210,36 @@ async function clearLocalDebugSessionRuntimeState() {
1210
1210
  async function clearRemoteCheckpoints() {
1211
1211
  await executeScenarioCommand({ subCommand: "clearCheckpoints" });
1212
1212
  }
1213
+ function pruneLocalStoryCheckpoints(story, preserved) {
1214
+ debugState.checkpoints = Object.fromEntries(
1215
+ Object.entries(debugState.checkpoints).filter(([key, checkpoint]) => {
1216
+ if (checkpoint.cursor.story !== story) {
1217
+ return true;
1218
+ }
1219
+ return preserved.has(key);
1220
+ })
1221
+ );
1222
+ }
1223
+ async function syncCurrentExecutionCursor(fallbackStory = null) {
1224
+ debugState.currentCursor = await executeScenarioCommand({
1225
+ subCommand: "getExecutionCursor"
1226
+ });
1227
+ debugState.currentStory = debugState.currentCursor?.story ?? fallbackStory;
1228
+ }
1229
+ async function executeRestartBoundary(boundary, story) {
1230
+ if (boundary.kind === "checkpoint") {
1231
+ await debugSessionController.restoreCheckpoint(boundary.checkpointKey, { story });
1232
+ return;
1233
+ }
1234
+ if (!appStateAdapter?.restartStoryFromHead) {
1235
+ throw new Error("Restarting a story from an explicit entry is not supported by the app state adapter");
1236
+ }
1237
+ await appStateAdapter.restartStoryFromHead({
1238
+ story: boundary.story,
1239
+ entry: boundary.kind === "restart-story" ? boundary.entry : boundary.paragraph
1240
+ });
1241
+ await syncCurrentExecutionCursor(boundary.story);
1242
+ }
1213
1243
  async function syncLocalWarpCheckpoint(targetMarkerId) {
1214
1244
  const cursor = await executeScenarioCommand({
1215
1245
  subCommand: "getExecutionCursor"
@@ -1269,15 +1299,17 @@ async function prepareJumpStart(markerId, options) {
1269
1299
  subCommand: "restoreCheckpoint",
1270
1300
  key: markerId
1271
1301
  });
1272
- if (!restored2) {
1273
- throw new Error(`Failed to restore checkpoint ${markerId}`);
1274
- }
1275
- if (appStateAdapter) {
1276
- await appStateAdapter.restore(checkpoint.appState);
1302
+ if (restored2) {
1303
+ if (appStateAdapter) {
1304
+ await appStateAdapter.restore(checkpoint.appState);
1305
+ }
1306
+ debugState.currentCursor = checkpoint.cursor;
1307
+ debugState.currentStory = checkpoint.cursor.story;
1308
+ return false;
1277
1309
  }
1278
- debugState.currentCursor = checkpoint.cursor;
1279
- debugState.currentStory = checkpoint.cursor.story;
1280
- return false;
1310
+ debugState.checkpoints = Object.fromEntries(
1311
+ Object.entries(debugState.checkpoints).filter(([key]) => key !== markerId)
1312
+ );
1281
1313
  }
1282
1314
  const lookup = await executeScenarioCommand({
1283
1315
  subCommand: "getFastForwardCheckpoint",
@@ -1297,7 +1329,8 @@ async function prepareJumpStart(markerId, options) {
1297
1329
  }
1298
1330
  const startCheckpoint = debugState.checkpoints[startCheckpointKey];
1299
1331
  if (!startCheckpoint) {
1300
- throw new Error(`Local checkpoint ${startCheckpointKey} not found`);
1332
+ await restartJumpStoryFromHead(targetStory, currentStory);
1333
+ return true;
1301
1334
  }
1302
1335
  const restored = await executeScenarioCommand({
1303
1336
  subCommand: "restoreCheckpoint",
@@ -1440,6 +1473,46 @@ var debugSessionController = {
1440
1473
  throw error;
1441
1474
  }
1442
1475
  });
1476
+ },
1477
+ async replaceStory(options) {
1478
+ debugState.restoring = true;
1479
+ debugState.lastError = null;
1480
+ try {
1481
+ const outcome = await enqueueDebugOperation(
1482
+ async () => executeScenarioCommand({
1483
+ subCommand: "replaceStoryData",
1484
+ name: options.story,
1485
+ content: options.content
1486
+ })
1487
+ );
1488
+ if (outcome.plan.mode === "invalidate-only") {
1489
+ pruneLocalStoryCheckpoints(options.story, /* @__PURE__ */ new Set());
1490
+ }
1491
+ if (outcome.plan.mode === "reposition") {
1492
+ if (!outcome.plan.boundary) {
1493
+ throw new Error("Story replace outcome is missing an executable boundary");
1494
+ }
1495
+ const seekMode = options.seekMode ?? "warp";
1496
+ const replaceTargetMarkerId = options.targetMarkerId ?? outcome.plan.targetMarkerId;
1497
+ await executeRestartBoundary(outcome.plan.boundary, options.story);
1498
+ if (replaceTargetMarkerId && !(outcome.plan.boundary.kind === "checkpoint" && outcome.plan.boundary.checkpointKey === replaceTargetMarkerId)) {
1499
+ if (seekMode === "warp") {
1500
+ await runWarp({
1501
+ markerId: replaceTargetMarkerId,
1502
+ boundary: "after"
1503
+ });
1504
+ } else if (seekMode === "fast-forward") {
1505
+ await runFastForwardToMarker(replaceTargetMarkerId);
1506
+ }
1507
+ }
1508
+ }
1509
+ return outcome;
1510
+ } catch (error) {
1511
+ debugState.lastError = toErrorMessage(error);
1512
+ throw error;
1513
+ } finally {
1514
+ debugState.restoring = false;
1515
+ }
1443
1516
  }
1444
1517
  };
1445
1518
  function registerAppStateAdapter(adapter) {
@@ -1544,7 +1617,8 @@ function useDebugSession() {
1544
1617
  stopDebugSession,
1545
1618
  restoreCheckpoint: debugSessionController.restoreCheckpoint,
1546
1619
  warp: debugSessionController.warp,
1547
- switchRoute: debugSessionController.switchRoute
1620
+ switchRoute: debugSessionController.switchRoute,
1621
+ replaceStory: debugSessionController.replaceStory
1548
1622
  };
1549
1623
  }
1550
1624
 
@@ -1634,6 +1708,9 @@ function isJumpRequestMessage(message) {
1634
1708
  function isRouteRequestMessage(message) {
1635
1709
  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));
1636
1710
  }
1711
+ function isStoryReplaceMessage(message) {
1712
+ return message.type === "story:replace" && typeof message.sessionId === "string" && typeof message.requestId === "number" && typeof message.story === "string" && typeof message.content === "string" && (message.seekMode === void 0 || message.seekMode === "off" || message.seekMode === "fast-forward" || message.seekMode === "warp") && (message.targetMarkerId === void 0 || typeof message.targetMarkerId === "string");
1713
+ }
1637
1714
  function isUiReplaceMessage(message) {
1638
1715
  return message.type === "ui:replace" && typeof message.sessionId === "string";
1639
1716
  }
@@ -1720,6 +1797,35 @@ function handleRuntimeDebugRequest(socket, sessionId, message) {
1720
1797
  });
1721
1798
  return;
1722
1799
  }
1800
+ if (isStoryReplaceMessage(message)) {
1801
+ setRuntimeDebugVariableEmissionSuspended(true);
1802
+ void controller.replaceStory({
1803
+ story: message.story,
1804
+ content: message.content,
1805
+ seekMode: message.seekMode,
1806
+ targetMarkerId: message.targetMarkerId
1807
+ }).then(async () => {
1808
+ sendRuntimeDebugMessage(socket, {
1809
+ type: "story:replace:done",
1810
+ sessionId,
1811
+ requestId: message.requestId,
1812
+ story: message.story
1813
+ });
1814
+ await syncRuntimeDebugVariablesSnapshotSafe("story-replace-done");
1815
+ }).catch(async (error) => {
1816
+ sendRuntimeDebugMessage(socket, {
1817
+ type: "story:replace:error",
1818
+ sessionId,
1819
+ requestId: message.requestId,
1820
+ story: message.story,
1821
+ error: error instanceof Error ? error.message : String(error)
1822
+ });
1823
+ await syncRuntimeDebugVariablesSnapshotSafe("story-replace-error");
1824
+ }).finally(() => {
1825
+ setRuntimeDebugVariableEmissionSuspended(false);
1826
+ });
1827
+ return;
1828
+ }
1723
1829
  setRuntimeDebugVariableEmissionSuspended(true);
1724
1830
  void controller.switchRoute(message.page, message.params).then(async () => {
1725
1831
  sendRuntimeDebugMessage(socket, {
@@ -1857,6 +1963,10 @@ async function startRuntimeDebugSession() {
1857
1963
  handleRuntimeDebugRequest(socket, sessionId, message);
1858
1964
  return;
1859
1965
  }
1966
+ if (isStoryReplaceMessage(message)) {
1967
+ handleRuntimeDebugRequest(socket, sessionId, message);
1968
+ return;
1969
+ }
1860
1970
  if (isUiReplaceMessage(message)) {
1861
1971
  handleRuntimeDebugRequest(socket, sessionId, message);
1862
1972
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momoyu-ink/kit",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Software Development Kit of Moyu Visual Novel Engine",
5
5
  "main": "dist/lib.cjs",
6
6
  "module": "dist/lib.mjs",