@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
@@ -3409,12 +3409,60 @@ var init_useEventBus = __esm({
3409
3409
  useEventBus_default = useEventBus;
3410
3410
  }
3411
3411
  });
3412
+ function useTapReveal(options = {}) {
3413
+ const { onReveal, onDismiss, refs, enabled = true } = options;
3414
+ const [revealed, setRevealed] = React93.useState(false);
3415
+ const onRevealRef = React93.useRef(onReveal);
3416
+ const onDismissRef = React93.useRef(onDismiss);
3417
+ onRevealRef.current = onReveal;
3418
+ onDismissRef.current = onDismiss;
3419
+ const reveal = React93.useCallback(() => {
3420
+ setRevealed((wasRevealed) => {
3421
+ if (!wasRevealed) onRevealRef.current?.();
3422
+ return true;
3423
+ });
3424
+ }, []);
3425
+ const dismiss = React93.useCallback(() => {
3426
+ setRevealed((wasRevealed) => {
3427
+ if (wasRevealed) onDismissRef.current?.();
3428
+ return false;
3429
+ });
3430
+ }, []);
3431
+ const onPointerDown = React93.useCallback(
3432
+ (e) => {
3433
+ if (!enabled || e.pointerType === "mouse") return;
3434
+ if (revealed) dismiss();
3435
+ else reveal();
3436
+ },
3437
+ [enabled, revealed, reveal, dismiss]
3438
+ );
3439
+ React93.useEffect(() => {
3440
+ if (!revealed || typeof document === "undefined") return;
3441
+ const onDocDown = (ev) => {
3442
+ const target = ev.target;
3443
+ if (!(target instanceof Node)) return;
3444
+ const inside = (refs ?? []).some((r2) => r2.current?.contains(target));
3445
+ if (!inside) dismiss();
3446
+ };
3447
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
3448
+ return () => {
3449
+ clearTimeout(id);
3450
+ document.removeEventListener("pointerdown", onDocDown);
3451
+ };
3452
+ }, [revealed, refs, dismiss]);
3453
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
3454
+ }
3455
+ var init_useTapReveal = __esm({
3456
+ "hooks/useTapReveal.ts"() {
3457
+ }
3458
+ });
3412
3459
  var paddingStyles, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles, displayStyles, overflowStyles2, positionStyles, Box;
3413
3460
  var init_Box = __esm({
3414
3461
  "components/core/atoms/Box.tsx"() {
3415
3462
  "use client";
3416
3463
  init_cn();
3417
3464
  init_useEventBus();
3465
+ init_useTapReveal();
3418
3466
  paddingStyles = {
3419
3467
  none: "p-0",
3420
3468
  xs: "p-1",
@@ -3540,10 +3588,12 @@ var init_Box = __esm({
3540
3588
  action,
3541
3589
  actionPayload,
3542
3590
  hoverEvent,
3591
+ tapReveal = true,
3543
3592
  maxWidth,
3544
3593
  onClick,
3545
3594
  onMouseEnter,
3546
3595
  onMouseLeave,
3596
+ onPointerDown,
3547
3597
  ...rest
3548
3598
  }, ref) => {
3549
3599
  const eventBus = useEventBus();
@@ -3566,6 +3616,19 @@ var init_Box = __esm({
3566
3616
  }
3567
3617
  onMouseLeave?.(e);
3568
3618
  }, [hoverEvent, eventBus, onMouseLeave]);
3619
+ const { triggerProps } = useTapReveal({
3620
+ enabled: tapReveal && !!hoverEvent,
3621
+ onReveal: React93.useCallback(() => {
3622
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
3623
+ }, [hoverEvent, eventBus]),
3624
+ onDismiss: React93.useCallback(() => {
3625
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
3626
+ }, [hoverEvent, eventBus])
3627
+ });
3628
+ const handlePointerDown = React93.useCallback((e) => {
3629
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
3630
+ onPointerDown?.(e);
3631
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
3569
3632
  const isClickable = action || onClick;
3570
3633
  return React93__namespace.default.createElement(
3571
3634
  Component2,
@@ -3593,6 +3656,7 @@ var init_Box = __esm({
3593
3656
  onClick: isClickable ? handleClick : void 0,
3594
3657
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
3595
3658
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
3659
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
3596
3660
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3597
3661
  ...rest
3598
3662
  },
@@ -8668,6 +8732,11 @@ var init_TextHighlight = __esm({
8668
8732
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
8669
8733
  onMouseLeave?.();
8670
8734
  },
8735
+ onPointerDown: (e) => {
8736
+ if (e.pointerType === "mouse") return;
8737
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
8738
+ onMouseEnter?.();
8739
+ },
8671
8740
  role: "button",
8672
8741
  tabIndex: 0,
8673
8742
  onKeyDown: (e) => {
@@ -8843,6 +8912,7 @@ var init_LawReferenceTooltip = __esm({
8843
8912
  init_Typography();
8844
8913
  init_Divider();
8845
8914
  init_cn();
8915
+ init_useTapReveal();
8846
8916
  positionStyles2 = {
8847
8917
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
8848
8918
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -8864,6 +8934,7 @@ var init_LawReferenceTooltip = __esm({
8864
8934
  const { t } = hooks.useTranslate();
8865
8935
  const [isVisible, setIsVisible] = React93__namespace.default.useState(false);
8866
8936
  const timeoutRef = React93__namespace.default.useRef(null);
8937
+ const triggerRef = React93__namespace.default.useRef(null);
8867
8938
  const handleMouseEnter = () => {
8868
8939
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
8869
8940
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -8872,6 +8943,8 @@ var init_LawReferenceTooltip = __esm({
8872
8943
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
8873
8944
  setIsVisible(false);
8874
8945
  };
8946
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
8947
+ const open = isVisible || revealed;
8875
8948
  React93__namespace.default.useEffect(() => {
8876
8949
  return () => {
8877
8950
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -8880,6 +8953,7 @@ var init_LawReferenceTooltip = __esm({
8880
8953
  return /* @__PURE__ */ jsxRuntime.jsxs(
8881
8954
  Box,
8882
8955
  {
8956
+ ref: triggerRef,
8883
8957
  as: "span",
8884
8958
  position: "relative",
8885
8959
  display: "inline-block",
@@ -8888,9 +8962,10 @@ var init_LawReferenceTooltip = __esm({
8888
8962
  onMouseLeave: handleMouseLeave,
8889
8963
  onFocus: handleMouseEnter,
8890
8964
  onBlur: handleMouseLeave,
8965
+ onPointerDown: triggerProps.onPointerDown,
8891
8966
  children: [
8892
8967
  children,
8893
- isVisible && /* @__PURE__ */ jsxRuntime.jsxs(
8968
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
8894
8969
  Box,
8895
8970
  {
8896
8971
  padding: "sm",
@@ -14306,6 +14381,36 @@ function useCamera() {
14306
14381
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
14307
14382
  drawFn?.();
14308
14383
  }, []);
14384
+ const handlePointerDown = React93.useCallback((e) => {
14385
+ handleMouseDown(e);
14386
+ }, [handleMouseDown]);
14387
+ const handlePointerUp = React93.useCallback(() => {
14388
+ handleMouseUp();
14389
+ }, [handleMouseUp]);
14390
+ const handlePointerMove = React93.useCallback((e, drawFn) => {
14391
+ return handleMouseMove(e, drawFn);
14392
+ }, [handleMouseMove]);
14393
+ const panBy = React93.useCallback((dx, dy, drawFn) => {
14394
+ cameraRef.current.x -= dx;
14395
+ cameraRef.current.y -= dy;
14396
+ targetCameraRef.current = null;
14397
+ drawFn?.();
14398
+ }, []);
14399
+ const zoomAtPoint = React93.useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
14400
+ const cam = cameraRef.current;
14401
+ const oldZoom = cam.zoom;
14402
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
14403
+ if (newZoom === oldZoom) {
14404
+ drawFn?.();
14405
+ return;
14406
+ }
14407
+ const inv = 1 / oldZoom - 1 / newZoom;
14408
+ cam.x += (centerX - viewportSize.width / 2) * inv;
14409
+ cam.y += (centerY - viewportSize.height / 2) * inv;
14410
+ cam.zoom = newZoom;
14411
+ targetCameraRef.current = null;
14412
+ drawFn?.();
14413
+ }, []);
14309
14414
  const screenToWorld = React93.useCallback((clientX, clientY, canvas, viewportSize) => {
14310
14415
  const rect = canvas.getBoundingClientRect();
14311
14416
  const screenX = clientX - rect.left;
@@ -14337,6 +14442,11 @@ function useCamera() {
14337
14442
  handleMouseMove,
14338
14443
  handleMouseLeave,
14339
14444
  handleWheel,
14445
+ handlePointerDown,
14446
+ handlePointerUp,
14447
+ handlePointerMove,
14448
+ panBy,
14449
+ zoomAtPoint,
14340
14450
  screenToWorld,
14341
14451
  lerpToTarget
14342
14452
  };
@@ -14346,6 +14456,113 @@ var init_useCamera = __esm({
14346
14456
  "use client";
14347
14457
  }
14348
14458
  });
14459
+ function localPoint(canvas, clientX, clientY) {
14460
+ if (!canvas) return { x: clientX, y: clientY };
14461
+ const rect = canvas.getBoundingClientRect();
14462
+ return { x: clientX - rect.left, y: clientY - rect.top };
14463
+ }
14464
+ function useCanvasGestures(options) {
14465
+ const {
14466
+ canvasRef,
14467
+ enabled = true,
14468
+ wheelStep = 1.1,
14469
+ onPointerDown,
14470
+ onPointerMove,
14471
+ onPointerUp,
14472
+ onZoom,
14473
+ onPanDelta,
14474
+ onMultiTouchStart
14475
+ } = options;
14476
+ const pointers = React93.useRef(/* @__PURE__ */ new Map());
14477
+ const pinch = React93.useRef(null);
14478
+ const computePinch = React93.useCallback(() => {
14479
+ const pts = [...pointers.current.values()];
14480
+ const dx = pts[1].x - pts[0].x;
14481
+ const dy = pts[1].y - pts[0].y;
14482
+ return {
14483
+ distance: Math.hypot(dx, dy) || 1,
14484
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
14485
+ };
14486
+ }, []);
14487
+ const handlePointerDown = React93.useCallback(
14488
+ (e) => {
14489
+ if (!enabled) return;
14490
+ e.currentTarget.setPointerCapture(e.pointerId);
14491
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
14492
+ if (pointers.current.size === 2) {
14493
+ onMultiTouchStart?.();
14494
+ pinch.current = computePinch();
14495
+ } else if (pointers.current.size === 1) {
14496
+ onPointerDown?.(e);
14497
+ }
14498
+ },
14499
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
14500
+ );
14501
+ const handlePointerMove = React93.useCallback(
14502
+ (e) => {
14503
+ if (!enabled) return;
14504
+ if (pointers.current.has(e.pointerId)) {
14505
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
14506
+ }
14507
+ if (pointers.current.size >= 2 && pinch.current) {
14508
+ const next = computePinch();
14509
+ const prev = pinch.current;
14510
+ if (next.distance !== prev.distance) {
14511
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
14512
+ }
14513
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
14514
+ pinch.current = next;
14515
+ return;
14516
+ }
14517
+ onPointerMove?.(e);
14518
+ },
14519
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
14520
+ );
14521
+ const endPointer = React93.useCallback(
14522
+ (e, fireUp) => {
14523
+ const wasMulti = pointers.current.size >= 2;
14524
+ pointers.current.delete(e.pointerId);
14525
+ if (pointers.current.size < 2) pinch.current = null;
14526
+ if (wasMulti && pointers.current.size === 1) return;
14527
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
14528
+ },
14529
+ [onPointerUp]
14530
+ );
14531
+ const handlePointerUp = React93.useCallback(
14532
+ (e) => {
14533
+ if (!enabled) return;
14534
+ endPointer(e, true);
14535
+ },
14536
+ [enabled, endPointer]
14537
+ );
14538
+ const handlePointerCancel = React93.useCallback(
14539
+ (e) => {
14540
+ if (!enabled) return;
14541
+ endPointer(e, false);
14542
+ },
14543
+ [enabled, endPointer]
14544
+ );
14545
+ const handleWheel = React93.useCallback(
14546
+ (e) => {
14547
+ if (!enabled) return;
14548
+ e.preventDefault();
14549
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
14550
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
14551
+ },
14552
+ [enabled, canvasRef, wheelStep, onZoom]
14553
+ );
14554
+ return {
14555
+ onPointerDown: handlePointerDown,
14556
+ onPointerMove: handlePointerMove,
14557
+ onPointerUp: handlePointerUp,
14558
+ onPointerCancel: handlePointerCancel,
14559
+ onWheel: handleWheel
14560
+ };
14561
+ }
14562
+ var init_useCanvasGestures = __esm({
14563
+ "hooks/useCanvasGestures.ts"() {
14564
+ }
14565
+ });
14349
14566
  function unitAtlasUrl(unit) {
14350
14567
  if (unit.spriteSheet) return unit.spriteSheet;
14351
14568
  const sprite = unit.sprite;
@@ -14724,11 +14941,12 @@ function IsometricCanvas({
14724
14941
  cameraRef,
14725
14942
  targetCameraRef,
14726
14943
  dragDistance,
14727
- handleMouseDown,
14728
- handleMouseUp,
14729
- handleMouseMove,
14730
14944
  handleMouseLeave,
14731
- handleWheel,
14945
+ handlePointerDown,
14946
+ handlePointerUp,
14947
+ handlePointerMove,
14948
+ panBy,
14949
+ zoomAtPoint,
14732
14950
  screenToWorld,
14733
14951
  lerpToTarget
14734
14952
  } = useCamera();
@@ -15163,11 +15381,16 @@ function IsometricCanvas({
15163
15381
  cancelAnimationFrame(rafIdRef.current);
15164
15382
  };
15165
15383
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
15166
- const handleMouseMoveWithCamera = React93.useCallback((e) => {
15167
- if (enableCamera) {
15168
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
15169
- if (wasPanning) return;
15170
- }
15384
+ const singlePointerActiveRef = React93.useRef(false);
15385
+ const handleCanvasPointerDown = React93.useCallback((e) => {
15386
+ singlePointerActiveRef.current = true;
15387
+ if (enableCamera) handlePointerDown(e);
15388
+ }, [enableCamera, handlePointerDown]);
15389
+ const handleCanvasPointerMove = React93.useCallback((e) => {
15390
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
15391
+ }, [enableCamera, handlePointerMove, draw]);
15392
+ const handleCanvasHover = React93.useCallback((e) => {
15393
+ if (singlePointerActiveRef.current) return;
15171
15394
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
15172
15395
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
15173
15396
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -15178,26 +15401,10 @@ function IsometricCanvas({
15178
15401
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
15179
15402
  onTileHover?.(isoPos.x, isoPos.y);
15180
15403
  }
15181
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15182
- const handleMouseLeaveWithCamera = React93.useCallback(() => {
15183
- handleMouseLeave();
15184
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
15185
- onTileLeave?.();
15186
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
15187
- const handleWheelWithCamera = React93.useCallback((e) => {
15188
- if (enableCamera) {
15189
- handleWheel(e, () => draw(animTimeRef.current));
15190
- }
15191
- }, [enableCamera, handleWheel, draw]);
15192
- React93.useEffect(() => {
15193
- const canvas = canvasRef.current;
15194
- if (!canvas) return;
15195
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
15196
- return () => {
15197
- canvas.removeEventListener("wheel", handleWheelWithCamera);
15198
- };
15199
- }, [handleWheelWithCamera]);
15200
- const handleClick = React93.useCallback((e) => {
15404
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15405
+ const handleCanvasPointerUp = React93.useCallback((e) => {
15406
+ singlePointerActiveRef.current = false;
15407
+ if (enableCamera) handlePointerUp();
15201
15408
  if (dragDistance() > 5) return;
15202
15409
  if (!canvasRef.current) return;
15203
15410
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -15215,7 +15422,32 @@ function IsometricCanvas({
15215
15422
  onTileClick?.(isoPos.x, isoPos.y);
15216
15423
  }
15217
15424
  }
15218
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15425
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15426
+ const handleCanvasPointerLeave = React93.useCallback(() => {
15427
+ handleMouseLeave();
15428
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
15429
+ onTileLeave?.();
15430
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
15431
+ const applyZoom = React93.useCallback((factor, centerX, centerY) => {
15432
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
15433
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
15434
+ const applyPanDelta = React93.useCallback((dx, dy) => {
15435
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
15436
+ }, [enableCamera, panBy, draw]);
15437
+ const cancelSinglePointer = React93.useCallback(() => {
15438
+ singlePointerActiveRef.current = false;
15439
+ if (enableCamera) handlePointerUp();
15440
+ }, [enableCamera, handlePointerUp]);
15441
+ const gestureHandlers = useCanvasGestures({
15442
+ canvasRef,
15443
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
15444
+ onPointerDown: handleCanvasPointerDown,
15445
+ onPointerMove: handleCanvasPointerMove,
15446
+ onPointerUp: handleCanvasPointerUp,
15447
+ onZoom: applyZoom,
15448
+ onPanDelta: applyPanDelta,
15449
+ onMultiTouchStart: cancelSinglePointer
15450
+ });
15219
15451
  if (error) {
15220
15452
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
15221
15453
  }
@@ -15247,13 +15479,17 @@ function IsometricCanvas({
15247
15479
  {
15248
15480
  ref: canvasRef,
15249
15481
  "data-testid": "game-canvas",
15250
- onClick: handleClick,
15251
- onMouseDown: enableCamera ? handleMouseDown : void 0,
15252
- onMouseMove: handleMouseMoveWithCamera,
15253
- onMouseUp: enableCamera ? handleMouseUp : void 0,
15254
- onMouseLeave: handleMouseLeaveWithCamera,
15482
+ onPointerDown: gestureHandlers.onPointerDown,
15483
+ onPointerMove: (e) => {
15484
+ gestureHandlers.onPointerMove(e);
15485
+ handleCanvasHover(e);
15486
+ },
15487
+ onPointerUp: gestureHandlers.onPointerUp,
15488
+ onPointerCancel: gestureHandlers.onPointerCancel,
15489
+ onPointerLeave: handleCanvasPointerLeave,
15490
+ onWheel: gestureHandlers.onWheel,
15255
15491
  onContextMenu: (e) => e.preventDefault(),
15256
- className: "cursor-pointer",
15492
+ className: "cursor-pointer touch-none",
15257
15493
  style: {
15258
15494
  width: viewportSize.width,
15259
15495
  height: viewportSize.height
@@ -15307,6 +15543,7 @@ var init_IsometricCanvas = __esm({
15307
15543
  init_ErrorState();
15308
15544
  init_useImageCache();
15309
15545
  init_useCamera();
15546
+ init_useCanvasGestures();
15310
15547
  init_useUnitSpriteAtlas();
15311
15548
  init_verificationRegistry();
15312
15549
  init_isometric();
@@ -17593,6 +17830,10 @@ var init_StateMachineView = __esm({
17593
17830
  const handleMouseLeave2 = () => {
17594
17831
  onHover(null, 0, 0);
17595
17832
  };
17833
+ const handlePointerDown2 = (e) => {
17834
+ if (e.pointerType === "mouse") return;
17835
+ handleMouseEnter2();
17836
+ };
17596
17837
  return /* @__PURE__ */ jsxRuntime.jsxs(
17597
17838
  "g",
17598
17839
  {
@@ -17602,6 +17843,7 @@ var init_StateMachineView = __esm({
17602
17843
  onClick: () => onClick?.(bundle),
17603
17844
  onMouseEnter: handleMouseEnter2,
17604
17845
  onMouseLeave: handleMouseLeave2,
17846
+ onPointerDown: handlePointerDown2,
17605
17847
  style: { pointerEvents: "auto" },
17606
17848
  children: [
17607
17849
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -17719,6 +17961,10 @@ var init_StateMachineView = __esm({
17719
17961
  const handleMouseLeave = React93.useCallback(() => {
17720
17962
  onHover(null, 0, 0);
17721
17963
  }, [onHover]);
17964
+ const handlePointerDown = React93.useCallback((e) => {
17965
+ if (e.pointerType === "mouse") return;
17966
+ handleMouseEnter();
17967
+ }, [handleMouseEnter]);
17722
17968
  const uniqueMarkerId = `arrow-${bundle.id}`;
17723
17969
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
17724
17970
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -17730,6 +17976,7 @@ var init_StateMachineView = __esm({
17730
17976
  onClick: () => onClick?.(bundle),
17731
17977
  onMouseEnter: handleMouseEnter,
17732
17978
  onMouseLeave: handleMouseLeave,
17979
+ onPointerDown: handlePointerDown,
17733
17980
  style: { pointerEvents: "auto" },
17734
17981
  children: [
17735
17982
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -26228,10 +26475,84 @@ function SubMenu({
26228
26475
  );
26229
26476
  return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
26230
26477
  }
26478
+ function MenuItemRow({
26479
+ item,
26480
+ itemId,
26481
+ hasSubMenu,
26482
+ isDanger,
26483
+ direction,
26484
+ isSubMenuOpen,
26485
+ activeSubMenuRef,
26486
+ eventBus,
26487
+ onItemClick,
26488
+ openSubMenu
26489
+ }) {
26490
+ const rowRef = React93.useRef(null);
26491
+ const { triggerProps } = useTapReveal({
26492
+ enabled: hasSubMenu,
26493
+ onReveal: () => openSubMenu(itemId, rowRef.current),
26494
+ refs: [rowRef]
26495
+ });
26496
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
26497
+ /* @__PURE__ */ jsxRuntime.jsx(
26498
+ Box,
26499
+ {
26500
+ ref: rowRef,
26501
+ as: "button",
26502
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
26503
+ "aria-disabled": item.disabled || void 0,
26504
+ onMouseEnter: (e) => {
26505
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
26506
+ },
26507
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
26508
+ "data-testid": item.event ? `action-${item.event}` : void 0,
26509
+ className: cn(
26510
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
26511
+ "text-sm transition-colors",
26512
+ "hover:bg-muted",
26513
+ "focus:outline-none focus:bg-muted",
26514
+ "disabled:opacity-50 disabled:cursor-not-allowed",
26515
+ item.disabled && "cursor-not-allowed",
26516
+ isDanger && "text-error hover:bg-error/10"
26517
+ ),
26518
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
26519
+ 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" })),
26520
+ /* @__PURE__ */ jsxRuntime.jsx(
26521
+ Typography,
26522
+ {
26523
+ variant: "small",
26524
+ className: cn("flex-1", isDanger && "text-red-600"),
26525
+ children: item.label
26526
+ }
26527
+ ),
26528
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
26529
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
26530
+ Icon,
26531
+ {
26532
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
26533
+ size: "sm",
26534
+ className: "flex-shrink-0"
26535
+ }
26536
+ )
26537
+ ] })
26538
+ }
26539
+ ),
26540
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
26541
+ SubMenu,
26542
+ {
26543
+ items: item.subMenu,
26544
+ itemRef: activeSubMenuRef,
26545
+ direction,
26546
+ eventBus
26547
+ }
26548
+ )
26549
+ ] });
26550
+ }
26231
26551
  var MENU_GAP, menuContainerStyles, Menu;
26232
26552
  var init_Menu = __esm({
26233
26553
  "components/core/molecules/Menu.tsx"() {
26234
26554
  "use client";
26555
+ init_useTapReveal();
26235
26556
  init_Box();
26236
26557
  init_Icon();
26237
26558
  init_Divider();
@@ -26286,6 +26607,10 @@ var init_Menu = __esm({
26286
26607
  setIsOpen(false);
26287
26608
  }
26288
26609
  };
26610
+ const openSubMenu = (itemId, el) => {
26611
+ setActiveSubMenu(itemId);
26612
+ setActiveSubMenuRef(el);
26613
+ };
26289
26614
  React93.useEffect(() => {
26290
26615
  if (isOpen) {
26291
26616
  updatePosition();
@@ -26329,61 +26654,22 @@ var init_Menu = __esm({
26329
26654
  if (isDivider) {
26330
26655
  return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
26331
26656
  }
26332
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
26333
- /* @__PURE__ */ jsxRuntime.jsx(
26334
- Box,
26335
- {
26336
- as: "button",
26337
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
26338
- "aria-disabled": item.disabled || void 0,
26339
- onMouseEnter: (e) => {
26340
- if (hasSubMenu) {
26341
- setActiveSubMenu(itemId);
26342
- setActiveSubMenuRef(e.currentTarget);
26343
- }
26344
- },
26345
- "data-testid": item.event ? `action-${item.event}` : void 0,
26346
- className: cn(
26347
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
26348
- "text-sm transition-colors",
26349
- "hover:bg-muted",
26350
- "focus:outline-none focus:bg-muted",
26351
- "disabled:opacity-50 disabled:cursor-not-allowed",
26352
- item.disabled && "cursor-not-allowed",
26353
- isDanger && "text-error hover:bg-error/10"
26354
- ),
26355
- children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
26356
- 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" })),
26357
- /* @__PURE__ */ jsxRuntime.jsx(
26358
- Typography,
26359
- {
26360
- variant: "small",
26361
- className: cn("flex-1", isDanger && "text-red-600"),
26362
- children: item.label
26363
- }
26364
- ),
26365
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
26366
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
26367
- Icon,
26368
- {
26369
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
26370
- size: "sm",
26371
- className: "flex-shrink-0"
26372
- }
26373
- )
26374
- ] })
26375
- }
26376
- ),
26377
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
26378
- SubMenu,
26379
- {
26380
- items: item.subMenu,
26381
- itemRef: activeSubMenuRef,
26382
- direction,
26383
- eventBus
26384
- }
26385
- )
26386
- ] }, itemId);
26657
+ return /* @__PURE__ */ jsxRuntime.jsx(
26658
+ MenuItemRow,
26659
+ {
26660
+ item,
26661
+ itemId,
26662
+ hasSubMenu,
26663
+ isDanger,
26664
+ direction,
26665
+ isSubMenuOpen: activeSubMenu === itemId,
26666
+ activeSubMenuRef,
26667
+ eventBus,
26668
+ onItemClick: handleItemClick,
26669
+ openSubMenu
26670
+ },
26671
+ itemId
26672
+ );
26387
26673
  });
26388
26674
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
26389
26675
  "div",
@@ -28686,6 +28972,7 @@ var init_Popover = __esm({
28686
28972
  "use client";
28687
28973
  init_Typography();
28688
28974
  init_cn();
28975
+ init_useTapReveal();
28689
28976
  arrowClasses = {
28690
28977
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
28691
28978
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -28756,18 +29043,32 @@ var init_Popover = __esm({
28756
29043
  document.addEventListener("mousedown", handleClickOutside);
28757
29044
  return () => document.removeEventListener("mousedown", handleClickOutside);
28758
29045
  }, [isOpen, trigger]);
28759
- const triggerProps = trigger === "click" ? {
29046
+ const { triggerProps: tapTriggerProps } = useTapReveal({
29047
+ enabled: trigger === "hover",
29048
+ onReveal: handleOpen,
29049
+ onDismiss: handleClose,
29050
+ refs: [triggerRef, popoverRef]
29051
+ });
29052
+ const handlerProps = trigger === "click" ? {
28760
29053
  onClick: handleToggle
28761
29054
  } : {
28762
29055
  onMouseEnter: handleOpen,
28763
- onMouseLeave: handleClose
29056
+ onMouseLeave: handleClose,
29057
+ onPointerDown: tapTriggerProps.onPointerDown
28764
29058
  };
28765
29059
  const childElement = React93__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
29060
+ const childPointerDown = childElement.props.onPointerDown;
28766
29061
  const triggerElement = React93__namespace.default.cloneElement(
28767
29062
  childElement,
28768
29063
  {
28769
29064
  ref: triggerRef,
28770
- ...triggerProps
29065
+ ...handlerProps,
29066
+ ...trigger === "hover" ? {
29067
+ onPointerDown: (e) => {
29068
+ tapTriggerProps.onPointerDown(e);
29069
+ childPointerDown?.(e);
29070
+ }
29071
+ } : void 0
28771
29072
  }
28772
29073
  );
28773
29074
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -29298,6 +29599,7 @@ var init_Tooltip = __esm({
29298
29599
  "use client";
29299
29600
  init_Typography();
29300
29601
  init_cn();
29602
+ init_useTapReveal();
29301
29603
  TRIGGER_GAP2 = 8;
29302
29604
  arrowClasses2 = {
29303
29605
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -29342,6 +29644,11 @@ var init_Tooltip = __esm({
29342
29644
  setIsVisible(false);
29343
29645
  }, hideDelay);
29344
29646
  };
29647
+ const { triggerProps } = useTapReveal({
29648
+ onReveal: handleMouseEnter,
29649
+ onDismiss: handleMouseLeave,
29650
+ refs: [triggerRef, tooltipRef]
29651
+ });
29345
29652
  React93.useLayoutEffect(() => {
29346
29653
  if (isVisible) {
29347
29654
  updatePosition();
@@ -29354,12 +29661,17 @@ var init_Tooltip = __esm({
29354
29661
  };
29355
29662
  }, []);
29356
29663
  const triggerElement = React93__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
29664
+ const childPointerDown = triggerElement.props.onPointerDown;
29357
29665
  const trigger = React93__namespace.default.cloneElement(triggerElement, {
29358
29666
  ref: triggerRef,
29359
29667
  onMouseEnter: handleMouseEnter,
29360
29668
  onMouseLeave: handleMouseLeave,
29361
29669
  onFocus: handleMouseEnter,
29362
- onBlur: handleMouseLeave
29670
+ onBlur: handleMouseLeave,
29671
+ onPointerDown: (e) => {
29672
+ triggerProps.onPointerDown(e);
29673
+ childPointerDown?.(e);
29674
+ }
29363
29675
  });
29364
29676
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
29365
29677
  "div",
@@ -32709,6 +33021,14 @@ var init_GraphView = __esm({
32709
33021
  },
32710
33022
  [onNodeClick]
32711
33023
  );
33024
+ const handleNodePointerDown = React93.useCallback(
33025
+ (e, node) => {
33026
+ if (e.pointerType === "mouse") return;
33027
+ handleNodeMouseEnter(node);
33028
+ handleNodeClickInternal(node);
33029
+ },
33030
+ [handleNodeMouseEnter, handleNodeClickInternal]
33031
+ );
32712
33032
  if (nodes.length === 0) {
32713
33033
  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") }) });
32714
33034
  }
@@ -32768,6 +33088,7 @@ var init_GraphView = __esm({
32768
33088
  onMouseEnter: () => handleNodeMouseEnter(node),
32769
33089
  onMouseLeave: handleNodeMouseLeave,
32770
33090
  onClick: () => handleNodeClickInternal(node),
33091
+ onPointerDown: (e) => handleNodePointerDown(e, node),
32771
33092
  children: [
32772
33093
  /* @__PURE__ */ jsxRuntime.jsx(
32773
33094
  "circle",
@@ -32848,13 +33169,13 @@ var init_MapView = __esm({
32848
33169
  shadowSize: [41, 41]
32849
33170
  });
32850
33171
  L.Marker.prototype.options.icon = defaultIcon;
32851
- const { useEffect: useEffect87, useRef: useRef80, useCallback: useCallback130, useState: useState122 } = React93__namespace.default;
33172
+ const { useEffect: useEffect88, useRef: useRef82, useCallback: useCallback132, useState: useState123 } = React93__namespace.default;
32852
33173
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32853
33174
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32854
33175
  function MapUpdater({ centerLat, centerLng, zoom }) {
32855
33176
  const map = useMap();
32856
- const prevRef = useRef80({ centerLat, centerLng, zoom });
32857
- useEffect87(() => {
33177
+ const prevRef = useRef82({ centerLat, centerLng, zoom });
33178
+ useEffect88(() => {
32858
33179
  const prev = prevRef.current;
32859
33180
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32860
33181
  map.setView([centerLat, centerLng], zoom);
@@ -32865,7 +33186,7 @@ var init_MapView = __esm({
32865
33186
  }
32866
33187
  function MapClickHandler({ onMapClick }) {
32867
33188
  const map = useMap();
32868
- useEffect87(() => {
33189
+ useEffect88(() => {
32869
33190
  if (!onMapClick) return;
32870
33191
  const handler = (e) => {
32871
33192
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32893,8 +33214,8 @@ var init_MapView = __esm({
32893
33214
  showAttribution = true
32894
33215
  }) {
32895
33216
  const eventBus = useEventBus4();
32896
- const [clickedPosition, setClickedPosition] = useState122(null);
32897
- const handleMapClick = useCallback130((lat, lng) => {
33217
+ const [clickedPosition, setClickedPosition] = useState123(null);
33218
+ const handleMapClick = useCallback132((lat, lng) => {
32898
33219
  if (showClickedPin) {
32899
33220
  setClickedPosition({ lat, lng });
32900
33221
  }
@@ -32903,7 +33224,7 @@ var init_MapView = __esm({
32903
33224
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
32904
33225
  }
32905
33226
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
32906
- const handleMarkerClick = useCallback130((marker) => {
33227
+ const handleMarkerClick = useCallback132((marker) => {
32907
33228
  onMarkerClick?.(marker);
32908
33229
  if (markerClickEvent) {
32909
33230
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -33244,7 +33565,7 @@ var init_StarRating = __esm({
33244
33565
  "aria-valuenow": value,
33245
33566
  tabIndex: readOnly ? void 0 : 0,
33246
33567
  onKeyDown: handleKeyDown,
33247
- onMouseLeave: () => setHoverValue(null),
33568
+ onPointerLeave: () => setHoverValue(null),
33248
33569
  children: Array.from({ length: max }, (_, i) => {
33249
33570
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
33250
33571
  const isFull = fillLevel >= 1;
@@ -33254,7 +33575,7 @@ var init_StarRating = __esm({
33254
33575
  {
33255
33576
  className: "relative inline-block",
33256
33577
  onClick: () => handleStarClick(i, false),
33257
- onMouseMove: (e) => {
33578
+ onPointerMove: (e) => {
33258
33579
  if (readOnly) return;
33259
33580
  const rect = e.currentTarget.getBoundingClientRect();
33260
33581
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -37144,7 +37465,7 @@ var init_PositionedCanvas = __esm({
37144
37465
  {
37145
37466
  ref: containerRef,
37146
37467
  "data-testid": "positioned-canvas",
37147
- className: "relative bg-background border border-border rounded-container overflow-hidden",
37468
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
37148
37469
  style: { width, height },
37149
37470
  onClick: handleContainerClick,
37150
37471
  children: items.map((item) => {
@@ -40904,6 +41225,7 @@ var init_GraphCanvas = __esm({
40904
41225
  init_ErrorState();
40905
41226
  init_EmptyState();
40906
41227
  init_useEventBus();
41228
+ init_useCanvasGestures();
40907
41229
  GROUP_COLORS2 = [
40908
41230
  "var(--color-primary)",
40909
41231
  "var(--color-success)",
@@ -40941,6 +41263,10 @@ var init_GraphCanvas = __esm({
40941
41263
  const animRef = React93.useRef(0);
40942
41264
  const [zoom, setZoom] = React93.useState(1);
40943
41265
  const [offset, setOffset] = React93.useState({ x: 0, y: 0 });
41266
+ const zoomRef = React93.useRef(zoom);
41267
+ zoomRef.current = zoom;
41268
+ const offsetRef = React93.useRef(offset);
41269
+ offsetRef.current = offset;
40944
41270
  const [hoveredNode, setHoveredNode] = React93.useState(null);
40945
41271
  const nodesRef = React93.useRef([]);
40946
41272
  const [, forceUpdate] = React93.useState(0);
@@ -41200,25 +41526,27 @@ var init_GraphCanvas = __esm({
41200
41526
  setZoom(1);
41201
41527
  setOffset({ x: 0, y: 0 });
41202
41528
  }, []);
41203
- const handleWheel = React93.useCallback(
41204
- (e) => {
41205
- if (!interactive) return;
41206
- e.preventDefault();
41207
- const coords = toCoords(e);
41208
- if (!coords) return;
41209
- const oldZoom = zoom;
41210
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
41211
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
41212
- if (newZoom === oldZoom) return;
41213
- setOffset((o) => ({
41214
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
41215
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
41216
- }));
41217
- setZoom(newZoom);
41218
- },
41219
- [interactive, toCoords, zoom]
41220
- );
41221
- const handleMouseDown = React93.useCallback(
41529
+ const applyZoom = React93.useCallback((factor, cx, cy) => {
41530
+ if (!interactive) return;
41531
+ const oldZoom = zoomRef.current;
41532
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
41533
+ if (newZoom === oldZoom) return;
41534
+ const o = offsetRef.current;
41535
+ setOffset({
41536
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
41537
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
41538
+ });
41539
+ setZoom(newZoom);
41540
+ }, [interactive]);
41541
+ const applyPanDelta = React93.useCallback((dx, dy) => {
41542
+ if (!interactive) return;
41543
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
41544
+ }, [interactive]);
41545
+ const cancelSinglePointer = React93.useCallback(() => {
41546
+ interactionRef.current.mode = "none";
41547
+ interactionRef.current.dragNodeId = null;
41548
+ }, []);
41549
+ const handlePointerDown = React93.useCallback(
41222
41550
  (e) => {
41223
41551
  const coords = toCoords(e);
41224
41552
  if (!coords) return;
@@ -41240,7 +41568,7 @@ var init_GraphCanvas = __esm({
41240
41568
  },
41241
41569
  [toCoords, nodeAt, draggable, interactive, offset]
41242
41570
  );
41243
- const handleMouseMove = React93.useCallback(
41571
+ const handlePointerMove = React93.useCallback(
41244
41572
  (e) => {
41245
41573
  const state = interactionRef.current;
41246
41574
  if (state.mode === "panning") {
@@ -41269,7 +41597,7 @@ var init_GraphCanvas = __esm({
41269
41597
  },
41270
41598
  [toCoords, nodeAt]
41271
41599
  );
41272
- const handleMouseUp = React93.useCallback(
41600
+ const handlePointerUp = React93.useCallback(
41273
41601
  (e) => {
41274
41602
  const state = interactionRef.current;
41275
41603
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -41286,11 +41614,19 @@ var init_GraphCanvas = __esm({
41286
41614
  },
41287
41615
  [toCoords, nodeAt, handleNodeClick]
41288
41616
  );
41289
- const handleMouseLeave = React93.useCallback(() => {
41290
- interactionRef.current.mode = "none";
41291
- interactionRef.current.dragNodeId = null;
41617
+ const handlePointerLeave = React93.useCallback(() => {
41292
41618
  setHoveredNode(null);
41293
41619
  }, []);
41620
+ const gestureHandlers = useCanvasGestures({
41621
+ canvasRef,
41622
+ enabled: interactive || draggable,
41623
+ onPointerDown: handlePointerDown,
41624
+ onPointerMove: handlePointerMove,
41625
+ onPointerUp: handlePointerUp,
41626
+ onZoom: applyZoom,
41627
+ onPanDelta: applyPanDelta,
41628
+ onMultiTouchStart: cancelSinglePointer
41629
+ });
41294
41630
  const handleDoubleClick = React93.useCallback(
41295
41631
  (e) => {
41296
41632
  const coords = toCoords(e);
@@ -41359,13 +41695,14 @@ var init_GraphCanvas = __esm({
41359
41695
  ref: canvasRef,
41360
41696
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
41361
41697
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
41362
- className: "w-full cursor-grab active:cursor-grabbing",
41698
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
41363
41699
  style: { height },
41364
- onWheel: handleWheel,
41365
- onMouseDown: handleMouseDown,
41366
- onMouseMove: handleMouseMove,
41367
- onMouseUp: handleMouseUp,
41368
- onMouseLeave: handleMouseLeave,
41700
+ onPointerDown: gestureHandlers.onPointerDown,
41701
+ onPointerMove: gestureHandlers.onPointerMove,
41702
+ onPointerUp: gestureHandlers.onPointerUp,
41703
+ onPointerCancel: gestureHandlers.onPointerCancel,
41704
+ onPointerLeave: handlePointerLeave,
41705
+ onWheel: gestureHandlers.onWheel,
41369
41706
  onDoubleClick: handleDoubleClick
41370
41707
  }
41371
41708
  ) }),
@@ -45837,6 +46174,9 @@ var init_GameCanvas3D2 = __esm({
45837
46174
  target: cameraTarget,
45838
46175
  enableDamping: true,
45839
46176
  dampingFactor: 0.05,
46177
+ enableZoom: true,
46178
+ enablePan: true,
46179
+ touches: { ONE: THREE3__namespace.TOUCH.ROTATE, TWO: THREE3__namespace.TOUCH.DOLLY_PAN },
45840
46180
  minDistance: 2,
45841
46181
  maxDistance: 100,
45842
46182
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -51167,20 +51507,21 @@ var init_SplitPane = __esm({
51167
51507
  const [ratio, setRatio] = React93.useState(initialRatio);
51168
51508
  const containerRef = React93.useRef(null);
51169
51509
  const isDragging = React93.useRef(false);
51170
- const handleMouseDown = React93.useCallback(
51510
+ const handlePointerDown = React93.useCallback(
51171
51511
  (e) => {
51172
51512
  if (!resizable) return;
51173
51513
  e.preventDefault();
51174
51514
  isDragging.current = true;
51175
- const handleMouseMove = (e2) => {
51515
+ e.currentTarget.setPointerCapture(e.pointerId);
51516
+ const handlePointerMove = (ev) => {
51176
51517
  if (!isDragging.current || !containerRef.current) return;
51177
51518
  const rect = containerRef.current.getBoundingClientRect();
51178
51519
  let newRatio;
51179
51520
  if (direction === "horizontal") {
51180
- const x = e2.clientX - rect.left;
51521
+ const x = ev.clientX - rect.left;
51181
51522
  newRatio = x / rect.width * 100;
51182
51523
  } else {
51183
- const y = e2.clientY - rect.top;
51524
+ const y = ev.clientY - rect.top;
51184
51525
  newRatio = y / rect.height * 100;
51185
51526
  }
51186
51527
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -51188,13 +51529,15 @@ var init_SplitPane = __esm({
51188
51529
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
51189
51530
  setRatio(newRatio);
51190
51531
  };
51191
- const handleMouseUp = () => {
51532
+ const handlePointerUp = () => {
51192
51533
  isDragging.current = false;
51193
- document.removeEventListener("mousemove", handleMouseMove);
51194
- document.removeEventListener("mouseup", handleMouseUp);
51534
+ document.removeEventListener("pointermove", handlePointerMove);
51535
+ document.removeEventListener("pointerup", handlePointerUp);
51536
+ document.removeEventListener("pointercancel", handlePointerUp);
51195
51537
  };
51196
- document.addEventListener("mousemove", handleMouseMove);
51197
- document.addEventListener("mouseup", handleMouseUp);
51538
+ document.addEventListener("pointermove", handlePointerMove);
51539
+ document.addEventListener("pointerup", handlePointerUp);
51540
+ document.addEventListener("pointercancel", handlePointerUp);
51198
51541
  },
51199
51542
  [direction, minSize, resizable]
51200
51543
  );
@@ -51223,9 +51566,9 @@ var init_SplitPane = __esm({
51223
51566
  resizable && /* @__PURE__ */ jsxRuntime.jsx(
51224
51567
  "div",
51225
51568
  {
51226
- onMouseDown: handleMouseDown,
51569
+ onPointerDown: handlePointerDown,
51227
51570
  className: cn(
51228
- "flex-shrink-0 bg-border transition-colors",
51571
+ "flex-shrink-0 bg-border transition-colors touch-none",
51229
51572
  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"
51230
51573
  )
51231
51574
  }