@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.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
 
@@ -1169,20 +1197,28 @@ function createStackNavigator(options) {
1169
1197
  }
1170
1198
  function createStaticNavigation(navigator) {
1171
1199
  return () => {
1172
- const navState = useSnapshot2(navigator._state);
1173
- const { pages, overlays } = navigator._descriptors;
1174
- const pageDescriptor = pages[navState.currentPage];
1175
- const Page = pageDescriptor?.component || (() => null);
1176
1200
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1177
- /* @__PURE__ */ jsx(PageParamsContext.Provider, { value: navState.pageParams[navState.currentPage], children: /* @__PURE__ */ jsx(Page, {}) }),
1178
- navState.overlayStack.map((overlay) => {
1179
- const overlayDescriptor = overlays[overlay.type];
1180
- const OverlayComponent = overlayDescriptor?.component || (() => null);
1181
- return /* @__PURE__ */ jsx(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ jsx(OverlayComponent, {}) }, overlay.id);
1182
- })
1201
+ /* @__PURE__ */ jsx(CurrentPage, { navigator }),
1202
+ /* @__PURE__ */ jsx(OverlayStack, { navigator })
1183
1203
  ] });
1184
1204
  };
1185
1205
  }
1206
+ function CurrentPage({ navigator }) {
1207
+ const navState = useSnapshot2(navigator._state);
1208
+ const { currentPage, pageParams } = navState;
1209
+ const pageDescriptor = navigator._descriptors.pages[currentPage];
1210
+ const Page = pageDescriptor?.component || (() => null);
1211
+ return /* @__PURE__ */ jsx(PageParamsContext.Provider, { value: pageParams[currentPage], children: /* @__PURE__ */ jsx(Page, {}) });
1212
+ }
1213
+ function OverlayStack({ navigator }) {
1214
+ const navState = useSnapshot2(navigator._state);
1215
+ const { overlays } = navigator._descriptors;
1216
+ return navState.overlayStack.map((overlay) => {
1217
+ const overlayDescriptor = overlays[overlay.type];
1218
+ const OverlayComponent = overlayDescriptor?.component || (() => null);
1219
+ return /* @__PURE__ */ jsx(OverlayParamsContext.Provider, { value: navState.overlayParams[overlay.id], children: /* @__PURE__ */ jsx(OverlayComponent, {}) }, overlay.id);
1220
+ });
1221
+ }
1186
1222
  function getNavigator() {
1187
1223
  if (!globalNavigator) {
1188
1224
  throw new Error("Navigator not initialized. Call createStackNavigator first.");
@@ -1378,8 +1414,343 @@ function Checkbox({
1378
1414
  );
1379
1415
  }
1380
1416
 
1417
+ // src/components/input.tsx
1418
+ import { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState3 } from "react";
1419
+ var Input = forwardRef(function Input2({
1420
+ value,
1421
+ defaultValue = "",
1422
+ placeholder = "",
1423
+ disabled = false,
1424
+ readOnly = false,
1425
+ autoFocus = false,
1426
+ width,
1427
+ height,
1428
+ paddingX = 0,
1429
+ textStyle,
1430
+ placeholderStyle,
1431
+ caret,
1432
+ background,
1433
+ onInput,
1434
+ onChange,
1435
+ onFocus,
1436
+ onBlur,
1437
+ onCompositionStart,
1438
+ onCompositionUpdate,
1439
+ onCompositionEnd,
1440
+ interactive,
1441
+ cursor = "text",
1442
+ onMouseEnter,
1443
+ onMouseLeave,
1444
+ onMouseDown,
1445
+ onMouseUp,
1446
+ onTouchStart,
1447
+ onTouchEnd,
1448
+ onTouchCancel,
1449
+ ...nodeProps
1450
+ }, ref) {
1451
+ const editableRef = useRef(null);
1452
+ const initialValueRef = useRef(value ?? defaultValue);
1453
+ const initialValue = initialValueRef.current;
1454
+ const stateRef = useRef({
1455
+ value: initialValue,
1456
+ isComposing: false,
1457
+ compositionText: ""
1458
+ });
1459
+ const [state, setState] = useState3(stateRef.current);
1460
+ const [focused, setFocused] = useState3(false);
1461
+ const [hovered, setHovered] = useState3(false);
1462
+ const [pressed, setPressed] = useState3(false);
1463
+ const [caretPosition, setCaretPosition] = useState3();
1464
+ const [caretVisible, setCaretVisible] = useState3(true);
1465
+ const shouldAutoFocusRef = useRef(autoFocus && !disabled);
1466
+ const viewportWidth = Math.max(0, Math.round(width));
1467
+ const viewportHeight = Math.max(0, Math.round(height));
1468
+ const contentWidth = Math.max(0, viewportWidth - paddingX * 2);
1469
+ const { width: caretWidth, height: configuredCaretHeight, blinkInterval, ...caretSpriteProps } = caret;
1470
+ const caretHeight = configuredCaretHeight ?? textStyle.fontSize ?? viewportHeight;
1471
+ 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;
1472
+ const displayValue = state.value + state.compositionText;
1473
+ const updateState = useCallback((nextState) => {
1474
+ const previousState = stateRef.current;
1475
+ if (previousState.value === nextState.value && previousState.isComposing === nextState.isComposing && previousState.compositionText === nextState.compositionText) {
1476
+ return;
1477
+ }
1478
+ const displayValueChanged = previousState.value + previousState.compositionText !== nextState.value + nextState.compositionText;
1479
+ stateRef.current = nextState;
1480
+ setState(nextState);
1481
+ if (displayValueChanged) {
1482
+ setCaretPosition(void 0);
1483
+ }
1484
+ setCaretVisible(true);
1485
+ }, []);
1486
+ const handleInput = useCallback(
1487
+ (event) => {
1488
+ updateState(event.state);
1489
+ onInput?.(event.state);
1490
+ },
1491
+ [onInput, updateState]
1492
+ );
1493
+ const handleChange = useCallback(
1494
+ (event) => {
1495
+ updateState(event.state);
1496
+ onChange?.(event.state, event.source);
1497
+ },
1498
+ [onChange, updateState]
1499
+ );
1500
+ const handleFocus = useCallback(
1501
+ (event) => {
1502
+ updateState(event.state);
1503
+ setFocused(true);
1504
+ onFocus?.(event.state);
1505
+ },
1506
+ [onFocus, updateState]
1507
+ );
1508
+ const handleBlur = useCallback(
1509
+ (event) => {
1510
+ updateState(event.state);
1511
+ setFocused(false);
1512
+ onBlur?.(event.state);
1513
+ },
1514
+ [onBlur, updateState]
1515
+ );
1516
+ const handleCompositionStart = useCallback(
1517
+ (event) => {
1518
+ updateState(event.state);
1519
+ onCompositionStart?.(event.state);
1520
+ },
1521
+ [onCompositionStart, updateState]
1522
+ );
1523
+ const handleCompositionUpdate = useCallback(
1524
+ (event) => {
1525
+ updateState(event.state);
1526
+ onCompositionUpdate?.(event.state);
1527
+ },
1528
+ [onCompositionUpdate, updateState]
1529
+ );
1530
+ const handleCompositionEnd = useCallback(
1531
+ (event) => {
1532
+ updateState(event.state);
1533
+ onCompositionEnd?.(event.state);
1534
+ },
1535
+ [onCompositionEnd, updateState]
1536
+ );
1537
+ const handleTextLayout = useCallback((event) => {
1538
+ if (event.text === stateRef.current.value + stateRef.current.compositionText) {
1539
+ setCaretPosition(event.endCursorPosition);
1540
+ }
1541
+ }, []);
1542
+ useImperativeHandle(
1543
+ ref,
1544
+ () => ({
1545
+ focus: () => {
1546
+ if (editableRef.current) {
1547
+ focusEditable(editableRef.current.nodeId);
1548
+ }
1549
+ },
1550
+ blur: () => {
1551
+ if (editableRef.current) {
1552
+ blurEditable(editableRef.current.nodeId);
1553
+ }
1554
+ },
1555
+ getState: () => stateRef.current
1556
+ }),
1557
+ []
1558
+ );
1559
+ useEffect2(() => {
1560
+ if (value === void 0 || value === stateRef.current.value) {
1561
+ return;
1562
+ }
1563
+ editableRef.current?.executeCommand({ subCommand: "setValue", value });
1564
+ const nextState = editableRef.current?.executeCommand({ subCommand: "getState" });
1565
+ if (nextState) {
1566
+ updateState(nextState);
1567
+ }
1568
+ }, [updateState, value]);
1569
+ useEffect2(() => {
1570
+ if (shouldAutoFocusRef.current && editableRef.current) {
1571
+ focusEditable(editableRef.current.nodeId);
1572
+ }
1573
+ }, []);
1574
+ useEffect2(() => {
1575
+ if (!pressed) {
1576
+ return;
1577
+ }
1578
+ const release = () => setPressed(false);
1579
+ const removeMouseUp = addEventListener("mouseup", release);
1580
+ const removeTouchEnd = addEventListener("touchend", release);
1581
+ const removeTouchCancel = addEventListener("touchcancel", release);
1582
+ return () => {
1583
+ removeMouseUp();
1584
+ removeTouchEnd();
1585
+ removeTouchCancel();
1586
+ };
1587
+ }, [pressed]);
1588
+ useEffect2(() => {
1589
+ if (disabled) {
1590
+ setHovered(false);
1591
+ setPressed(false);
1592
+ }
1593
+ }, [disabled]);
1594
+ useEffect2(() => {
1595
+ if (!focused) {
1596
+ setCaretVisible(false);
1597
+ return;
1598
+ }
1599
+ const expectedDisplayValue = displayValue;
1600
+ setCaretVisible(true);
1601
+ const timer = setInterval(() => {
1602
+ if (stateRef.current.value + stateRef.current.compositionText === expectedDisplayValue) {
1603
+ setCaretVisible((visible) => !visible);
1604
+ }
1605
+ }, blinkInterval ?? 500);
1606
+ return () => clearInterval(timer);
1607
+ }, [blinkInterval, displayValue, focused]);
1608
+ useEffect2(() => {
1609
+ if (!focused || !caretPosition) {
1610
+ return;
1611
+ }
1612
+ editableRef.current?.executeCommand({
1613
+ subCommand: "setCaretRect",
1614
+ x: paddingX + caretPosition[0],
1615
+ y: (viewportHeight - caretHeight) / 2,
1616
+ width: caretWidth,
1617
+ height: caretHeight
1618
+ });
1619
+ }, [caretHeight, caretPosition, caretWidth, focused, paddingX, viewportHeight]);
1620
+ const caretX = caretPosition?.[0] ?? 0;
1621
+ return /* @__PURE__ */ jsxs(
1622
+ "editable",
1623
+ {
1624
+ ...nodeProps,
1625
+ ref: editableRef,
1626
+ value: initialValue,
1627
+ disabled,
1628
+ readOnly,
1629
+ interactive: disabled ? false : interactive,
1630
+ cursor,
1631
+ onMouseEnter: mergeEvent(onMouseEnter, () => setHovered(true)),
1632
+ onMouseLeave: mergeEvent(onMouseLeave, () => {
1633
+ setHovered(false);
1634
+ setPressed(false);
1635
+ }),
1636
+ onMouseDown: mergeEvent(onMouseDown, () => setPressed(true)),
1637
+ onMouseUp: mergeEvent(onMouseUp, () => {
1638
+ setPressed(false);
1639
+ setHovered(true);
1640
+ }),
1641
+ onTouchStart: mergeEvent(onTouchStart, () => setPressed(true)),
1642
+ onTouchEnd: mergeEvent(onTouchEnd, () => {
1643
+ setPressed(false);
1644
+ setHovered(true);
1645
+ }),
1646
+ onTouchCancel: mergeEvent(onTouchCancel, () => {
1647
+ setHovered(false);
1648
+ setPressed(false);
1649
+ }),
1650
+ onInput: handleInput,
1651
+ onChange: handleChange,
1652
+ onFocus: handleFocus,
1653
+ onBlur: handleBlur,
1654
+ onCompositionStart: handleCompositionStart,
1655
+ onCompositionUpdate: handleCompositionUpdate,
1656
+ onCompositionEnd: handleCompositionEnd,
1657
+ children: [
1658
+ backgroundStyle ? /* @__PURE__ */ jsx("sprite", { ...backgroundStyle, targetWidth: viewportWidth, targetHeight: viewportHeight, interactive: false }) : null,
1659
+ /* @__PURE__ */ jsxs("clip", { x: paddingX, width: contentWidth, height: viewportHeight, interactive: false, children: [
1660
+ /* @__PURE__ */ jsx(
1661
+ "text",
1662
+ {
1663
+ ...textStyle,
1664
+ text: displayValue,
1665
+ parseMarkup: false,
1666
+ printMode: "instant",
1667
+ anchor: [0, 0.5],
1668
+ pivot: [0, 0.5],
1669
+ interactive: false,
1670
+ onTextLayout: handleTextLayout
1671
+ }
1672
+ ),
1673
+ state.value.length === 0 && state.compositionText.length === 0 && placeholder ? /* @__PURE__ */ jsx(
1674
+ "text",
1675
+ {
1676
+ ...textStyle,
1677
+ ...placeholderStyle,
1678
+ text: placeholder,
1679
+ parseMarkup: false,
1680
+ printMode: "instant",
1681
+ anchor: [0, 0.5],
1682
+ pivot: [0, 0.5],
1683
+ interactive: false
1684
+ }
1685
+ ) : null,
1686
+ /* @__PURE__ */ jsx(
1687
+ "sprite",
1688
+ {
1689
+ ...caretSpriteProps,
1690
+ x: caretX,
1691
+ y: (viewportHeight - caretHeight) / 2,
1692
+ mode: "nineslice",
1693
+ bounds: [0, 0, 0, 0],
1694
+ targetWidth: caretWidth,
1695
+ targetHeight: caretHeight,
1696
+ visible: focused && caretVisible && caretPosition !== void 0,
1697
+ interactive: false
1698
+ }
1699
+ )
1700
+ ] })
1701
+ ]
1702
+ }
1703
+ );
1704
+ });
1705
+
1706
+ // src/components/radio.tsx
1707
+ import { createContext as createContext3, useContext as useContext3, useState as useState4 } from "react";
1708
+ var RadioGroupContext = createContext3(null);
1709
+ function RadioGroup({ value, defaultValue, onValueChange, children, ...containerProps }) {
1710
+ const [internalValue, setInternalValue] = useState4(defaultValue);
1711
+ const currentValue = value ?? internalValue;
1712
+ return /* @__PURE__ */ jsx(
1713
+ RadioGroupContext.Provider,
1714
+ {
1715
+ value: {
1716
+ value: currentValue,
1717
+ select: (nextValue) => {
1718
+ if (nextValue === currentValue) {
1719
+ return;
1720
+ }
1721
+ if (value === void 0) {
1722
+ setInternalValue(nextValue);
1723
+ }
1724
+ onValueChange?.(nextValue);
1725
+ }
1726
+ },
1727
+ children: /* @__PURE__ */ jsx("container", { ...containerProps, children })
1728
+ }
1729
+ );
1730
+ }
1731
+ function Radio({ value, uncheckedSprite, checkedSprite, onPress, ...buttonProps }) {
1732
+ const group = useContext3(RadioGroupContext);
1733
+ if (!group) {
1734
+ throw new Error("Radio must be rendered inside a RadioGroup.");
1735
+ }
1736
+ const checked = group.value === value;
1737
+ return /* @__PURE__ */ jsx(
1738
+ Button,
1739
+ {
1740
+ ...buttonProps,
1741
+ sprite: checked ? checkedSprite : uncheckedSprite,
1742
+ onPress: (event) => {
1743
+ onPress?.(event);
1744
+ if (!event.defaultPrevented) {
1745
+ group.select(value);
1746
+ }
1747
+ }
1748
+ }
1749
+ );
1750
+ }
1751
+
1381
1752
  // src/components/select.tsx
1382
- import { useState as useState3 } from "react";
1753
+ import { useState as useState5 } from "react";
1383
1754
  function Select({
1384
1755
  value,
1385
1756
  defaultValue,
@@ -1398,8 +1769,8 @@ function Select({
1398
1769
  pivot = anchor,
1399
1770
  ...containerProps
1400
1771
  }) {
1401
- const [internalValue, setInternalValue] = useState3(defaultValue);
1402
- const [open, setOpen] = useState3(false);
1772
+ const [internalValue, setInternalValue] = useState5(defaultValue);
1773
+ const [open, setOpen] = useState5(false);
1403
1774
  const currentValue = value ?? internalValue;
1404
1775
  const currentOption = options.find((item) => item.value === currentValue);
1405
1776
  const { paddingX = 0, paddingY = 0, gap = 0, offsetX = 0, offsetY = 0, ...listSprite } = list;
@@ -1431,36 +1802,46 @@ function Select({
1431
1802
  }
1432
1803
  }
1433
1804
  ),
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,
1805
+ open && !disabled ? /* @__PURE__ */ jsx(
1806
+ "sprite",
1436
1807
  {
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
1808
+ ...listSprite,
1809
+ excludeFromLayout: true,
1810
+ targetHeight: listHeight,
1811
+ x: offsetX,
1812
+ y: (trigger.targetHeight ?? 0) + offsetY,
1813
+ children: /* @__PURE__ */ jsx("vbox", { x: paddingX, y: paddingY, gap, children: options.map((item) => /* @__PURE__ */ jsx(
1814
+ Button,
1815
+ {
1816
+ sprite: option,
1817
+ lockOn: item.value === currentValue ? "press" : void 0,
1818
+ text: item.text,
1819
+ textStyle,
1820
+ textOffsetX,
1821
+ textOffsetY,
1822
+ textAlign,
1823
+ onPress: (event) => {
1824
+ if (event.defaultPrevented) {
1825
+ return;
1826
+ }
1827
+ if (value === void 0) {
1828
+ setInternalValue(item.value);
1829
+ }
1830
+ onValueChange?.(item.value, item);
1831
+ setOpen(false);
1832
+ }
1833
+ },
1834
+ item.value
1835
+ )) })
1836
+ }
1837
+ ) : void 0
1457
1838
  ]
1458
1839
  }
1459
1840
  );
1460
1841
  }
1461
1842
 
1462
1843
  // src/components/slider.tsx
1463
- import { useCallback, useEffect as useEffect2, useRef, useState as useState4 } from "react";
1844
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "react";
1464
1845
  function clampValue(value) {
1465
1846
  if (Number.isNaN(value)) {
1466
1847
  return 0;
@@ -1490,14 +1871,14 @@ function Slider({
1490
1871
  onTouchCancel,
1491
1872
  ...containerProps
1492
1873
  }) {
1493
- const [internalValue, setInternalValue] = useState4(() => clampValue(defaultValue));
1494
- const [dragging, setDragging] = useState4(false);
1874
+ const [internalValue, setInternalValue] = useState6(() => clampValue(defaultValue));
1875
+ const [dragging, setDragging] = useState6(false);
1495
1876
  const currentValue = clampValue(value ?? internalValue);
1496
- const currentValueRef = useRef(currentValue);
1877
+ const currentValueRef = useRef2(currentValue);
1497
1878
  currentValueRef.current = currentValue;
1498
- const dragStateRef = useRef(null);
1879
+ const dragStateRef = useRef2(null);
1499
1880
  const distance = Math.max(0, track.targetWidth - thumb.targetWidth);
1500
- const setSliderValue = useCallback((nextValue) => {
1881
+ const setSliderValue = useCallback2((nextValue) => {
1501
1882
  const clampedValue = clampValue(nextValue);
1502
1883
  currentValueRef.current = clampedValue;
1503
1884
  if (value === void 0) {
@@ -1505,7 +1886,7 @@ function Slider({
1505
1886
  }
1506
1887
  onValueChange?.(clampedValue);
1507
1888
  }, [onValueChange, value]);
1508
- const endDragging = useCallback((commit) => {
1889
+ const endDragging = useCallback2((commit) => {
1509
1890
  if (dragStateRef.current === null) {
1510
1891
  return;
1511
1892
  }
@@ -1515,7 +1896,7 @@ function Slider({
1515
1896
  onValueCommit?.(currentValueRef.current);
1516
1897
  }
1517
1898
  }, [onValueCommit]);
1518
- useEffect2(() => {
1899
+ useEffect3(() => {
1519
1900
  const handleMouseUp = () => endDragging(true);
1520
1901
  const handleTouchEnd = (event) => {
1521
1902
  if (dragStateRef.current?.touchIdentifier === event.identifier) {
@@ -1536,7 +1917,7 @@ function Slider({
1536
1917
  removeTouchCancel();
1537
1918
  };
1538
1919
  }, [endDragging]);
1539
- useEffect2(() => {
1920
+ useEffect3(() => {
1540
1921
  if (disabled) {
1541
1922
  endDragging(false);
1542
1923
  }
@@ -1998,6 +2379,7 @@ var debugSessionController = {
1998
2379
  throw new Error("Page switching is not supported by the app state adapter");
1999
2380
  }
2000
2381
  await appStateAdapter.switchPage(routeName, params);
2382
+ await syncCurrentExecutionCursor();
2001
2383
  return;
2002
2384
  }
2003
2385
  if (navigator.hasOverlay(routeName)) {
@@ -2588,7 +2970,7 @@ function useFadeOut(outTime, pause = false, onFinished) {
2588
2970
 
2589
2971
  // src/hooks/useScrollView.ts
2590
2972
  import { useSpringValue } from "@react-spring/core";
2591
- import { useCallback as useCallback2, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState5 } from "react";
2973
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef3, useState as useState7 } from "react";
2592
2974
  function clamp(value, min, max) {
2593
2975
  return Math.max(min, Math.min(max, value));
2594
2976
  }
@@ -2608,13 +2990,13 @@ function useScrollView({
2608
2990
  initialPosition = "start",
2609
2991
  wheelStep = 72
2610
2992
  }) {
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);
2993
+ const [contentHeight, setContentHeight] = useState7(0);
2994
+ const [hasContentLayout, setHasContentLayout] = useState7(false);
2995
+ const scrollTargetRef = useRef3(0);
2996
+ const didSetInitialPositionRef = useRef3(false);
2997
+ const touchDraggingRef = useRef3(false);
2998
+ const touchStartYRef = useRef3(0);
2999
+ const touchStartOffsetRef = useRef3(0);
2618
3000
  const scrollOffset = useSpringValue(0, {
2619
3001
  config: {
2620
3002
  tension: 320,
@@ -2622,7 +3004,7 @@ function useScrollView({
2622
3004
  }
2623
3005
  });
2624
3006
  const maxScroll = useMemo(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
2625
- const scrollTo = useCallback2(
3007
+ const scrollTo = useCallback3(
2626
3008
  (nextOffset, immediate = false) => {
2627
3009
  const offset = clamp(nextOffset, 0, maxScroll);
2628
3010
  if (offset === scrollTargetRef.current && !immediate) {
@@ -2633,13 +3015,13 @@ function useScrollView({
2633
3015
  },
2634
3016
  [maxScroll, scrollOffset]
2635
3017
  );
2636
- const scrollToRatio = useCallback2(
3018
+ const scrollToRatio = useCallback3(
2637
3019
  (ratio, immediate = false) => {
2638
3020
  scrollTo(clamp(ratio, 0, 1) * maxScroll, immediate);
2639
3021
  },
2640
3022
  [maxScroll, scrollTo]
2641
3023
  );
2642
- const handleContentLayout = useCallback2(
3024
+ const handleContentLayout = useCallback3(
2643
3025
  (event) => {
2644
3026
  if (!didSetInitialPositionRef.current) {
2645
3027
  const initialOffset = initialPosition === "end" ? Math.max(0, event.height - viewportHeight) : 0;
@@ -2652,13 +3034,13 @@ function useScrollView({
2652
3034
  },
2653
3035
  [initialPosition, scrollOffset, viewportHeight]
2654
3036
  );
2655
- useEffect3(() => {
3037
+ useEffect4(() => {
2656
3038
  if (!hasContentLayout) {
2657
3039
  return;
2658
3040
  }
2659
3041
  scrollTo(scrollTargetRef.current, true);
2660
3042
  }, [hasContentLayout, maxScroll, scrollTo]);
2661
- const handleWheel = useCallback2(
3043
+ const handleWheel = useCallback3(
2662
3044
  (event) => {
2663
3045
  if (maxScroll <= 0) {
2664
3046
  return;
@@ -2673,7 +3055,7 @@ function useScrollView({
2673
3055
  },
2674
3056
  [maxScroll, scrollTo, viewportHeight, wheelStep]
2675
3057
  );
2676
- const handleTouchStart = useCallback2(
3058
+ const handleTouchStart = useCallback3(
2677
3059
  (event) => {
2678
3060
  if (maxScroll <= 0) {
2679
3061
  return;
@@ -2685,7 +3067,7 @@ function useScrollView({
2685
3067
  },
2686
3068
  [maxScroll]
2687
3069
  );
2688
- const handleTouchMove = useCallback2(
3070
+ const handleTouchMove = useCallback3(
2689
3071
  (event) => {
2690
3072
  if (!touchDraggingRef.current) {
2691
3073
  return;
@@ -2695,7 +3077,7 @@ function useScrollView({
2695
3077
  },
2696
3078
  [scrollTo]
2697
3079
  );
2698
- const handleTouchEnd = useCallback2((event) => {
3080
+ const handleTouchEnd = useCallback3((event) => {
2699
3081
  if (!touchDraggingRef.current) {
2700
3082
  return;
2701
3083
  }
@@ -2717,7 +3099,7 @@ function useScrollView({
2717
3099
  }
2718
3100
 
2719
3101
  // src/hooks/useScenario.ts
2720
- import { useEffect as useEffect4 } from "react";
3102
+ import { useEffect as useEffect5 } from "react";
2721
3103
  var currentScenarioSession = null;
2722
3104
  var scenarioSessionId = 0;
2723
3105
  var scenarioLifecycleQueue = Promise.resolve();
@@ -2859,7 +3241,7 @@ function setWaiting(time, skippable) {
2859
3241
  executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
2860
3242
  }
2861
3243
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2862
- useEffect4(() => {
3244
+ useEffect5(() => {
2863
3245
  const session = acquireScenarioSession({
2864
3246
  stories: normalizeStories(stories),
2865
3247
  startName,
@@ -2873,9 +3255,9 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2873
3255
  }
2874
3256
 
2875
3257
  // src/hooks/useSoundEffect.ts
2876
- import { useCallback as useCallback3, useEffect as useEffect5 } from "react";
3258
+ import { useCallback as useCallback4, useEffect as useEffect6 } from "react";
2877
3259
  function useSoundEffect(src) {
2878
- useEffect5(() => {
3260
+ useEffect6(() => {
2879
3261
  return () => {
2880
3262
  try {
2881
3263
  if (src) {
@@ -2890,7 +3272,7 @@ function useSoundEffect(src) {
2890
3272
  }
2891
3273
  };
2892
3274
  }, [src]);
2893
- return useCallback3(() => {
3275
+ return useCallback4(() => {
2894
3276
  try {
2895
3277
  if (src) {
2896
3278
  executePluginCommand("audio", {
@@ -2906,7 +3288,7 @@ function useSoundEffect(src) {
2906
3288
  }
2907
3289
 
2908
3290
  // src/hooks/useStage.ts
2909
- import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, createElement as createElement2, useCallback as useCallback4 } from "react";
3291
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect7, createElement as createElement2, useCallback as useCallback5 } from "react";
2910
3292
  import { proxy as proxy5, useSnapshot as useSnapshot5 } from "valtio";
2911
3293
  var SKIP_INTERVAL_MS = 80;
2912
3294
  var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
@@ -2992,6 +3374,9 @@ function createStage() {
2992
3374
  return addToRegistry(interruptCallbacks, cb);
2993
3375
  }
2994
3376
  function tryInterrupt() {
3377
+ if (tryFinishActiveWait()) {
3378
+ return true;
3379
+ }
2995
3380
  for (const cb of interruptCallbacks) {
2996
3381
  try {
2997
3382
  if (cb()) return true;
@@ -3039,6 +3424,8 @@ function createStage() {
3039
3424
  let _autoTicketSeq = 0;
3040
3425
  const _pendingAutoTickets = /* @__PURE__ */ new Map();
3041
3426
  let _fastForwardRuntime = null;
3427
+ let _activeWait = null;
3428
+ let _activeWaitSeq = 0;
3042
3429
  let _managedAdvanceInFlight = false;
3043
3430
  let _managedAdvancePending = false;
3044
3431
  function resetManagedAdvanceState() {
@@ -3127,12 +3514,53 @@ function createStage() {
3127
3514
  });
3128
3515
  }
3129
3516
  function requestNextLine() {
3517
+ if (_activeWait !== null) {
3518
+ return;
3519
+ }
3130
3520
  if (isManagedAdvanceActive()) {
3131
3521
  scheduleManagedAdvance();
3132
3522
  return;
3133
3523
  }
3134
3524
  void Promise.resolve(nextLine()).catch(handleManagedAdvanceError);
3135
3525
  }
3526
+ function invalidateActiveWait() {
3527
+ if (_activeWait === null) {
3528
+ return;
3529
+ }
3530
+ _activeWait.settled = true;
3531
+ _activeWait = null;
3532
+ }
3533
+ function settleActiveWait(wait, error) {
3534
+ if (_activeWait !== wait || wait.settled) {
3535
+ return;
3536
+ }
3537
+ wait.settled = true;
3538
+ _activeWait = null;
3539
+ if (error !== void 0) {
3540
+ console.error(`[Stage] Promise hold ${wait.id} rejected:`, error);
3541
+ }
3542
+ requestNextLine();
3543
+ }
3544
+ function tryFinishActiveWait() {
3545
+ const wait = _activeWait;
3546
+ if (wait === null || !wait.skippable || wait.settled) {
3547
+ return false;
3548
+ }
3549
+ const finish = wait.until.finish;
3550
+ if (typeof finish === "function") {
3551
+ try {
3552
+ finish.call(wait.until);
3553
+ } catch (error) {
3554
+ console.error(`[Stage] Failed to finish Promise hold ${wait.id}:`, error);
3555
+ }
3556
+ } else {
3557
+ console.warn(
3558
+ `[Stage] Skipping Promise hold ${wait.id} without finish(); the underlying operation may continue.`
3559
+ );
3560
+ }
3561
+ settleActiveWait(wait);
3562
+ return true;
3563
+ }
3136
3564
  function clearAutoResumeTimer() {
3137
3565
  if (_autoResumeTimer !== null) {
3138
3566
  clearTimeout(_autoResumeTimer);
@@ -3320,7 +3748,11 @@ function createStage() {
3320
3748
  if (isAnyBlockerActive()) return;
3321
3749
  skipState.active = true;
3322
3750
  invokeSkipCallbacks();
3751
+ const activeWait = _activeWait;
3323
3752
  tryInterrupt();
3753
+ if (activeWait !== null && activeWait !== _activeWait) {
3754
+ return;
3755
+ }
3324
3756
  scheduleSkipNextLine();
3325
3757
  }
3326
3758
  function stopSkip() {
@@ -3339,6 +3771,7 @@ function createStage() {
3339
3771
  if (fastForwardState.active) return;
3340
3772
  if (isAnyAutoBlockerActive()) return;
3341
3773
  autoState.active = true;
3774
+ if (_activeWait !== null) return;
3342
3775
  tryInterrupt();
3343
3776
  scheduleAutoNextLine();
3344
3777
  }
@@ -3375,6 +3808,7 @@ function createStage() {
3375
3808
  return fastForwardState.active;
3376
3809
  }
3377
3810
  function resetRuntimeState() {
3811
+ invalidateActiveWait();
3378
3812
  skipState.active = false;
3379
3813
  autoState.active = false;
3380
3814
  fastForwardState.active = false;
@@ -3420,7 +3854,7 @@ function createStage() {
3420
3854
  setWaiting(time, skippable);
3421
3855
  }
3422
3856
  },
3423
- hold() {
3857
+ hold(until, skippable = false) {
3424
3858
  handled = true;
3425
3859
  if (warpActive) {
3426
3860
  return;
@@ -3429,6 +3863,24 @@ function createStage() {
3429
3863
  requestNextLine();
3430
3864
  return;
3431
3865
  }
3866
+ if (until !== void 0) {
3867
+ invalidateActiveWait();
3868
+ const wait = {
3869
+ id: ++_activeWaitSeq,
3870
+ until,
3871
+ skippable,
3872
+ settled: false
3873
+ };
3874
+ _activeWait = wait;
3875
+ void Promise.resolve(until).then(
3876
+ () => settleActiveWait(wait),
3877
+ (error) => settleActiveWait(wait, error)
3878
+ );
3879
+ if (skipState.active) {
3880
+ tryFinishActiveWait();
3881
+ }
3882
+ return;
3883
+ }
3432
3884
  if (skipState.active && !unskippableFlag) {
3433
3885
  if (isAnyBlockerActive()) {
3434
3886
  stopSkip();
@@ -3488,6 +3940,7 @@ function createStage() {
3488
3940
  return parsed.data;
3489
3941
  }
3490
3942
  function dispatchCommand(e) {
3943
+ invalidateActiveWait();
3491
3944
  for (const cb of beforeHandleCommandCallbacks) {
3492
3945
  try {
3493
3946
  cb(e);
@@ -3516,6 +3969,7 @@ function createStage() {
3516
3969
  }
3517
3970
  }
3518
3971
  function dispatchTextLine(e) {
3972
+ invalidateActiveWait();
3519
3973
  const { control, wasHandled } = buildControl();
3520
3974
  for (const handler of textLineHandlers) {
3521
3975
  handler(e, control);
@@ -3549,6 +4003,7 @@ function createStage() {
3549
4003
  })
3550
4004
  ];
3551
4005
  return () => {
4006
+ resetRuntimeState();
3552
4007
  for (const cleanup of cleanups) cleanup();
3553
4008
  };
3554
4009
  }
@@ -3569,52 +4024,54 @@ function createStage() {
3569
4024
  startAuto,
3570
4025
  stopAuto,
3571
4026
  isAutoing,
4027
+ setDefaultAutoTailMs,
3572
4028
  startFastForward,
3573
4029
  stopFastForward,
3574
4030
  isFastForwarding,
4031
+ getSeekingType,
3575
4032
  resetRuntimeState,
3576
4033
  bindEvents
3577
4034
  };
3578
4035
  }
3579
- var StageContext = createContext3(null);
4036
+ var StageContext = createContext4(null);
3580
4037
  function useStageContext() {
3581
- const ctx = useContext3(StageContext);
4038
+ const ctx = useContext4(StageContext);
3582
4039
  if (!ctx) throw new Error("useStageContext must be used within StageContextProvider");
3583
4040
  return ctx;
3584
4041
  }
3585
4042
  function StageContextProvider({ stage, children }) {
3586
- useEffect6(() => {
4043
+ useEffect7(() => {
3587
4044
  return stage.bindEvents();
3588
4045
  }, [stage]);
3589
4046
  return createElement2(StageContext.Provider, { value: stage }, children);
3590
4047
  }
3591
4048
  function useSkipCallback(callback) {
3592
4049
  const stage = useStageContext();
3593
- useEffect6(() => {
4050
+ useEffect7(() => {
3594
4051
  return stage.addSkipCallback(callback);
3595
4052
  }, [stage, callback]);
3596
4053
  }
3597
4054
  function useInterruptCallback(callback) {
3598
4055
  const stage = useStageContext();
3599
- useEffect6(() => {
4056
+ useEffect7(() => {
3600
4057
  return stage.addInterruptCallback(callback);
3601
4058
  }, [stage, callback]);
3602
4059
  }
3603
4060
  function useSkipBlocker(callback) {
3604
4061
  const stage = useStageContext();
3605
- useEffect6(() => {
4062
+ useEffect7(() => {
3606
4063
  return stage.addSkipBlocker(callback);
3607
4064
  }, [stage, callback]);
3608
4065
  }
3609
4066
  function useAutoBlocker(callback) {
3610
4067
  const stage = useStageContext();
3611
- useEffect6(() => {
4068
+ useEffect7(() => {
3612
4069
  return stage.addAutoBlocker(callback);
3613
4070
  }, [stage, callback]);
3614
4071
  }
3615
4072
  function useAutoTicket() {
3616
4073
  const stage = useStageContext();
3617
- return useCallback4(
4074
+ return useCallback5(
3618
4075
  (options) => {
3619
4076
  return stage.issueAutoTicket(options);
3620
4077
  },
@@ -3623,7 +4080,7 @@ function useAutoTicket() {
3623
4080
  }
3624
4081
  function useBeforeHandleCommandCallback(callback) {
3625
4082
  const stage = useStageContext();
3626
- useEffect6(() => {
4083
+ useEffect7(() => {
3627
4084
  return stage.addBeforeHandleCommandCallback(callback);
3628
4085
  }, [stage, callback]);
3629
4086
  }
@@ -3634,14 +4091,18 @@ export {
3634
4091
  Button,
3635
4092
  Checkbox,
3636
4093
  DEBUG_EMPTY_PAGE,
4094
+ Input,
3637
4095
  OverlayParamsContext,
3638
4096
  PageParamsContext,
4097
+ Radio,
4098
+ RadioGroup,
3639
4099
  ScrollView,
3640
4100
  Select,
3641
4101
  Slider,
3642
4102
  StageContextProvider,
3643
4103
  animated as a,
3644
4104
  addEventListener,
4105
+ addFluidObserver,
3645
4106
  animated,
3646
4107
  autoState,
3647
4108
  createBubbleEvent,
@@ -3668,6 +4129,7 @@ export {
3668
4129
  moyu_exports as moyu,
3669
4130
  nextLine,
3670
4131
  registerAppStateAdapter,
4132
+ removeFluidObserver,
3671
4133
  replaceUiData,
3672
4134
  setDefaultAutoTailMs,
3673
4135
  setWaiting,