@momoyu-ink/kit 0.10.1 → 0.11.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/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
 
@@ -1181,20 +1203,28 @@ function createStackNavigator(options) {
1181
1203
  }
1182
1204
  function createStaticNavigation(navigator) {
1183
1205
  return () => {
1184
- const navState = (0, import_valtio2.useSnapshot)(navigator._state);
1185
- const { pages, overlays } = navigator._descriptors;
1186
- const pageDescriptor = pages[navState.currentPage];
1187
- const Page = pageDescriptor?.component || (() => null);
1188
1206
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1189
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageParamsContext.Provider, { value: navState.pageParams[navState.currentPage], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Page, {}) }),
1190
- navState.overlayStack.map((overlay) => {
1191
- const overlayDescriptor = overlays[overlay.type];
1192
- const OverlayComponent = overlayDescriptor?.component || (() => null);
1193
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayComponent, {}) }, overlay.id);
1194
- })
1207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CurrentPage, { navigator }),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayStack, { navigator })
1195
1209
  ] });
1196
1210
  };
1197
1211
  }
1212
+ function CurrentPage({ navigator }) {
1213
+ const navState = (0, import_valtio2.useSnapshot)(navigator._state);
1214
+ const { currentPage, pageParams } = navState;
1215
+ const pageDescriptor = navigator._descriptors.pages[currentPage];
1216
+ const Page = pageDescriptor?.component || (() => null);
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageParamsContext.Provider, { value: pageParams[currentPage], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Page, {}) });
1218
+ }
1219
+ function OverlayStack({ navigator }) {
1220
+ const navState = (0, import_valtio2.useSnapshot)(navigator._state);
1221
+ const { overlays } = navigator._descriptors;
1222
+ return navState.overlayStack.map((overlay) => {
1223
+ const overlayDescriptor = overlays[overlay.type];
1224
+ const OverlayComponent = overlayDescriptor?.component || (() => null);
1225
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverlayComponent, {}) }, overlay.id);
1226
+ });
1227
+ }
1198
1228
  function getNavigator() {
1199
1229
  if (!globalNavigator) {
1200
1230
  throw new Error("Navigator not initialized. Call createStackNavigator first.");
@@ -1390,8 +1420,343 @@ function Checkbox({
1390
1420
  );
1391
1421
  }
1392
1422
 
1393
- // src/components/select.tsx
1423
+ // src/components/input.tsx
1394
1424
  var import_react6 = require("react");
1425
+ var Input = (0, import_react6.forwardRef)(function Input2({
1426
+ value,
1427
+ defaultValue = "",
1428
+ placeholder = "",
1429
+ disabled = false,
1430
+ readOnly = false,
1431
+ autoFocus = false,
1432
+ width,
1433
+ height,
1434
+ paddingX = 0,
1435
+ textStyle,
1436
+ placeholderStyle,
1437
+ caret,
1438
+ background,
1439
+ onInput,
1440
+ onChange,
1441
+ onFocus,
1442
+ onBlur,
1443
+ onCompositionStart,
1444
+ onCompositionUpdate,
1445
+ onCompositionEnd,
1446
+ interactive,
1447
+ cursor = "text",
1448
+ onMouseEnter,
1449
+ onMouseLeave,
1450
+ onMouseDown,
1451
+ onMouseUp,
1452
+ onTouchStart,
1453
+ onTouchEnd,
1454
+ onTouchCancel,
1455
+ ...nodeProps
1456
+ }, ref) {
1457
+ const editableRef = (0, import_react6.useRef)(null);
1458
+ const initialValueRef = (0, import_react6.useRef)(value ?? defaultValue);
1459
+ const initialValue = initialValueRef.current;
1460
+ const stateRef = (0, import_react6.useRef)({
1461
+ value: initialValue,
1462
+ isComposing: false,
1463
+ compositionText: ""
1464
+ });
1465
+ const [state, setState] = (0, import_react6.useState)(stateRef.current);
1466
+ const [focused, setFocused] = (0, import_react6.useState)(false);
1467
+ const [hovered, setHovered] = (0, import_react6.useState)(false);
1468
+ const [pressed, setPressed] = (0, import_react6.useState)(false);
1469
+ const [caretPosition, setCaretPosition] = (0, import_react6.useState)();
1470
+ const [caretVisible, setCaretVisible] = (0, import_react6.useState)(true);
1471
+ const shouldAutoFocusRef = (0, import_react6.useRef)(autoFocus && !disabled);
1472
+ const viewportWidth = Math.max(0, Math.round(width));
1473
+ const viewportHeight = Math.max(0, Math.round(height));
1474
+ const contentWidth = Math.max(0, viewportWidth - paddingX * 2);
1475
+ const { width: caretWidth, height: configuredCaretHeight, blinkInterval, ...caretSpriteProps } = caret;
1476
+ const caretHeight = configuredCaretHeight ?? textStyle.fontSize ?? viewportHeight;
1477
+ 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;
1478
+ const displayValue = state.value + state.compositionText;
1479
+ const updateState = (0, import_react6.useCallback)((nextState) => {
1480
+ const previousState = stateRef.current;
1481
+ if (previousState.value === nextState.value && previousState.isComposing === nextState.isComposing && previousState.compositionText === nextState.compositionText) {
1482
+ return;
1483
+ }
1484
+ const displayValueChanged = previousState.value + previousState.compositionText !== nextState.value + nextState.compositionText;
1485
+ stateRef.current = nextState;
1486
+ setState(nextState);
1487
+ if (displayValueChanged) {
1488
+ setCaretPosition(void 0);
1489
+ }
1490
+ setCaretVisible(true);
1491
+ }, []);
1492
+ const handleInput = (0, import_react6.useCallback)(
1493
+ (event) => {
1494
+ updateState(event.state);
1495
+ onInput?.(event.state);
1496
+ },
1497
+ [onInput, updateState]
1498
+ );
1499
+ const handleChange = (0, import_react6.useCallback)(
1500
+ (event) => {
1501
+ updateState(event.state);
1502
+ onChange?.(event.state, event.source);
1503
+ },
1504
+ [onChange, updateState]
1505
+ );
1506
+ const handleFocus = (0, import_react6.useCallback)(
1507
+ (event) => {
1508
+ updateState(event.state);
1509
+ setFocused(true);
1510
+ onFocus?.(event.state);
1511
+ },
1512
+ [onFocus, updateState]
1513
+ );
1514
+ const handleBlur = (0, import_react6.useCallback)(
1515
+ (event) => {
1516
+ updateState(event.state);
1517
+ setFocused(false);
1518
+ onBlur?.(event.state);
1519
+ },
1520
+ [onBlur, updateState]
1521
+ );
1522
+ const handleCompositionStart = (0, import_react6.useCallback)(
1523
+ (event) => {
1524
+ updateState(event.state);
1525
+ onCompositionStart?.(event.state);
1526
+ },
1527
+ [onCompositionStart, updateState]
1528
+ );
1529
+ const handleCompositionUpdate = (0, import_react6.useCallback)(
1530
+ (event) => {
1531
+ updateState(event.state);
1532
+ onCompositionUpdate?.(event.state);
1533
+ },
1534
+ [onCompositionUpdate, updateState]
1535
+ );
1536
+ const handleCompositionEnd = (0, import_react6.useCallback)(
1537
+ (event) => {
1538
+ updateState(event.state);
1539
+ onCompositionEnd?.(event.state);
1540
+ },
1541
+ [onCompositionEnd, updateState]
1542
+ );
1543
+ const handleTextLayout = (0, import_react6.useCallback)((event) => {
1544
+ if (event.text === stateRef.current.value + stateRef.current.compositionText) {
1545
+ setCaretPosition(event.endCursorPosition);
1546
+ }
1547
+ }, []);
1548
+ (0, import_react6.useImperativeHandle)(
1549
+ ref,
1550
+ () => ({
1551
+ focus: () => {
1552
+ if (editableRef.current) {
1553
+ focusEditable(editableRef.current.nodeId);
1554
+ }
1555
+ },
1556
+ blur: () => {
1557
+ if (editableRef.current) {
1558
+ blurEditable(editableRef.current.nodeId);
1559
+ }
1560
+ },
1561
+ getState: () => stateRef.current
1562
+ }),
1563
+ []
1564
+ );
1565
+ (0, import_react6.useEffect)(() => {
1566
+ if (value === void 0 || value === stateRef.current.value) {
1567
+ return;
1568
+ }
1569
+ editableRef.current?.executeCommand({ subCommand: "setValue", value });
1570
+ const nextState = editableRef.current?.executeCommand({ subCommand: "getState" });
1571
+ if (nextState) {
1572
+ updateState(nextState);
1573
+ }
1574
+ }, [updateState, value]);
1575
+ (0, import_react6.useEffect)(() => {
1576
+ if (shouldAutoFocusRef.current && editableRef.current) {
1577
+ focusEditable(editableRef.current.nodeId);
1578
+ }
1579
+ }, []);
1580
+ (0, import_react6.useEffect)(() => {
1581
+ if (!pressed) {
1582
+ return;
1583
+ }
1584
+ const release = () => setPressed(false);
1585
+ const removeMouseUp = addEventListener("mouseup", release);
1586
+ const removeTouchEnd = addEventListener("touchend", release);
1587
+ const removeTouchCancel = addEventListener("touchcancel", release);
1588
+ return () => {
1589
+ removeMouseUp();
1590
+ removeTouchEnd();
1591
+ removeTouchCancel();
1592
+ };
1593
+ }, [pressed]);
1594
+ (0, import_react6.useEffect)(() => {
1595
+ if (disabled) {
1596
+ setHovered(false);
1597
+ setPressed(false);
1598
+ }
1599
+ }, [disabled]);
1600
+ (0, import_react6.useEffect)(() => {
1601
+ if (!focused) {
1602
+ setCaretVisible(false);
1603
+ return;
1604
+ }
1605
+ const expectedDisplayValue = displayValue;
1606
+ setCaretVisible(true);
1607
+ const timer = setInterval(() => {
1608
+ if (stateRef.current.value + stateRef.current.compositionText === expectedDisplayValue) {
1609
+ setCaretVisible((visible) => !visible);
1610
+ }
1611
+ }, blinkInterval ?? 500);
1612
+ return () => clearInterval(timer);
1613
+ }, [blinkInterval, displayValue, focused]);
1614
+ (0, import_react6.useEffect)(() => {
1615
+ if (!focused || !caretPosition) {
1616
+ return;
1617
+ }
1618
+ editableRef.current?.executeCommand({
1619
+ subCommand: "setCaretRect",
1620
+ x: paddingX + caretPosition[0],
1621
+ y: (viewportHeight - caretHeight) / 2,
1622
+ width: caretWidth,
1623
+ height: caretHeight
1624
+ });
1625
+ }, [caretHeight, caretPosition, caretWidth, focused, paddingX, viewportHeight]);
1626
+ const caretX = caretPosition?.[0] ?? 0;
1627
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1628
+ "editable",
1629
+ {
1630
+ ...nodeProps,
1631
+ ref: editableRef,
1632
+ value: initialValue,
1633
+ disabled,
1634
+ readOnly,
1635
+ interactive: disabled ? false : interactive,
1636
+ cursor,
1637
+ onMouseEnter: mergeEvent(onMouseEnter, () => setHovered(true)),
1638
+ onMouseLeave: mergeEvent(onMouseLeave, () => {
1639
+ setHovered(false);
1640
+ setPressed(false);
1641
+ }),
1642
+ onMouseDown: mergeEvent(onMouseDown, () => setPressed(true)),
1643
+ onMouseUp: mergeEvent(onMouseUp, () => {
1644
+ setPressed(false);
1645
+ setHovered(true);
1646
+ }),
1647
+ onTouchStart: mergeEvent(onTouchStart, () => setPressed(true)),
1648
+ onTouchEnd: mergeEvent(onTouchEnd, () => {
1649
+ setPressed(false);
1650
+ setHovered(true);
1651
+ }),
1652
+ onTouchCancel: mergeEvent(onTouchCancel, () => {
1653
+ setHovered(false);
1654
+ setPressed(false);
1655
+ }),
1656
+ onInput: handleInput,
1657
+ onChange: handleChange,
1658
+ onFocus: handleFocus,
1659
+ onBlur: handleBlur,
1660
+ onCompositionStart: handleCompositionStart,
1661
+ onCompositionUpdate: handleCompositionUpdate,
1662
+ onCompositionEnd: handleCompositionEnd,
1663
+ children: [
1664
+ backgroundStyle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("sprite", { ...backgroundStyle, targetWidth: viewportWidth, targetHeight: viewportHeight, interactive: false }) : null,
1665
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("clip", { x: paddingX, width: contentWidth, height: viewportHeight, interactive: false, children: [
1666
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1667
+ "text",
1668
+ {
1669
+ ...textStyle,
1670
+ text: displayValue,
1671
+ parseMarkup: false,
1672
+ printMode: "instant",
1673
+ anchor: [0, 0.5],
1674
+ pivot: [0, 0.5],
1675
+ interactive: false,
1676
+ onTextLayout: handleTextLayout
1677
+ }
1678
+ ),
1679
+ state.value.length === 0 && state.compositionText.length === 0 && placeholder ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1680
+ "text",
1681
+ {
1682
+ ...textStyle,
1683
+ ...placeholderStyle,
1684
+ text: placeholder,
1685
+ parseMarkup: false,
1686
+ printMode: "instant",
1687
+ anchor: [0, 0.5],
1688
+ pivot: [0, 0.5],
1689
+ interactive: false
1690
+ }
1691
+ ) : null,
1692
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1693
+ "sprite",
1694
+ {
1695
+ ...caretSpriteProps,
1696
+ x: caretX,
1697
+ y: (viewportHeight - caretHeight) / 2,
1698
+ mode: "nineslice",
1699
+ bounds: [0, 0, 0, 0],
1700
+ targetWidth: caretWidth,
1701
+ targetHeight: caretHeight,
1702
+ visible: focused && caretVisible && caretPosition !== void 0,
1703
+ interactive: false
1704
+ }
1705
+ )
1706
+ ] })
1707
+ ]
1708
+ }
1709
+ );
1710
+ });
1711
+
1712
+ // src/components/radio.tsx
1713
+ var import_react7 = require("react");
1714
+ var RadioGroupContext = (0, import_react7.createContext)(null);
1715
+ function RadioGroup({ value, defaultValue, onValueChange, children, ...containerProps }) {
1716
+ const [internalValue, setInternalValue] = (0, import_react7.useState)(defaultValue);
1717
+ const currentValue = value ?? internalValue;
1718
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1719
+ RadioGroupContext.Provider,
1720
+ {
1721
+ value: {
1722
+ value: currentValue,
1723
+ select: (nextValue) => {
1724
+ if (nextValue === currentValue) {
1725
+ return;
1726
+ }
1727
+ if (value === void 0) {
1728
+ setInternalValue(nextValue);
1729
+ }
1730
+ onValueChange?.(nextValue);
1731
+ }
1732
+ },
1733
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("container", { ...containerProps, children })
1734
+ }
1735
+ );
1736
+ }
1737
+ function Radio({ value, uncheckedSprite, checkedSprite, onPress, ...buttonProps }) {
1738
+ const group = (0, import_react7.useContext)(RadioGroupContext);
1739
+ if (!group) {
1740
+ throw new Error("Radio must be rendered inside a RadioGroup.");
1741
+ }
1742
+ const checked = group.value === value;
1743
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1744
+ Button,
1745
+ {
1746
+ ...buttonProps,
1747
+ sprite: checked ? checkedSprite : uncheckedSprite,
1748
+ onPress: (event) => {
1749
+ onPress?.(event);
1750
+ if (!event.defaultPrevented) {
1751
+ group.select(value);
1752
+ }
1753
+ }
1754
+ }
1755
+ );
1756
+ }
1757
+
1758
+ // src/components/select.tsx
1759
+ var import_react8 = require("react");
1395
1760
  function Select({
1396
1761
  value,
1397
1762
  defaultValue,
@@ -1410,8 +1775,8 @@ function Select({
1410
1775
  pivot = anchor,
1411
1776
  ...containerProps
1412
1777
  }) {
1413
- const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
1414
- const [open, setOpen] = (0, import_react6.useState)(false);
1778
+ const [internalValue, setInternalValue] = (0, import_react8.useState)(defaultValue);
1779
+ const [open, setOpen] = (0, import_react8.useState)(false);
1415
1780
  const currentValue = value ?? internalValue;
1416
1781
  const currentOption = options.find((item) => item.value === currentValue);
1417
1782
  const { paddingX = 0, paddingY = 0, gap = 0, offsetX = 0, offsetY = 0, ...listSprite } = list;
@@ -1443,36 +1808,46 @@ function Select({
1443
1808
  }
1444
1809
  }
1445
1810
  ),
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,
1811
+ open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1812
+ "sprite",
1448
1813
  {
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
1814
+ ...listSprite,
1815
+ excludeFromLayout: true,
1816
+ targetHeight: listHeight,
1817
+ x: offsetX,
1818
+ y: (trigger.targetHeight ?? 0) + offsetY,
1819
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1820
+ Button,
1821
+ {
1822
+ sprite: option,
1823
+ lockOn: item.value === currentValue ? "press" : void 0,
1824
+ text: item.text,
1825
+ textStyle,
1826
+ textOffsetX,
1827
+ textOffsetY,
1828
+ textAlign,
1829
+ onPress: (event) => {
1830
+ if (event.defaultPrevented) {
1831
+ return;
1832
+ }
1833
+ if (value === void 0) {
1834
+ setInternalValue(item.value);
1835
+ }
1836
+ onValueChange?.(item.value, item);
1837
+ setOpen(false);
1838
+ }
1839
+ },
1840
+ item.value
1841
+ )) })
1842
+ }
1843
+ ) : void 0
1469
1844
  ]
1470
1845
  }
1471
1846
  );
1472
1847
  }
1473
1848
 
1474
1849
  // src/components/slider.tsx
1475
- var import_react7 = require("react");
1850
+ var import_react9 = require("react");
1476
1851
  function clampValue(value) {
1477
1852
  if (Number.isNaN(value)) {
1478
1853
  return 0;
@@ -1502,14 +1877,14 @@ function Slider({
1502
1877
  onTouchCancel,
1503
1878
  ...containerProps
1504
1879
  }) {
1505
- const [internalValue, setInternalValue] = (0, import_react7.useState)(() => clampValue(defaultValue));
1506
- const [dragging, setDragging] = (0, import_react7.useState)(false);
1880
+ const [internalValue, setInternalValue] = (0, import_react9.useState)(() => clampValue(defaultValue));
1881
+ const [dragging, setDragging] = (0, import_react9.useState)(false);
1507
1882
  const currentValue = clampValue(value ?? internalValue);
1508
- const currentValueRef = (0, import_react7.useRef)(currentValue);
1883
+ const currentValueRef = (0, import_react9.useRef)(currentValue);
1509
1884
  currentValueRef.current = currentValue;
1510
- const dragStateRef = (0, import_react7.useRef)(null);
1885
+ const dragStateRef = (0, import_react9.useRef)(null);
1511
1886
  const distance = Math.max(0, track.targetWidth - thumb.targetWidth);
1512
- const setSliderValue = (0, import_react7.useCallback)((nextValue) => {
1887
+ const setSliderValue = (0, import_react9.useCallback)((nextValue) => {
1513
1888
  const clampedValue = clampValue(nextValue);
1514
1889
  currentValueRef.current = clampedValue;
1515
1890
  if (value === void 0) {
@@ -1517,7 +1892,7 @@ function Slider({
1517
1892
  }
1518
1893
  onValueChange?.(clampedValue);
1519
1894
  }, [onValueChange, value]);
1520
- const endDragging = (0, import_react7.useCallback)((commit) => {
1895
+ const endDragging = (0, import_react9.useCallback)((commit) => {
1521
1896
  if (dragStateRef.current === null) {
1522
1897
  return;
1523
1898
  }
@@ -1527,7 +1902,7 @@ function Slider({
1527
1902
  onValueCommit?.(currentValueRef.current);
1528
1903
  }
1529
1904
  }, [onValueCommit]);
1530
- (0, import_react7.useEffect)(() => {
1905
+ (0, import_react9.useEffect)(() => {
1531
1906
  const handleMouseUp = () => endDragging(true);
1532
1907
  const handleTouchEnd = (event) => {
1533
1908
  if (dragStateRef.current?.touchIdentifier === event.identifier) {
@@ -1548,7 +1923,7 @@ function Slider({
1548
1923
  removeTouchCancel();
1549
1924
  };
1550
1925
  }, [endDragging]);
1551
- (0, import_react7.useEffect)(() => {
1926
+ (0, import_react9.useEffect)(() => {
1552
1927
  if (disabled) {
1553
1928
  endDragging(false);
1554
1929
  }
@@ -2010,6 +2385,7 @@ var debugSessionController = {
2010
2385
  throw new Error("Page switching is not supported by the app state adapter");
2011
2386
  }
2012
2387
  await appStateAdapter.switchPage(routeName, params);
2388
+ await syncCurrentExecutionCursor();
2013
2389
  return;
2014
2390
  }
2015
2391
  if (navigator.hasOverlay(routeName)) {
@@ -2600,7 +2976,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
2600
2976
 
2601
2977
  // src/hooks/useScrollView.ts
2602
2978
  var import_core = require("@react-spring/core");
2603
- var import_react8 = require("react");
2979
+ var import_react10 = require("react");
2604
2980
  function clamp(value, min, max) {
2605
2981
  return Math.max(min, Math.min(max, value));
2606
2982
  }
@@ -2620,21 +2996,21 @@ function useScrollView({
2620
2996
  initialPosition = "start",
2621
2997
  wheelStep = 72
2622
2998
  }) {
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);
2999
+ const [contentHeight, setContentHeight] = (0, import_react10.useState)(0);
3000
+ const [hasContentLayout, setHasContentLayout] = (0, import_react10.useState)(false);
3001
+ const scrollTargetRef = (0, import_react10.useRef)(0);
3002
+ const didSetInitialPositionRef = (0, import_react10.useRef)(false);
3003
+ const touchDraggingRef = (0, import_react10.useRef)(false);
3004
+ const touchStartYRef = (0, import_react10.useRef)(0);
3005
+ const touchStartOffsetRef = (0, import_react10.useRef)(0);
2630
3006
  const scrollOffset = (0, import_core.useSpringValue)(0, {
2631
3007
  config: {
2632
3008
  tension: 320,
2633
3009
  friction: 32
2634
3010
  }
2635
3011
  });
2636
- const maxScroll = (0, import_react8.useMemo)(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
2637
- const scrollTo = (0, import_react8.useCallback)(
3012
+ const maxScroll = (0, import_react10.useMemo)(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
3013
+ const scrollTo = (0, import_react10.useCallback)(
2638
3014
  (nextOffset, immediate = false) => {
2639
3015
  const offset = clamp(nextOffset, 0, maxScroll);
2640
3016
  if (offset === scrollTargetRef.current && !immediate) {
@@ -2645,13 +3021,13 @@ function useScrollView({
2645
3021
  },
2646
3022
  [maxScroll, scrollOffset]
2647
3023
  );
2648
- const scrollToRatio = (0, import_react8.useCallback)(
3024
+ const scrollToRatio = (0, import_react10.useCallback)(
2649
3025
  (ratio, immediate = false) => {
2650
3026
  scrollTo(clamp(ratio, 0, 1) * maxScroll, immediate);
2651
3027
  },
2652
3028
  [maxScroll, scrollTo]
2653
3029
  );
2654
- const handleContentLayout = (0, import_react8.useCallback)(
3030
+ const handleContentLayout = (0, import_react10.useCallback)(
2655
3031
  (event) => {
2656
3032
  if (!didSetInitialPositionRef.current) {
2657
3033
  const initialOffset = initialPosition === "end" ? Math.max(0, event.height - viewportHeight) : 0;
@@ -2664,13 +3040,13 @@ function useScrollView({
2664
3040
  },
2665
3041
  [initialPosition, scrollOffset, viewportHeight]
2666
3042
  );
2667
- (0, import_react8.useEffect)(() => {
3043
+ (0, import_react10.useEffect)(() => {
2668
3044
  if (!hasContentLayout) {
2669
3045
  return;
2670
3046
  }
2671
3047
  scrollTo(scrollTargetRef.current, true);
2672
3048
  }, [hasContentLayout, maxScroll, scrollTo]);
2673
- const handleWheel = (0, import_react8.useCallback)(
3049
+ const handleWheel = (0, import_react10.useCallback)(
2674
3050
  (event) => {
2675
3051
  if (maxScroll <= 0) {
2676
3052
  return;
@@ -2685,7 +3061,7 @@ function useScrollView({
2685
3061
  },
2686
3062
  [maxScroll, scrollTo, viewportHeight, wheelStep]
2687
3063
  );
2688
- const handleTouchStart = (0, import_react8.useCallback)(
3064
+ const handleTouchStart = (0, import_react10.useCallback)(
2689
3065
  (event) => {
2690
3066
  if (maxScroll <= 0) {
2691
3067
  return;
@@ -2697,7 +3073,7 @@ function useScrollView({
2697
3073
  },
2698
3074
  [maxScroll]
2699
3075
  );
2700
- const handleTouchMove = (0, import_react8.useCallback)(
3076
+ const handleTouchMove = (0, import_react10.useCallback)(
2701
3077
  (event) => {
2702
3078
  if (!touchDraggingRef.current) {
2703
3079
  return;
@@ -2707,7 +3083,7 @@ function useScrollView({
2707
3083
  },
2708
3084
  [scrollTo]
2709
3085
  );
2710
- const handleTouchEnd = (0, import_react8.useCallback)((event) => {
3086
+ const handleTouchEnd = (0, import_react10.useCallback)((event) => {
2711
3087
  if (!touchDraggingRef.current) {
2712
3088
  return;
2713
3089
  }
@@ -2729,7 +3105,7 @@ function useScrollView({
2729
3105
  }
2730
3106
 
2731
3107
  // src/hooks/useScenario.ts
2732
- var import_react9 = require("react");
3108
+ var import_react11 = require("react");
2733
3109
  var currentScenarioSession = null;
2734
3110
  var scenarioSessionId = 0;
2735
3111
  var scenarioLifecycleQueue = Promise.resolve();
@@ -2871,7 +3247,7 @@ function setWaiting(time, skippable) {
2871
3247
  executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
2872
3248
  }
2873
3249
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2874
- (0, import_react9.useEffect)(() => {
3250
+ (0, import_react11.useEffect)(() => {
2875
3251
  const session = acquireScenarioSession({
2876
3252
  stories: normalizeStories(stories),
2877
3253
  startName,
@@ -2885,9 +3261,9 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2885
3261
  }
2886
3262
 
2887
3263
  // src/hooks/useSoundEffect.ts
2888
- var import_react10 = require("react");
3264
+ var import_react12 = require("react");
2889
3265
  function useSoundEffect(src) {
2890
- (0, import_react10.useEffect)(() => {
3266
+ (0, import_react12.useEffect)(() => {
2891
3267
  return () => {
2892
3268
  try {
2893
3269
  if (src) {
@@ -2902,7 +3278,7 @@ function useSoundEffect(src) {
2902
3278
  }
2903
3279
  };
2904
3280
  }, [src]);
2905
- return (0, import_react10.useCallback)(() => {
3281
+ return (0, import_react12.useCallback)(() => {
2906
3282
  try {
2907
3283
  if (src) {
2908
3284
  executePluginCommand("audio", {
@@ -2918,7 +3294,7 @@ function useSoundEffect(src) {
2918
3294
  }
2919
3295
 
2920
3296
  // src/hooks/useStage.ts
2921
- var import_react11 = require("react");
3297
+ var import_react13 = require("react");
2922
3298
  var import_valtio5 = require("valtio");
2923
3299
  var SKIP_INTERVAL_MS = 80;
2924
3300
  var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
@@ -3004,6 +3380,9 @@ function createStage() {
3004
3380
  return addToRegistry(interruptCallbacks, cb);
3005
3381
  }
3006
3382
  function tryInterrupt() {
3383
+ if (tryFinishActiveWait()) {
3384
+ return true;
3385
+ }
3007
3386
  for (const cb of interruptCallbacks) {
3008
3387
  try {
3009
3388
  if (cb()) return true;
@@ -3051,6 +3430,8 @@ function createStage() {
3051
3430
  let _autoTicketSeq = 0;
3052
3431
  const _pendingAutoTickets = /* @__PURE__ */ new Map();
3053
3432
  let _fastForwardRuntime = null;
3433
+ let _activeWait = null;
3434
+ let _activeWaitSeq = 0;
3054
3435
  let _managedAdvanceInFlight = false;
3055
3436
  let _managedAdvancePending = false;
3056
3437
  function resetManagedAdvanceState() {
@@ -3139,12 +3520,53 @@ function createStage() {
3139
3520
  });
3140
3521
  }
3141
3522
  function requestNextLine() {
3523
+ if (_activeWait !== null) {
3524
+ return;
3525
+ }
3142
3526
  if (isManagedAdvanceActive()) {
3143
3527
  scheduleManagedAdvance();
3144
3528
  return;
3145
3529
  }
3146
3530
  void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
3147
3531
  }
3532
+ function invalidateActiveWait() {
3533
+ if (_activeWait === null) {
3534
+ return;
3535
+ }
3536
+ _activeWait.settled = true;
3537
+ _activeWait = null;
3538
+ }
3539
+ function settleActiveWait(wait, error) {
3540
+ if (_activeWait !== wait || wait.settled) {
3541
+ return;
3542
+ }
3543
+ wait.settled = true;
3544
+ _activeWait = null;
3545
+ if (error !== void 0) {
3546
+ console.error(`[Stage] Promise hold ${wait.id} rejected:`, error);
3547
+ }
3548
+ requestNextLine();
3549
+ }
3550
+ function tryFinishActiveWait() {
3551
+ const wait = _activeWait;
3552
+ if (wait === null || !wait.skippable || wait.settled) {
3553
+ return false;
3554
+ }
3555
+ const finish = wait.until.finish;
3556
+ if (typeof finish === "function") {
3557
+ try {
3558
+ finish.call(wait.until);
3559
+ } catch (error) {
3560
+ console.error(`[Stage] Failed to finish Promise hold ${wait.id}:`, error);
3561
+ }
3562
+ } else {
3563
+ console.warn(
3564
+ `[Stage] Skipping Promise hold ${wait.id} without finish(); the underlying operation may continue.`
3565
+ );
3566
+ }
3567
+ settleActiveWait(wait);
3568
+ return true;
3569
+ }
3148
3570
  function clearAutoResumeTimer() {
3149
3571
  if (_autoResumeTimer !== null) {
3150
3572
  clearTimeout(_autoResumeTimer);
@@ -3332,7 +3754,11 @@ function createStage() {
3332
3754
  if (isAnyBlockerActive()) return;
3333
3755
  skipState.active = true;
3334
3756
  invokeSkipCallbacks();
3757
+ const activeWait = _activeWait;
3335
3758
  tryInterrupt();
3759
+ if (activeWait !== null && activeWait !== _activeWait) {
3760
+ return;
3761
+ }
3336
3762
  scheduleSkipNextLine();
3337
3763
  }
3338
3764
  function stopSkip() {
@@ -3351,6 +3777,7 @@ function createStage() {
3351
3777
  if (fastForwardState.active) return;
3352
3778
  if (isAnyAutoBlockerActive()) return;
3353
3779
  autoState.active = true;
3780
+ if (_activeWait !== null) return;
3354
3781
  tryInterrupt();
3355
3782
  scheduleAutoNextLine();
3356
3783
  }
@@ -3387,6 +3814,7 @@ function createStage() {
3387
3814
  return fastForwardState.active;
3388
3815
  }
3389
3816
  function resetRuntimeState() {
3817
+ invalidateActiveWait();
3390
3818
  skipState.active = false;
3391
3819
  autoState.active = false;
3392
3820
  fastForwardState.active = false;
@@ -3432,7 +3860,7 @@ function createStage() {
3432
3860
  setWaiting(time, skippable);
3433
3861
  }
3434
3862
  },
3435
- hold() {
3863
+ hold(until, skippable = false) {
3436
3864
  handled = true;
3437
3865
  if (warpActive) {
3438
3866
  return;
@@ -3441,6 +3869,24 @@ function createStage() {
3441
3869
  requestNextLine();
3442
3870
  return;
3443
3871
  }
3872
+ if (until !== void 0) {
3873
+ invalidateActiveWait();
3874
+ const wait = {
3875
+ id: ++_activeWaitSeq,
3876
+ until,
3877
+ skippable,
3878
+ settled: false
3879
+ };
3880
+ _activeWait = wait;
3881
+ void Promise.resolve(until).then(
3882
+ () => settleActiveWait(wait),
3883
+ (error) => settleActiveWait(wait, error)
3884
+ );
3885
+ if (skipState.active) {
3886
+ tryFinishActiveWait();
3887
+ }
3888
+ return;
3889
+ }
3444
3890
  if (skipState.active && !unskippableFlag) {
3445
3891
  if (isAnyBlockerActive()) {
3446
3892
  stopSkip();
@@ -3500,6 +3946,7 @@ function createStage() {
3500
3946
  return parsed.data;
3501
3947
  }
3502
3948
  function dispatchCommand(e) {
3949
+ invalidateActiveWait();
3503
3950
  for (const cb of beforeHandleCommandCallbacks) {
3504
3951
  try {
3505
3952
  cb(e);
@@ -3528,6 +3975,7 @@ function createStage() {
3528
3975
  }
3529
3976
  }
3530
3977
  function dispatchTextLine(e) {
3978
+ invalidateActiveWait();
3531
3979
  const { control, wasHandled } = buildControl();
3532
3980
  for (const handler of textLineHandlers) {
3533
3981
  handler(e, control);
@@ -3561,6 +4009,7 @@ function createStage() {
3561
4009
  })
3562
4010
  ];
3563
4011
  return () => {
4012
+ resetRuntimeState();
3564
4013
  for (const cleanup of cleanups) cleanup();
3565
4014
  };
3566
4015
  }
@@ -3581,52 +4030,54 @@ function createStage() {
3581
4030
  startAuto,
3582
4031
  stopAuto,
3583
4032
  isAutoing,
4033
+ setDefaultAutoTailMs,
3584
4034
  startFastForward,
3585
4035
  stopFastForward,
3586
4036
  isFastForwarding,
4037
+ getSeekingType,
3587
4038
  resetRuntimeState,
3588
4039
  bindEvents
3589
4040
  };
3590
4041
  }
3591
- var StageContext = (0, import_react11.createContext)(null);
4042
+ var StageContext = (0, import_react13.createContext)(null);
3592
4043
  function useStageContext() {
3593
- const ctx = (0, import_react11.useContext)(StageContext);
4044
+ const ctx = (0, import_react13.useContext)(StageContext);
3594
4045
  if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
3595
4046
  return ctx;
3596
4047
  }
3597
4048
  function StageContextProvider({ stage, children }) {
3598
- (0, import_react11.useEffect)(() => {
4049
+ (0, import_react13.useEffect)(() => {
3599
4050
  return stage.bindEvents();
3600
4051
  }, [stage]);
3601
- return (0, import_react11.createElement)(StageContext.Provider, { value: stage }, children);
4052
+ return (0, import_react13.createElement)(StageContext.Provider, { value: stage }, children);
3602
4053
  }
3603
4054
  function useSkipCallback(callback) {
3604
4055
  const stage = useStageContext();
3605
- (0, import_react11.useEffect)(() => {
4056
+ (0, import_react13.useEffect)(() => {
3606
4057
  return stage.addSkipCallback(callback);
3607
4058
  }, [stage, callback]);
3608
4059
  }
3609
4060
  function useInterruptCallback(callback) {
3610
4061
  const stage = useStageContext();
3611
- (0, import_react11.useEffect)(() => {
4062
+ (0, import_react13.useEffect)(() => {
3612
4063
  return stage.addInterruptCallback(callback);
3613
4064
  }, [stage, callback]);
3614
4065
  }
3615
4066
  function useSkipBlocker(callback) {
3616
4067
  const stage = useStageContext();
3617
- (0, import_react11.useEffect)(() => {
4068
+ (0, import_react13.useEffect)(() => {
3618
4069
  return stage.addSkipBlocker(callback);
3619
4070
  }, [stage, callback]);
3620
4071
  }
3621
4072
  function useAutoBlocker(callback) {
3622
4073
  const stage = useStageContext();
3623
- (0, import_react11.useEffect)(() => {
4074
+ (0, import_react13.useEffect)(() => {
3624
4075
  return stage.addAutoBlocker(callback);
3625
4076
  }, [stage, callback]);
3626
4077
  }
3627
4078
  function useAutoTicket() {
3628
4079
  const stage = useStageContext();
3629
- return (0, import_react11.useCallback)(
4080
+ return (0, import_react13.useCallback)(
3630
4081
  (options) => {
3631
4082
  return stage.issueAutoTicket(options);
3632
4083
  },
@@ -3635,7 +4086,7 @@ function useAutoTicket() {
3635
4086
  }
3636
4087
  function useBeforeHandleCommandCallback(callback) {
3637
4088
  const stage = useStageContext();
3638
- (0, import_react11.useEffect)(() => {
4089
+ (0, import_react13.useEffect)(() => {
3639
4090
  return stage.addBeforeHandleCommandCallback(callback);
3640
4091
  }, [stage, callback]);
3641
4092
  }