@momoyu-ink/kit 0.9.1 → 0.10.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
@@ -19,9 +19,14 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
19
19
  // src/lib.ts
20
20
  var lib_exports = {};
21
21
  __export(lib_exports, {
22
+ Button: () => Button,
23
+ Checkbox: () => Checkbox,
22
24
  DEBUG_EMPTY_PAGE: () => DEBUG_EMPTY_PAGE,
23
25
  OverlayParamsContext: () => OverlayParamsContext,
24
26
  PageParamsContext: () => PageParamsContext,
27
+ ScrollView: () => ScrollView,
28
+ Select: () => Select,
29
+ Slider: () => Slider,
25
30
  StageContextProvider: () => StageContextProvider,
26
31
  a: () => animated,
27
32
  addEventListener: () => addEventListener,
@@ -47,6 +52,7 @@ __export(lib_exports, {
47
52
  initGlobalVariableDefaults: () => initGlobalVariableDefaults,
48
53
  initUiData: () => initUiData,
49
54
  loadVariablesDocument: () => loadVariablesDocument,
55
+ mergeEvent: () => mergeEvent,
50
56
  moyu: () => moyu_exports,
51
57
  nextLine: () => nextLine,
52
58
  registerAppStateAdapter: () => registerAppStateAdapter,
@@ -73,6 +79,7 @@ __export(lib_exports, {
73
79
  useNavigationParams: () => useNavigationParams,
74
80
  useNavigationState: () => useNavigationState,
75
81
  useScenario: () => useScenario,
82
+ useScrollView: () => useScrollView,
76
83
  useSkipBlocker: () => useSkipBlocker,
77
84
  useSkipCallback: () => useSkipCallback,
78
85
  useSoundEffect: () => useSoundEffect,
@@ -782,6 +789,8 @@ import { Globals, colors, createStringInterpolator } from "@react-spring/shared"
782
789
  // src/spring/primitives.ts
783
790
  var primitives = [
784
791
  "container",
792
+ "vbox",
793
+ "hbox",
785
794
  "sprite",
786
795
  "text",
787
796
  "clip",
@@ -940,6 +949,19 @@ function getStageSize() {
940
949
  });
941
950
  return stageSize;
942
951
  }
952
+ function mergeEvent(handlers, defaultHandler) {
953
+ return (event) => {
954
+ if (handlers) {
955
+ const handlerList = Array.isArray(handlers) ? handlers : [handlers];
956
+ for (const handler of handlerList) {
957
+ handler(event);
958
+ }
959
+ }
960
+ if (!event.defaultPrevented) {
961
+ defaultHandler?.(event);
962
+ }
963
+ };
964
+ }
943
965
 
944
966
  // src/gameState.ts
945
967
  import { createContext, createElement, useContext } from "react";
@@ -1017,7 +1039,11 @@ function createGameStateContext(defaults) {
1017
1039
  // src/components/navigation.tsx
1018
1040
  import { createContext as createContext2, useContext as useContext2 } from "react";
1019
1041
  import { proxy as proxy2, useSnapshot as useSnapshot2 } from "valtio";
1042
+
1043
+ // src/jsx-runtime.ts
1020
1044
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1045
+
1046
+ // src/components/navigation.tsx
1021
1047
  var PageParamsContext = createContext2(void 0);
1022
1048
  var OverlayParamsContext = createContext2(void 0);
1023
1049
  var DEBUG_EMPTY_PAGE = "debug-empty";
@@ -1173,6 +1199,440 @@ function useNavigationParams() {
1173
1199
  return overlayParams ?? pageParams ?? {};
1174
1200
  }
1175
1201
 
1202
+ // src/components/scroll-view.tsx
1203
+ function ScrollView({ width, height, controller, children, clipProps, contentProps }) {
1204
+ return /* @__PURE__ */ jsx(
1205
+ "clip",
1206
+ {
1207
+ ...clipProps,
1208
+ width,
1209
+ height,
1210
+ onWheel: controller.handleWheel,
1211
+ onTouchStart: controller.handleTouchStart,
1212
+ onTouchMove: controller.handleTouchMove,
1213
+ onTouchEnd: controller.handleTouchEnd,
1214
+ onTouchCancel: controller.handleTouchEnd,
1215
+ children: /* @__PURE__ */ jsx(
1216
+ animated.vbox,
1217
+ {
1218
+ ...contentProps,
1219
+ y: controller.scrollOffset.to((value) => -value),
1220
+ onLayout: controller.handleContentLayout,
1221
+ children
1222
+ }
1223
+ )
1224
+ }
1225
+ );
1226
+ }
1227
+
1228
+ // src/components/button.tsx
1229
+ import { useEffect, useState } from "react";
1230
+
1231
+ // src/components/control.ts
1232
+ function resolveControlStateValue(value, state) {
1233
+ if (!Array.isArray(value)) {
1234
+ return value;
1235
+ }
1236
+ const index = state === "idle" ? 0 : state === "hover" ? 1 : state === "press" ? 2 : 3;
1237
+ return value[index] ?? value[0];
1238
+ }
1239
+
1240
+ // src/components/button.tsx
1241
+ function Button({
1242
+ sprite,
1243
+ disabled = false,
1244
+ lockOn,
1245
+ onPress,
1246
+ text,
1247
+ textStyle,
1248
+ textOffsetX,
1249
+ textOffsetY,
1250
+ textAlign = "center",
1251
+ children,
1252
+ anchor,
1253
+ pivot = anchor,
1254
+ interactive,
1255
+ onMouseEnter,
1256
+ onMouseLeave,
1257
+ onMouseDown,
1258
+ onMouseUp,
1259
+ onTouchStart,
1260
+ onTouchEnd,
1261
+ onTouchCancel,
1262
+ ...containerProps
1263
+ }) {
1264
+ const [hovered, setHovered] = useState(false);
1265
+ const [pressed, setPressed] = useState(false);
1266
+ useEffect(() => {
1267
+ if (!pressed) {
1268
+ return;
1269
+ }
1270
+ const release = () => setPressed(false);
1271
+ const removeMouseUp = addEventListener("mouseup", release);
1272
+ const removeTouchEnd = addEventListener("touchend", release);
1273
+ const removeTouchCancel = addEventListener("touchcancel", release);
1274
+ return () => {
1275
+ removeMouseUp();
1276
+ removeTouchEnd();
1277
+ removeTouchCancel();
1278
+ };
1279
+ }, [pressed]);
1280
+ useEffect(() => {
1281
+ if (disabled) {
1282
+ setHovered(false);
1283
+ setPressed(false);
1284
+ }
1285
+ }, [disabled]);
1286
+ const state = disabled ? "disabled" : lockOn ?? (pressed ? "press" : hovered ? "hover" : "idle");
1287
+ const { src, ...spriteProps } = sprite;
1288
+ const resolvedTextStyle = textStyle ? resolveControlStateValue(textStyle, state) : void 0;
1289
+ const textAnchor = textAlign === "left" ? [0, 0.5] : textAlign === "right" ? [1, 0.5] : [0.5, 0.5];
1290
+ return /* @__PURE__ */ jsx(
1291
+ "container",
1292
+ {
1293
+ ...containerProps,
1294
+ anchor,
1295
+ pivot,
1296
+ interactive: disabled ? false : interactive,
1297
+ onMouseEnter: mergeEvent(onMouseEnter, (event) => {
1298
+ if (event.targetId === event.currentTargetId) {
1299
+ event.stopPropagation();
1300
+ return;
1301
+ }
1302
+ setHovered(true);
1303
+ }),
1304
+ onMouseLeave: mergeEvent(onMouseLeave, () => {
1305
+ setHovered(false);
1306
+ setPressed(false);
1307
+ }),
1308
+ onMouseDown: mergeEvent(onMouseDown, () => setPressed(true)),
1309
+ onMouseUp: mergeEvent(onMouseUp, (event) => {
1310
+ if (pressed) {
1311
+ onPress?.(event);
1312
+ }
1313
+ setPressed(false);
1314
+ setHovered(true);
1315
+ }),
1316
+ onTouchStart: mergeEvent(onTouchStart, () => setPressed(true)),
1317
+ onTouchEnd: mergeEvent(onTouchEnd, (event) => {
1318
+ if (pressed) {
1319
+ onPress?.(event);
1320
+ }
1321
+ setPressed(false);
1322
+ setHovered(true);
1323
+ }),
1324
+ onTouchCancel: mergeEvent(onTouchCancel, () => {
1325
+ setHovered(false);
1326
+ setPressed(false);
1327
+ }),
1328
+ onClick: (event) => event.stopPropagation(),
1329
+ children: /* @__PURE__ */ jsxs("sprite", { ...spriteProps, src: resolveControlStateValue(src, state), cursor: "pointer", children: [
1330
+ text !== void 0 ? /* @__PURE__ */ jsx(
1331
+ "text",
1332
+ {
1333
+ ...resolvedTextStyle,
1334
+ text,
1335
+ x: textOffsetX,
1336
+ y: textOffsetY,
1337
+ interactive: false,
1338
+ anchor: textAnchor,
1339
+ pivot: textAnchor
1340
+ }
1341
+ ) : void 0,
1342
+ children
1343
+ ] })
1344
+ }
1345
+ );
1346
+ }
1347
+
1348
+ // src/components/checkbox.tsx
1349
+ import { useState as useState2 } from "react";
1350
+ function Checkbox({
1351
+ checked,
1352
+ defaultChecked = false,
1353
+ onCheckedChange,
1354
+ uncheckedSprite,
1355
+ checkedSprite,
1356
+ onPress,
1357
+ ...buttonProps
1358
+ }) {
1359
+ const [internalChecked, setInternalChecked] = useState2(defaultChecked);
1360
+ const currentChecked = checked ?? internalChecked;
1361
+ return /* @__PURE__ */ jsx(
1362
+ Button,
1363
+ {
1364
+ ...buttonProps,
1365
+ sprite: currentChecked ? checkedSprite : uncheckedSprite,
1366
+ onPress: (event) => {
1367
+ onPress?.(event);
1368
+ if (event.defaultPrevented) {
1369
+ return;
1370
+ }
1371
+ const nextChecked = !currentChecked;
1372
+ if (checked === void 0) {
1373
+ setInternalChecked(nextChecked);
1374
+ }
1375
+ onCheckedChange?.(nextChecked);
1376
+ }
1377
+ }
1378
+ );
1379
+ }
1380
+
1381
+ // src/components/select.tsx
1382
+ import { useState as useState3 } from "react";
1383
+ function Select({
1384
+ value,
1385
+ defaultValue,
1386
+ onValueChange,
1387
+ onPress,
1388
+ disabled = false,
1389
+ options,
1390
+ trigger,
1391
+ list,
1392
+ option,
1393
+ textStyle,
1394
+ textOffsetX,
1395
+ textOffsetY,
1396
+ textAlign,
1397
+ anchor,
1398
+ pivot = anchor,
1399
+ ...containerProps
1400
+ }) {
1401
+ const [internalValue, setInternalValue] = useState3(defaultValue);
1402
+ const [open, setOpen] = useState3(false);
1403
+ const currentValue = value ?? internalValue;
1404
+ const currentOption = options.find((item) => item.value === currentValue);
1405
+ const { paddingX = 0, paddingY = 0, gap = 0, offsetX = 0, offsetY = 0, ...listSprite } = list;
1406
+ const listHeight = paddingY * 2 + options.length * option.targetHeight + Math.max(0, options.length - 1) * gap;
1407
+ return /* @__PURE__ */ jsxs(
1408
+ "container",
1409
+ {
1410
+ ...containerProps,
1411
+ anchor,
1412
+ pivot,
1413
+ interactive: disabled ? false : containerProps.interactive,
1414
+ children: [
1415
+ /* @__PURE__ */ jsx(
1416
+ Button,
1417
+ {
1418
+ sprite: trigger,
1419
+ disabled,
1420
+ lockOn: open ? "press" : void 0,
1421
+ text: currentOption?.text ?? "",
1422
+ textStyle,
1423
+ textOffsetX,
1424
+ textOffsetY,
1425
+ textAlign,
1426
+ onPress: (event) => {
1427
+ onPress?.(event);
1428
+ if (!event.defaultPrevented) {
1429
+ setOpen((active) => !active);
1430
+ }
1431
+ }
1432
+ }
1433
+ ),
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,
1436
+ {
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
1457
+ ]
1458
+ }
1459
+ );
1460
+ }
1461
+
1462
+ // src/components/slider.tsx
1463
+ import { useCallback, useEffect as useEffect2, useRef, useState as useState4 } from "react";
1464
+ function clampValue(value) {
1465
+ if (Number.isNaN(value)) {
1466
+ return 0;
1467
+ }
1468
+ return Math.max(0, Math.min(1, value));
1469
+ }
1470
+ function Slider({
1471
+ value,
1472
+ defaultValue = 0,
1473
+ onValueChange,
1474
+ onValueCommit,
1475
+ onPress,
1476
+ disabled = false,
1477
+ track,
1478
+ thumb,
1479
+ anchor,
1480
+ pivot = anchor,
1481
+ interactive,
1482
+ onMouseEnter,
1483
+ onMouseLeave,
1484
+ onMouseDown,
1485
+ onMouseUp,
1486
+ onMouseMove,
1487
+ onTouchStart,
1488
+ onTouchMove,
1489
+ onTouchEnd,
1490
+ onTouchCancel,
1491
+ ...containerProps
1492
+ }) {
1493
+ const [internalValue, setInternalValue] = useState4(() => clampValue(defaultValue));
1494
+ const [dragging, setDragging] = useState4(false);
1495
+ const currentValue = clampValue(value ?? internalValue);
1496
+ const currentValueRef = useRef(currentValue);
1497
+ currentValueRef.current = currentValue;
1498
+ const dragStateRef = useRef(null);
1499
+ const distance = Math.max(0, track.targetWidth - thumb.targetWidth);
1500
+ const setSliderValue = useCallback((nextValue) => {
1501
+ const clampedValue = clampValue(nextValue);
1502
+ currentValueRef.current = clampedValue;
1503
+ if (value === void 0) {
1504
+ setInternalValue(clampedValue);
1505
+ }
1506
+ onValueChange?.(clampedValue);
1507
+ }, [onValueChange, value]);
1508
+ const endDragging = useCallback((commit) => {
1509
+ if (dragStateRef.current === null) {
1510
+ return;
1511
+ }
1512
+ dragStateRef.current = null;
1513
+ setDragging(false);
1514
+ if (commit) {
1515
+ onValueCommit?.(currentValueRef.current);
1516
+ }
1517
+ }, [onValueCommit]);
1518
+ useEffect2(() => {
1519
+ const handleMouseUp = () => endDragging(true);
1520
+ const handleTouchEnd = (event) => {
1521
+ if (dragStateRef.current?.touchIdentifier === event.identifier) {
1522
+ endDragging(true);
1523
+ }
1524
+ };
1525
+ const handleTouchCancel2 = (event) => {
1526
+ if (dragStateRef.current?.touchIdentifier === event.identifier) {
1527
+ endDragging(false);
1528
+ }
1529
+ };
1530
+ const removeMouseUp = addEventListener("mouseup", handleMouseUp);
1531
+ const removeTouchEnd = addEventListener("touchend", handleTouchEnd);
1532
+ const removeTouchCancel = addEventListener("touchcancel", handleTouchCancel2);
1533
+ return () => {
1534
+ removeMouseUp();
1535
+ removeTouchEnd();
1536
+ removeTouchCancel();
1537
+ };
1538
+ }, [endDragging]);
1539
+ useEffect2(() => {
1540
+ if (disabled) {
1541
+ endDragging(false);
1542
+ }
1543
+ }, [disabled, endDragging]);
1544
+ const startDragging = (event) => {
1545
+ event.stopPropagation();
1546
+ if (dragStateRef.current !== null) {
1547
+ return;
1548
+ }
1549
+ const thumbPosition = currentValueRef.current * distance;
1550
+ dragStateRef.current = {
1551
+ startPosition: event.offsetX,
1552
+ startValue: currentValueRef.current,
1553
+ touchIdentifier: "identifier" in event ? event.identifier : void 0,
1554
+ startedOnThumb: event.offsetX >= thumbPosition && event.offsetX <= thumbPosition + thumb.targetWidth,
1555
+ moved: false
1556
+ };
1557
+ setDragging(true);
1558
+ };
1559
+ const moveDragging = (event) => {
1560
+ const dragState = dragStateRef.current;
1561
+ if (dragState === null || "identifier" in event && dragState.touchIdentifier !== event.identifier || distance === 0) {
1562
+ return;
1563
+ }
1564
+ event.stopPropagation();
1565
+ const delta = event.offsetX - dragState.startPosition;
1566
+ if (delta !== 0) {
1567
+ dragState.moved = true;
1568
+ }
1569
+ setSliderValue(dragState.startValue + delta / distance);
1570
+ };
1571
+ const handlePress = (event) => {
1572
+ const dragState = dragStateRef.current;
1573
+ if (dragState === null || "identifier" in event && dragState.touchIdentifier !== event.identifier) {
1574
+ return;
1575
+ }
1576
+ onPress?.(event);
1577
+ if (event.defaultPrevented) {
1578
+ endDragging(false);
1579
+ return;
1580
+ }
1581
+ if (!dragState.moved && !dragState.startedOnThumb && distance > 0) {
1582
+ setSliderValue((event.offsetX - thumb.targetWidth / 2) / distance);
1583
+ }
1584
+ endDragging(true);
1585
+ };
1586
+ const handleTouchCancel = (event) => {
1587
+ if (dragStateRef.current?.touchIdentifier === event.identifier) {
1588
+ endDragging(false);
1589
+ }
1590
+ };
1591
+ return /* @__PURE__ */ jsx(
1592
+ Button,
1593
+ {
1594
+ ...containerProps,
1595
+ anchor,
1596
+ pivot,
1597
+ interactive,
1598
+ disabled,
1599
+ lockOn: dragging ? "press" : void 0,
1600
+ sprite: {
1601
+ ...track,
1602
+ pivot: [0, 0.5],
1603
+ y: (track.targetHeight ?? 0) / 2
1604
+ },
1605
+ onPress: handlePress,
1606
+ onMouseEnter,
1607
+ onMouseLeave,
1608
+ onMouseDown: mergeEvent(onMouseDown, startDragging),
1609
+ onMouseUp,
1610
+ onMouseMove: mergeEvent(onMouseMove, moveDragging),
1611
+ onTouchStart: mergeEvent(onTouchStart, startDragging),
1612
+ onTouchMove: mergeEvent(onTouchMove, moveDragging),
1613
+ onTouchEnd,
1614
+ onTouchCancel: mergeEvent(onTouchCancel, handleTouchCancel),
1615
+ children: /* @__PURE__ */ jsx(
1616
+ Button,
1617
+ {
1618
+ sprite: {
1619
+ ...thumb,
1620
+ anchor: [0, 0.5],
1621
+ pivot: [0, 0.5],
1622
+ x: 0,
1623
+ interactive: false
1624
+ },
1625
+ lockOn: dragging ? "press" : void 0,
1626
+ x: currentValue * distance,
1627
+ anchor: [0, 0.5],
1628
+ pivot: [0, 0.5],
1629
+ interactive: false
1630
+ }
1631
+ )
1632
+ }
1633
+ );
1634
+ }
1635
+
1176
1636
  // src/debug/session.ts
1177
1637
  import { useSnapshot as useSnapshot3, proxy as proxy3 } from "valtio";
1178
1638
  var debugState = proxy3({
@@ -2120,14 +2580,144 @@ function useFadeOut(outTime, pause = false, onFinished) {
2120
2580
  );
2121
2581
  const skip = () => {
2122
2582
  api.stop();
2123
- api.set({ opacity: 1 });
2583
+ api.set({ opacity: 0 });
2124
2584
  onFinished?.();
2125
2585
  };
2126
2586
  return [style, api, skip];
2127
2587
  }
2128
2588
 
2589
+ // src/hooks/useScrollView.ts
2590
+ import { useSpringValue } from "@react-spring/core";
2591
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState5 } from "react";
2592
+ function clamp(value, min, max) {
2593
+ return Math.max(min, Math.min(max, value));
2594
+ }
2595
+ function normalizeWheelDelta(event, viewportHeight) {
2596
+ switch (event.deltaMode) {
2597
+ case "line":
2598
+ return event.deltaY * 36;
2599
+ case "page":
2600
+ return event.deltaY * viewportHeight * 0.85;
2601
+ case "pixel":
2602
+ default:
2603
+ return event.deltaY;
2604
+ }
2605
+ }
2606
+ function useScrollView({
2607
+ viewportHeight,
2608
+ initialPosition = "start",
2609
+ wheelStep = 72
2610
+ }) {
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);
2618
+ const scrollOffset = useSpringValue(0, {
2619
+ config: {
2620
+ tension: 320,
2621
+ friction: 32
2622
+ }
2623
+ });
2624
+ const maxScroll = useMemo(() => Math.max(0, contentHeight - viewportHeight), [contentHeight, viewportHeight]);
2625
+ const scrollTo = useCallback2(
2626
+ (nextOffset, immediate = false) => {
2627
+ const offset = clamp(nextOffset, 0, maxScroll);
2628
+ if (offset === scrollTargetRef.current && !immediate) {
2629
+ return;
2630
+ }
2631
+ scrollTargetRef.current = offset;
2632
+ scrollOffset.start(offset, { immediate });
2633
+ },
2634
+ [maxScroll, scrollOffset]
2635
+ );
2636
+ const scrollToRatio = useCallback2(
2637
+ (ratio, immediate = false) => {
2638
+ scrollTo(clamp(ratio, 0, 1) * maxScroll, immediate);
2639
+ },
2640
+ [maxScroll, scrollTo]
2641
+ );
2642
+ const handleContentLayout = useCallback2(
2643
+ (event) => {
2644
+ if (!didSetInitialPositionRef.current) {
2645
+ const initialOffset = initialPosition === "end" ? Math.max(0, event.height - viewportHeight) : 0;
2646
+ didSetInitialPositionRef.current = true;
2647
+ scrollTargetRef.current = initialOffset;
2648
+ scrollOffset.start(initialOffset, { immediate: true });
2649
+ }
2650
+ setContentHeight(event.height);
2651
+ setHasContentLayout(true);
2652
+ },
2653
+ [initialPosition, scrollOffset, viewportHeight]
2654
+ );
2655
+ useEffect3(() => {
2656
+ if (!hasContentLayout) {
2657
+ return;
2658
+ }
2659
+ scrollTo(scrollTargetRef.current, true);
2660
+ }, [hasContentLayout, maxScroll, scrollTo]);
2661
+ const handleWheel = useCallback2(
2662
+ (event) => {
2663
+ if (maxScroll <= 0) {
2664
+ return;
2665
+ }
2666
+ const delta = normalizeWheelDelta(event, viewportHeight);
2667
+ if (delta === 0) {
2668
+ return;
2669
+ }
2670
+ event.stopPropagation();
2671
+ const step = -Math.sign(delta) * Math.max(wheelStep, Math.min(Math.abs(delta), viewportHeight * 0.45));
2672
+ scrollTo(scrollTargetRef.current + step);
2673
+ },
2674
+ [maxScroll, scrollTo, viewportHeight, wheelStep]
2675
+ );
2676
+ const handleTouchStart = useCallback2(
2677
+ (event) => {
2678
+ if (maxScroll <= 0) {
2679
+ return;
2680
+ }
2681
+ event.stopPropagation();
2682
+ touchDraggingRef.current = true;
2683
+ touchStartYRef.current = event.clientY;
2684
+ touchStartOffsetRef.current = scrollTargetRef.current;
2685
+ },
2686
+ [maxScroll]
2687
+ );
2688
+ const handleTouchMove = useCallback2(
2689
+ (event) => {
2690
+ if (!touchDraggingRef.current) {
2691
+ return;
2692
+ }
2693
+ event.stopPropagation();
2694
+ scrollTo(touchStartOffsetRef.current + touchStartYRef.current - event.clientY, true);
2695
+ },
2696
+ [scrollTo]
2697
+ );
2698
+ const handleTouchEnd = useCallback2((event) => {
2699
+ if (!touchDraggingRef.current) {
2700
+ return;
2701
+ }
2702
+ event.stopPropagation();
2703
+ touchDraggingRef.current = false;
2704
+ }, []);
2705
+ return {
2706
+ scrollOffset,
2707
+ contentHeight,
2708
+ maxScroll,
2709
+ handleContentLayout,
2710
+ handleWheel,
2711
+ handleTouchStart,
2712
+ handleTouchMove,
2713
+ handleTouchEnd,
2714
+ scrollTo,
2715
+ scrollToRatio
2716
+ };
2717
+ }
2718
+
2129
2719
  // src/hooks/useScenario.ts
2130
- import { useEffect } from "react";
2720
+ import { useEffect as useEffect4 } from "react";
2131
2721
  var currentScenarioSession = null;
2132
2722
  var scenarioSessionId = 0;
2133
2723
  var scenarioLifecycleQueue = Promise.resolve();
@@ -2269,7 +2859,7 @@ function setWaiting(time, skippable) {
2269
2859
  executePluginCommand("scenario", { subCommand: "setWaiting", time, skippable });
2270
2860
  }
2271
2861
  function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2272
- useEffect(() => {
2862
+ useEffect4(() => {
2273
2863
  const session = acquireScenarioSession({
2274
2864
  stories: normalizeStories(stories),
2275
2865
  startName,
@@ -2283,9 +2873,9 @@ function useScenario(stories, startName, entryName, goNextOnLoad = false) {
2283
2873
  }
2284
2874
 
2285
2875
  // src/hooks/useSoundEffect.ts
2286
- import { useCallback, useEffect as useEffect2 } from "react";
2876
+ import { useCallback as useCallback3, useEffect as useEffect5 } from "react";
2287
2877
  function useSoundEffect(src) {
2288
- useEffect2(() => {
2878
+ useEffect5(() => {
2289
2879
  return () => {
2290
2880
  try {
2291
2881
  if (src) {
@@ -2300,7 +2890,7 @@ function useSoundEffect(src) {
2300
2890
  }
2301
2891
  };
2302
2892
  }, [src]);
2303
- return useCallback(() => {
2893
+ return useCallback3(() => {
2304
2894
  try {
2305
2895
  if (src) {
2306
2896
  executePluginCommand("audio", {
@@ -2316,7 +2906,7 @@ function useSoundEffect(src) {
2316
2906
  }
2317
2907
 
2318
2908
  // src/hooks/useStage.ts
2319
- import { createContext as createContext3, useContext as useContext3, useEffect as useEffect3, createElement as createElement2, useCallback as useCallback2 } from "react";
2909
+ import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, createElement as createElement2, useCallback as useCallback4 } from "react";
2320
2910
  import { proxy as proxy5, useSnapshot as useSnapshot5 } from "valtio";
2321
2911
  var SKIP_INTERVAL_MS = 80;
2322
2912
  var DEFAULT_FAST_FORWARD_STEP_LIMIT = 1e4;
@@ -2993,38 +3583,38 @@ function useStageContext() {
2993
3583
  return ctx;
2994
3584
  }
2995
3585
  function StageContextProvider({ stage, children }) {
2996
- useEffect3(() => {
3586
+ useEffect6(() => {
2997
3587
  return stage.bindEvents();
2998
3588
  }, [stage]);
2999
3589
  return createElement2(StageContext.Provider, { value: stage }, children);
3000
3590
  }
3001
3591
  function useSkipCallback(callback) {
3002
3592
  const stage = useStageContext();
3003
- useEffect3(() => {
3593
+ useEffect6(() => {
3004
3594
  return stage.addSkipCallback(callback);
3005
3595
  }, [stage, callback]);
3006
3596
  }
3007
3597
  function useInterruptCallback(callback) {
3008
3598
  const stage = useStageContext();
3009
- useEffect3(() => {
3599
+ useEffect6(() => {
3010
3600
  return stage.addInterruptCallback(callback);
3011
3601
  }, [stage, callback]);
3012
3602
  }
3013
3603
  function useSkipBlocker(callback) {
3014
3604
  const stage = useStageContext();
3015
- useEffect3(() => {
3605
+ useEffect6(() => {
3016
3606
  return stage.addSkipBlocker(callback);
3017
3607
  }, [stage, callback]);
3018
3608
  }
3019
3609
  function useAutoBlocker(callback) {
3020
3610
  const stage = useStageContext();
3021
- useEffect3(() => {
3611
+ useEffect6(() => {
3022
3612
  return stage.addAutoBlocker(callback);
3023
3613
  }, [stage, callback]);
3024
3614
  }
3025
3615
  function useAutoTicket() {
3026
3616
  const stage = useStageContext();
3027
- return useCallback2(
3617
+ return useCallback4(
3028
3618
  (options) => {
3029
3619
  return stage.issueAutoTicket(options);
3030
3620
  },
@@ -3033,7 +3623,7 @@ function useAutoTicket() {
3033
3623
  }
3034
3624
  function useBeforeHandleCommandCallback(callback) {
3035
3625
  const stage = useStageContext();
3036
- useEffect3(() => {
3626
+ useEffect6(() => {
3037
3627
  return stage.addBeforeHandleCommandCallback(callback);
3038
3628
  }, [stage, callback]);
3039
3629
  }
@@ -3041,9 +3631,14 @@ function useBeforeHandleCommandCallback(callback) {
3041
3631
  // src/lib.ts
3042
3632
  export * from "@react-spring/core";
3043
3633
  export {
3634
+ Button,
3635
+ Checkbox,
3044
3636
  DEBUG_EMPTY_PAGE,
3045
3637
  OverlayParamsContext,
3046
3638
  PageParamsContext,
3639
+ ScrollView,
3640
+ Select,
3641
+ Slider,
3047
3642
  StageContextProvider,
3048
3643
  animated as a,
3049
3644
  addEventListener,
@@ -3069,6 +3664,7 @@ export {
3069
3664
  initGlobalVariableDefaults,
3070
3665
  initUiData,
3071
3666
  loadVariablesDocument,
3667
+ mergeEvent,
3072
3668
  moyu_exports as moyu,
3073
3669
  nextLine,
3074
3670
  registerAppStateAdapter,
@@ -3095,6 +3691,7 @@ export {
3095
3691
  useNavigationParams,
3096
3692
  useNavigationState,
3097
3693
  useScenario,
3694
+ useScrollView,
3098
3695
  useSkipBlocker,
3099
3696
  useSkipCallback,
3100
3697
  useSoundEffect,