@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
@@ -335,12 +335,60 @@ var init_useEventBus = __esm({
335
335
  useEventBus_default = useEventBus;
336
336
  }
337
337
  });
338
+ function useTapReveal(options = {}) {
339
+ const { onReveal, onDismiss, refs, enabled = true } = options;
340
+ const [revealed, setRevealed] = React84.useState(false);
341
+ const onRevealRef = React84.useRef(onReveal);
342
+ const onDismissRef = React84.useRef(onDismiss);
343
+ onRevealRef.current = onReveal;
344
+ onDismissRef.current = onDismiss;
345
+ const reveal = React84.useCallback(() => {
346
+ setRevealed((wasRevealed) => {
347
+ if (!wasRevealed) onRevealRef.current?.();
348
+ return true;
349
+ });
350
+ }, []);
351
+ const dismiss = React84.useCallback(() => {
352
+ setRevealed((wasRevealed) => {
353
+ if (wasRevealed) onDismissRef.current?.();
354
+ return false;
355
+ });
356
+ }, []);
357
+ const onPointerDown = React84.useCallback(
358
+ (e) => {
359
+ if (!enabled || e.pointerType === "mouse") return;
360
+ if (revealed) dismiss();
361
+ else reveal();
362
+ },
363
+ [enabled, revealed, reveal, dismiss]
364
+ );
365
+ React84.useEffect(() => {
366
+ if (!revealed || typeof document === "undefined") return;
367
+ const onDocDown = (ev) => {
368
+ const target = ev.target;
369
+ if (!(target instanceof Node)) return;
370
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
371
+ if (!inside) dismiss();
372
+ };
373
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
374
+ return () => {
375
+ clearTimeout(id);
376
+ document.removeEventListener("pointerdown", onDocDown);
377
+ };
378
+ }, [revealed, refs, dismiss]);
379
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
380
+ }
381
+ var init_useTapReveal = __esm({
382
+ "hooks/useTapReveal.ts"() {
383
+ }
384
+ });
338
385
  var paddingStyles, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles, displayStyles, overflowStyles, positionStyles, Box;
339
386
  var init_Box = __esm({
340
387
  "components/core/atoms/Box.tsx"() {
341
388
  "use client";
342
389
  init_cn();
343
390
  init_useEventBus();
391
+ init_useTapReveal();
344
392
  paddingStyles = {
345
393
  none: "p-0",
346
394
  xs: "p-1",
@@ -466,10 +514,12 @@ var init_Box = __esm({
466
514
  action,
467
515
  actionPayload,
468
516
  hoverEvent,
517
+ tapReveal = true,
469
518
  maxWidth,
470
519
  onClick,
471
520
  onMouseEnter,
472
521
  onMouseLeave,
522
+ onPointerDown,
473
523
  ...rest
474
524
  }, ref) => {
475
525
  const eventBus = useEventBus();
@@ -492,6 +542,19 @@ var init_Box = __esm({
492
542
  }
493
543
  onMouseLeave?.(e);
494
544
  }, [hoverEvent, eventBus, onMouseLeave]);
545
+ const { triggerProps } = useTapReveal({
546
+ enabled: tapReveal && !!hoverEvent,
547
+ onReveal: React84.useCallback(() => {
548
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
549
+ }, [hoverEvent, eventBus]),
550
+ onDismiss: React84.useCallback(() => {
551
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
552
+ }, [hoverEvent, eventBus])
553
+ });
554
+ const handlePointerDown = React84.useCallback((e) => {
555
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
556
+ onPointerDown?.(e);
557
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
495
558
  const isClickable = action || onClick;
496
559
  return React84__namespace.default.createElement(
497
560
  Component2,
@@ -519,6 +582,7 @@ var init_Box = __esm({
519
582
  onClick: isClickable ? handleClick : void 0,
520
583
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
521
584
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
585
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
522
586
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
523
587
  ...rest
524
588
  },
@@ -4929,6 +4993,11 @@ var init_TextHighlight = __esm({
4929
4993
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
4930
4994
  onMouseLeave?.();
4931
4995
  },
4996
+ onPointerDown: (e) => {
4997
+ if (e.pointerType === "mouse") return;
4998
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
4999
+ onMouseEnter?.();
5000
+ },
4932
5001
  role: "button",
4933
5002
  tabIndex: 0,
4934
5003
  onKeyDown: (e) => {
@@ -5104,6 +5173,7 @@ var init_LawReferenceTooltip = __esm({
5104
5173
  init_Typography();
5105
5174
  init_Divider();
5106
5175
  init_cn();
5176
+ init_useTapReveal();
5107
5177
  positionStyles2 = {
5108
5178
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
5109
5179
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -5125,6 +5195,7 @@ var init_LawReferenceTooltip = __esm({
5125
5195
  const { t } = hooks.useTranslate();
5126
5196
  const [isVisible, setIsVisible] = React84__namespace.default.useState(false);
5127
5197
  const timeoutRef = React84__namespace.default.useRef(null);
5198
+ const triggerRef = React84__namespace.default.useRef(null);
5128
5199
  const handleMouseEnter = () => {
5129
5200
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5130
5201
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -5133,6 +5204,8 @@ var init_LawReferenceTooltip = __esm({
5133
5204
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5134
5205
  setIsVisible(false);
5135
5206
  };
5207
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
5208
+ const open = isVisible || revealed;
5136
5209
  React84__namespace.default.useEffect(() => {
5137
5210
  return () => {
5138
5211
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -5141,6 +5214,7 @@ var init_LawReferenceTooltip = __esm({
5141
5214
  return /* @__PURE__ */ jsxRuntime.jsxs(
5142
5215
  Box,
5143
5216
  {
5217
+ ref: triggerRef,
5144
5218
  as: "span",
5145
5219
  position: "relative",
5146
5220
  display: "inline-block",
@@ -5149,9 +5223,10 @@ var init_LawReferenceTooltip = __esm({
5149
5223
  onMouseLeave: handleMouseLeave,
5150
5224
  onFocus: handleMouseEnter,
5151
5225
  onBlur: handleMouseLeave,
5226
+ onPointerDown: triggerProps.onPointerDown,
5152
5227
  children: [
5153
5228
  children,
5154
- isVisible && /* @__PURE__ */ jsxRuntime.jsxs(
5229
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
5155
5230
  Box,
5156
5231
  {
5157
5232
  padding: "sm",
@@ -10567,6 +10642,36 @@ function useCamera() {
10567
10642
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
10568
10643
  drawFn?.();
10569
10644
  }, []);
10645
+ const handlePointerDown = React84.useCallback((e) => {
10646
+ handleMouseDown(e);
10647
+ }, [handleMouseDown]);
10648
+ const handlePointerUp = React84.useCallback(() => {
10649
+ handleMouseUp();
10650
+ }, [handleMouseUp]);
10651
+ const handlePointerMove = React84.useCallback((e, drawFn) => {
10652
+ return handleMouseMove(e, drawFn);
10653
+ }, [handleMouseMove]);
10654
+ const panBy = React84.useCallback((dx, dy, drawFn) => {
10655
+ cameraRef.current.x -= dx;
10656
+ cameraRef.current.y -= dy;
10657
+ targetCameraRef.current = null;
10658
+ drawFn?.();
10659
+ }, []);
10660
+ const zoomAtPoint = React84.useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
10661
+ const cam = cameraRef.current;
10662
+ const oldZoom = cam.zoom;
10663
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
10664
+ if (newZoom === oldZoom) {
10665
+ drawFn?.();
10666
+ return;
10667
+ }
10668
+ const inv = 1 / oldZoom - 1 / newZoom;
10669
+ cam.x += (centerX - viewportSize.width / 2) * inv;
10670
+ cam.y += (centerY - viewportSize.height / 2) * inv;
10671
+ cam.zoom = newZoom;
10672
+ targetCameraRef.current = null;
10673
+ drawFn?.();
10674
+ }, []);
10570
10675
  const screenToWorld = React84.useCallback((clientX, clientY, canvas, viewportSize) => {
10571
10676
  const rect = canvas.getBoundingClientRect();
10572
10677
  const screenX = clientX - rect.left;
@@ -10598,6 +10703,11 @@ function useCamera() {
10598
10703
  handleMouseMove,
10599
10704
  handleMouseLeave,
10600
10705
  handleWheel,
10706
+ handlePointerDown,
10707
+ handlePointerUp,
10708
+ handlePointerMove,
10709
+ panBy,
10710
+ zoomAtPoint,
10601
10711
  screenToWorld,
10602
10712
  lerpToTarget
10603
10713
  };
@@ -10607,6 +10717,113 @@ var init_useCamera = __esm({
10607
10717
  "use client";
10608
10718
  }
10609
10719
  });
10720
+ function localPoint(canvas, clientX, clientY) {
10721
+ if (!canvas) return { x: clientX, y: clientY };
10722
+ const rect = canvas.getBoundingClientRect();
10723
+ return { x: clientX - rect.left, y: clientY - rect.top };
10724
+ }
10725
+ function useCanvasGestures(options) {
10726
+ const {
10727
+ canvasRef,
10728
+ enabled = true,
10729
+ wheelStep = 1.1,
10730
+ onPointerDown,
10731
+ onPointerMove,
10732
+ onPointerUp,
10733
+ onZoom,
10734
+ onPanDelta,
10735
+ onMultiTouchStart
10736
+ } = options;
10737
+ const pointers = React84.useRef(/* @__PURE__ */ new Map());
10738
+ const pinch = React84.useRef(null);
10739
+ const computePinch = React84.useCallback(() => {
10740
+ const pts = [...pointers.current.values()];
10741
+ const dx = pts[1].x - pts[0].x;
10742
+ const dy = pts[1].y - pts[0].y;
10743
+ return {
10744
+ distance: Math.hypot(dx, dy) || 1,
10745
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
10746
+ };
10747
+ }, []);
10748
+ const handlePointerDown = React84.useCallback(
10749
+ (e) => {
10750
+ if (!enabled) return;
10751
+ e.currentTarget.setPointerCapture(e.pointerId);
10752
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
10753
+ if (pointers.current.size === 2) {
10754
+ onMultiTouchStart?.();
10755
+ pinch.current = computePinch();
10756
+ } else if (pointers.current.size === 1) {
10757
+ onPointerDown?.(e);
10758
+ }
10759
+ },
10760
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
10761
+ );
10762
+ const handlePointerMove = React84.useCallback(
10763
+ (e) => {
10764
+ if (!enabled) return;
10765
+ if (pointers.current.has(e.pointerId)) {
10766
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
10767
+ }
10768
+ if (pointers.current.size >= 2 && pinch.current) {
10769
+ const next = computePinch();
10770
+ const prev = pinch.current;
10771
+ if (next.distance !== prev.distance) {
10772
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
10773
+ }
10774
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
10775
+ pinch.current = next;
10776
+ return;
10777
+ }
10778
+ onPointerMove?.(e);
10779
+ },
10780
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
10781
+ );
10782
+ const endPointer = React84.useCallback(
10783
+ (e, fireUp) => {
10784
+ const wasMulti = pointers.current.size >= 2;
10785
+ pointers.current.delete(e.pointerId);
10786
+ if (pointers.current.size < 2) pinch.current = null;
10787
+ if (wasMulti && pointers.current.size === 1) return;
10788
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
10789
+ },
10790
+ [onPointerUp]
10791
+ );
10792
+ const handlePointerUp = React84.useCallback(
10793
+ (e) => {
10794
+ if (!enabled) return;
10795
+ endPointer(e, true);
10796
+ },
10797
+ [enabled, endPointer]
10798
+ );
10799
+ const handlePointerCancel = React84.useCallback(
10800
+ (e) => {
10801
+ if (!enabled) return;
10802
+ endPointer(e, false);
10803
+ },
10804
+ [enabled, endPointer]
10805
+ );
10806
+ const handleWheel = React84.useCallback(
10807
+ (e) => {
10808
+ if (!enabled) return;
10809
+ e.preventDefault();
10810
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
10811
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
10812
+ },
10813
+ [enabled, canvasRef, wheelStep, onZoom]
10814
+ );
10815
+ return {
10816
+ onPointerDown: handlePointerDown,
10817
+ onPointerMove: handlePointerMove,
10818
+ onPointerUp: handlePointerUp,
10819
+ onPointerCancel: handlePointerCancel,
10820
+ onWheel: handleWheel
10821
+ };
10822
+ }
10823
+ var init_useCanvasGestures = __esm({
10824
+ "hooks/useCanvasGestures.ts"() {
10825
+ }
10826
+ });
10610
10827
  function unitAtlasUrl(unit) {
10611
10828
  if (unit.spriteSheet) return unit.spriteSheet;
10612
10829
  const sprite = unit.sprite;
@@ -10985,11 +11202,12 @@ function IsometricCanvas({
10985
11202
  cameraRef,
10986
11203
  targetCameraRef,
10987
11204
  dragDistance,
10988
- handleMouseDown,
10989
- handleMouseUp,
10990
- handleMouseMove,
10991
11205
  handleMouseLeave,
10992
- handleWheel,
11206
+ handlePointerDown,
11207
+ handlePointerUp,
11208
+ handlePointerMove,
11209
+ panBy,
11210
+ zoomAtPoint,
10993
11211
  screenToWorld,
10994
11212
  lerpToTarget
10995
11213
  } = useCamera();
@@ -11424,11 +11642,16 @@ function IsometricCanvas({
11424
11642
  cancelAnimationFrame(rafIdRef.current);
11425
11643
  };
11426
11644
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
11427
- const handleMouseMoveWithCamera = React84.useCallback((e) => {
11428
- if (enableCamera) {
11429
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
11430
- if (wasPanning) return;
11431
- }
11645
+ const singlePointerActiveRef = React84.useRef(false);
11646
+ const handleCanvasPointerDown = React84.useCallback((e) => {
11647
+ singlePointerActiveRef.current = true;
11648
+ if (enableCamera) handlePointerDown(e);
11649
+ }, [enableCamera, handlePointerDown]);
11650
+ const handleCanvasPointerMove = React84.useCallback((e) => {
11651
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
11652
+ }, [enableCamera, handlePointerMove, draw]);
11653
+ const handleCanvasHover = React84.useCallback((e) => {
11654
+ if (singlePointerActiveRef.current) return;
11432
11655
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
11433
11656
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
11434
11657
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -11439,26 +11662,10 @@ function IsometricCanvas({
11439
11662
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
11440
11663
  onTileHover?.(isoPos.x, isoPos.y);
11441
11664
  }
11442
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11443
- const handleMouseLeaveWithCamera = React84.useCallback(() => {
11444
- handleMouseLeave();
11445
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11446
- onTileLeave?.();
11447
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11448
- const handleWheelWithCamera = React84.useCallback((e) => {
11449
- if (enableCamera) {
11450
- handleWheel(e, () => draw(animTimeRef.current));
11451
- }
11452
- }, [enableCamera, handleWheel, draw]);
11453
- React84.useEffect(() => {
11454
- const canvas = canvasRef.current;
11455
- if (!canvas) return;
11456
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
11457
- return () => {
11458
- canvas.removeEventListener("wheel", handleWheelWithCamera);
11459
- };
11460
- }, [handleWheelWithCamera]);
11461
- const handleClick = React84.useCallback((e) => {
11665
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11666
+ const handleCanvasPointerUp = React84.useCallback((e) => {
11667
+ singlePointerActiveRef.current = false;
11668
+ if (enableCamera) handlePointerUp();
11462
11669
  if (dragDistance() > 5) return;
11463
11670
  if (!canvasRef.current) return;
11464
11671
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -11476,7 +11683,32 @@ function IsometricCanvas({
11476
11683
  onTileClick?.(isoPos.x, isoPos.y);
11477
11684
  }
11478
11685
  }
11479
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11686
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11687
+ const handleCanvasPointerLeave = React84.useCallback(() => {
11688
+ handleMouseLeave();
11689
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11690
+ onTileLeave?.();
11691
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11692
+ const applyZoom = React84.useCallback((factor, centerX, centerY) => {
11693
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
11694
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
11695
+ const applyPanDelta = React84.useCallback((dx, dy) => {
11696
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
11697
+ }, [enableCamera, panBy, draw]);
11698
+ const cancelSinglePointer = React84.useCallback(() => {
11699
+ singlePointerActiveRef.current = false;
11700
+ if (enableCamera) handlePointerUp();
11701
+ }, [enableCamera, handlePointerUp]);
11702
+ const gestureHandlers = useCanvasGestures({
11703
+ canvasRef,
11704
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
11705
+ onPointerDown: handleCanvasPointerDown,
11706
+ onPointerMove: handleCanvasPointerMove,
11707
+ onPointerUp: handleCanvasPointerUp,
11708
+ onZoom: applyZoom,
11709
+ onPanDelta: applyPanDelta,
11710
+ onMultiTouchStart: cancelSinglePointer
11711
+ });
11480
11712
  if (error) {
11481
11713
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
11482
11714
  }
@@ -11508,13 +11740,17 @@ function IsometricCanvas({
11508
11740
  {
11509
11741
  ref: canvasRef,
11510
11742
  "data-testid": "game-canvas",
11511
- onClick: handleClick,
11512
- onMouseDown: enableCamera ? handleMouseDown : void 0,
11513
- onMouseMove: handleMouseMoveWithCamera,
11514
- onMouseUp: enableCamera ? handleMouseUp : void 0,
11515
- onMouseLeave: handleMouseLeaveWithCamera,
11743
+ onPointerDown: gestureHandlers.onPointerDown,
11744
+ onPointerMove: (e) => {
11745
+ gestureHandlers.onPointerMove(e);
11746
+ handleCanvasHover(e);
11747
+ },
11748
+ onPointerUp: gestureHandlers.onPointerUp,
11749
+ onPointerCancel: gestureHandlers.onPointerCancel,
11750
+ onPointerLeave: handleCanvasPointerLeave,
11751
+ onWheel: gestureHandlers.onWheel,
11516
11752
  onContextMenu: (e) => e.preventDefault(),
11517
- className: "cursor-pointer",
11753
+ className: "cursor-pointer touch-none",
11518
11754
  style: {
11519
11755
  width: viewportSize.width,
11520
11756
  height: viewportSize.height
@@ -11568,6 +11804,7 @@ var init_IsometricCanvas = __esm({
11568
11804
  init_ErrorState();
11569
11805
  init_useImageCache();
11570
11806
  init_useCamera();
11807
+ init_useCanvasGestures();
11571
11808
  init_useUnitSpriteAtlas();
11572
11809
  init_verificationRegistry();
11573
11810
  init_isometric();
@@ -14683,6 +14920,10 @@ var init_StateMachineView = __esm({
14683
14920
  const handleMouseLeave2 = () => {
14684
14921
  onHover(null, 0, 0);
14685
14922
  };
14923
+ const handlePointerDown2 = (e) => {
14924
+ if (e.pointerType === "mouse") return;
14925
+ handleMouseEnter2();
14926
+ };
14686
14927
  return /* @__PURE__ */ jsxRuntime.jsxs(
14687
14928
  "g",
14688
14929
  {
@@ -14692,6 +14933,7 @@ var init_StateMachineView = __esm({
14692
14933
  onClick: () => onClick?.(bundle),
14693
14934
  onMouseEnter: handleMouseEnter2,
14694
14935
  onMouseLeave: handleMouseLeave2,
14936
+ onPointerDown: handlePointerDown2,
14695
14937
  style: { pointerEvents: "auto" },
14696
14938
  children: [
14697
14939
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -14809,6 +15051,10 @@ var init_StateMachineView = __esm({
14809
15051
  const handleMouseLeave = React84.useCallback(() => {
14810
15052
  onHover(null, 0, 0);
14811
15053
  }, [onHover]);
15054
+ const handlePointerDown = React84.useCallback((e) => {
15055
+ if (e.pointerType === "mouse") return;
15056
+ handleMouseEnter();
15057
+ }, [handleMouseEnter]);
14812
15058
  const uniqueMarkerId = `arrow-${bundle.id}`;
14813
15059
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
14814
15060
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -14820,6 +15066,7 @@ var init_StateMachineView = __esm({
14820
15066
  onClick: () => onClick?.(bundle),
14821
15067
  onMouseEnter: handleMouseEnter,
14822
15068
  onMouseLeave: handleMouseLeave,
15069
+ onPointerDown: handlePointerDown,
14823
15070
  style: { pointerEvents: "auto" },
14824
15071
  children: [
14825
15072
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -23318,10 +23565,84 @@ function SubMenu({
23318
23565
  );
23319
23566
  return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
23320
23567
  }
23568
+ function MenuItemRow({
23569
+ item,
23570
+ itemId,
23571
+ hasSubMenu,
23572
+ isDanger,
23573
+ direction,
23574
+ isSubMenuOpen,
23575
+ activeSubMenuRef,
23576
+ eventBus,
23577
+ onItemClick,
23578
+ openSubMenu
23579
+ }) {
23580
+ const rowRef = React84.useRef(null);
23581
+ const { triggerProps } = useTapReveal({
23582
+ enabled: hasSubMenu,
23583
+ onReveal: () => openSubMenu(itemId, rowRef.current),
23584
+ refs: [rowRef]
23585
+ });
23586
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
23587
+ /* @__PURE__ */ jsxRuntime.jsx(
23588
+ Box,
23589
+ {
23590
+ ref: rowRef,
23591
+ as: "button",
23592
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
23593
+ "aria-disabled": item.disabled || void 0,
23594
+ onMouseEnter: (e) => {
23595
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
23596
+ },
23597
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
23598
+ "data-testid": item.event ? `action-${item.event}` : void 0,
23599
+ className: cn(
23600
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23601
+ "text-sm transition-colors",
23602
+ "hover:bg-muted",
23603
+ "focus:outline-none focus:bg-muted",
23604
+ "disabled:opacity-50 disabled:cursor-not-allowed",
23605
+ item.disabled && "cursor-not-allowed",
23606
+ isDanger && "text-error hover:bg-error/10"
23607
+ ),
23608
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23609
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
23610
+ /* @__PURE__ */ jsxRuntime.jsx(
23611
+ Typography,
23612
+ {
23613
+ variant: "small",
23614
+ className: cn("flex-1", isDanger && "text-red-600"),
23615
+ children: item.label
23616
+ }
23617
+ ),
23618
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23619
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
23620
+ Icon,
23621
+ {
23622
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
23623
+ size: "sm",
23624
+ className: "flex-shrink-0"
23625
+ }
23626
+ )
23627
+ ] })
23628
+ }
23629
+ ),
23630
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
23631
+ SubMenu,
23632
+ {
23633
+ items: item.subMenu,
23634
+ itemRef: activeSubMenuRef,
23635
+ direction,
23636
+ eventBus
23637
+ }
23638
+ )
23639
+ ] });
23640
+ }
23321
23641
  var MENU_GAP, menuContainerStyles, Menu;
23322
23642
  var init_Menu = __esm({
23323
23643
  "components/core/molecules/Menu.tsx"() {
23324
23644
  "use client";
23645
+ init_useTapReveal();
23325
23646
  init_Box();
23326
23647
  init_Icon();
23327
23648
  init_Divider();
@@ -23376,6 +23697,10 @@ var init_Menu = __esm({
23376
23697
  setIsOpen(false);
23377
23698
  }
23378
23699
  };
23700
+ const openSubMenu = (itemId, el) => {
23701
+ setActiveSubMenu(itemId);
23702
+ setActiveSubMenuRef(el);
23703
+ };
23379
23704
  React84.useEffect(() => {
23380
23705
  if (isOpen) {
23381
23706
  updatePosition();
@@ -23419,61 +23744,22 @@ var init_Menu = __esm({
23419
23744
  if (isDivider) {
23420
23745
  return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
23421
23746
  }
23422
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
23423
- /* @__PURE__ */ jsxRuntime.jsx(
23424
- Box,
23425
- {
23426
- as: "button",
23427
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
23428
- "aria-disabled": item.disabled || void 0,
23429
- onMouseEnter: (e) => {
23430
- if (hasSubMenu) {
23431
- setActiveSubMenu(itemId);
23432
- setActiveSubMenuRef(e.currentTarget);
23433
- }
23434
- },
23435
- "data-testid": item.event ? `action-${item.event}` : void 0,
23436
- className: cn(
23437
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23438
- "text-sm transition-colors",
23439
- "hover:bg-muted",
23440
- "focus:outline-none focus:bg-muted",
23441
- "disabled:opacity-50 disabled:cursor-not-allowed",
23442
- item.disabled && "cursor-not-allowed",
23443
- isDanger && "text-error hover:bg-error/10"
23444
- ),
23445
- children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23446
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
23447
- /* @__PURE__ */ jsxRuntime.jsx(
23448
- Typography,
23449
- {
23450
- variant: "small",
23451
- className: cn("flex-1", isDanger && "text-red-600"),
23452
- children: item.label
23453
- }
23454
- ),
23455
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23456
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
23457
- Icon,
23458
- {
23459
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
23460
- size: "sm",
23461
- className: "flex-shrink-0"
23462
- }
23463
- )
23464
- ] })
23465
- }
23466
- ),
23467
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
23468
- SubMenu,
23469
- {
23470
- items: item.subMenu,
23471
- itemRef: activeSubMenuRef,
23472
- direction,
23473
- eventBus
23474
- }
23475
- )
23476
- ] }, itemId);
23747
+ return /* @__PURE__ */ jsxRuntime.jsx(
23748
+ MenuItemRow,
23749
+ {
23750
+ item,
23751
+ itemId,
23752
+ hasSubMenu,
23753
+ isDanger,
23754
+ direction,
23755
+ isSubMenuOpen: activeSubMenu === itemId,
23756
+ activeSubMenuRef,
23757
+ eventBus,
23758
+ onItemClick: handleItemClick,
23759
+ openSubMenu
23760
+ },
23761
+ itemId
23762
+ );
23477
23763
  });
23478
23764
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
23479
23765
  "div",
@@ -25776,6 +26062,7 @@ var init_Popover = __esm({
25776
26062
  "use client";
25777
26063
  init_Typography();
25778
26064
  init_cn();
26065
+ init_useTapReveal();
25779
26066
  arrowClasses = {
25780
26067
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
25781
26068
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -25846,18 +26133,32 @@ var init_Popover = __esm({
25846
26133
  document.addEventListener("mousedown", handleClickOutside);
25847
26134
  return () => document.removeEventListener("mousedown", handleClickOutside);
25848
26135
  }, [isOpen, trigger]);
25849
- const triggerProps = trigger === "click" ? {
26136
+ const { triggerProps: tapTriggerProps } = useTapReveal({
26137
+ enabled: trigger === "hover",
26138
+ onReveal: handleOpen,
26139
+ onDismiss: handleClose,
26140
+ refs: [triggerRef, popoverRef]
26141
+ });
26142
+ const handlerProps = trigger === "click" ? {
25850
26143
  onClick: handleToggle
25851
26144
  } : {
25852
26145
  onMouseEnter: handleOpen,
25853
- onMouseLeave: handleClose
26146
+ onMouseLeave: handleClose,
26147
+ onPointerDown: tapTriggerProps.onPointerDown
25854
26148
  };
25855
26149
  const childElement = React84__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
26150
+ const childPointerDown = childElement.props.onPointerDown;
25856
26151
  const triggerElement = React84__namespace.default.cloneElement(
25857
26152
  childElement,
25858
26153
  {
25859
26154
  ref: triggerRef,
25860
- ...triggerProps
26155
+ ...handlerProps,
26156
+ ...trigger === "hover" ? {
26157
+ onPointerDown: (e) => {
26158
+ tapTriggerProps.onPointerDown(e);
26159
+ childPointerDown?.(e);
26160
+ }
26161
+ } : void 0
25861
26162
  }
25862
26163
  );
25863
26164
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -26388,6 +26689,7 @@ var init_Tooltip = __esm({
26388
26689
  "use client";
26389
26690
  init_Typography();
26390
26691
  init_cn();
26692
+ init_useTapReveal();
26391
26693
  TRIGGER_GAP2 = 8;
26392
26694
  arrowClasses2 = {
26393
26695
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -26432,6 +26734,11 @@ var init_Tooltip = __esm({
26432
26734
  setIsVisible(false);
26433
26735
  }, hideDelay);
26434
26736
  };
26737
+ const { triggerProps } = useTapReveal({
26738
+ onReveal: handleMouseEnter,
26739
+ onDismiss: handleMouseLeave,
26740
+ refs: [triggerRef, tooltipRef]
26741
+ });
26435
26742
  React84.useLayoutEffect(() => {
26436
26743
  if (isVisible) {
26437
26744
  updatePosition();
@@ -26444,12 +26751,17 @@ var init_Tooltip = __esm({
26444
26751
  };
26445
26752
  }, []);
26446
26753
  const triggerElement = React84__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
26754
+ const childPointerDown = triggerElement.props.onPointerDown;
26447
26755
  const trigger = React84__namespace.default.cloneElement(triggerElement, {
26448
26756
  ref: triggerRef,
26449
26757
  onMouseEnter: handleMouseEnter,
26450
26758
  onMouseLeave: handleMouseLeave,
26451
26759
  onFocus: handleMouseEnter,
26452
- onBlur: handleMouseLeave
26760
+ onBlur: handleMouseLeave,
26761
+ onPointerDown: (e) => {
26762
+ triggerProps.onPointerDown(e);
26763
+ childPointerDown?.(e);
26764
+ }
26453
26765
  });
26454
26766
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
26455
26767
  "div",
@@ -29799,6 +30111,14 @@ var init_GraphView = __esm({
29799
30111
  },
29800
30112
  [onNodeClick]
29801
30113
  );
30114
+ const handleNodePointerDown = React84.useCallback(
30115
+ (e, node) => {
30116
+ if (e.pointerType === "mouse") return;
30117
+ handleNodeMouseEnter(node);
30118
+ handleNodeClickInternal(node);
30119
+ },
30120
+ [handleNodeMouseEnter, handleNodeClickInternal]
30121
+ );
29802
30122
  if (nodes.length === 0) {
29803
30123
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("flex items-center justify-center", className), style: { width: w, height: h }, children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-muted-foreground text-sm", children: t("display.noGraphData") }) });
29804
30124
  }
@@ -29858,6 +30178,7 @@ var init_GraphView = __esm({
29858
30178
  onMouseEnter: () => handleNodeMouseEnter(node),
29859
30179
  onMouseLeave: handleNodeMouseLeave,
29860
30180
  onClick: () => handleNodeClickInternal(node),
30181
+ onPointerDown: (e) => handleNodePointerDown(e, node),
29861
30182
  children: [
29862
30183
  /* @__PURE__ */ jsxRuntime.jsx(
29863
30184
  "circle",
@@ -29938,13 +30259,13 @@ var init_MapView = __esm({
29938
30259
  shadowSize: [41, 41]
29939
30260
  });
29940
30261
  L.Marker.prototype.options.icon = defaultIcon;
29941
- const { useEffect: useEffect83, useRef: useRef78, useCallback: useCallback124, useState: useState114 } = React84__namespace.default;
30262
+ const { useEffect: useEffect84, useRef: useRef80, useCallback: useCallback126, useState: useState115 } = React84__namespace.default;
29942
30263
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29943
30264
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29944
30265
  function MapUpdater({ centerLat, centerLng, zoom }) {
29945
30266
  const map = useMap();
29946
- const prevRef = useRef78({ centerLat, centerLng, zoom });
29947
- useEffect83(() => {
30267
+ const prevRef = useRef80({ centerLat, centerLng, zoom });
30268
+ useEffect84(() => {
29948
30269
  const prev = prevRef.current;
29949
30270
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29950
30271
  map.setView([centerLat, centerLng], zoom);
@@ -29955,7 +30276,7 @@ var init_MapView = __esm({
29955
30276
  }
29956
30277
  function MapClickHandler({ onMapClick }) {
29957
30278
  const map = useMap();
29958
- useEffect83(() => {
30279
+ useEffect84(() => {
29959
30280
  if (!onMapClick) return;
29960
30281
  const handler = (e) => {
29961
30282
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29983,8 +30304,8 @@ var init_MapView = __esm({
29983
30304
  showAttribution = true
29984
30305
  }) {
29985
30306
  const eventBus = useEventBus4();
29986
- const [clickedPosition, setClickedPosition] = useState114(null);
29987
- const handleMapClick = useCallback124((lat, lng) => {
30307
+ const [clickedPosition, setClickedPosition] = useState115(null);
30308
+ const handleMapClick = useCallback126((lat, lng) => {
29988
30309
  if (showClickedPin) {
29989
30310
  setClickedPosition({ lat, lng });
29990
30311
  }
@@ -29993,7 +30314,7 @@ var init_MapView = __esm({
29993
30314
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29994
30315
  }
29995
30316
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29996
- const handleMarkerClick = useCallback124((marker) => {
30317
+ const handleMarkerClick = useCallback126((marker) => {
29997
30318
  onMarkerClick?.(marker);
29998
30319
  if (markerClickEvent) {
29999
30320
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30334,7 +30655,7 @@ var init_StarRating = __esm({
30334
30655
  "aria-valuenow": value,
30335
30656
  tabIndex: readOnly ? void 0 : 0,
30336
30657
  onKeyDown: handleKeyDown,
30337
- onMouseLeave: () => setHoverValue(null),
30658
+ onPointerLeave: () => setHoverValue(null),
30338
30659
  children: Array.from({ length: max }, (_, i) => {
30339
30660
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
30340
30661
  const isFull = fillLevel >= 1;
@@ -30344,7 +30665,7 @@ var init_StarRating = __esm({
30344
30665
  {
30345
30666
  className: "relative inline-block",
30346
30667
  onClick: () => handleStarClick(i, false),
30347
- onMouseMove: (e) => {
30668
+ onPointerMove: (e) => {
30348
30669
  if (readOnly) return;
30349
30670
  const rect = e.currentTarget.getBoundingClientRect();
30350
30671
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -34234,7 +34555,7 @@ var init_PositionedCanvas = __esm({
34234
34555
  {
34235
34556
  ref: containerRef,
34236
34557
  "data-testid": "positioned-canvas",
34237
- className: "relative bg-background border border-border rounded-container overflow-hidden",
34558
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
34238
34559
  style: { width, height },
34239
34560
  onClick: handleContainerClick,
34240
34561
  children: items.map((item) => {
@@ -38403,6 +38724,7 @@ var init_GraphCanvas = __esm({
38403
38724
  init_ErrorState();
38404
38725
  init_EmptyState();
38405
38726
  init_useEventBus();
38727
+ init_useCanvasGestures();
38406
38728
  GROUP_COLORS2 = [
38407
38729
  "var(--color-primary)",
38408
38730
  "var(--color-success)",
@@ -38440,6 +38762,10 @@ var init_GraphCanvas = __esm({
38440
38762
  const animRef = React84.useRef(0);
38441
38763
  const [zoom, setZoom] = React84.useState(1);
38442
38764
  const [offset, setOffset] = React84.useState({ x: 0, y: 0 });
38765
+ const zoomRef = React84.useRef(zoom);
38766
+ zoomRef.current = zoom;
38767
+ const offsetRef = React84.useRef(offset);
38768
+ offsetRef.current = offset;
38443
38769
  const [hoveredNode, setHoveredNode] = React84.useState(null);
38444
38770
  const nodesRef = React84.useRef([]);
38445
38771
  const [, forceUpdate] = React84.useState(0);
@@ -38699,25 +39025,27 @@ var init_GraphCanvas = __esm({
38699
39025
  setZoom(1);
38700
39026
  setOffset({ x: 0, y: 0 });
38701
39027
  }, []);
38702
- const handleWheel = React84.useCallback(
38703
- (e) => {
38704
- if (!interactive) return;
38705
- e.preventDefault();
38706
- const coords = toCoords(e);
38707
- if (!coords) return;
38708
- const oldZoom = zoom;
38709
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38710
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38711
- if (newZoom === oldZoom) return;
38712
- setOffset((o) => ({
38713
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38714
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38715
- }));
38716
- setZoom(newZoom);
38717
- },
38718
- [interactive, toCoords, zoom]
38719
- );
38720
- const handleMouseDown = React84.useCallback(
39028
+ const applyZoom = React84.useCallback((factor, cx, cy) => {
39029
+ if (!interactive) return;
39030
+ const oldZoom = zoomRef.current;
39031
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39032
+ if (newZoom === oldZoom) return;
39033
+ const o = offsetRef.current;
39034
+ setOffset({
39035
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
39036
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
39037
+ });
39038
+ setZoom(newZoom);
39039
+ }, [interactive]);
39040
+ const applyPanDelta = React84.useCallback((dx, dy) => {
39041
+ if (!interactive) return;
39042
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
39043
+ }, [interactive]);
39044
+ const cancelSinglePointer = React84.useCallback(() => {
39045
+ interactionRef.current.mode = "none";
39046
+ interactionRef.current.dragNodeId = null;
39047
+ }, []);
39048
+ const handlePointerDown = React84.useCallback(
38721
39049
  (e) => {
38722
39050
  const coords = toCoords(e);
38723
39051
  if (!coords) return;
@@ -38739,7 +39067,7 @@ var init_GraphCanvas = __esm({
38739
39067
  },
38740
39068
  [toCoords, nodeAt, draggable, interactive, offset]
38741
39069
  );
38742
- const handleMouseMove = React84.useCallback(
39070
+ const handlePointerMove = React84.useCallback(
38743
39071
  (e) => {
38744
39072
  const state = interactionRef.current;
38745
39073
  if (state.mode === "panning") {
@@ -38768,7 +39096,7 @@ var init_GraphCanvas = __esm({
38768
39096
  },
38769
39097
  [toCoords, nodeAt]
38770
39098
  );
38771
- const handleMouseUp = React84.useCallback(
39099
+ const handlePointerUp = React84.useCallback(
38772
39100
  (e) => {
38773
39101
  const state = interactionRef.current;
38774
39102
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -38785,11 +39113,19 @@ var init_GraphCanvas = __esm({
38785
39113
  },
38786
39114
  [toCoords, nodeAt, handleNodeClick]
38787
39115
  );
38788
- const handleMouseLeave = React84.useCallback(() => {
38789
- interactionRef.current.mode = "none";
38790
- interactionRef.current.dragNodeId = null;
39116
+ const handlePointerLeave = React84.useCallback(() => {
38791
39117
  setHoveredNode(null);
38792
39118
  }, []);
39119
+ const gestureHandlers = useCanvasGestures({
39120
+ canvasRef,
39121
+ enabled: interactive || draggable,
39122
+ onPointerDown: handlePointerDown,
39123
+ onPointerMove: handlePointerMove,
39124
+ onPointerUp: handlePointerUp,
39125
+ onZoom: applyZoom,
39126
+ onPanDelta: applyPanDelta,
39127
+ onMultiTouchStart: cancelSinglePointer
39128
+ });
38793
39129
  const handleDoubleClick = React84.useCallback(
38794
39130
  (e) => {
38795
39131
  const coords = toCoords(e);
@@ -38858,13 +39194,14 @@ var init_GraphCanvas = __esm({
38858
39194
  ref: canvasRef,
38859
39195
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38860
39196
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38861
- className: "w-full cursor-grab active:cursor-grabbing",
39197
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
38862
39198
  style: { height },
38863
- onWheel: handleWheel,
38864
- onMouseDown: handleMouseDown,
38865
- onMouseMove: handleMouseMove,
38866
- onMouseUp: handleMouseUp,
38867
- onMouseLeave: handleMouseLeave,
39199
+ onPointerDown: gestureHandlers.onPointerDown,
39200
+ onPointerMove: gestureHandlers.onPointerMove,
39201
+ onPointerUp: gestureHandlers.onPointerUp,
39202
+ onPointerCancel: gestureHandlers.onPointerCancel,
39203
+ onPointerLeave: handlePointerLeave,
39204
+ onWheel: gestureHandlers.onWheel,
38868
39205
  onDoubleClick: handleDoubleClick
38869
39206
  }
38870
39207
  ) }),
@@ -43336,6 +43673,9 @@ var init_GameCanvas3D2 = __esm({
43336
43673
  target: cameraTarget,
43337
43674
  enableDamping: true,
43338
43675
  dampingFactor: 0.05,
43676
+ enableZoom: true,
43677
+ enablePan: true,
43678
+ touches: { ONE: THREE3__namespace.TOUCH.ROTATE, TWO: THREE3__namespace.TOUCH.DOLLY_PAN },
43339
43679
  minDistance: 2,
43340
43680
  maxDistance: 100,
43341
43681
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -48666,20 +49006,21 @@ var init_SplitPane = __esm({
48666
49006
  const [ratio, setRatio] = React84.useState(initialRatio);
48667
49007
  const containerRef = React84.useRef(null);
48668
49008
  const isDragging = React84.useRef(false);
48669
- const handleMouseDown = React84.useCallback(
49009
+ const handlePointerDown = React84.useCallback(
48670
49010
  (e) => {
48671
49011
  if (!resizable) return;
48672
49012
  e.preventDefault();
48673
49013
  isDragging.current = true;
48674
- const handleMouseMove = (e2) => {
49014
+ e.currentTarget.setPointerCapture(e.pointerId);
49015
+ const handlePointerMove = (ev) => {
48675
49016
  if (!isDragging.current || !containerRef.current) return;
48676
49017
  const rect = containerRef.current.getBoundingClientRect();
48677
49018
  let newRatio;
48678
49019
  if (direction === "horizontal") {
48679
- const x = e2.clientX - rect.left;
49020
+ const x = ev.clientX - rect.left;
48680
49021
  newRatio = x / rect.width * 100;
48681
49022
  } else {
48682
- const y = e2.clientY - rect.top;
49023
+ const y = ev.clientY - rect.top;
48683
49024
  newRatio = y / rect.height * 100;
48684
49025
  }
48685
49026
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -48687,13 +49028,15 @@ var init_SplitPane = __esm({
48687
49028
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
48688
49029
  setRatio(newRatio);
48689
49030
  };
48690
- const handleMouseUp = () => {
49031
+ const handlePointerUp = () => {
48691
49032
  isDragging.current = false;
48692
- document.removeEventListener("mousemove", handleMouseMove);
48693
- document.removeEventListener("mouseup", handleMouseUp);
49033
+ document.removeEventListener("pointermove", handlePointerMove);
49034
+ document.removeEventListener("pointerup", handlePointerUp);
49035
+ document.removeEventListener("pointercancel", handlePointerUp);
48694
49036
  };
48695
- document.addEventListener("mousemove", handleMouseMove);
48696
- document.addEventListener("mouseup", handleMouseUp);
49037
+ document.addEventListener("pointermove", handlePointerMove);
49038
+ document.addEventListener("pointerup", handlePointerUp);
49039
+ document.addEventListener("pointercancel", handlePointerUp);
48697
49040
  },
48698
49041
  [direction, minSize, resizable]
48699
49042
  );
@@ -48722,9 +49065,9 @@ var init_SplitPane = __esm({
48722
49065
  resizable && /* @__PURE__ */ jsxRuntime.jsx(
48723
49066
  "div",
48724
49067
  {
48725
- onMouseDown: handleMouseDown,
49068
+ onPointerDown: handlePointerDown,
48726
49069
  className: cn(
48727
- "flex-shrink-0 bg-border transition-colors",
49070
+ "flex-shrink-0 bg-border transition-colors touch-none",
48728
49071
  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"
48729
49072
  )
48730
49073
  }