@almadar/ui 5.62.0 → 5.63.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.
Files changed (35) hide show
  1. package/dist/avl/index.cjs +490 -147
  2. package/dist/avl/index.js +490 -147
  3. package/dist/components/core/atoms/Box.d.ts +2 -0
  4. package/dist/components/core/molecules/CalendarGrid.d.ts +9 -1
  5. package/dist/components/core/molecules/PositionedCanvas.d.ts +14 -1
  6. package/dist/components/core/molecules/ReplyTree.d.ts +13 -1
  7. package/dist/components/game/molecules/three/index.cjs +64 -0
  8. package/dist/components/game/molecules/three/index.js +64 -0
  9. package/dist/components/game/organisms/hooks/useCamera.d.ts +16 -0
  10. package/dist/components/index.cjs +490 -148
  11. package/dist/components/index.js +490 -148
  12. package/dist/components/marketing/organisms/CaseStudyOrganism.d.ts +10 -1
  13. package/dist/components/marketing/organisms/FeatureGridOrganism.d.ts +10 -1
  14. package/dist/components/marketing/organisms/HeroOrganism.d.ts +24 -1
  15. package/dist/components/marketing/organisms/PricingOrganism.d.ts +14 -1
  16. package/dist/components/marketing/organisms/ShowcaseOrganism.d.ts +14 -1
  17. package/dist/components/marketing/organisms/StatsOrganism.d.ts +6 -1
  18. package/dist/components/marketing/organisms/StepFlowOrganism.d.ts +9 -1
  19. package/dist/components/marketing/organisms/TeamOrganism.d.ts +10 -1
  20. package/dist/docs/index.cjs +64 -0
  21. package/dist/docs/index.d.cts +2 -0
  22. package/dist/docs/index.js +64 -0
  23. package/dist/hooks/index.cjs +148 -0
  24. package/dist/hooks/index.d.ts +2 -0
  25. package/dist/hooks/index.js +147 -1
  26. package/dist/hooks/useCanvasGestures.d.ts +44 -0
  27. package/dist/hooks/useTapReveal.d.ts +32 -0
  28. package/dist/marketing/index.cjs +59 -0
  29. package/dist/marketing/index.d.cts +2 -0
  30. package/dist/marketing/index.js +59 -0
  31. package/dist/providers/index.cjs +490 -147
  32. package/dist/providers/index.js +490 -147
  33. package/dist/runtime/index.cjs +490 -147
  34. package/dist/runtime/index.js +490 -147
  35. package/package.json +2 -2
@@ -288,12 +288,60 @@ var init_useEventBus = __esm({
288
288
  useEventBus_default = useEventBus;
289
289
  }
290
290
  });
291
+ function useTapReveal(options = {}) {
292
+ const { onReveal, onDismiss, refs, enabled = true } = options;
293
+ const [revealed, setRevealed] = useState(false);
294
+ const onRevealRef = useRef(onReveal);
295
+ const onDismissRef = useRef(onDismiss);
296
+ onRevealRef.current = onReveal;
297
+ onDismissRef.current = onDismiss;
298
+ const reveal = useCallback(() => {
299
+ setRevealed((wasRevealed) => {
300
+ if (!wasRevealed) onRevealRef.current?.();
301
+ return true;
302
+ });
303
+ }, []);
304
+ const dismiss = useCallback(() => {
305
+ setRevealed((wasRevealed) => {
306
+ if (wasRevealed) onDismissRef.current?.();
307
+ return false;
308
+ });
309
+ }, []);
310
+ const onPointerDown = useCallback(
311
+ (e) => {
312
+ if (!enabled || e.pointerType === "mouse") return;
313
+ if (revealed) dismiss();
314
+ else reveal();
315
+ },
316
+ [enabled, revealed, reveal, dismiss]
317
+ );
318
+ useEffect(() => {
319
+ if (!revealed || typeof document === "undefined") return;
320
+ const onDocDown = (ev) => {
321
+ const target = ev.target;
322
+ if (!(target instanceof Node)) return;
323
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
324
+ if (!inside) dismiss();
325
+ };
326
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
327
+ return () => {
328
+ clearTimeout(id);
329
+ document.removeEventListener("pointerdown", onDocDown);
330
+ };
331
+ }, [revealed, refs, dismiss]);
332
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
333
+ }
334
+ var init_useTapReveal = __esm({
335
+ "hooks/useTapReveal.ts"() {
336
+ }
337
+ });
291
338
  var paddingStyles, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles, displayStyles, overflowStyles, positionStyles, Box;
292
339
  var init_Box = __esm({
293
340
  "components/core/atoms/Box.tsx"() {
294
341
  "use client";
295
342
  init_cn();
296
343
  init_useEventBus();
344
+ init_useTapReveal();
297
345
  paddingStyles = {
298
346
  none: "p-0",
299
347
  xs: "p-1",
@@ -419,10 +467,12 @@ var init_Box = __esm({
419
467
  action,
420
468
  actionPayload,
421
469
  hoverEvent,
470
+ tapReveal = true,
422
471
  maxWidth,
423
472
  onClick,
424
473
  onMouseEnter,
425
474
  onMouseLeave,
475
+ onPointerDown,
426
476
  ...rest
427
477
  }, ref) => {
428
478
  const eventBus = useEventBus();
@@ -445,6 +495,19 @@ var init_Box = __esm({
445
495
  }
446
496
  onMouseLeave?.(e);
447
497
  }, [hoverEvent, eventBus, onMouseLeave]);
498
+ const { triggerProps } = useTapReveal({
499
+ enabled: tapReveal && !!hoverEvent,
500
+ onReveal: useCallback(() => {
501
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
502
+ }, [hoverEvent, eventBus]),
503
+ onDismiss: useCallback(() => {
504
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
505
+ }, [hoverEvent, eventBus])
506
+ });
507
+ const handlePointerDown = useCallback((e) => {
508
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
509
+ onPointerDown?.(e);
510
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
448
511
  const isClickable = action || onClick;
449
512
  return React84__default.createElement(
450
513
  Component2,
@@ -472,6 +535,7 @@ var init_Box = __esm({
472
535
  onClick: isClickable ? handleClick : void 0,
473
536
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
474
537
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
538
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
475
539
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
476
540
  ...rest
477
541
  },
@@ -4882,6 +4946,11 @@ var init_TextHighlight = __esm({
4882
4946
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
4883
4947
  onMouseLeave?.();
4884
4948
  },
4949
+ onPointerDown: (e) => {
4950
+ if (e.pointerType === "mouse") return;
4951
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
4952
+ onMouseEnter?.();
4953
+ },
4885
4954
  role: "button",
4886
4955
  tabIndex: 0,
4887
4956
  onKeyDown: (e) => {
@@ -5057,6 +5126,7 @@ var init_LawReferenceTooltip = __esm({
5057
5126
  init_Typography();
5058
5127
  init_Divider();
5059
5128
  init_cn();
5129
+ init_useTapReveal();
5060
5130
  positionStyles2 = {
5061
5131
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
5062
5132
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -5078,6 +5148,7 @@ var init_LawReferenceTooltip = __esm({
5078
5148
  const { t } = useTranslate();
5079
5149
  const [isVisible, setIsVisible] = React84__default.useState(false);
5080
5150
  const timeoutRef = React84__default.useRef(null);
5151
+ const triggerRef = React84__default.useRef(null);
5081
5152
  const handleMouseEnter = () => {
5082
5153
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5083
5154
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -5086,6 +5157,8 @@ var init_LawReferenceTooltip = __esm({
5086
5157
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5087
5158
  setIsVisible(false);
5088
5159
  };
5160
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
5161
+ const open = isVisible || revealed;
5089
5162
  React84__default.useEffect(() => {
5090
5163
  return () => {
5091
5164
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -5094,6 +5167,7 @@ var init_LawReferenceTooltip = __esm({
5094
5167
  return /* @__PURE__ */ jsxs(
5095
5168
  Box,
5096
5169
  {
5170
+ ref: triggerRef,
5097
5171
  as: "span",
5098
5172
  position: "relative",
5099
5173
  display: "inline-block",
@@ -5102,9 +5176,10 @@ var init_LawReferenceTooltip = __esm({
5102
5176
  onMouseLeave: handleMouseLeave,
5103
5177
  onFocus: handleMouseEnter,
5104
5178
  onBlur: handleMouseLeave,
5179
+ onPointerDown: triggerProps.onPointerDown,
5105
5180
  children: [
5106
5181
  children,
5107
- isVisible && /* @__PURE__ */ jsxs(
5182
+ open && /* @__PURE__ */ jsxs(
5108
5183
  Box,
5109
5184
  {
5110
5185
  padding: "sm",
@@ -10520,6 +10595,36 @@ function useCamera() {
10520
10595
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
10521
10596
  drawFn?.();
10522
10597
  }, []);
10598
+ const handlePointerDown = useCallback((e) => {
10599
+ handleMouseDown(e);
10600
+ }, [handleMouseDown]);
10601
+ const handlePointerUp = useCallback(() => {
10602
+ handleMouseUp();
10603
+ }, [handleMouseUp]);
10604
+ const handlePointerMove = useCallback((e, drawFn) => {
10605
+ return handleMouseMove(e, drawFn);
10606
+ }, [handleMouseMove]);
10607
+ const panBy = useCallback((dx, dy, drawFn) => {
10608
+ cameraRef.current.x -= dx;
10609
+ cameraRef.current.y -= dy;
10610
+ targetCameraRef.current = null;
10611
+ drawFn?.();
10612
+ }, []);
10613
+ const zoomAtPoint = useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
10614
+ const cam = cameraRef.current;
10615
+ const oldZoom = cam.zoom;
10616
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
10617
+ if (newZoom === oldZoom) {
10618
+ drawFn?.();
10619
+ return;
10620
+ }
10621
+ const inv = 1 / oldZoom - 1 / newZoom;
10622
+ cam.x += (centerX - viewportSize.width / 2) * inv;
10623
+ cam.y += (centerY - viewportSize.height / 2) * inv;
10624
+ cam.zoom = newZoom;
10625
+ targetCameraRef.current = null;
10626
+ drawFn?.();
10627
+ }, []);
10523
10628
  const screenToWorld = useCallback((clientX, clientY, canvas, viewportSize) => {
10524
10629
  const rect = canvas.getBoundingClientRect();
10525
10630
  const screenX = clientX - rect.left;
@@ -10551,6 +10656,11 @@ function useCamera() {
10551
10656
  handleMouseMove,
10552
10657
  handleMouseLeave,
10553
10658
  handleWheel,
10659
+ handlePointerDown,
10660
+ handlePointerUp,
10661
+ handlePointerMove,
10662
+ panBy,
10663
+ zoomAtPoint,
10554
10664
  screenToWorld,
10555
10665
  lerpToTarget
10556
10666
  };
@@ -10560,6 +10670,113 @@ var init_useCamera = __esm({
10560
10670
  "use client";
10561
10671
  }
10562
10672
  });
10673
+ function localPoint(canvas, clientX, clientY) {
10674
+ if (!canvas) return { x: clientX, y: clientY };
10675
+ const rect = canvas.getBoundingClientRect();
10676
+ return { x: clientX - rect.left, y: clientY - rect.top };
10677
+ }
10678
+ function useCanvasGestures(options) {
10679
+ const {
10680
+ canvasRef,
10681
+ enabled = true,
10682
+ wheelStep = 1.1,
10683
+ onPointerDown,
10684
+ onPointerMove,
10685
+ onPointerUp,
10686
+ onZoom,
10687
+ onPanDelta,
10688
+ onMultiTouchStart
10689
+ } = options;
10690
+ const pointers = useRef(/* @__PURE__ */ new Map());
10691
+ const pinch = useRef(null);
10692
+ const computePinch = useCallback(() => {
10693
+ const pts = [...pointers.current.values()];
10694
+ const dx = pts[1].x - pts[0].x;
10695
+ const dy = pts[1].y - pts[0].y;
10696
+ return {
10697
+ distance: Math.hypot(dx, dy) || 1,
10698
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
10699
+ };
10700
+ }, []);
10701
+ const handlePointerDown = useCallback(
10702
+ (e) => {
10703
+ if (!enabled) return;
10704
+ e.currentTarget.setPointerCapture(e.pointerId);
10705
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
10706
+ if (pointers.current.size === 2) {
10707
+ onMultiTouchStart?.();
10708
+ pinch.current = computePinch();
10709
+ } else if (pointers.current.size === 1) {
10710
+ onPointerDown?.(e);
10711
+ }
10712
+ },
10713
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
10714
+ );
10715
+ const handlePointerMove = useCallback(
10716
+ (e) => {
10717
+ if (!enabled) return;
10718
+ if (pointers.current.has(e.pointerId)) {
10719
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
10720
+ }
10721
+ if (pointers.current.size >= 2 && pinch.current) {
10722
+ const next = computePinch();
10723
+ const prev = pinch.current;
10724
+ if (next.distance !== prev.distance) {
10725
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
10726
+ }
10727
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
10728
+ pinch.current = next;
10729
+ return;
10730
+ }
10731
+ onPointerMove?.(e);
10732
+ },
10733
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
10734
+ );
10735
+ const endPointer = useCallback(
10736
+ (e, fireUp) => {
10737
+ const wasMulti = pointers.current.size >= 2;
10738
+ pointers.current.delete(e.pointerId);
10739
+ if (pointers.current.size < 2) pinch.current = null;
10740
+ if (wasMulti && pointers.current.size === 1) return;
10741
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
10742
+ },
10743
+ [onPointerUp]
10744
+ );
10745
+ const handlePointerUp = useCallback(
10746
+ (e) => {
10747
+ if (!enabled) return;
10748
+ endPointer(e, true);
10749
+ },
10750
+ [enabled, endPointer]
10751
+ );
10752
+ const handlePointerCancel = useCallback(
10753
+ (e) => {
10754
+ if (!enabled) return;
10755
+ endPointer(e, false);
10756
+ },
10757
+ [enabled, endPointer]
10758
+ );
10759
+ const handleWheel = useCallback(
10760
+ (e) => {
10761
+ if (!enabled) return;
10762
+ e.preventDefault();
10763
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
10764
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
10765
+ },
10766
+ [enabled, canvasRef, wheelStep, onZoom]
10767
+ );
10768
+ return {
10769
+ onPointerDown: handlePointerDown,
10770
+ onPointerMove: handlePointerMove,
10771
+ onPointerUp: handlePointerUp,
10772
+ onPointerCancel: handlePointerCancel,
10773
+ onWheel: handleWheel
10774
+ };
10775
+ }
10776
+ var init_useCanvasGestures = __esm({
10777
+ "hooks/useCanvasGestures.ts"() {
10778
+ }
10779
+ });
10563
10780
  function unitAtlasUrl(unit) {
10564
10781
  if (unit.spriteSheet) return unit.spriteSheet;
10565
10782
  const sprite = unit.sprite;
@@ -10938,11 +11155,12 @@ function IsometricCanvas({
10938
11155
  cameraRef,
10939
11156
  targetCameraRef,
10940
11157
  dragDistance,
10941
- handleMouseDown,
10942
- handleMouseUp,
10943
- handleMouseMove,
10944
11158
  handleMouseLeave,
10945
- handleWheel,
11159
+ handlePointerDown,
11160
+ handlePointerUp,
11161
+ handlePointerMove,
11162
+ panBy,
11163
+ zoomAtPoint,
10946
11164
  screenToWorld,
10947
11165
  lerpToTarget
10948
11166
  } = useCamera();
@@ -11377,11 +11595,16 @@ function IsometricCanvas({
11377
11595
  cancelAnimationFrame(rafIdRef.current);
11378
11596
  };
11379
11597
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
11380
- const handleMouseMoveWithCamera = useCallback((e) => {
11381
- if (enableCamera) {
11382
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
11383
- if (wasPanning) return;
11384
- }
11598
+ const singlePointerActiveRef = useRef(false);
11599
+ const handleCanvasPointerDown = useCallback((e) => {
11600
+ singlePointerActiveRef.current = true;
11601
+ if (enableCamera) handlePointerDown(e);
11602
+ }, [enableCamera, handlePointerDown]);
11603
+ const handleCanvasPointerMove = useCallback((e) => {
11604
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
11605
+ }, [enableCamera, handlePointerMove, draw]);
11606
+ const handleCanvasHover = useCallback((e) => {
11607
+ if (singlePointerActiveRef.current) return;
11385
11608
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
11386
11609
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
11387
11610
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -11392,26 +11615,10 @@ function IsometricCanvas({
11392
11615
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
11393
11616
  onTileHover?.(isoPos.x, isoPos.y);
11394
11617
  }
11395
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11396
- const handleMouseLeaveWithCamera = useCallback(() => {
11397
- handleMouseLeave();
11398
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11399
- onTileLeave?.();
11400
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11401
- const handleWheelWithCamera = useCallback((e) => {
11402
- if (enableCamera) {
11403
- handleWheel(e, () => draw(animTimeRef.current));
11404
- }
11405
- }, [enableCamera, handleWheel, draw]);
11406
- useEffect(() => {
11407
- const canvas = canvasRef.current;
11408
- if (!canvas) return;
11409
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
11410
- return () => {
11411
- canvas.removeEventListener("wheel", handleWheelWithCamera);
11412
- };
11413
- }, [handleWheelWithCamera]);
11414
- const handleClick = useCallback((e) => {
11618
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11619
+ const handleCanvasPointerUp = useCallback((e) => {
11620
+ singlePointerActiveRef.current = false;
11621
+ if (enableCamera) handlePointerUp();
11415
11622
  if (dragDistance() > 5) return;
11416
11623
  if (!canvasRef.current) return;
11417
11624
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -11429,7 +11636,32 @@ function IsometricCanvas({
11429
11636
  onTileClick?.(isoPos.x, isoPos.y);
11430
11637
  }
11431
11638
  }
11432
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11639
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11640
+ const handleCanvasPointerLeave = useCallback(() => {
11641
+ handleMouseLeave();
11642
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11643
+ onTileLeave?.();
11644
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11645
+ const applyZoom = useCallback((factor, centerX, centerY) => {
11646
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
11647
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
11648
+ const applyPanDelta = useCallback((dx, dy) => {
11649
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
11650
+ }, [enableCamera, panBy, draw]);
11651
+ const cancelSinglePointer = useCallback(() => {
11652
+ singlePointerActiveRef.current = false;
11653
+ if (enableCamera) handlePointerUp();
11654
+ }, [enableCamera, handlePointerUp]);
11655
+ const gestureHandlers = useCanvasGestures({
11656
+ canvasRef,
11657
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
11658
+ onPointerDown: handleCanvasPointerDown,
11659
+ onPointerMove: handleCanvasPointerMove,
11660
+ onPointerUp: handleCanvasPointerUp,
11661
+ onZoom: applyZoom,
11662
+ onPanDelta: applyPanDelta,
11663
+ onMultiTouchStart: cancelSinglePointer
11664
+ });
11433
11665
  if (error) {
11434
11666
  return /* @__PURE__ */ jsx(ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
11435
11667
  }
@@ -11461,13 +11693,17 @@ function IsometricCanvas({
11461
11693
  {
11462
11694
  ref: canvasRef,
11463
11695
  "data-testid": "game-canvas",
11464
- onClick: handleClick,
11465
- onMouseDown: enableCamera ? handleMouseDown : void 0,
11466
- onMouseMove: handleMouseMoveWithCamera,
11467
- onMouseUp: enableCamera ? handleMouseUp : void 0,
11468
- onMouseLeave: handleMouseLeaveWithCamera,
11696
+ onPointerDown: gestureHandlers.onPointerDown,
11697
+ onPointerMove: (e) => {
11698
+ gestureHandlers.onPointerMove(e);
11699
+ handleCanvasHover(e);
11700
+ },
11701
+ onPointerUp: gestureHandlers.onPointerUp,
11702
+ onPointerCancel: gestureHandlers.onPointerCancel,
11703
+ onPointerLeave: handleCanvasPointerLeave,
11704
+ onWheel: gestureHandlers.onWheel,
11469
11705
  onContextMenu: (e) => e.preventDefault(),
11470
- className: "cursor-pointer",
11706
+ className: "cursor-pointer touch-none",
11471
11707
  style: {
11472
11708
  width: viewportSize.width,
11473
11709
  height: viewportSize.height
@@ -11521,6 +11757,7 @@ var init_IsometricCanvas = __esm({
11521
11757
  init_ErrorState();
11522
11758
  init_useImageCache();
11523
11759
  init_useCamera();
11760
+ init_useCanvasGestures();
11524
11761
  init_useUnitSpriteAtlas();
11525
11762
  init_verificationRegistry();
11526
11763
  init_isometric();
@@ -14636,6 +14873,10 @@ var init_StateMachineView = __esm({
14636
14873
  const handleMouseLeave2 = () => {
14637
14874
  onHover(null, 0, 0);
14638
14875
  };
14876
+ const handlePointerDown2 = (e) => {
14877
+ if (e.pointerType === "mouse") return;
14878
+ handleMouseEnter2();
14879
+ };
14639
14880
  return /* @__PURE__ */ jsxs(
14640
14881
  "g",
14641
14882
  {
@@ -14645,6 +14886,7 @@ var init_StateMachineView = __esm({
14645
14886
  onClick: () => onClick?.(bundle),
14646
14887
  onMouseEnter: handleMouseEnter2,
14647
14888
  onMouseLeave: handleMouseLeave2,
14889
+ onPointerDown: handlePointerDown2,
14648
14890
  style: { pointerEvents: "auto" },
14649
14891
  children: [
14650
14892
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -14762,6 +15004,10 @@ var init_StateMachineView = __esm({
14762
15004
  const handleMouseLeave = useCallback(() => {
14763
15005
  onHover(null, 0, 0);
14764
15006
  }, [onHover]);
15007
+ const handlePointerDown = useCallback((e) => {
15008
+ if (e.pointerType === "mouse") return;
15009
+ handleMouseEnter();
15010
+ }, [handleMouseEnter]);
14765
15011
  const uniqueMarkerId = `arrow-${bundle.id}`;
14766
15012
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
14767
15013
  return /* @__PURE__ */ jsxs(
@@ -14773,6 +15019,7 @@ var init_StateMachineView = __esm({
14773
15019
  onClick: () => onClick?.(bundle),
14774
15020
  onMouseEnter: handleMouseEnter,
14775
15021
  onMouseLeave: handleMouseLeave,
15022
+ onPointerDown: handlePointerDown,
14776
15023
  style: { pointerEvents: "auto" },
14777
15024
  children: [
14778
15025
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -23271,10 +23518,84 @@ function SubMenu({
23271
23518
  );
23272
23519
  return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
23273
23520
  }
23521
+ function MenuItemRow({
23522
+ item,
23523
+ itemId,
23524
+ hasSubMenu,
23525
+ isDanger,
23526
+ direction,
23527
+ isSubMenuOpen,
23528
+ activeSubMenuRef,
23529
+ eventBus,
23530
+ onItemClick,
23531
+ openSubMenu
23532
+ }) {
23533
+ const rowRef = useRef(null);
23534
+ const { triggerProps } = useTapReveal({
23535
+ enabled: hasSubMenu,
23536
+ onReveal: () => openSubMenu(itemId, rowRef.current),
23537
+ refs: [rowRef]
23538
+ });
23539
+ return /* @__PURE__ */ jsxs(Box, { children: [
23540
+ /* @__PURE__ */ jsx(
23541
+ Box,
23542
+ {
23543
+ ref: rowRef,
23544
+ as: "button",
23545
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
23546
+ "aria-disabled": item.disabled || void 0,
23547
+ onMouseEnter: (e) => {
23548
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
23549
+ },
23550
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
23551
+ "data-testid": item.event ? `action-${item.event}` : void 0,
23552
+ className: cn(
23553
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23554
+ "text-sm transition-colors",
23555
+ "hover:bg-muted",
23556
+ "focus:outline-none focus:bg-muted",
23557
+ "disabled:opacity-50 disabled:cursor-not-allowed",
23558
+ item.disabled && "cursor-not-allowed",
23559
+ isDanger && "text-error hover:bg-error/10"
23560
+ ),
23561
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23562
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
23563
+ /* @__PURE__ */ jsx(
23564
+ Typography,
23565
+ {
23566
+ variant: "small",
23567
+ className: cn("flex-1", isDanger && "text-red-600"),
23568
+ children: item.label
23569
+ }
23570
+ ),
23571
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23572
+ hasSubMenu && /* @__PURE__ */ jsx(
23573
+ Icon,
23574
+ {
23575
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
23576
+ size: "sm",
23577
+ className: "flex-shrink-0"
23578
+ }
23579
+ )
23580
+ ] })
23581
+ }
23582
+ ),
23583
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsx(
23584
+ SubMenu,
23585
+ {
23586
+ items: item.subMenu,
23587
+ itemRef: activeSubMenuRef,
23588
+ direction,
23589
+ eventBus
23590
+ }
23591
+ )
23592
+ ] });
23593
+ }
23274
23594
  var MENU_GAP, menuContainerStyles, Menu;
23275
23595
  var init_Menu = __esm({
23276
23596
  "components/core/molecules/Menu.tsx"() {
23277
23597
  "use client";
23598
+ init_useTapReveal();
23278
23599
  init_Box();
23279
23600
  init_Icon();
23280
23601
  init_Divider();
@@ -23329,6 +23650,10 @@ var init_Menu = __esm({
23329
23650
  setIsOpen(false);
23330
23651
  }
23331
23652
  };
23653
+ const openSubMenu = (itemId, el) => {
23654
+ setActiveSubMenu(itemId);
23655
+ setActiveSubMenuRef(el);
23656
+ };
23332
23657
  useEffect(() => {
23333
23658
  if (isOpen) {
23334
23659
  updatePosition();
@@ -23372,61 +23697,22 @@ var init_Menu = __esm({
23372
23697
  if (isDivider) {
23373
23698
  return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
23374
23699
  }
23375
- return /* @__PURE__ */ jsxs(Box, { children: [
23376
- /* @__PURE__ */ jsx(
23377
- Box,
23378
- {
23379
- as: "button",
23380
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
23381
- "aria-disabled": item.disabled || void 0,
23382
- onMouseEnter: (e) => {
23383
- if (hasSubMenu) {
23384
- setActiveSubMenu(itemId);
23385
- setActiveSubMenuRef(e.currentTarget);
23386
- }
23387
- },
23388
- "data-testid": item.event ? `action-${item.event}` : void 0,
23389
- className: cn(
23390
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23391
- "text-sm transition-colors",
23392
- "hover:bg-muted",
23393
- "focus:outline-none focus:bg-muted",
23394
- "disabled:opacity-50 disabled:cursor-not-allowed",
23395
- item.disabled && "cursor-not-allowed",
23396
- isDanger && "text-error hover:bg-error/10"
23397
- ),
23398
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23399
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
23400
- /* @__PURE__ */ jsx(
23401
- Typography,
23402
- {
23403
- variant: "small",
23404
- className: cn("flex-1", isDanger && "text-red-600"),
23405
- children: item.label
23406
- }
23407
- ),
23408
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23409
- hasSubMenu && /* @__PURE__ */ jsx(
23410
- Icon,
23411
- {
23412
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
23413
- size: "sm",
23414
- className: "flex-shrink-0"
23415
- }
23416
- )
23417
- ] })
23418
- }
23419
- ),
23420
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
23421
- SubMenu,
23422
- {
23423
- items: item.subMenu,
23424
- itemRef: activeSubMenuRef,
23425
- direction,
23426
- eventBus
23427
- }
23428
- )
23429
- ] }, itemId);
23700
+ return /* @__PURE__ */ jsx(
23701
+ MenuItemRow,
23702
+ {
23703
+ item,
23704
+ itemId,
23705
+ hasSubMenu,
23706
+ isDanger,
23707
+ direction,
23708
+ isSubMenuOpen: activeSubMenu === itemId,
23709
+ activeSubMenuRef,
23710
+ eventBus,
23711
+ onItemClick: handleItemClick,
23712
+ openSubMenu
23713
+ },
23714
+ itemId
23715
+ );
23430
23716
  });
23431
23717
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
23432
23718
  "div",
@@ -25729,6 +26015,7 @@ var init_Popover = __esm({
25729
26015
  "use client";
25730
26016
  init_Typography();
25731
26017
  init_cn();
26018
+ init_useTapReveal();
25732
26019
  arrowClasses = {
25733
26020
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
25734
26021
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -25799,18 +26086,32 @@ var init_Popover = __esm({
25799
26086
  document.addEventListener("mousedown", handleClickOutside);
25800
26087
  return () => document.removeEventListener("mousedown", handleClickOutside);
25801
26088
  }, [isOpen, trigger]);
25802
- const triggerProps = trigger === "click" ? {
26089
+ const { triggerProps: tapTriggerProps } = useTapReveal({
26090
+ enabled: trigger === "hover",
26091
+ onReveal: handleOpen,
26092
+ onDismiss: handleClose,
26093
+ refs: [triggerRef, popoverRef]
26094
+ });
26095
+ const handlerProps = trigger === "click" ? {
25803
26096
  onClick: handleToggle
25804
26097
  } : {
25805
26098
  onMouseEnter: handleOpen,
25806
- onMouseLeave: handleClose
26099
+ onMouseLeave: handleClose,
26100
+ onPointerDown: tapTriggerProps.onPointerDown
25807
26101
  };
25808
26102
  const childElement = React84__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26103
+ const childPointerDown = childElement.props.onPointerDown;
25809
26104
  const triggerElement = React84__default.cloneElement(
25810
26105
  childElement,
25811
26106
  {
25812
26107
  ref: triggerRef,
25813
- ...triggerProps
26108
+ ...handlerProps,
26109
+ ...trigger === "hover" ? {
26110
+ onPointerDown: (e) => {
26111
+ tapTriggerProps.onPointerDown(e);
26112
+ childPointerDown?.(e);
26113
+ }
26114
+ } : void 0
25814
26115
  }
25815
26116
  );
25816
26117
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
@@ -26341,6 +26642,7 @@ var init_Tooltip = __esm({
26341
26642
  "use client";
26342
26643
  init_Typography();
26343
26644
  init_cn();
26645
+ init_useTapReveal();
26344
26646
  TRIGGER_GAP2 = 8;
26345
26647
  arrowClasses2 = {
26346
26648
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -26385,6 +26687,11 @@ var init_Tooltip = __esm({
26385
26687
  setIsVisible(false);
26386
26688
  }, hideDelay);
26387
26689
  };
26690
+ const { triggerProps } = useTapReveal({
26691
+ onReveal: handleMouseEnter,
26692
+ onDismiss: handleMouseLeave,
26693
+ refs: [triggerRef, tooltipRef]
26694
+ });
26388
26695
  useLayoutEffect(() => {
26389
26696
  if (isVisible) {
26390
26697
  updatePosition();
@@ -26397,12 +26704,17 @@ var init_Tooltip = __esm({
26397
26704
  };
26398
26705
  }, []);
26399
26706
  const triggerElement = React84__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26707
+ const childPointerDown = triggerElement.props.onPointerDown;
26400
26708
  const trigger = React84__default.cloneElement(triggerElement, {
26401
26709
  ref: triggerRef,
26402
26710
  onMouseEnter: handleMouseEnter,
26403
26711
  onMouseLeave: handleMouseLeave,
26404
26712
  onFocus: handleMouseEnter,
26405
- onBlur: handleMouseLeave
26713
+ onBlur: handleMouseLeave,
26714
+ onPointerDown: (e) => {
26715
+ triggerProps.onPointerDown(e);
26716
+ childPointerDown?.(e);
26717
+ }
26406
26718
  });
26407
26719
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxs(
26408
26720
  "div",
@@ -29752,6 +30064,14 @@ var init_GraphView = __esm({
29752
30064
  },
29753
30065
  [onNodeClick]
29754
30066
  );
30067
+ const handleNodePointerDown = useCallback(
30068
+ (e, node) => {
30069
+ if (e.pointerType === "mouse") return;
30070
+ handleNodeMouseEnter(node);
30071
+ handleNodeClickInternal(node);
30072
+ },
30073
+ [handleNodeMouseEnter, handleNodeClickInternal]
30074
+ );
29755
30075
  if (nodes.length === 0) {
29756
30076
  return /* @__PURE__ */ jsx(Box, { className: cn("flex items-center justify-center", className), style: { width: w, height: h }, children: /* @__PURE__ */ jsx(Box, { className: "text-muted-foreground text-sm", children: t("display.noGraphData") }) });
29757
30077
  }
@@ -29811,6 +30131,7 @@ var init_GraphView = __esm({
29811
30131
  onMouseEnter: () => handleNodeMouseEnter(node),
29812
30132
  onMouseLeave: handleNodeMouseLeave,
29813
30133
  onClick: () => handleNodeClickInternal(node),
30134
+ onPointerDown: (e) => handleNodePointerDown(e, node),
29814
30135
  children: [
29815
30136
  /* @__PURE__ */ jsx(
29816
30137
  "circle",
@@ -29891,13 +30212,13 @@ var init_MapView = __esm({
29891
30212
  shadowSize: [41, 41]
29892
30213
  });
29893
30214
  L.Marker.prototype.options.icon = defaultIcon;
29894
- const { useEffect: useEffect83, useRef: useRef78, useCallback: useCallback124, useState: useState114 } = React84__default;
30215
+ const { useEffect: useEffect84, useRef: useRef80, useCallback: useCallback126, useState: useState115 } = React84__default;
29895
30216
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29896
30217
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29897
30218
  function MapUpdater({ centerLat, centerLng, zoom }) {
29898
30219
  const map = useMap();
29899
- const prevRef = useRef78({ centerLat, centerLng, zoom });
29900
- useEffect83(() => {
30220
+ const prevRef = useRef80({ centerLat, centerLng, zoom });
30221
+ useEffect84(() => {
29901
30222
  const prev = prevRef.current;
29902
30223
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29903
30224
  map.setView([centerLat, centerLng], zoom);
@@ -29908,7 +30229,7 @@ var init_MapView = __esm({
29908
30229
  }
29909
30230
  function MapClickHandler({ onMapClick }) {
29910
30231
  const map = useMap();
29911
- useEffect83(() => {
30232
+ useEffect84(() => {
29912
30233
  if (!onMapClick) return;
29913
30234
  const handler = (e) => {
29914
30235
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29936,8 +30257,8 @@ var init_MapView = __esm({
29936
30257
  showAttribution = true
29937
30258
  }) {
29938
30259
  const eventBus = useEventBus4();
29939
- const [clickedPosition, setClickedPosition] = useState114(null);
29940
- const handleMapClick = useCallback124((lat, lng) => {
30260
+ const [clickedPosition, setClickedPosition] = useState115(null);
30261
+ const handleMapClick = useCallback126((lat, lng) => {
29941
30262
  if (showClickedPin) {
29942
30263
  setClickedPosition({ lat, lng });
29943
30264
  }
@@ -29946,7 +30267,7 @@ var init_MapView = __esm({
29946
30267
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29947
30268
  }
29948
30269
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29949
- const handleMarkerClick = useCallback124((marker) => {
30270
+ const handleMarkerClick = useCallback126((marker) => {
29950
30271
  onMarkerClick?.(marker);
29951
30272
  if (markerClickEvent) {
29952
30273
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30287,7 +30608,7 @@ var init_StarRating = __esm({
30287
30608
  "aria-valuenow": value,
30288
30609
  tabIndex: readOnly ? void 0 : 0,
30289
30610
  onKeyDown: handleKeyDown,
30290
- onMouseLeave: () => setHoverValue(null),
30611
+ onPointerLeave: () => setHoverValue(null),
30291
30612
  children: Array.from({ length: max }, (_, i) => {
30292
30613
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
30293
30614
  const isFull = fillLevel >= 1;
@@ -30297,7 +30618,7 @@ var init_StarRating = __esm({
30297
30618
  {
30298
30619
  className: "relative inline-block",
30299
30620
  onClick: () => handleStarClick(i, false),
30300
- onMouseMove: (e) => {
30621
+ onPointerMove: (e) => {
30301
30622
  if (readOnly) return;
30302
30623
  const rect = e.currentTarget.getBoundingClientRect();
30303
30624
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -34187,7 +34508,7 @@ var init_PositionedCanvas = __esm({
34187
34508
  {
34188
34509
  ref: containerRef,
34189
34510
  "data-testid": "positioned-canvas",
34190
- className: "relative bg-background border border-border rounded-container overflow-hidden",
34511
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
34191
34512
  style: { width, height },
34192
34513
  onClick: handleContainerClick,
34193
34514
  children: items.map((item) => {
@@ -38356,6 +38677,7 @@ var init_GraphCanvas = __esm({
38356
38677
  init_ErrorState();
38357
38678
  init_EmptyState();
38358
38679
  init_useEventBus();
38680
+ init_useCanvasGestures();
38359
38681
  GROUP_COLORS2 = [
38360
38682
  "var(--color-primary)",
38361
38683
  "var(--color-success)",
@@ -38393,6 +38715,10 @@ var init_GraphCanvas = __esm({
38393
38715
  const animRef = useRef(0);
38394
38716
  const [zoom, setZoom] = useState(1);
38395
38717
  const [offset, setOffset] = useState({ x: 0, y: 0 });
38718
+ const zoomRef = useRef(zoom);
38719
+ zoomRef.current = zoom;
38720
+ const offsetRef = useRef(offset);
38721
+ offsetRef.current = offset;
38396
38722
  const [hoveredNode, setHoveredNode] = useState(null);
38397
38723
  const nodesRef = useRef([]);
38398
38724
  const [, forceUpdate] = useState(0);
@@ -38652,25 +38978,27 @@ var init_GraphCanvas = __esm({
38652
38978
  setZoom(1);
38653
38979
  setOffset({ x: 0, y: 0 });
38654
38980
  }, []);
38655
- const handleWheel = useCallback(
38656
- (e) => {
38657
- if (!interactive) return;
38658
- e.preventDefault();
38659
- const coords = toCoords(e);
38660
- if (!coords) return;
38661
- const oldZoom = zoom;
38662
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38663
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38664
- if (newZoom === oldZoom) return;
38665
- setOffset((o) => ({
38666
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38667
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38668
- }));
38669
- setZoom(newZoom);
38670
- },
38671
- [interactive, toCoords, zoom]
38672
- );
38673
- const handleMouseDown = useCallback(
38981
+ const applyZoom = useCallback((factor, cx, cy) => {
38982
+ if (!interactive) return;
38983
+ const oldZoom = zoomRef.current;
38984
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38985
+ if (newZoom === oldZoom) return;
38986
+ const o = offsetRef.current;
38987
+ setOffset({
38988
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
38989
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
38990
+ });
38991
+ setZoom(newZoom);
38992
+ }, [interactive]);
38993
+ const applyPanDelta = useCallback((dx, dy) => {
38994
+ if (!interactive) return;
38995
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
38996
+ }, [interactive]);
38997
+ const cancelSinglePointer = useCallback(() => {
38998
+ interactionRef.current.mode = "none";
38999
+ interactionRef.current.dragNodeId = null;
39000
+ }, []);
39001
+ const handlePointerDown = useCallback(
38674
39002
  (e) => {
38675
39003
  const coords = toCoords(e);
38676
39004
  if (!coords) return;
@@ -38692,7 +39020,7 @@ var init_GraphCanvas = __esm({
38692
39020
  },
38693
39021
  [toCoords, nodeAt, draggable, interactive, offset]
38694
39022
  );
38695
- const handleMouseMove = useCallback(
39023
+ const handlePointerMove = useCallback(
38696
39024
  (e) => {
38697
39025
  const state = interactionRef.current;
38698
39026
  if (state.mode === "panning") {
@@ -38721,7 +39049,7 @@ var init_GraphCanvas = __esm({
38721
39049
  },
38722
39050
  [toCoords, nodeAt]
38723
39051
  );
38724
- const handleMouseUp = useCallback(
39052
+ const handlePointerUp = useCallback(
38725
39053
  (e) => {
38726
39054
  const state = interactionRef.current;
38727
39055
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -38738,11 +39066,19 @@ var init_GraphCanvas = __esm({
38738
39066
  },
38739
39067
  [toCoords, nodeAt, handleNodeClick]
38740
39068
  );
38741
- const handleMouseLeave = useCallback(() => {
38742
- interactionRef.current.mode = "none";
38743
- interactionRef.current.dragNodeId = null;
39069
+ const handlePointerLeave = useCallback(() => {
38744
39070
  setHoveredNode(null);
38745
39071
  }, []);
39072
+ const gestureHandlers = useCanvasGestures({
39073
+ canvasRef,
39074
+ enabled: interactive || draggable,
39075
+ onPointerDown: handlePointerDown,
39076
+ onPointerMove: handlePointerMove,
39077
+ onPointerUp: handlePointerUp,
39078
+ onZoom: applyZoom,
39079
+ onPanDelta: applyPanDelta,
39080
+ onMultiTouchStart: cancelSinglePointer
39081
+ });
38746
39082
  const handleDoubleClick = useCallback(
38747
39083
  (e) => {
38748
39084
  const coords = toCoords(e);
@@ -38811,13 +39147,14 @@ var init_GraphCanvas = __esm({
38811
39147
  ref: canvasRef,
38812
39148
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38813
39149
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38814
- className: "w-full cursor-grab active:cursor-grabbing",
39150
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
38815
39151
  style: { height },
38816
- onWheel: handleWheel,
38817
- onMouseDown: handleMouseDown,
38818
- onMouseMove: handleMouseMove,
38819
- onMouseUp: handleMouseUp,
38820
- onMouseLeave: handleMouseLeave,
39152
+ onPointerDown: gestureHandlers.onPointerDown,
39153
+ onPointerMove: gestureHandlers.onPointerMove,
39154
+ onPointerUp: gestureHandlers.onPointerUp,
39155
+ onPointerCancel: gestureHandlers.onPointerCancel,
39156
+ onPointerLeave: handlePointerLeave,
39157
+ onWheel: gestureHandlers.onWheel,
38821
39158
  onDoubleClick: handleDoubleClick
38822
39159
  }
38823
39160
  ) }),
@@ -43289,6 +43626,9 @@ var init_GameCanvas3D2 = __esm({
43289
43626
  target: cameraTarget,
43290
43627
  enableDamping: true,
43291
43628
  dampingFactor: 0.05,
43629
+ enableZoom: true,
43630
+ enablePan: true,
43631
+ touches: { ONE: THREE3.TOUCH.ROTATE, TWO: THREE3.TOUCH.DOLLY_PAN },
43292
43632
  minDistance: 2,
43293
43633
  maxDistance: 100,
43294
43634
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -48619,20 +48959,21 @@ var init_SplitPane = __esm({
48619
48959
  const [ratio, setRatio] = useState(initialRatio);
48620
48960
  const containerRef = useRef(null);
48621
48961
  const isDragging = useRef(false);
48622
- const handleMouseDown = useCallback(
48962
+ const handlePointerDown = useCallback(
48623
48963
  (e) => {
48624
48964
  if (!resizable) return;
48625
48965
  e.preventDefault();
48626
48966
  isDragging.current = true;
48627
- const handleMouseMove = (e2) => {
48967
+ e.currentTarget.setPointerCapture(e.pointerId);
48968
+ const handlePointerMove = (ev) => {
48628
48969
  if (!isDragging.current || !containerRef.current) return;
48629
48970
  const rect = containerRef.current.getBoundingClientRect();
48630
48971
  let newRatio;
48631
48972
  if (direction === "horizontal") {
48632
- const x = e2.clientX - rect.left;
48973
+ const x = ev.clientX - rect.left;
48633
48974
  newRatio = x / rect.width * 100;
48634
48975
  } else {
48635
- const y = e2.clientY - rect.top;
48976
+ const y = ev.clientY - rect.top;
48636
48977
  newRatio = y / rect.height * 100;
48637
48978
  }
48638
48979
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -48640,13 +48981,15 @@ var init_SplitPane = __esm({
48640
48981
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
48641
48982
  setRatio(newRatio);
48642
48983
  };
48643
- const handleMouseUp = () => {
48984
+ const handlePointerUp = () => {
48644
48985
  isDragging.current = false;
48645
- document.removeEventListener("mousemove", handleMouseMove);
48646
- document.removeEventListener("mouseup", handleMouseUp);
48986
+ document.removeEventListener("pointermove", handlePointerMove);
48987
+ document.removeEventListener("pointerup", handlePointerUp);
48988
+ document.removeEventListener("pointercancel", handlePointerUp);
48647
48989
  };
48648
- document.addEventListener("mousemove", handleMouseMove);
48649
- document.addEventListener("mouseup", handleMouseUp);
48990
+ document.addEventListener("pointermove", handlePointerMove);
48991
+ document.addEventListener("pointerup", handlePointerUp);
48992
+ document.addEventListener("pointercancel", handlePointerUp);
48650
48993
  },
48651
48994
  [direction, minSize, resizable]
48652
48995
  );
@@ -48675,9 +49018,9 @@ var init_SplitPane = __esm({
48675
49018
  resizable && /* @__PURE__ */ jsx(
48676
49019
  "div",
48677
49020
  {
48678
- onMouseDown: handleMouseDown,
49021
+ onPointerDown: handlePointerDown,
48679
49022
  className: cn(
48680
- "flex-shrink-0 bg-border transition-colors",
49023
+ "flex-shrink-0 bg-border transition-colors touch-none",
48681
49024
  isHorizontal ? "w-1 cursor-col-resize hover:w-1.5 hover:bg-muted-foreground" : "h-1 cursor-row-resize hover:h-1.5 hover:bg-muted-foreground"
48682
49025
  )
48683
49026
  }