@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.mjs CHANGED
@@ -22,14 +22,18 @@ __export(lib_exports, {
22
22
  Button: () => Button,
23
23
  Checkbox: () => Checkbox,
24
24
  DEBUG_EMPTY_PAGE: () => DEBUG_EMPTY_PAGE,
25
+ Input: () => Input,
25
26
  OverlayParamsContext: () => OverlayParamsContext,
26
27
  PageParamsContext: () => PageParamsContext,
28
+ Radio: () => Radio,
29
+ RadioGroup: () => RadioGroup,
27
30
  ScrollView: () => ScrollView,
28
31
  Select: () => Select,
29
32
  Slider: () => Slider,
30
33
  StageContextProvider: () => StageContextProvider,
31
34
  a: () => animated,
32
35
  addEventListener: () => addEventListener,
36
+ addFluidObserver: () => addFluidObserver,
33
37
  animated: () => animated,
34
38
  autoState: () => autoState,
35
39
  createBubbleEvent: () => createBubbleEvent,
@@ -56,6 +60,7 @@ __export(lib_exports, {
56
60
  moyu: () => moyu_exports,
57
61
  nextLine: () => nextLine,
58
62
  registerAppStateAdapter: () => registerAppStateAdapter,
63
+ removeFluidObserver: () => removeFluidObserver,
59
64
  replaceUiData: () => replaceUiData,
60
65
  setDefaultAutoTailMs: () => setDefaultAutoTailMs,
61
66
  setWaiting: () => setWaiting,
@@ -97,10 +102,12 @@ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constant
97
102
  var moyu_exports = {};
98
103
  __export(moyu_exports, {
99
104
  addChild: () => addChild,
105
+ blurEditable: () => blurEditable,
100
106
  createInstance: () => createInstance,
101
107
  destroyInstance: () => destroyInstance,
102
108
  executeNodeCommand: () => executeNodeCommand,
103
109
  executePluginCommand: () => executePluginCommand,
110
+ focusEditable: () => focusEditable,
104
111
  insertChild: () => insertChild,
105
112
  insertChildBefore: () => insertChildBefore,
106
113
  removeChild: () => removeChild,
@@ -138,6 +145,12 @@ function removeChild(nodeId, childNodeId) {
138
145
  function updateProps(nodeId, props) {
139
146
  moyu.pushCommand("update_props", [nodeId, props]);
140
147
  }
148
+ function focusEditable(nodeId) {
149
+ moyu.pushCommand("focus_editable", [nodeId]);
150
+ }
151
+ function blurEditable(nodeId) {
152
+ moyu.pushCommand("blur_editable", [nodeId]);
153
+ }
141
154
  function executeNodeCommand(nodeId, payload) {
142
155
  return moyu.executeNodeCommand(nodeId, payload);
143
156
  }
@@ -781,10 +794,18 @@ globalThis.__moyu_eval_sandbox = (code) => {
781
794
  var spring_exports = {};
782
795
  __export(spring_exports, {
783
796
  a: () => animated,
784
- animated: () => animated
797
+ addFluidObserver: () => addFluidObserver,
798
+ animated: () => animated,
799
+ removeFluidObserver: () => removeFluidObserver
785
800
  });
786
801
  import { createHost } from "@react-spring/animated";
787
- import { Globals, colors, createStringInterpolator } from "@react-spring/shared";
802
+ import {
803
+ Globals,
804
+ addFluidObserver as observeFluidValue,
805
+ colors,
806
+ createStringInterpolator,
807
+ removeFluidObserver as unobserveFluidValue
808
+ } from "@react-spring/shared";
788
809
 
789
810
  // src/spring/primitives.ts
790
811
  var primitives = [
@@ -815,6 +836,8 @@ var host = createHost(primitives, {
815
836
  }
816
837
  });
817
838
  var animated = host.animated;
839
+ var addFluidObserver = observeFluidValue;
840
+ var removeFluidObserver = unobserveFluidValue;
818
841
 
819
842
  // src/lib.ts
820
843
  __reExport(lib_exports, spring_exports);
@@ -839,7 +862,8 @@ function createBubbleEvent(body, targetId) {
839
862
 
840
863
  // src/events/globals.ts
841
864
  var globalEventListeners = {};
842
- var globalRequestAnimationFrameListeners = [];
865
+ var globalRequestAnimationFrameListeners = /* @__PURE__ */ new Map();
866
+ var nextAnimationFrameHandle = 0;
843
867
  var BUBBLE_EVENT_NAMES = ["mouseevent", "touchevent", "keyboardevent", "wheelevent"];
844
868
  globalThis.__moyu_receive_event = (raw_event) => {
845
869
  const { name, body } = raw_event;
@@ -866,7 +890,8 @@ globalThis.__moyu_receive_event = (raw_event) => {
866
890
  return;
867
891
  }
868
892
  case "animationframecallbackevent": {
869
- const listeners = globalRequestAnimationFrameListeners.splice(0);
893
+ const listeners = [...globalRequestAnimationFrameListeners.values()];
894
+ globalRequestAnimationFrameListeners.clear();
870
895
  const { timestamp } = body;
871
896
  for (const listener of listeners) {
872
897
  listener?.(timestamp);
@@ -924,10 +949,13 @@ function handleBubbleEvent(name, body) {
924
949
  }
925
950
  if (globalThis.__moyu_native) {
926
951
  globalThis.requestAnimationFrame = (callback) => {
927
- return globalRequestAnimationFrameListeners.push(callback) - 1;
952
+ const handle = nextAnimationFrameHandle;
953
+ nextAnimationFrameHandle += 1;
954
+ globalRequestAnimationFrameListeners.set(handle, callback);
955
+ return handle;
928
956
  };
929
957
  globalThis.cancelAnimationFrame = (handle) => {
930
- globalRequestAnimationFrameListeners.splice(handle, 1);
958
+ globalRequestAnimationFrameListeners.delete(handle);
931
959
  };
932
960
  }
933
961
 
@@ -1378,8 +1406,343 @@ function Checkbox({
1378
1406
  );
1379
1407
  }
1380
1408
 
1409
+ // src/components/input.tsx
1410
+ import { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState3 } from "react";
1411
+ var Input = forwardRef(function Input2({
1412
+ value,
1413
+ defaultValue = "",
1414
+ placeholder = "",
1415
+ disabled = false,
1416
+ readOnly = false,
1417
+ autoFocus = false,
1418
+ width,
1419
+ height,
1420
+ paddingX = 0,
1421
+ textStyle,
1422
+ placeholderStyle,
1423
+ caret,
1424
+ background,
1425
+ onInput,
1426
+ onChange,
1427
+ onFocus,
1428
+ onBlur,
1429
+ onCompositionStart,
1430
+ onCompositionUpdate,
1431
+ onCompositionEnd,
1432
+ interactive,
1433
+ cursor = "text",
1434
+ onMouseEnter,
1435
+ onMouseLeave,
1436
+ onMouseDown,
1437
+ onMouseUp,
1438
+ onTouchStart,
1439
+ onTouchEnd,
1440
+ onTouchCancel,
1441
+ ...nodeProps
1442
+ }, ref) {
1443
+ const editableRef = useRef(null);
1444
+ const initialValueRef = useRef(value ?? defaultValue);
1445
+ const initialValue = initialValueRef.current;
1446
+ const stateRef = useRef({
1447
+ value: initialValue,
1448
+ isComposing: false,
1449
+ compositionText: ""
1450
+ });
1451
+ const [state, setState] = useState3(stateRef.current);
1452
+ const [focused, setFocused] = useState3(false);
1453
+ const [hovered, setHovered] = useState3(false);
1454
+ const [pressed, setPressed] = useState3(false);
1455
+ const [caretPosition, setCaretPosition] = useState3();
1456
+ const [caretVisible, setCaretVisible] = useState3(true);
1457
+ const shouldAutoFocusRef = useRef(autoFocus && !disabled);
1458
+ const viewportWidth = Math.max(0, Math.round(width));
1459
+ const viewportHeight = Math.max(0, Math.round(height));
1460
+ const contentWidth = Math.max(0, viewportWidth - paddingX * 2);
1461
+ const { width: caretWidth, height: configuredCaretHeight, blinkInterval, ...caretSpriteProps } = caret;
1462
+ const caretHeight = configuredCaretHeight ?? textStyle.fontSize ?? viewportHeight;
1463
+ 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;
1464
+ const displayValue = state.value + state.compositionText;
1465
+ const updateState = useCallback((nextState) => {
1466
+ const previousState = stateRef.current;
1467
+ if (previousState.value === nextState.value && previousState.isComposing === nextState.isComposing && previousState.compositionText === nextState.compositionText) {
1468
+ return;
1469
+ }
1470
+ const displayValueChanged = previousState.value + previousState.compositionText !== nextState.value + nextState.compositionText;
1471
+ stateRef.current = nextState;
1472
+ setState(nextState);
1473
+ if (displayValueChanged) {
1474
+ setCaretPosition(void 0);
1475
+ }
1476
+ setCaretVisible(true);
1477
+ }, []);
1478
+ const handleInput = useCallback(
1479
+ (event) => {
1480
+ updateState(event.state);
1481
+ onInput?.(event.state);
1482
+ },
1483
+ [onInput, updateState]
1484
+ );
1485
+ const handleChange = useCallback(
1486
+ (event) => {
1487
+ updateState(event.state);
1488
+ onChange?.(event.state, event.source);
1489
+ },
1490
+ [onChange, updateState]
1491
+ );
1492
+ const handleFocus = useCallback(
1493
+ (event) => {
1494
+ updateState(event.state);
1495
+ setFocused(true);
1496
+ onFocus?.(event.state);
1497
+ },
1498
+ [onFocus, updateState]
1499
+ );
1500
+ const handleBlur = useCallback(
1501
+ (event) => {
1502
+ updateState(event.state);
1503
+ setFocused(false);
1504
+ onBlur?.(event.state);
1505
+ },
1506
+ [onBlur, updateState]
1507
+ );
1508
+ const handleCompositionStart = useCallback(
1509
+ (event) => {
1510
+ updateState(event.state);
1511
+ onCompositionStart?.(event.state);
1512
+ },
1513
+ [onCompositionStart, updateState]
1514
+ );
1515
+ const handleCompositionUpdate = useCallback(
1516
+ (event) => {
1517
+ updateState(event.state);
1518
+ onCompositionUpdate?.(event.state);
1519
+ },
1520
+ [onCompositionUpdate, updateState]
1521
+ );
1522
+ const handleCompositionEnd = useCallback(
1523
+ (event) => {
1524
+ updateState(event.state);
1525
+ onCompositionEnd?.(event.state);
1526
+ },
1527
+ [onCompositionEnd, updateState]
1528
+ );
1529
+ const handleTextLayout = useCallback((event) => {
1530
+ if (event.text === stateRef.current.value + stateRef.current.compositionText) {
1531
+ setCaretPosition(event.endCursorPosition);
1532
+ }
1533
+ }, []);
1534
+ useImperativeHandle(
1535
+ ref,
1536
+ () => ({
1537
+ focus: () => {
1538
+ if (editableRef.current) {
1539
+ focusEditable(editableRef.current.nodeId);
1540
+ }
1541
+ },
1542
+ blur: () => {
1543
+ if (editableRef.current) {
1544
+ blurEditable(editableRef.current.nodeId);
1545
+ }
1546
+ },
1547
+ getState: () => stateRef.current
1548
+ }),
1549
+ []
1550
+ );
1551
+ useEffect2(() => {
1552
+ if (value === void 0 || value === stateRef.current.value) {
1553
+ return;
1554
+ }
1555
+ editableRef.current?.executeCommand({ subCommand: "setValue", value });
1556
+ const nextState = editableRef.current?.executeCommand({ subCommand: "getState" });
1557
+ if (nextState) {
1558
+ updateState(nextState);
1559
+ }
1560
+ }, [updateState, value]);
1561
+ useEffect2(() => {
1562
+ if (shouldAutoFocusRef.current && editableRef.current) {
1563
+ focusEditable(editableRef.current.nodeId);
1564
+ }
1565
+ }, []);
1566
+ useEffect2(() => {
1567
+ if (!pressed) {
1568
+ return;
1569
+ }
1570
+ const release = () => setPressed(false);
1571
+ const removeMouseUp = addEventListener("mouseup", release);
1572
+ const removeTouchEnd = addEventListener("touchend", release);
1573
+ const removeTouchCancel = addEventListener("touchcancel", release);
1574
+ return () => {
1575
+ removeMouseUp();
1576
+ removeTouchEnd();
1577
+ removeTouchCancel();
1578
+ };
1579
+ }, [pressed]);
1580
+ useEffect2(() => {
1581
+ if (disabled) {
1582
+ setHovered(false);
1583
+ setPressed(false);
1584
+ }
1585
+ }, [disabled]);
1586
+ useEffect2(() => {
1587
+ if (!focused) {
1588
+ setCaretVisible(false);
1589
+ return;
1590
+ }
1591
+ const expectedDisplayValue = displayValue;
1592
+ setCaretVisible(true);
1593
+ const timer = setInterval(() => {
1594
+ if (stateRef.current.value + stateRef.current.compositionText === expectedDisplayValue) {
1595
+ setCaretVisible((visible) => !visible);
1596
+ }
1597
+ }, blinkInterval ?? 500);
1598
+ return () => clearInterval(timer);
1599
+ }, [blinkInterval, displayValue, focused]);
1600
+ useEffect2(() => {
1601
+ if (!focused || !caretPosition) {
1602
+ return;
1603
+ }
1604
+ editableRef.current?.executeCommand({
1605
+ subCommand: "setCaretRect",
1606
+ x: paddingX + caretPosition[0],
1607
+ y: (viewportHeight - caretHeight) / 2,
1608
+ width: caretWidth,
1609
+ height: caretHeight
1610
+ });
1611
+ }, [caretHeight, caretPosition, caretWidth, focused, paddingX, viewportHeight]);
1612
+ const caretX = caretPosition?.[0] ?? 0;
1613
+ return /* @__PURE__ */ jsxs(
1614
+ "editable",
1615
+ {
1616
+ ...nodeProps,
1617
+ ref: editableRef,
1618
+ value: initialValue,
1619
+ disabled,
1620
+ readOnly,
1621
+ interactive: disabled ? false : interactive,
1622
+ cursor,
1623
+ onMouseEnter: mergeEvent(onMouseEnter, () => setHovered(true)),
1624
+ onMouseLeave: mergeEvent(onMouseLeave, () => {
1625
+ setHovered(false);
1626
+ setPressed(false);
1627
+ }),
1628
+ onMouseDown: mergeEvent(onMouseDown, () => setPressed(true)),
1629
+ onMouseUp: mergeEvent(onMouseUp, () => {
1630
+ setPressed(false);
1631
+ setHovered(true);
1632
+ }),
1633
+ onTouchStart: mergeEvent(onTouchStart, () => setPressed(true)),
1634
+ onTouchEnd: mergeEvent(onTouchEnd, () => {
1635
+ setPressed(false);
1636
+ setHovered(true);
1637
+ }),
1638
+ onTouchCancel: mergeEvent(onTouchCancel, () => {
1639
+ setHovered(false);
1640
+ setPressed(false);
1641
+ }),
1642
+ onInput: handleInput,
1643
+ onChange: handleChange,
1644
+ onFocus: handleFocus,
1645
+ onBlur: handleBlur,
1646
+ onCompositionStart: handleCompositionStart,
1647
+ onCompositionUpdate: handleCompositionUpdate,
1648
+ onCompositionEnd: handleCompositionEnd,
1649
+ children: [
1650
+ backgroundStyle ? /* @__PURE__ */ jsx("sprite", { ...backgroundStyle, targetWidth: viewportWidth, targetHeight: viewportHeight, interactive: false }) : null,
1651
+ /* @__PURE__ */ jsxs("clip", { x: paddingX, width: contentWidth, height: viewportHeight, interactive: false, children: [
1652
+ /* @__PURE__ */ jsx(
1653
+ "text",
1654
+ {
1655
+ ...textStyle,
1656
+ text: displayValue,
1657
+ parseMarkup: false,
1658
+ printMode: "instant",
1659
+ anchor: [0, 0.5],
1660
+ pivot: [0, 0.5],
1661
+ interactive: false,
1662
+ onTextLayout: handleTextLayout
1663
+ }
1664
+ ),
1665
+ state.value.length === 0 && state.compositionText.length === 0 && placeholder ? /* @__PURE__ */ jsx(
1666
+ "text",
1667
+ {
1668
+ ...textStyle,
1669
+ ...placeholderStyle,
1670
+ text: placeholder,
1671
+ parseMarkup: false,
1672
+ printMode: "instant",
1673
+ anchor: [0, 0.5],
1674
+ pivot: [0, 0.5],
1675
+ interactive: false
1676
+ }
1677
+ ) : null,
1678
+ /* @__PURE__ */ jsx(
1679
+ "sprite",
1680
+ {
1681
+ ...caretSpriteProps,
1682
+ x: caretX,
1683
+ y: (viewportHeight - caretHeight) / 2,
1684
+ mode: "nineslice",
1685
+ bounds: [0, 0, 0, 0],
1686
+ targetWidth: caretWidth,
1687
+ targetHeight: caretHeight,
1688
+ visible: focused && caretVisible && caretPosition !== void 0,
1689
+ interactive: false
1690
+ }
1691
+ )
1692
+ ] })
1693
+ ]
1694
+ }
1695
+ );
1696
+ });
1697
+
1698
+ // src/components/radio.tsx
1699
+ import { createContext as createContext3, useContext as useContext3, useState as useState4 } from "react";
1700
+ var RadioGroupContext = createContext3(null);
1701
+ function RadioGroup({ value, defaultValue, onValueChange, children, ...containerProps }) {
1702
+ const [internalValue, setInternalValue] = useState4(defaultValue);
1703
+ const currentValue = value ?? internalValue;
1704
+ return /* @__PURE__ */ jsx(
1705
+ RadioGroupContext.Provider,
1706
+ {
1707
+ value: {
1708
+ value: currentValue,
1709
+ select: (nextValue) => {
1710
+ if (nextValue === currentValue) {
1711
+ return;
1712
+ }
1713
+ if (value === void 0) {
1714
+ setInternalValue(nextValue);
1715
+ }
1716
+ onValueChange?.(nextValue);
1717
+ }
1718
+ },
1719
+ children: /* @__PURE__ */ jsx("container", { ...containerProps, children })
1720
+ }
1721
+ );
1722
+ }
1723
+ function Radio({ value, uncheckedSprite, checkedSprite, onPress, ...buttonProps }) {
1724
+ const group = useContext3(RadioGroupContext);
1725
+ if (!group) {
1726
+ throw new Error("Radio must be rendered inside a RadioGroup.");
1727
+ }
1728
+ const checked = group.value === value;
1729
+ return /* @__PURE__ */ jsx(
1730
+ Button,
1731
+ {
1732
+ ...buttonProps,
1733
+ sprite: checked ? checkedSprite : uncheckedSprite,
1734
+ onPress: (event) => {
1735
+ onPress?.(event);
1736
+ if (!event.defaultPrevented) {
1737
+ group.select(value);
1738
+ }
1739
+ }
1740
+ }
1741
+ );
1742
+ }
1743
+
1381
1744
  // src/components/select.tsx
1382
- import { useState as useState3 } from "react";
1745
+ import { useState as useState5 } from "react";
1383
1746
  function Select({
1384
1747
  value,
1385
1748
  defaultValue,
@@ -1398,8 +1761,8 @@ function Select({
1398
1761
  pivot = anchor,
1399
1762
  ...containerProps
1400
1763
  }) {
1401
- const [internalValue, setInternalValue] = useState3(defaultValue);
1402
- const [open, setOpen] = useState3(false);
1764
+ const [internalValue, setInternalValue] = useState5(defaultValue);
1765
+ const [open, setOpen] = useState5(false);
1403
1766
  const currentValue = value ?? internalValue;
1404
1767
  const currentOption = options.find((item) => item.value === currentValue);
1405
1768
  const { paddingX = 0, paddingY = 0, gap = 0, offsetX = 0, offsetY = 0, ...listSprite } = list;
@@ -1431,36 +1794,46 @@ function Select({
1431
1794
  }
1432
1795
  }
1433
1796
  ),
1434
- open && !disabled ? /* @__PURE__ */ jsx("sprite", { ...listSprite, targetHeight: listHeight, x: offsetX, y: (trigger.targetHeight ?? 0) + offsetY, children: /* @__PURE__ */ jsx("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ jsx(
1435
- Button,
1797
+ open && !disabled ? /* @__PURE__ */ jsx(
1798
+ "sprite",
1436
1799
  {
1437
- sprite: option,
1438
- lockOn: item.value === currentValue ? "press" : void 0,
1439
- text: item.text,
1440
- textStyle,
1441
- textOffsetX,
1442
- textOffsetY,
1443
- textAlign,
1444
- onPress: (event) => {
1445
- if (event.defaultPrevented) {
1446
- return;
1447
- }
1448
- if (value === void 0) {
1449
- setInternalValue(item.value);
1450
- }
1451
- onValueChange?.(item.value, item);
1452
- setOpen(false);
1453
- }
1454
- },
1455
- item.value
1456
- )) }) }) : void 0
1800
+ ...listSprite,
1801
+ excludeFromLayout: true,
1802
+ targetHeight: listHeight,
1803
+ x: offsetX,
1804
+ y: (trigger.targetHeight ?? 0) + offsetY,
1805
+ children: /* @__PURE__ */ jsx("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ jsx(
1806
+ Button,
1807
+ {
1808
+ sprite: option,
1809
+ lockOn: item.value === currentValue ? "press" : void 0,
1810
+ text: item.text,
1811
+ textStyle,
1812
+ textOffsetX,
1813
+ textOffsetY,
1814
+ textAlign,
1815
+ onPress: (event) => {
1816
+ if (event.defaultPrevented) {
1817
+ return;
1818
+ }
1819
+ if (value === void 0) {
1820
+ setInternalValue(item.value);
1821
+ }
1822
+ onValueChange?.(item.value, item);
1823
+ setOpen(false);
1824
+ }
1825
+ },
1826
+ item.value
1827
+ )) })
1828
+ }
1829
+ ) : void 0
1457
1830
  ]
1458
1831
  }
1459
1832
  );
1460
1833
  }
1461
1834
 
1462
1835
  // src/components/slider.tsx
1463
- import { useCallback, useEffect as useEffect2, useRef, useState as useState4 } from "react";
1836
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "react";
1464
1837
  function clampValue(value) {
1465
1838
  if (Number.isNaN(value)) {
1466
1839
  return 0;
@@ -1490,14 +1863,14 @@ function Slider({
1490
1863
  onTouchCancel,
1491
1864
  ...containerProps
1492
1865
  }) {
1493
- const [internalValue, setInternalValue] = useState4(() => clampValue(defaultValue));
1494
- const [dragging, setDragging] = useState4(false);
1866
+ const [internalValue, setInternalValue] = useState6(() => clampValue(defaultValue));
1867
+ const [dragging, setDragging] = useState6(false);
1495
1868
  const currentValue = clampValue(value ?? internalValue);
1496
- const currentValueRef = useRef(currentValue);
1869
+ const currentValueRef = useRef2(currentValue);
1497
1870
  currentValueRef.current = currentValue;
1498
- const dragStateRef = useRef(null);
1871
+ const dragStateRef = useRef2(null);
1499
1872
  const distance = Math.max(0, track.targetWidth - thumb.targetWidth);
1500
- const setSliderValue = useCallback((nextValue) => {
1873
+ const setSliderValue = useCallback2((nextValue) => {
1501
1874
  const clampedValue = clampValue(nextValue);
1502
1875
  currentValueRef.current = clampedValue;
1503
1876
  if (value === void 0) {
@@ -1505,7 +1878,7 @@ function Slider({
1505
1878
  }
1506
1879
  onValueChange?.(clampedValue);
1507
1880
  }, [onValueChange, value]);
1508
- const endDragging = useCallback((commit) => {
1881
+ const endDragging = useCallback2((commit) => {
1509
1882
  if (dragStateRef.current === null) {
1510
1883
  return;
1511
1884
  }
@@ -1515,7 +1888,7 @@ function Slider({
1515
1888
  onValueCommit?.(currentValueRef.current);
1516
1889
  }
1517
1890
  }, [onValueCommit]);
1518
- useEffect2(() => {
1891
+ useEffect3(() => {
1519
1892
  const handleMouseUp = () => endDragging(true);
1520
1893
  const handleTouchEnd = (event) => {
1521
1894
  if (dragStateRef.current?.touchIdentifier === event.identifier) {
@@ -1536,7 +1909,7 @@ function Slider({
1536
1909
  removeTouchCancel();
1537
1910
  };
1538
1911
  }, [endDragging]);
1539
- useEffect2(() => {
1912
+ useEffect3(() => {
1540
1913
  if (disabled) {
1541
1914
  endDragging(false);
1542
1915
  }
@@ -1998,6 +2371,7 @@ var debugSessionController = {
1998
2371
  throw new Error("Page switching is not supported by the app state adapter");
1999
2372
  }
2000
2373
  await appStateAdapter.switchPage(routeName, params);
2374
+ await syncCurrentExecutionCursor();
2001
2375
  return;
2002
2376
  }
2003
2377
  if (navigator.hasOverlay(routeName)) {
@@ -2588,7 +2962,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
2588
2962
 
2589
2963
  // src/hooks/useScrollView.ts
2590
2964
  import { useSpringValue } from "@react-spring/core";
2591
- import { useCallback as useCallback2, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState5 } from "react";
2965
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef3, useState as useState7 } from "react";
2592
2966
  function clamp(value, min, max) {
2593
2967
  return Math.max(min, Math.min(max, value));
2594
2968
  }
@@ -2608,13 +2982,13 @@ function useScrollView({
2608
2982
  initialPosition = "start",
2609
2983
  wheelStep = 72
2610
2984
  }) {
2611
- const [contentHeight, setContentHeight] = useState5(0);
2612
- const [hasContentLayout, setHasContentLayout] = useState5(false);
2613
- const scrollTargetRef = useRef2(0);
2614
- const didSetInitialPositionRef = useRef2(false);
2615
- const touchDraggingRef = useRef2(false);
2616
- const touchStartYRef = useRef2(0);
2617
- const touchStartOffsetRef = useRef2(0);
2985
+ const [contentHeight, setContentHeight] = useState7(0);
2986
+ const [hasContentLayout, setHasContentLayout] = useState7(false);
2987
+ const scrollTargetRef = useRef3(0);
2988
+ const didSetInitialPositionRef = useRef3(false);
2989
+ const touchDraggingRef = useRef3(false);
2990
+ const touchStartYRef = useRef3(0);
2991
+ const touchStartOffsetRef = useRef3(0);
2618
2992
  const scrollOffset = useSpringValue(0, {
2619
2993
  config: {
2620
2994
  tension: 320,
@@ -2622,7 +2996,7 @@ function useScrollView({
2622
2996
  }
2623
2997
  });
2624
2998
  const maxScroll = useMemo(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
2625
- const scrollTo = useCallback2(
2999
+ const scrollTo = useCallback3(
2626
3000
  (nextOffset, immediate = false) => {
2627
3001
  const offset = clamp(nextOffset, 0, maxScroll);
2628
3002
  if (offset === scrollTargetRef.current && !immediate) {
@@ -2633,13 +3007,13 @@ function useScrollView({
2633
3007
  },
2634
3008
  [maxScroll, scrollOffset]
2635
3009
  );
2636
- const scrollToRatio = useCallback2(
3010
+ const scrollToRatio = useCallback3(
2637
3011
  (ratio, immediate = false) => {
2638
3012
  scrollTo(clamp(ratio, 0, 1) * maxScroll, immediate);
2639
3013
  },
2640
3014
  [maxScroll, scrollTo]
2641
3015
  );
2642
- const handleContentLayout = useCallback2(
3016
+ const handleContentLayout = useCallback3(
2643
3017
  (event) => {
2644
3018
  if (!didSetInitialPositionRef.current) {
2645
3019
  const initialOffset = initialPosition === "end" ? Math.max(0, event.height - viewportHeight) : 0;
@@ -2652,13 +3026,13 @@ function useScrollView({
2652
3026
  },
2653
3027
  [initialPosition, scrollOffset, viewportHeight]
2654
3028
  );
2655
- useEffect3(() => {
3029
+ useEffect4(() => {
2656
3030
  if (!hasContentLayout) {
2657
3031
  return;
2658
3032
  }
2659
3033
  scrollTo(scrollTargetRef.current, true);
2660
3034
  }, [hasContentLayout, maxScroll, scrollTo]);
2661
- const handleWheel = useCallback2(
3035
+ const handleWheel = useCallback3(
2662
3036
  (event) => {
2663
3037
  if (maxScroll <= 0) {
2664
3038
  return;
@@ -2673,7 +3047,7 @@ function useScrollView({
2673
3047
  },
2674
3048
  [maxScroll, scrollTo, viewportHeight, wheelStep]
2675
3049
  );
2676
- const handleTouchStart = useCallback2(
3050
+ const handleTouchStart = useCallback3(
2677
3051
  (event) => {
2678
3052
  if (maxScroll <= 0) {
2679
3053
  return;
@@ -2685,7 +3059,7 @@ function useScrollView({
2685
3059
  },
2686
3060
  [maxScroll]
2687
3061
  );
2688
- const handleTouchMove = useCallback2(
3062
+ const handleTouchMove = useCallback3(
2689
3063
  (event) => {
2690
3064
  if (!touchDraggingRef.current) {
2691
3065
  return;
@@ -2695,7 +3069,7 @@ function useScrollView({
2695
3069
  },
2696
3070
  [scrollTo]
2697
3071
  );
2698
- const handleTouchEnd = useCallback2((event) => {
3072
+ const handleTouchEnd = useCallback3((event) => {
2699
3073
  if (!touchDraggingRef.current) {
2700
3074
  return;
2701
3075
  }
@@ -2717,7 +3091,7 @@ function useScrollView({
2717
3091
  }
2718
3092
 
2719
3093
  // src/hooks/useScenario.ts
2720
- import { useEffect as useEffect4 } from "react";
3094
+ import { useEffect as useEffect5 } from "react";
2721
3095
  var currentScenarioSession = null;
2722
3096
  var scenarioSessionId = 0;
2723
3097
  var scenarioLifecycleQueue = Promise.resolve();
@@ -2859,7 +3233,7 @@ function setWaiting(time, skippable) {
2859
3233
  executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
2860
3234
  }
2861
3235
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2862
- useEffect4(() => {
3236
+ useEffect5(() => {
2863
3237
  const session = acquireScenarioSession({
2864
3238
  stories: normalizeStories(stories),
2865
3239
  startName,
@@ -2873,9 +3247,9 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2873
3247
  }
2874
3248
 
2875
3249
  // src/hooks/useSoundEffect.ts
2876
- import { useCallback as useCallback3, useEffect as useEffect5 } from "react";
3250
+ import { useCallback as useCallback4, useEffect as useEffect6 } from "react";
2877
3251
  function useSoundEffect(src) {
2878
- useEffect5(() => {
3252
+ useEffect6(() => {
2879
3253
  return () => {
2880
3254
  try {
2881
3255
  if (src) {
@@ -2890,7 +3264,7 @@ function useSoundEffect(src) {
2890
3264
  }
2891
3265
  };
2892
3266
  }, [src]);
2893
- return useCallback3(() => {
3267
+ return useCallback4(() => {
2894
3268
  try {
2895
3269
  if (src) {
2896
3270
  executePluginCommand("audio", {
@@ -2906,7 +3280,7 @@ function useSoundEffect(src) {
2906
3280
  }
2907
3281
 
2908
3282
  // src/hooks/useStage.ts
2909
- import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, createElement as createElement2, useCallback as useCallback4 } from "react";
3283
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect7, createElement as createElement2, useCallback as useCallback5 } from "react";
2910
3284
  import { proxy as proxy5, useSnapshot as useSnapshot5 } from "valtio";
2911
3285
  var SKIP_INTERVAL_MS = 80;
2912
3286
  var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
@@ -2992,6 +3366,9 @@ function createStage() {
2992
3366
  return addToRegistry(interruptCallbacks, cb);
2993
3367
  }
2994
3368
  function tryInterrupt() {
3369
+ if (tryFinishActiveWait()) {
3370
+ return true;
3371
+ }
2995
3372
  for (const cb of interruptCallbacks) {
2996
3373
  try {
2997
3374
  if (cb()) return true;
@@ -3039,6 +3416,8 @@ function createStage() {
3039
3416
  let _autoTicketSeq = 0;
3040
3417
  const _pendingAutoTickets = /* @__PURE__ */ new Map();
3041
3418
  let _fastForwardRuntime = null;
3419
+ let _activeWait = null;
3420
+ let _activeWaitSeq = 0;
3042
3421
  let _managedAdvanceInFlight = false;
3043
3422
  let _managedAdvancePending = false;
3044
3423
  function resetManagedAdvanceState() {
@@ -3127,12 +3506,53 @@ function createStage() {
3127
3506
  });
3128
3507
  }
3129
3508
  function requestNextLine() {
3509
+ if (_activeWait !== null) {
3510
+ return;
3511
+ }
3130
3512
  if (isManagedAdvanceActive()) {
3131
3513
  scheduleManagedAdvance();
3132
3514
  return;
3133
3515
  }
3134
3516
  void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
3135
3517
  }
3518
+ function invalidateActiveWait() {
3519
+ if (_activeWait === null) {
3520
+ return;
3521
+ }
3522
+ _activeWait.settled = true;
3523
+ _activeWait = null;
3524
+ }
3525
+ function settleActiveWait(wait, error) {
3526
+ if (_activeWait !== wait || wait.settled) {
3527
+ return;
3528
+ }
3529
+ wait.settled = true;
3530
+ _activeWait = null;
3531
+ if (error !== void 0) {
3532
+ console.error(`[Stage] Promise hold ${wait.id} rejected:`, error);
3533
+ }
3534
+ requestNextLine();
3535
+ }
3536
+ function tryFinishActiveWait() {
3537
+ const wait = _activeWait;
3538
+ if (wait === null || !wait.skippable || wait.settled) {
3539
+ return false;
3540
+ }
3541
+ const finish = wait.until.finish;
3542
+ if (typeof finish === "function") {
3543
+ try {
3544
+ finish.call(wait.until);
3545
+ } catch (error) {
3546
+ console.error(`[Stage] Failed to finish Promise hold ${wait.id}:`, error);
3547
+ }
3548
+ } else {
3549
+ console.warn(
3550
+ `[Stage] Skipping Promise hold ${wait.id} without finish(); the underlying operation may continue.`
3551
+ );
3552
+ }
3553
+ settleActiveWait(wait);
3554
+ return true;
3555
+ }
3136
3556
  function clearAutoResumeTimer() {
3137
3557
  if (_autoResumeTimer !== null) {
3138
3558
  clearTimeout(_autoResumeTimer);
@@ -3320,7 +3740,11 @@ function createStage() {
3320
3740
  if (isAnyBlockerActive()) return;
3321
3741
  skipState.active = true;
3322
3742
  invokeSkipCallbacks();
3743
+ const activeWait = _activeWait;
3323
3744
  tryInterrupt();
3745
+ if (activeWait !== null && activeWait !== _activeWait) {
3746
+ return;
3747
+ }
3324
3748
  scheduleSkipNextLine();
3325
3749
  }
3326
3750
  function stopSkip() {
@@ -3339,6 +3763,7 @@ function createStage() {
3339
3763
  if (fastForwardState.active) return;
3340
3764
  if (isAnyAutoBlockerActive()) return;
3341
3765
  autoState.active = true;
3766
+ if (_activeWait !== null) return;
3342
3767
  tryInterrupt();
3343
3768
  scheduleAutoNextLine();
3344
3769
  }
@@ -3375,6 +3800,7 @@ function createStage() {
3375
3800
  return fastForwardState.active;
3376
3801
  }
3377
3802
  function resetRuntimeState() {
3803
+ invalidateActiveWait();
3378
3804
  skipState.active = false;
3379
3805
  autoState.active = false;
3380
3806
  fastForwardState.active = false;
@@ -3420,7 +3846,7 @@ function createStage() {
3420
3846
  setWaiting(time, skippable);
3421
3847
  }
3422
3848
  },
3423
- hold() {
3849
+ hold(until, skippable = false) {
3424
3850
  handled = true;
3425
3851
  if (warpActive) {
3426
3852
  return;
@@ -3429,6 +3855,24 @@ function createStage() {
3429
3855
  requestNextLine();
3430
3856
  return;
3431
3857
  }
3858
+ if (until !== void 0) {
3859
+ invalidateActiveWait();
3860
+ const wait = {
3861
+ id: ++_activeWaitSeq,
3862
+ until,
3863
+ skippable,
3864
+ settled: false
3865
+ };
3866
+ _activeWait = wait;
3867
+ void Promise.resolve(until).then(
3868
+ () => settleActiveWait(wait),
3869
+ (error) => settleActiveWait(wait, error)
3870
+ );
3871
+ if (skipState.active) {
3872
+ tryFinishActiveWait();
3873
+ }
3874
+ return;
3875
+ }
3432
3876
  if (skipState.active && !unskippableFlag) {
3433
3877
  if (isAnyBlockerActive()) {
3434
3878
  stopSkip();
@@ -3488,6 +3932,7 @@ function createStage() {
3488
3932
  return parsed.data;
3489
3933
  }
3490
3934
  function dispatchCommand(e) {
3935
+ invalidateActiveWait();
3491
3936
  for (const cb of beforeHandleCommandCallbacks) {
3492
3937
  try {
3493
3938
  cb(e);
@@ -3516,6 +3961,7 @@ function createStage() {
3516
3961
  }
3517
3962
  }
3518
3963
  function dispatchTextLine(e) {
3964
+ invalidateActiveWait();
3519
3965
  const { control, wasHandled } = buildControl();
3520
3966
  for (const handler of textLineHandlers) {
3521
3967
  handler(e, control);
@@ -3549,6 +3995,7 @@ function createStage() {
3549
3995
  })
3550
3996
  ];
3551
3997
  return () => {
3998
+ resetRuntimeState();
3552
3999
  for (const cleanup of cleanups) cleanup();
3553
4000
  };
3554
4001
  }
@@ -3569,52 +4016,54 @@ function createStage() {
3569
4016
  startAuto,
3570
4017
  stopAuto,
3571
4018
  isAutoing,
4019
+ setDefaultAutoTailMs,
3572
4020
  startFastForward,
3573
4021
  stopFastForward,
3574
4022
  isFastForwarding,
4023
+ getSeekingType,
3575
4024
  resetRuntimeState,
3576
4025
  bindEvents
3577
4026
  };
3578
4027
  }
3579
- var StageContext = createContext3(null);
4028
+ var StageContext = createContext4(null);
3580
4029
  function useStageContext() {
3581
- const ctx = useContext3(StageContext);
4030
+ const ctx = useContext4(StageContext);
3582
4031
  if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
3583
4032
  return ctx;
3584
4033
  }
3585
4034
  function StageContextProvider({ stage, children }) {
3586
- useEffect6(() => {
4035
+ useEffect7(() => {
3587
4036
  return stage.bindEvents();
3588
4037
  }, [stage]);
3589
4038
  return createElement2(StageContext.Provider, { value: stage }, children);
3590
4039
  }
3591
4040
  function useSkipCallback(callback) {
3592
4041
  const stage = useStageContext();
3593
- useEffect6(() => {
4042
+ useEffect7(() => {
3594
4043
  return stage.addSkipCallback(callback);
3595
4044
  }, [stage, callback]);
3596
4045
  }
3597
4046
  function useInterruptCallback(callback) {
3598
4047
  const stage = useStageContext();
3599
- useEffect6(() => {
4048
+ useEffect7(() => {
3600
4049
  return stage.addInterruptCallback(callback);
3601
4050
  }, [stage, callback]);
3602
4051
  }
3603
4052
  function useSkipBlocker(callback) {
3604
4053
  const stage = useStageContext();
3605
- useEffect6(() => {
4054
+ useEffect7(() => {
3606
4055
  return stage.addSkipBlocker(callback);
3607
4056
  }, [stage, callback]);
3608
4057
  }
3609
4058
  function useAutoBlocker(callback) {
3610
4059
  const stage = useStageContext();
3611
- useEffect6(() => {
4060
+ useEffect7(() => {
3612
4061
  return stage.addAutoBlocker(callback);
3613
4062
  }, [stage, callback]);
3614
4063
  }
3615
4064
  function useAutoTicket() {
3616
4065
  const stage = useStageContext();
3617
- return useCallback4(
4066
+ return useCallback5(
3618
4067
  (options) => {
3619
4068
  return stage.issueAutoTicket(options);
3620
4069
  },
@@ -3623,7 +4072,7 @@ function useAutoTicket() {
3623
4072
  }
3624
4073
  function useBeforeHandleCommandCallback(callback) {
3625
4074
  const stage = useStageContext();
3626
- useEffect6(() => {
4075
+ useEffect7(() => {
3627
4076
  return stage.addBeforeHandleCommandCallback(callback);
3628
4077
  }, [stage, callback]);
3629
4078
  }
@@ -3634,14 +4083,18 @@ export {
3634
4083
  Button,
3635
4084
  Checkbox,
3636
4085
  DEBUG_EMPTY_PAGE,
4086
+ Input,
3637
4087
  OverlayParamsContext,
3638
4088
  PageParamsContext,
4089
+ Radio,
4090
+ RadioGroup,
3639
4091
  ScrollView,
3640
4092
  Select,
3641
4093
  Slider,
3642
4094
  StageContextProvider,
3643
4095
  animated as a,
3644
4096
  addEventListener,
4097
+ addFluidObserver,
3645
4098
  animated,
3646
4099
  autoState,
3647
4100
  createBubbleEvent,
@@ -3668,6 +4121,7 @@ export {
3668
4121
  moyu_exports as moyu,
3669
4122
  nextLine,
3670
4123
  registerAppStateAdapter,
4124
+ removeFluidObserver,
3671
4125
  replaceUiData,
3672
4126
  setDefaultAutoTailMs,
3673
4127
  setWaiting,