@momoyu-ink/kit 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib.cjs CHANGED
@@ -34,14 +34,18 @@ __export(lib_exports, {
34
34
  Button: () => Button,
35
35
  Checkbox: () => Checkbox,
36
36
  DEBUG_EMPTY_PAGE: () => DEBUG_EMPTY_PAGE,
37
+ Input: () => Input,
37
38
  OverlayParamsContext: () => OverlayParamsContext,
38
39
  PageParamsContext: () => PageParamsContext,
40
+ Radio: () => Radio,
41
+ RadioGroup: () => RadioGroup,
39
42
  ScrollView: () => ScrollView,
40
43
  Select: () => Select,
41
44
  Slider: () => Slider,
42
45
  StageContextProvider: () => StageContextProvider,
43
46
  a: () => animated,
44
47
  addEventListener: () => addEventListener,
48
+ addFluidObserver: () => addFluidObserver,
45
49
  animated: () => animated,
46
50
  autoState: () => autoState,
47
51
  createBubbleEvent: () => createBubbleEvent,
@@ -68,6 +72,7 @@ __export(lib_exports, {
68
72
  moyu: () => moyu_exports,
69
73
  nextLine: () => nextLine,
70
74
  registerAppStateAdapter: () => registerAppStateAdapter,
75
+ removeFluidObserver: () => removeFluidObserver,
71
76
  replaceUiData: () => replaceUiData,
72
77
  setDefaultAutoTailMs: () => setDefaultAutoTailMs,
73
78
  setWaiting: () => setWaiting,
@@ -110,10 +115,12 @@ var import_constants = require("react-reconciler/constants.js");
110
115
  var moyu_exports = {};
111
116
  __export(moyu_exports, {
112
117
  addChild: () => addChild,
118
+ blurEditable: () => blurEditable,
113
119
  createInstance: () => createInstance,
114
120
  destroyInstance: () => destroyInstance,
115
121
  executeNodeCommand: () => executeNodeCommand,
116
122
  executePluginCommand: () => executePluginCommand,
123
+ focusEditable: () => focusEditable,
117
124
  insertChild: () => insertChild,
118
125
  insertChildBefore: () => insertChildBefore,
119
126
  removeChild: () => removeChild,
@@ -151,6 +158,12 @@ function removeChild(nodeId, childNodeId) {
151
158
  function updateProps(nodeId, props) {
152
159
  moyu.pushCommand("update_props", [nodeId, props]);
153
160
  }
161
+ function focusEditable(nodeId) {
162
+ moyu.pushCommand("focus_editable", [nodeId]);
163
+ }
164
+ function blurEditable(nodeId) {
165
+ moyu.pushCommand("blur_editable", [nodeId]);
166
+ }
154
167
  function executeNodeCommand(nodeId, payload) {
155
168
  return moyu.executeNodeCommand(nodeId, payload);
156
169
  }
@@ -794,7 +807,9 @@ globalThis.__moyu_eval_sandbox = (code) => {
794
807
  var spring_exports = {};
795
808
  __export(spring_exports, {
796
809
  a: () => animated,
797
- animated: () => animated
810
+ addFluidObserver: () => addFluidObserver,
811
+ animated: () => animated,
812
+ removeFluidObserver: () => removeFluidObserver
798
813
  });
799
814
  var import_animated = require("@react-spring/animated");
800
815
  var import_shared = require("@react-spring/shared");
@@ -827,6 +842,8 @@ var host = (0, import_animated.createHost)(primitives, {
827
842
  }
828
843
  });
829
844
  var animated = host.animated;
845
+ var addFluidObserver = import_shared.addFluidObserver;
846
+ var removeFluidObserver = import_shared.removeFluidObserver;
830
847
 
831
848
  // src/lib.ts
832
849
  __reExport(lib_exports, spring_exports, module.exports);
@@ -851,7 +868,8 @@ function createBubbleEvent(body, targetId) {
851
868
 
852
869
  // src/events/globals.ts
853
870
  var globalEventListeners = {};
854
- var globalRequestAnimationFrameListeners = [];
871
+ var globalRequestAnimationFrameListeners = /* @__PURE__ */ new Map();
872
+ var nextAnimationFrameHandle = 0;
855
873
  var BUBBLE_EVENT_NAMES = ["mouseevent", "touchevent", "keyboardevent", "wheelevent"];
856
874
  globalThis.__moyu_receive_event = (raw_event) => {
857
875
  const { name, body } = raw_event;
@@ -878,7 +896,8 @@ globalThis.__moyu_receive_event = (raw_event) => {
878
896
  return;
879
897
  }
880
898
  case "animationframecallbackevent": {
881
- const listeners = globalRequestAnimationFrameListeners.splice(0);
899
+ const listeners = [...globalRequestAnimationFrameListeners.values()];
900
+ globalRequestAnimationFrameListeners.clear();
882
901
  const { timestamp } = body;
883
902
  for (const listener of listeners) {
884
903
  listener?.(timestamp);
@@ -936,10 +955,13 @@ function handleBubbleEvent(name, body) {
936
955
  }
937
956
  if (globalThis.__moyu_native) {
938
957
  globalThis.requestAnimationFrame = (callback) => {
939
- return globalRequestAnimationFrameListeners.push(callback) - 1;
958
+ const handle = nextAnimationFrameHandle;
959
+ nextAnimationFrameHandle += 1;
960
+ globalRequestAnimationFrameListeners.set(handle, callback);
961
+ return handle;
940
962
  };
941
963
  globalThis.cancelAnimationFrame = (handle) => {
942
- globalRequestAnimationFrameListeners.splice(handle, 1);
964
+ globalRequestAnimationFrameListeners.delete(handle);
943
965
  };
944
966
  }
945
967
 
@@ -1390,8 +1412,343 @@ function Checkbox({
1390
1412
  );
1391
1413
  }
1392
1414
 
1393
- // src/components/select.tsx
1415
+ // src/components/input.tsx
1394
1416
  var import_react6 = require("react");
1417
+ var Input = (0, import_react6.forwardRef)(function Input2({
1418
+ value,
1419
+ defaultValue = "",
1420
+ placeholder = "",
1421
+ disabled = false,
1422
+ readOnly = false,
1423
+ autoFocus = false,
1424
+ width,
1425
+ height,
1426
+ paddingX = 0,
1427
+ textStyle,
1428
+ placeholderStyle,
1429
+ caret,
1430
+ background,
1431
+ onInput,
1432
+ onChange,
1433
+ onFocus,
1434
+ onBlur,
1435
+ onCompositionStart,
1436
+ onCompositionUpdate,
1437
+ onCompositionEnd,
1438
+ interactive,
1439
+ cursor = "text",
1440
+ onMouseEnter,
1441
+ onMouseLeave,
1442
+ onMouseDown,
1443
+ onMouseUp,
1444
+ onTouchStart,
1445
+ onTouchEnd,
1446
+ onTouchCancel,
1447
+ ...nodeProps
1448
+ }, ref) {
1449
+ const editableRef = (0, import_react6.useRef)(null);
1450
+ const initialValueRef = (0, import_react6.useRef)(value ?? defaultValue);
1451
+ const initialValue = initialValueRef.current;
1452
+ const stateRef = (0, import_react6.useRef)({
1453
+ value: initialValue,
1454
+ isComposing: false,
1455
+ compositionText: ""
1456
+ });
1457
+ const [state, setState] = (0, import_react6.useState)(stateRef.current);
1458
+ const [focused, setFocused] = (0, import_react6.useState)(false);
1459
+ const [hovered, setHovered] = (0, import_react6.useState)(false);
1460
+ const [pressed, setPressed] = (0, import_react6.useState)(false);
1461
+ const [caretPosition, setCaretPosition] = (0, import_react6.useState)();
1462
+ const [caretVisible, setCaretVisible] = (0, import_react6.useState)(true);
1463
+ const shouldAutoFocusRef = (0, import_react6.useRef)(autoFocus && !disabled);
1464
+ const viewportWidth = Math.max(0, Math.round(width));
1465
+ const viewportHeight = Math.max(0, Math.round(height));
1466
+ const contentWidth = Math.max(0, viewportWidth - paddingX * 2);
1467
+ const { width: caretWidth, height: configuredCaretHeight, blinkInterval, ...caretSpriteProps } = caret;
1468
+ const caretHeight = configuredCaretHeight ?? textStyle.fontSize ?? viewportHeight;
1469
+ const backgroundStyle = background && "idle" in background ? disabled ? background.disabled ?? background.idle : readOnly ? background.readOnly ?? background.idle : pressed ? background.press ?? background.idle : focused ? background.focused ?? background.idle : hovered ? background.hover ?? background.idle : background.idle : background;
1470
+ const displayValue = state.value + state.compositionText;
1471
+ const updateState = (0, import_react6.useCallback)((nextState) => {
1472
+ const previousState = stateRef.current;
1473
+ if (previousState.value === nextState.value && previousState.isComposing === nextState.isComposing && previousState.compositionText === nextState.compositionText) {
1474
+ return;
1475
+ }
1476
+ const displayValueChanged = previousState.value + previousState.compositionText !== nextState.value + nextState.compositionText;
1477
+ stateRef.current = nextState;
1478
+ setState(nextState);
1479
+ if (displayValueChanged) {
1480
+ setCaretPosition(void 0);
1481
+ }
1482
+ setCaretVisible(true);
1483
+ }, []);
1484
+ const handleInput = (0, import_react6.useCallback)(
1485
+ (event) => {
1486
+ updateState(event.state);
1487
+ onInput?.(event.state);
1488
+ },
1489
+ [onInput, updateState]
1490
+ );
1491
+ const handleChange = (0, import_react6.useCallback)(
1492
+ (event) => {
1493
+ updateState(event.state);
1494
+ onChange?.(event.state, event.source);
1495
+ },
1496
+ [onChange, updateState]
1497
+ );
1498
+ const handleFocus = (0, import_react6.useCallback)(
1499
+ (event) => {
1500
+ updateState(event.state);
1501
+ setFocused(true);
1502
+ onFocus?.(event.state);
1503
+ },
1504
+ [onFocus, updateState]
1505
+ );
1506
+ const handleBlur = (0, import_react6.useCallback)(
1507
+ (event) => {
1508
+ updateState(event.state);
1509
+ setFocused(false);
1510
+ onBlur?.(event.state);
1511
+ },
1512
+ [onBlur, updateState]
1513
+ );
1514
+ const handleCompositionStart = (0, import_react6.useCallback)(
1515
+ (event) => {
1516
+ updateState(event.state);
1517
+ onCompositionStart?.(event.state);
1518
+ },
1519
+ [onCompositionStart, updateState]
1520
+ );
1521
+ const handleCompositionUpdate = (0, import_react6.useCallback)(
1522
+ (event) => {
1523
+ updateState(event.state);
1524
+ onCompositionUpdate?.(event.state);
1525
+ },
1526
+ [onCompositionUpdate, updateState]
1527
+ );
1528
+ const handleCompositionEnd = (0, import_react6.useCallback)(
1529
+ (event) => {
1530
+ updateState(event.state);
1531
+ onCompositionEnd?.(event.state);
1532
+ },
1533
+ [onCompositionEnd, updateState]
1534
+ );
1535
+ const handleTextLayout = (0, import_react6.useCallback)((event) => {
1536
+ if (event.text === stateRef.current.value + stateRef.current.compositionText) {
1537
+ setCaretPosition(event.endCursorPosition);
1538
+ }
1539
+ }, []);
1540
+ (0, import_react6.useImperativeHandle)(
1541
+ ref,
1542
+ () => ({
1543
+ focus: () => {
1544
+ if (editableRef.current) {
1545
+ focusEditable(editableRef.current.nodeId);
1546
+ }
1547
+ },
1548
+ blur: () => {
1549
+ if (editableRef.current) {
1550
+ blurEditable(editableRef.current.nodeId);
1551
+ }
1552
+ },
1553
+ getState: () => stateRef.current
1554
+ }),
1555
+ []
1556
+ );
1557
+ (0, import_react6.useEffect)(() => {
1558
+ if (value === void 0 || value === stateRef.current.value) {
1559
+ return;
1560
+ }
1561
+ editableRef.current?.executeCommand({ subCommand: "setValue", value });
1562
+ const nextState = editableRef.current?.executeCommand({ subCommand: "getState" });
1563
+ if (nextState) {
1564
+ updateState(nextState);
1565
+ }
1566
+ }, [updateState, value]);
1567
+ (0, import_react6.useEffect)(() => {
1568
+ if (shouldAutoFocusRef.current && editableRef.current) {
1569
+ focusEditable(editableRef.current.nodeId);
1570
+ }
1571
+ }, []);
1572
+ (0, import_react6.useEffect)(() => {
1573
+ if (!pressed) {
1574
+ return;
1575
+ }
1576
+ const release = () => setPressed(false);
1577
+ const removeMouseUp = addEventListener("mouseup", release);
1578
+ const removeTouchEnd = addEventListener("touchend", release);
1579
+ const removeTouchCancel = addEventListener("touchcancel", release);
1580
+ return () => {
1581
+ removeMouseUp();
1582
+ removeTouchEnd();
1583
+ removeTouchCancel();
1584
+ };
1585
+ }, [pressed]);
1586
+ (0, import_react6.useEffect)(() => {
1587
+ if (disabled) {
1588
+ setHovered(false);
1589
+ setPressed(false);
1590
+ }
1591
+ }, [disabled]);
1592
+ (0, import_react6.useEffect)(() => {
1593
+ if (!focused) {
1594
+ setCaretVisible(false);
1595
+ return;
1596
+ }
1597
+ const expectedDisplayValue = displayValue;
1598
+ setCaretVisible(true);
1599
+ const timer = setInterval(() => {
1600
+ if (stateRef.current.value + stateRef.current.compositionText === expectedDisplayValue) {
1601
+ setCaretVisible((visible) => !visible);
1602
+ }
1603
+ }, blinkInterval ?? 500);
1604
+ return () => clearInterval(timer);
1605
+ }, [blinkInterval, displayValue, focused]);
1606
+ (0, import_react6.useEffect)(() => {
1607
+ if (!focused || !caretPosition) {
1608
+ return;
1609
+ }
1610
+ editableRef.current?.executeCommand({
1611
+ subCommand: "setCaretRect",
1612
+ x: paddingX + caretPosition[0],
1613
+ y: (viewportHeight - caretHeight) / 2,
1614
+ width: caretWidth,
1615
+ height: caretHeight
1616
+ });
1617
+ }, [caretHeight, caretPosition, caretWidth, focused, paddingX, viewportHeight]);
1618
+ const caretX = caretPosition?.[0] ?? 0;
1619
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1620
+ "editable",
1621
+ {
1622
+ ...nodeProps,
1623
+ ref: editableRef,
1624
+ value: initialValue,
1625
+ disabled,
1626
+ readOnly,
1627
+ interactive: disabled ? false : interactive,
1628
+ cursor,
1629
+ onMouseEnter: mergeEvent(onMouseEnter, () => setHovered(true)),
1630
+ onMouseLeave: mergeEvent(onMouseLeave, () => {
1631
+ setHovered(false);
1632
+ setPressed(false);
1633
+ }),
1634
+ onMouseDown: mergeEvent(onMouseDown, () => setPressed(true)),
1635
+ onMouseUp: mergeEvent(onMouseUp, () => {
1636
+ setPressed(false);
1637
+ setHovered(true);
1638
+ }),
1639
+ onTouchStart: mergeEvent(onTouchStart, () => setPressed(true)),
1640
+ onTouchEnd: mergeEvent(onTouchEnd, () => {
1641
+ setPressed(false);
1642
+ setHovered(true);
1643
+ }),
1644
+ onTouchCancel: mergeEvent(onTouchCancel, () => {
1645
+ setHovered(false);
1646
+ setPressed(false);
1647
+ }),
1648
+ onInput: handleInput,
1649
+ onChange: handleChange,
1650
+ onFocus: handleFocus,
1651
+ onBlur: handleBlur,
1652
+ onCompositionStart: handleCompositionStart,
1653
+ onCompositionUpdate: handleCompositionUpdate,
1654
+ onCompositionEnd: handleCompositionEnd,
1655
+ children: [
1656
+ backgroundStyle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("sprite", { ...backgroundStyle, targetWidth: viewportWidth, targetHeight: viewportHeight, interactive: false }) : null,
1657
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("clip", { x: paddingX, width: contentWidth, height: viewportHeight, interactive: false, children: [
1658
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1659
+ "text",
1660
+ {
1661
+ ...textStyle,
1662
+ text: displayValue,
1663
+ parseMarkup: false,
1664
+ printMode: "instant",
1665
+ anchor: [0, 0.5],
1666
+ pivot: [0, 0.5],
1667
+ interactive: false,
1668
+ onTextLayout: handleTextLayout
1669
+ }
1670
+ ),
1671
+ state.value.length === 0 && state.compositionText.length === 0 && placeholder ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1672
+ "text",
1673
+ {
1674
+ ...textStyle,
1675
+ ...placeholderStyle,
1676
+ text: placeholder,
1677
+ parseMarkup: false,
1678
+ printMode: "instant",
1679
+ anchor: [0, 0.5],
1680
+ pivot: [0, 0.5],
1681
+ interactive: false
1682
+ }
1683
+ ) : null,
1684
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1685
+ "sprite",
1686
+ {
1687
+ ...caretSpriteProps,
1688
+ x: caretX,
1689
+ y: (viewportHeight - caretHeight) / 2,
1690
+ mode: "nineslice",
1691
+ bounds: [0, 0, 0, 0],
1692
+ targetWidth: caretWidth,
1693
+ targetHeight: caretHeight,
1694
+ visible: focused && caretVisible && caretPosition !== void 0,
1695
+ interactive: false
1696
+ }
1697
+ )
1698
+ ] })
1699
+ ]
1700
+ }
1701
+ );
1702
+ });
1703
+
1704
+ // src/components/radio.tsx
1705
+ var import_react7 = require("react");
1706
+ var RadioGroupContext = (0, import_react7.createContext)(null);
1707
+ function RadioGroup({ value, defaultValue, onValueChange, children, ...containerProps }) {
1708
+ const [internalValue, setInternalValue] = (0, import_react7.useState)(defaultValue);
1709
+ const currentValue = value ?? internalValue;
1710
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1711
+ RadioGroupContext.Provider,
1712
+ {
1713
+ value: {
1714
+ value: currentValue,
1715
+ select: (nextValue) => {
1716
+ if (nextValue === currentValue) {
1717
+ return;
1718
+ }
1719
+ if (value === void 0) {
1720
+ setInternalValue(nextValue);
1721
+ }
1722
+ onValueChange?.(nextValue);
1723
+ }
1724
+ },
1725
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("container", { ...containerProps, children })
1726
+ }
1727
+ );
1728
+ }
1729
+ function Radio({ value, uncheckedSprite, checkedSprite, onPress, ...buttonProps }) {
1730
+ const group = (0, import_react7.useContext)(RadioGroupContext);
1731
+ if (!group) {
1732
+ throw new Error("Radio must be rendered inside a RadioGroup.");
1733
+ }
1734
+ const checked = group.value === value;
1735
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1736
+ Button,
1737
+ {
1738
+ ...buttonProps,
1739
+ sprite: checked ? checkedSprite : uncheckedSprite,
1740
+ onPress: (event) => {
1741
+ onPress?.(event);
1742
+ if (!event.defaultPrevented) {
1743
+ group.select(value);
1744
+ }
1745
+ }
1746
+ }
1747
+ );
1748
+ }
1749
+
1750
+ // src/components/select.tsx
1751
+ var import_react8 = require("react");
1395
1752
  function Select({
1396
1753
  value,
1397
1754
  defaultValue,
@@ -1410,8 +1767,8 @@ function Select({
1410
1767
  pivot = anchor,
1411
1768
  ...containerProps
1412
1769
  }) {
1413
- const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
1414
- const [open, setOpen] = (0, import_react6.useState)(false);
1770
+ const [internalValue, setInternalValue] = (0, import_react8.useState)(defaultValue);
1771
+ const [open, setOpen] = (0, import_react8.useState)(false);
1415
1772
  const currentValue = value ?? internalValue;
1416
1773
  const currentOption = options.find((item) => item.value === currentValue);
1417
1774
  const { paddingX = 0, paddingY = 0, gap = 0, offsetX = 0, offsetY = 0, ...listSprite } = list;
@@ -1443,36 +1800,46 @@ function Select({
1443
1800
  }
1444
1801
  }
1445
1802
  ),
1446
- open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("sprite", { ...listSprite, targetHeight: listHeight, x: offsetX, y: (trigger.targetHeight ?? 0) + offsetY, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1447
- Button,
1803
+ open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1804
+ "sprite",
1448
1805
  {
1449
- sprite: option,
1450
- lockOn: item.value === currentValue ? "press" : void 0,
1451
- text: item.text,
1452
- textStyle,
1453
- textOffsetX,
1454
- textOffsetY,
1455
- textAlign,
1456
- onPress: (event) => {
1457
- if (event.defaultPrevented) {
1458
- return;
1459
- }
1460
- if (value === void 0) {
1461
- setInternalValue(item.value);
1462
- }
1463
- onValueChange?.(item.value, item);
1464
- setOpen(false);
1465
- }
1466
- },
1467
- item.value
1468
- )) }) }) : void 0
1806
+ ...listSprite,
1807
+ excludeFromLayout: true,
1808
+ targetHeight: listHeight,
1809
+ x: offsetX,
1810
+ y: (trigger.targetHeight ?? 0) + offsetY,
1811
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1812
+ Button,
1813
+ {
1814
+ sprite: option,
1815
+ lockOn: item.value === currentValue ? "press" : void 0,
1816
+ text: item.text,
1817
+ textStyle,
1818
+ textOffsetX,
1819
+ textOffsetY,
1820
+ textAlign,
1821
+ onPress: (event) => {
1822
+ if (event.defaultPrevented) {
1823
+ return;
1824
+ }
1825
+ if (value === void 0) {
1826
+ setInternalValue(item.value);
1827
+ }
1828
+ onValueChange?.(item.value, item);
1829
+ setOpen(false);
1830
+ }
1831
+ },
1832
+ item.value
1833
+ )) })
1834
+ }
1835
+ ) : void 0
1469
1836
  ]
1470
1837
  }
1471
1838
  );
1472
1839
  }
1473
1840
 
1474
1841
  // src/components/slider.tsx
1475
- var import_react7 = require("react");
1842
+ var import_react9 = require("react");
1476
1843
  function clampValue(value) {
1477
1844
  if (Number.isNaN(value)) {
1478
1845
  return 0;
@@ -1502,14 +1869,14 @@ function Slider({
1502
1869
  onTouchCancel,
1503
1870
  ...containerProps
1504
1871
  }) {
1505
- const [internalValue, setInternalValue] = (0, import_react7.useState)(() => clampValue(defaultValue));
1506
- const [dragging, setDragging] = (0, import_react7.useState)(false);
1872
+ const [internalValue, setInternalValue] = (0, import_react9.useState)(() => clampValue(defaultValue));
1873
+ const [dragging, setDragging] = (0, import_react9.useState)(false);
1507
1874
  const currentValue = clampValue(value ?? internalValue);
1508
- const currentValueRef = (0, import_react7.useRef)(currentValue);
1875
+ const currentValueRef = (0, import_react9.useRef)(currentValue);
1509
1876
  currentValueRef.current = currentValue;
1510
- const dragStateRef = (0, import_react7.useRef)(null);
1877
+ const dragStateRef = (0, import_react9.useRef)(null);
1511
1878
  const distance = Math.max(0, track.targetWidth - thumb.targetWidth);
1512
- const setSliderValue = (0, import_react7.useCallback)((nextValue) => {
1879
+ const setSliderValue = (0, import_react9.useCallback)((nextValue) => {
1513
1880
  const clampedValue = clampValue(nextValue);
1514
1881
  currentValueRef.current = clampedValue;
1515
1882
  if (value === void 0) {
@@ -1517,7 +1884,7 @@ function Slider({
1517
1884
  }
1518
1885
  onValueChange?.(clampedValue);
1519
1886
  }, [onValueChange, value]);
1520
- const endDragging = (0, import_react7.useCallback)((commit) => {
1887
+ const endDragging = (0, import_react9.useCallback)((commit) => {
1521
1888
  if (dragStateRef.current === null) {
1522
1889
  return;
1523
1890
  }
@@ -1527,7 +1894,7 @@ function Slider({
1527
1894
  onValueCommit?.(currentValueRef.current);
1528
1895
  }
1529
1896
  }, [onValueCommit]);
1530
- (0, import_react7.useEffect)(() => {
1897
+ (0, import_react9.useEffect)(() => {
1531
1898
  const handleMouseUp = () => endDragging(true);
1532
1899
  const handleTouchEnd = (event) => {
1533
1900
  if (dragStateRef.current?.touchIdentifier === event.identifier) {
@@ -1548,7 +1915,7 @@ function Slider({
1548
1915
  removeTouchCancel();
1549
1916
  };
1550
1917
  }, [endDragging]);
1551
- (0, import_react7.useEffect)(() => {
1918
+ (0, import_react9.useEffect)(() => {
1552
1919
  if (disabled) {
1553
1920
  endDragging(false);
1554
1921
  }
@@ -2010,6 +2377,7 @@ var debugSessionController = {
2010
2377
  throw new Error("Page switching is not supported by the app state adapter");
2011
2378
  }
2012
2379
  await appStateAdapter.switchPage(routeName, params);
2380
+ await syncCurrentExecutionCursor();
2013
2381
  return;
2014
2382
  }
2015
2383
  if (navigator.hasOverlay(routeName)) {
@@ -2600,7 +2968,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
2600
2968
 
2601
2969
  // src/hooks/useScrollView.ts
2602
2970
  var import_core = require("@react-spring/core");
2603
- var import_react8 = require("react");
2971
+ var import_react10 = require("react");
2604
2972
  function clamp(value, min, max) {
2605
2973
  return Math.max(min, Math.min(max, value));
2606
2974
  }
@@ -2620,21 +2988,21 @@ function useScrollView({
2620
2988
  initialPosition = "start",
2621
2989
  wheelStep = 72
2622
2990
  }) {
2623
- const [contentHeight, setContentHeight] = (0, import_react8.useState)(0);
2624
- const [hasContentLayout, setHasContentLayout] = (0, import_react8.useState)(false);
2625
- const scrollTargetRef = (0, import_react8.useRef)(0);
2626
- const didSetInitialPositionRef = (0, import_react8.useRef)(false);
2627
- const touchDraggingRef = (0, import_react8.useRef)(false);
2628
- const touchStartYRef = (0, import_react8.useRef)(0);
2629
- const touchStartOffsetRef = (0, import_react8.useRef)(0);
2991
+ const [contentHeight, setContentHeight] = (0, import_react10.useState)(0);
2992
+ const [hasContentLayout, setHasContentLayout] = (0, import_react10.useState)(false);
2993
+ const scrollTargetRef = (0, import_react10.useRef)(0);
2994
+ const didSetInitialPositionRef = (0, import_react10.useRef)(false);
2995
+ const touchDraggingRef = (0, import_react10.useRef)(false);
2996
+ const touchStartYRef = (0, import_react10.useRef)(0);
2997
+ const touchStartOffsetRef = (0, import_react10.useRef)(0);
2630
2998
  const scrollOffset = (0, import_core.useSpringValue)(0, {
2631
2999
  config: {
2632
3000
  tension: 320,
2633
3001
  friction: 32
2634
3002
  }
2635
3003
  });
2636
- const maxScroll = (0, import_react8.useMemo)(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
2637
- const scrollTo = (0, import_react8.useCallback)(
3004
+ const maxScroll = (0, import_react10.useMemo)(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
3005
+ const scrollTo = (0, import_react10.useCallback)(
2638
3006
  (nextOffset, immediate = false) => {
2639
3007
  const offset = clamp(nextOffset, 0, maxScroll);
2640
3008
  if (offset === scrollTargetRef.current && !immediate) {
@@ -2645,13 +3013,13 @@ function useScrollView({
2645
3013
  },
2646
3014
  [maxScroll, scrollOffset]
2647
3015
  );
2648
- const scrollToRatio = (0, import_react8.useCallback)(
3016
+ const scrollToRatio = (0, import_react10.useCallback)(
2649
3017
  (ratio, immediate = false) => {
2650
3018
  scrollTo(clamp(ratio, 0, 1) * maxScroll, immediate);
2651
3019
  },
2652
3020
  [maxScroll, scrollTo]
2653
3021
  );
2654
- const handleContentLayout = (0, import_react8.useCallback)(
3022
+ const handleContentLayout = (0, import_react10.useCallback)(
2655
3023
  (event) => {
2656
3024
  if (!didSetInitialPositionRef.current) {
2657
3025
  const initialOffset = initialPosition === "end" ? Math.max(0, event.height - viewportHeight) : 0;
@@ -2664,13 +3032,13 @@ function useScrollView({
2664
3032
  },
2665
3033
  [initialPosition, scrollOffset, viewportHeight]
2666
3034
  );
2667
- (0, import_react8.useEffect)(() => {
3035
+ (0, import_react10.useEffect)(() => {
2668
3036
  if (!hasContentLayout) {
2669
3037
  return;
2670
3038
  }
2671
3039
  scrollTo(scrollTargetRef.current, true);
2672
3040
  }, [hasContentLayout, maxScroll, scrollTo]);
2673
- const handleWheel = (0, import_react8.useCallback)(
3041
+ const handleWheel = (0, import_react10.useCallback)(
2674
3042
  (event) => {
2675
3043
  if (maxScroll <= 0) {
2676
3044
  return;
@@ -2685,7 +3053,7 @@ function useScrollView({
2685
3053
  },
2686
3054
  [maxScroll, scrollTo, viewportHeight, wheelStep]
2687
3055
  );
2688
- const handleTouchStart = (0, import_react8.useCallback)(
3056
+ const handleTouchStart = (0, import_react10.useCallback)(
2689
3057
  (event) => {
2690
3058
  if (maxScroll <= 0) {
2691
3059
  return;
@@ -2697,7 +3065,7 @@ function useScrollView({
2697
3065
  },
2698
3066
  [maxScroll]
2699
3067
  );
2700
- const handleTouchMove = (0, import_react8.useCallback)(
3068
+ const handleTouchMove = (0, import_react10.useCallback)(
2701
3069
  (event) => {
2702
3070
  if (!touchDraggingRef.current) {
2703
3071
  return;
@@ -2707,7 +3075,7 @@ function useScrollView({
2707
3075
  },
2708
3076
  [scrollTo]
2709
3077
  );
2710
- const handleTouchEnd = (0, import_react8.useCallback)((event) => {
3078
+ const handleTouchEnd = (0, import_react10.useCallback)((event) => {
2711
3079
  if (!touchDraggingRef.current) {
2712
3080
  return;
2713
3081
  }
@@ -2729,7 +3097,7 @@ function useScrollView({
2729
3097
  }
2730
3098
 
2731
3099
  // src/hooks/useScenario.ts
2732
- var import_react9 = require("react");
3100
+ var import_react11 = require("react");
2733
3101
  var currentScenarioSession = null;
2734
3102
  var scenarioSessionId = 0;
2735
3103
  var scenarioLifecycleQueue = Promise.resolve();
@@ -2871,7 +3239,7 @@ function setWaiting(time, skippable) {
2871
3239
  executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
2872
3240
  }
2873
3241
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2874
- (0, import_react9.useEffect)(() => {
3242
+ (0, import_react11.useEffect)(() => {
2875
3243
  const session = acquireScenarioSession({
2876
3244
  stories: normalizeStories(stories),
2877
3245
  startName,
@@ -2885,9 +3253,9 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2885
3253
  }
2886
3254
 
2887
3255
  // src/hooks/useSoundEffect.ts
2888
- var import_react10 = require("react");
3256
+ var import_react12 = require("react");
2889
3257
  function useSoundEffect(src) {
2890
- (0, import_react10.useEffect)(() => {
3258
+ (0, import_react12.useEffect)(() => {
2891
3259
  return () => {
2892
3260
  try {
2893
3261
  if (src) {
@@ -2902,7 +3270,7 @@ function useSoundEffect(src) {
2902
3270
  }
2903
3271
  };
2904
3272
  }, [src]);
2905
- return (0, import_react10.useCallback)(() => {
3273
+ return (0, import_react12.useCallback)(() => {
2906
3274
  try {
2907
3275
  if (src) {
2908
3276
  executePluginCommand("audio", {
@@ -2918,7 +3286,7 @@ function useSoundEffect(src) {
2918
3286
  }
2919
3287
 
2920
3288
  // src/hooks/useStage.ts
2921
- var import_react11 = require("react");
3289
+ var import_react13 = require("react");
2922
3290
  var import_valtio5 = require("valtio");
2923
3291
  var SKIP_INTERVAL_MS = 80;
2924
3292
  var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
@@ -3004,6 +3372,9 @@ function createStage() {
3004
3372
  return addToRegistry(interruptCallbacks, cb);
3005
3373
  }
3006
3374
  function tryInterrupt() {
3375
+ if (tryFinishActiveWait()) {
3376
+ return true;
3377
+ }
3007
3378
  for (const cb of interruptCallbacks) {
3008
3379
  try {
3009
3380
  if (cb()) return true;
@@ -3051,6 +3422,8 @@ function createStage() {
3051
3422
  let _autoTicketSeq = 0;
3052
3423
  const _pendingAutoTickets = /* @__PURE__ */ new Map();
3053
3424
  let _fastForwardRuntime = null;
3425
+ let _activeWait = null;
3426
+ let _activeWaitSeq = 0;
3054
3427
  let _managedAdvanceInFlight = false;
3055
3428
  let _managedAdvancePending = false;
3056
3429
  function resetManagedAdvanceState() {
@@ -3139,12 +3512,53 @@ function createStage() {
3139
3512
  });
3140
3513
  }
3141
3514
  function requestNextLine() {
3515
+ if (_activeWait !== null) {
3516
+ return;
3517
+ }
3142
3518
  if (isManagedAdvanceActive()) {
3143
3519
  scheduleManagedAdvance();
3144
3520
  return;
3145
3521
  }
3146
3522
  void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
3147
3523
  }
3524
+ function invalidateActiveWait() {
3525
+ if (_activeWait === null) {
3526
+ return;
3527
+ }
3528
+ _activeWait.settled = true;
3529
+ _activeWait = null;
3530
+ }
3531
+ function settleActiveWait(wait, error) {
3532
+ if (_activeWait !== wait || wait.settled) {
3533
+ return;
3534
+ }
3535
+ wait.settled = true;
3536
+ _activeWait = null;
3537
+ if (error !== void 0) {
3538
+ console.error(`[Stage] Promise hold ${wait.id} rejected:`, error);
3539
+ }
3540
+ requestNextLine();
3541
+ }
3542
+ function tryFinishActiveWait() {
3543
+ const wait = _activeWait;
3544
+ if (wait === null || !wait.skippable || wait.settled) {
3545
+ return false;
3546
+ }
3547
+ const finish = wait.until.finish;
3548
+ if (typeof finish === "function") {
3549
+ try {
3550
+ finish.call(wait.until);
3551
+ } catch (error) {
3552
+ console.error(`[Stage] Failed to finish Promise hold ${wait.id}:`, error);
3553
+ }
3554
+ } else {
3555
+ console.warn(
3556
+ `[Stage] Skipping Promise hold ${wait.id} without finish(); the underlying operation may continue.`
3557
+ );
3558
+ }
3559
+ settleActiveWait(wait);
3560
+ return true;
3561
+ }
3148
3562
  function clearAutoResumeTimer() {
3149
3563
  if (_autoResumeTimer !== null) {
3150
3564
  clearTimeout(_autoResumeTimer);
@@ -3332,7 +3746,11 @@ function createStage() {
3332
3746
  if (isAnyBlockerActive()) return;
3333
3747
  skipState.active = true;
3334
3748
  invokeSkipCallbacks();
3749
+ const activeWait = _activeWait;
3335
3750
  tryInterrupt();
3751
+ if (activeWait !== null && activeWait !== _activeWait) {
3752
+ return;
3753
+ }
3336
3754
  scheduleSkipNextLine();
3337
3755
  }
3338
3756
  function stopSkip() {
@@ -3351,6 +3769,7 @@ function createStage() {
3351
3769
  if (fastForwardState.active) return;
3352
3770
  if (isAnyAutoBlockerActive()) return;
3353
3771
  autoState.active = true;
3772
+ if (_activeWait !== null) return;
3354
3773
  tryInterrupt();
3355
3774
  scheduleAutoNextLine();
3356
3775
  }
@@ -3387,6 +3806,7 @@ function createStage() {
3387
3806
  return fastForwardState.active;
3388
3807
  }
3389
3808
  function resetRuntimeState() {
3809
+ invalidateActiveWait();
3390
3810
  skipState.active = false;
3391
3811
  autoState.active = false;
3392
3812
  fastForwardState.active = false;
@@ -3432,7 +3852,7 @@ function createStage() {
3432
3852
  setWaiting(time, skippable);
3433
3853
  }
3434
3854
  },
3435
- hold() {
3855
+ hold(until, skippable = false) {
3436
3856
  handled = true;
3437
3857
  if (warpActive) {
3438
3858
  return;
@@ -3441,6 +3861,24 @@ function createStage() {
3441
3861
  requestNextLine();
3442
3862
  return;
3443
3863
  }
3864
+ if (until !== void 0) {
3865
+ invalidateActiveWait();
3866
+ const wait = {
3867
+ id: ++_activeWaitSeq,
3868
+ until,
3869
+ skippable,
3870
+ settled: false
3871
+ };
3872
+ _activeWait = wait;
3873
+ void Promise.resolve(until).then(
3874
+ () => settleActiveWait(wait),
3875
+ (error) => settleActiveWait(wait, error)
3876
+ );
3877
+ if (skipState.active) {
3878
+ tryFinishActiveWait();
3879
+ }
3880
+ return;
3881
+ }
3444
3882
  if (skipState.active && !unskippableFlag) {
3445
3883
  if (isAnyBlockerActive()) {
3446
3884
  stopSkip();
@@ -3500,6 +3938,7 @@ function createStage() {
3500
3938
  return parsed.data;
3501
3939
  }
3502
3940
  function dispatchCommand(e) {
3941
+ invalidateActiveWait();
3503
3942
  for (const cb of beforeHandleCommandCallbacks) {
3504
3943
  try {
3505
3944
  cb(e);
@@ -3528,6 +3967,7 @@ function createStage() {
3528
3967
  }
3529
3968
  }
3530
3969
  function dispatchTextLine(e) {
3970
+ invalidateActiveWait();
3531
3971
  const { control, wasHandled } = buildControl();
3532
3972
  for (const handler of textLineHandlers) {
3533
3973
  handler(e, control);
@@ -3561,6 +4001,7 @@ function createStage() {
3561
4001
  })
3562
4002
  ];
3563
4003
  return () => {
4004
+ resetRuntimeState();
3564
4005
  for (const cleanup of cleanups) cleanup();
3565
4006
  };
3566
4007
  }
@@ -3581,52 +4022,54 @@ function createStage() {
3581
4022
  startAuto,
3582
4023
  stopAuto,
3583
4024
  isAutoing,
4025
+ setDefaultAutoTailMs,
3584
4026
  startFastForward,
3585
4027
  stopFastForward,
3586
4028
  isFastForwarding,
4029
+ getSeekingType,
3587
4030
  resetRuntimeState,
3588
4031
  bindEvents
3589
4032
  };
3590
4033
  }
3591
- var StageContext = (0, import_react11.createContext)(null);
4034
+ var StageContext = (0, import_react13.createContext)(null);
3592
4035
  function useStageContext() {
3593
- const ctx = (0, import_react11.useContext)(StageContext);
4036
+ const ctx = (0, import_react13.useContext)(StageContext);
3594
4037
  if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
3595
4038
  return ctx;
3596
4039
  }
3597
4040
  function StageContextProvider({ stage, children }) {
3598
- (0, import_react11.useEffect)(() => {
4041
+ (0, import_react13.useEffect)(() => {
3599
4042
  return stage.bindEvents();
3600
4043
  }, [stage]);
3601
- return (0, import_react11.createElement)(StageContext.Provider, { value: stage }, children);
4044
+ return (0, import_react13.createElement)(StageContext.Provider, { value: stage }, children);
3602
4045
  }
3603
4046
  function useSkipCallback(callback) {
3604
4047
  const stage = useStageContext();
3605
- (0, import_react11.useEffect)(() => {
4048
+ (0, import_react13.useEffect)(() => {
3606
4049
  return stage.addSkipCallback(callback);
3607
4050
  }, [stage, callback]);
3608
4051
  }
3609
4052
  function useInterruptCallback(callback) {
3610
4053
  const stage = useStageContext();
3611
- (0, import_react11.useEffect)(() => {
4054
+ (0, import_react13.useEffect)(() => {
3612
4055
  return stage.addInterruptCallback(callback);
3613
4056
  }, [stage, callback]);
3614
4057
  }
3615
4058
  function useSkipBlocker(callback) {
3616
4059
  const stage = useStageContext();
3617
- (0, import_react11.useEffect)(() => {
4060
+ (0, import_react13.useEffect)(() => {
3618
4061
  return stage.addSkipBlocker(callback);
3619
4062
  }, [stage, callback]);
3620
4063
  }
3621
4064
  function useAutoBlocker(callback) {
3622
4065
  const stage = useStageContext();
3623
- (0, import_react11.useEffect)(() => {
4066
+ (0, import_react13.useEffect)(() => {
3624
4067
  return stage.addAutoBlocker(callback);
3625
4068
  }, [stage, callback]);
3626
4069
  }
3627
4070
  function useAutoTicket() {
3628
4071
  const stage = useStageContext();
3629
- return (0, import_react11.useCallback)(
4072
+ return (0, import_react13.useCallback)(
3630
4073
  (options) => {
3631
4074
  return stage.issueAutoTicket(options);
3632
4075
  },
@@ -3635,7 +4078,7 @@ function useAutoTicket() {
3635
4078
  }
3636
4079
  function useBeforeHandleCommandCallback(callback) {
3637
4080
  const stage = useStageContext();
3638
- (0, import_react11.useEffect)(() => {
4081
+ (0, import_react13.useEffect)(() => {
3639
4082
  return stage.addBeforeHandleCommandCallback(callback);
3640
4083
  }, [stage, callback]);
3641
4084
  }