@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
@@ -3248,12 +3248,60 @@ var init_Avatar = __esm({
3248
3248
  exports.Avatar.displayName = "Avatar";
3249
3249
  }
3250
3250
  });
3251
+ function useTapReveal(options = {}) {
3252
+ const { onReveal, onDismiss, refs, enabled = true } = options;
3253
+ const [revealed, setRevealed] = React79.useState(false);
3254
+ const onRevealRef = React79.useRef(onReveal);
3255
+ const onDismissRef = React79.useRef(onDismiss);
3256
+ onRevealRef.current = onReveal;
3257
+ onDismissRef.current = onDismiss;
3258
+ const reveal = React79.useCallback(() => {
3259
+ setRevealed((wasRevealed) => {
3260
+ if (!wasRevealed) onRevealRef.current?.();
3261
+ return true;
3262
+ });
3263
+ }, []);
3264
+ const dismiss = React79.useCallback(() => {
3265
+ setRevealed((wasRevealed) => {
3266
+ if (wasRevealed) onDismissRef.current?.();
3267
+ return false;
3268
+ });
3269
+ }, []);
3270
+ const onPointerDown = React79.useCallback(
3271
+ (e) => {
3272
+ if (!enabled || e.pointerType === "mouse") return;
3273
+ if (revealed) dismiss();
3274
+ else reveal();
3275
+ },
3276
+ [enabled, revealed, reveal, dismiss]
3277
+ );
3278
+ React79.useEffect(() => {
3279
+ if (!revealed || typeof document === "undefined") return;
3280
+ const onDocDown = (ev) => {
3281
+ const target = ev.target;
3282
+ if (!(target instanceof Node)) return;
3283
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
3284
+ if (!inside) dismiss();
3285
+ };
3286
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
3287
+ return () => {
3288
+ clearTimeout(id);
3289
+ document.removeEventListener("pointerdown", onDocDown);
3290
+ };
3291
+ }, [revealed, refs, dismiss]);
3292
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
3293
+ }
3294
+ var init_useTapReveal = __esm({
3295
+ "hooks/useTapReveal.ts"() {
3296
+ }
3297
+ });
3251
3298
  var paddingStyles2, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles2, displayStyles, overflowStyles, positionStyles; exports.Box = void 0;
3252
3299
  var init_Box = __esm({
3253
3300
  "components/core/atoms/Box.tsx"() {
3254
3301
  "use client";
3255
3302
  init_cn();
3256
3303
  init_useEventBus();
3304
+ init_useTapReveal();
3257
3305
  paddingStyles2 = {
3258
3306
  none: "p-0",
3259
3307
  xs: "p-1",
@@ -3379,10 +3427,12 @@ var init_Box = __esm({
3379
3427
  action,
3380
3428
  actionPayload,
3381
3429
  hoverEvent,
3430
+ tapReveal = true,
3382
3431
  maxWidth,
3383
3432
  onClick,
3384
3433
  onMouseEnter,
3385
3434
  onMouseLeave,
3435
+ onPointerDown,
3386
3436
  ...rest
3387
3437
  }, ref) => {
3388
3438
  const eventBus = useEventBus();
@@ -3405,6 +3455,19 @@ var init_Box = __esm({
3405
3455
  }
3406
3456
  onMouseLeave?.(e);
3407
3457
  }, [hoverEvent, eventBus, onMouseLeave]);
3458
+ const { triggerProps } = useTapReveal({
3459
+ enabled: tapReveal && !!hoverEvent,
3460
+ onReveal: React79.useCallback(() => {
3461
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
3462
+ }, [hoverEvent, eventBus]),
3463
+ onDismiss: React79.useCallback(() => {
3464
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
3465
+ }, [hoverEvent, eventBus])
3466
+ });
3467
+ const handlePointerDown = React79.useCallback((e) => {
3468
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
3469
+ onPointerDown?.(e);
3470
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
3408
3471
  const isClickable = action || onClick;
3409
3472
  return React79__namespace.default.createElement(
3410
3473
  Component2,
@@ -3432,6 +3495,7 @@ var init_Box = __esm({
3432
3495
  onClick: isClickable ? handleClick : void 0,
3433
3496
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
3434
3497
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
3498
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
3435
3499
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3436
3500
  ...rest
3437
3501
  },
@@ -4188,6 +4252,11 @@ var init_TextHighlight = __esm({
4188
4252
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
4189
4253
  onMouseLeave?.();
4190
4254
  },
4255
+ onPointerDown: (e) => {
4256
+ if (e.pointerType === "mouse") return;
4257
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
4258
+ onMouseEnter?.();
4259
+ },
4191
4260
  role: "button",
4192
4261
  tabIndex: 0,
4193
4262
  onKeyDown: (e) => {
@@ -4686,6 +4755,7 @@ var init_LawReferenceTooltip = __esm({
4686
4755
  init_Typography();
4687
4756
  init_Divider();
4688
4757
  init_cn();
4758
+ init_useTapReveal();
4689
4759
  positionStyles2 = {
4690
4760
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
4691
4761
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -4707,6 +4777,7 @@ var init_LawReferenceTooltip = __esm({
4707
4777
  const { t } = hooks.useTranslate();
4708
4778
  const [isVisible, setIsVisible] = React79__namespace.default.useState(false);
4709
4779
  const timeoutRef = React79__namespace.default.useRef(null);
4780
+ const triggerRef = React79__namespace.default.useRef(null);
4710
4781
  const handleMouseEnter = () => {
4711
4782
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4712
4783
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -4715,6 +4786,8 @@ var init_LawReferenceTooltip = __esm({
4715
4786
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4716
4787
  setIsVisible(false);
4717
4788
  };
4789
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
4790
+ const open = isVisible || revealed;
4718
4791
  React79__namespace.default.useEffect(() => {
4719
4792
  return () => {
4720
4793
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -4723,6 +4796,7 @@ var init_LawReferenceTooltip = __esm({
4723
4796
  return /* @__PURE__ */ jsxRuntime.jsxs(
4724
4797
  exports.Box,
4725
4798
  {
4799
+ ref: triggerRef,
4726
4800
  as: "span",
4727
4801
  position: "relative",
4728
4802
  display: "inline-block",
@@ -4731,9 +4805,10 @@ var init_LawReferenceTooltip = __esm({
4731
4805
  onMouseLeave: handleMouseLeave,
4732
4806
  onFocus: handleMouseEnter,
4733
4807
  onBlur: handleMouseLeave,
4808
+ onPointerDown: triggerProps.onPointerDown,
4734
4809
  children: [
4735
4810
  children,
4736
- isVisible && /* @__PURE__ */ jsxRuntime.jsxs(
4811
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
4737
4812
  exports.Box,
4738
4813
  {
4739
4814
  padding: "sm",
@@ -9378,6 +9453,36 @@ function useCamera() {
9378
9453
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
9379
9454
  drawFn?.();
9380
9455
  }, []);
9456
+ const handlePointerDown = React79.useCallback((e) => {
9457
+ handleMouseDown(e);
9458
+ }, [handleMouseDown]);
9459
+ const handlePointerUp = React79.useCallback(() => {
9460
+ handleMouseUp();
9461
+ }, [handleMouseUp]);
9462
+ const handlePointerMove = React79.useCallback((e, drawFn) => {
9463
+ return handleMouseMove(e, drawFn);
9464
+ }, [handleMouseMove]);
9465
+ const panBy = React79.useCallback((dx, dy, drawFn) => {
9466
+ cameraRef.current.x -= dx;
9467
+ cameraRef.current.y -= dy;
9468
+ targetCameraRef.current = null;
9469
+ drawFn?.();
9470
+ }, []);
9471
+ const zoomAtPoint = React79.useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
9472
+ const cam = cameraRef.current;
9473
+ const oldZoom = cam.zoom;
9474
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
9475
+ if (newZoom === oldZoom) {
9476
+ drawFn?.();
9477
+ return;
9478
+ }
9479
+ const inv = 1 / oldZoom - 1 / newZoom;
9480
+ cam.x += (centerX - viewportSize.width / 2) * inv;
9481
+ cam.y += (centerY - viewportSize.height / 2) * inv;
9482
+ cam.zoom = newZoom;
9483
+ targetCameraRef.current = null;
9484
+ drawFn?.();
9485
+ }, []);
9381
9486
  const screenToWorld = React79.useCallback((clientX, clientY, canvas, viewportSize) => {
9382
9487
  const rect = canvas.getBoundingClientRect();
9383
9488
  const screenX = clientX - rect.left;
@@ -9409,6 +9514,11 @@ function useCamera() {
9409
9514
  handleMouseMove,
9410
9515
  handleMouseLeave,
9411
9516
  handleWheel,
9517
+ handlePointerDown,
9518
+ handlePointerUp,
9519
+ handlePointerMove,
9520
+ panBy,
9521
+ zoomAtPoint,
9412
9522
  screenToWorld,
9413
9523
  lerpToTarget
9414
9524
  };
@@ -9418,6 +9528,113 @@ var init_useCamera = __esm({
9418
9528
  "use client";
9419
9529
  }
9420
9530
  });
9531
+ function localPoint(canvas, clientX, clientY) {
9532
+ if (!canvas) return { x: clientX, y: clientY };
9533
+ const rect = canvas.getBoundingClientRect();
9534
+ return { x: clientX - rect.left, y: clientY - rect.top };
9535
+ }
9536
+ function useCanvasGestures(options) {
9537
+ const {
9538
+ canvasRef,
9539
+ enabled = true,
9540
+ wheelStep = 1.1,
9541
+ onPointerDown,
9542
+ onPointerMove,
9543
+ onPointerUp,
9544
+ onZoom,
9545
+ onPanDelta,
9546
+ onMultiTouchStart
9547
+ } = options;
9548
+ const pointers = React79.useRef(/* @__PURE__ */ new Map());
9549
+ const pinch = React79.useRef(null);
9550
+ const computePinch = React79.useCallback(() => {
9551
+ const pts = [...pointers.current.values()];
9552
+ const dx = pts[1].x - pts[0].x;
9553
+ const dy = pts[1].y - pts[0].y;
9554
+ return {
9555
+ distance: Math.hypot(dx, dy) || 1,
9556
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
9557
+ };
9558
+ }, []);
9559
+ const handlePointerDown = React79.useCallback(
9560
+ (e) => {
9561
+ if (!enabled) return;
9562
+ e.currentTarget.setPointerCapture(e.pointerId);
9563
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
9564
+ if (pointers.current.size === 2) {
9565
+ onMultiTouchStart?.();
9566
+ pinch.current = computePinch();
9567
+ } else if (pointers.current.size === 1) {
9568
+ onPointerDown?.(e);
9569
+ }
9570
+ },
9571
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
9572
+ );
9573
+ const handlePointerMove = React79.useCallback(
9574
+ (e) => {
9575
+ if (!enabled) return;
9576
+ if (pointers.current.has(e.pointerId)) {
9577
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
9578
+ }
9579
+ if (pointers.current.size >= 2 && pinch.current) {
9580
+ const next = computePinch();
9581
+ const prev = pinch.current;
9582
+ if (next.distance !== prev.distance) {
9583
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
9584
+ }
9585
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
9586
+ pinch.current = next;
9587
+ return;
9588
+ }
9589
+ onPointerMove?.(e);
9590
+ },
9591
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
9592
+ );
9593
+ const endPointer = React79.useCallback(
9594
+ (e, fireUp) => {
9595
+ const wasMulti = pointers.current.size >= 2;
9596
+ pointers.current.delete(e.pointerId);
9597
+ if (pointers.current.size < 2) pinch.current = null;
9598
+ if (wasMulti && pointers.current.size === 1) return;
9599
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
9600
+ },
9601
+ [onPointerUp]
9602
+ );
9603
+ const handlePointerUp = React79.useCallback(
9604
+ (e) => {
9605
+ if (!enabled) return;
9606
+ endPointer(e, true);
9607
+ },
9608
+ [enabled, endPointer]
9609
+ );
9610
+ const handlePointerCancel = React79.useCallback(
9611
+ (e) => {
9612
+ if (!enabled) return;
9613
+ endPointer(e, false);
9614
+ },
9615
+ [enabled, endPointer]
9616
+ );
9617
+ const handleWheel = React79.useCallback(
9618
+ (e) => {
9619
+ if (!enabled) return;
9620
+ e.preventDefault();
9621
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
9622
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
9623
+ },
9624
+ [enabled, canvasRef, wheelStep, onZoom]
9625
+ );
9626
+ return {
9627
+ onPointerDown: handlePointerDown,
9628
+ onPointerMove: handlePointerMove,
9629
+ onPointerUp: handlePointerUp,
9630
+ onPointerCancel: handlePointerCancel,
9631
+ onWheel: handleWheel
9632
+ };
9633
+ }
9634
+ var init_useCanvasGestures = __esm({
9635
+ "hooks/useCanvasGestures.ts"() {
9636
+ }
9637
+ });
9421
9638
 
9422
9639
  // components/game/organisms/utils/spriteSheetConstants.ts
9423
9640
  exports.SHEET_COLUMNS = void 0; exports.SPRITE_SHEET_LAYOUT = void 0;
@@ -9923,13 +10140,13 @@ function IsometricCanvas({
9923
10140
  const {
9924
10141
  cameraRef,
9925
10142
  targetCameraRef,
9926
- isDragging,
9927
10143
  dragDistance,
9928
- handleMouseDown,
9929
- handleMouseUp,
9930
- handleMouseMove,
9931
10144
  handleMouseLeave,
9932
- handleWheel,
10145
+ handlePointerDown,
10146
+ handlePointerUp,
10147
+ handlePointerMove,
10148
+ panBy,
10149
+ zoomAtPoint,
9933
10150
  screenToWorld,
9934
10151
  lerpToTarget
9935
10152
  } = useCamera();
@@ -10364,11 +10581,16 @@ function IsometricCanvas({
10364
10581
  cancelAnimationFrame(rafIdRef.current);
10365
10582
  };
10366
10583
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
10367
- const handleMouseMoveWithCamera = React79.useCallback((e) => {
10368
- if (enableCamera) {
10369
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
10370
- if (wasPanning) return;
10371
- }
10584
+ const singlePointerActiveRef = React79.useRef(false);
10585
+ const handleCanvasPointerDown = React79.useCallback((e) => {
10586
+ singlePointerActiveRef.current = true;
10587
+ if (enableCamera) handlePointerDown(e);
10588
+ }, [enableCamera, handlePointerDown]);
10589
+ const handleCanvasPointerMove = React79.useCallback((e) => {
10590
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
10591
+ }, [enableCamera, handlePointerMove, draw]);
10592
+ const handleCanvasHover = React79.useCallback((e) => {
10593
+ if (singlePointerActiveRef.current) return;
10372
10594
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
10373
10595
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
10374
10596
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -10379,26 +10601,10 @@ function IsometricCanvas({
10379
10601
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
10380
10602
  onTileHover?.(isoPos.x, isoPos.y);
10381
10603
  }
10382
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
10383
- const handleMouseLeaveWithCamera = React79.useCallback(() => {
10384
- handleMouseLeave();
10385
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
10386
- onTileLeave?.();
10387
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
10388
- const handleWheelWithCamera = React79.useCallback((e) => {
10389
- if (enableCamera) {
10390
- handleWheel(e, () => draw(animTimeRef.current));
10391
- }
10392
- }, [enableCamera, handleWheel, draw]);
10393
- React79.useEffect(() => {
10394
- const canvas = canvasRef.current;
10395
- if (!canvas) return;
10396
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
10397
- return () => {
10398
- canvas.removeEventListener("wheel", handleWheelWithCamera);
10399
- };
10400
- }, [handleWheelWithCamera]);
10401
- const handleClick = React79.useCallback((e) => {
10604
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
10605
+ const handleCanvasPointerUp = React79.useCallback((e) => {
10606
+ singlePointerActiveRef.current = false;
10607
+ if (enableCamera) handlePointerUp();
10402
10608
  if (dragDistance() > 5) return;
10403
10609
  if (!canvasRef.current) return;
10404
10610
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -10416,7 +10622,32 @@ function IsometricCanvas({
10416
10622
  onTileClick?.(isoPos.x, isoPos.y);
10417
10623
  }
10418
10624
  }
10419
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
10625
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
10626
+ const handleCanvasPointerLeave = React79.useCallback(() => {
10627
+ handleMouseLeave();
10628
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
10629
+ onTileLeave?.();
10630
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
10631
+ const applyZoom = React79.useCallback((factor, centerX, centerY) => {
10632
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
10633
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
10634
+ const applyPanDelta = React79.useCallback((dx, dy) => {
10635
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
10636
+ }, [enableCamera, panBy, draw]);
10637
+ const cancelSinglePointer = React79.useCallback(() => {
10638
+ singlePointerActiveRef.current = false;
10639
+ if (enableCamera) handlePointerUp();
10640
+ }, [enableCamera, handlePointerUp]);
10641
+ const gestureHandlers = useCanvasGestures({
10642
+ canvasRef,
10643
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
10644
+ onPointerDown: handleCanvasPointerDown,
10645
+ onPointerMove: handleCanvasPointerMove,
10646
+ onPointerUp: handleCanvasPointerUp,
10647
+ onZoom: applyZoom,
10648
+ onPanDelta: applyPanDelta,
10649
+ onMultiTouchStart: cancelSinglePointer
10650
+ });
10420
10651
  if (error) {
10421
10652
  return /* @__PURE__ */ jsxRuntime.jsx(exports.ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
10422
10653
  }
@@ -10448,13 +10679,17 @@ function IsometricCanvas({
10448
10679
  {
10449
10680
  ref: canvasRef,
10450
10681
  "data-testid": "game-canvas",
10451
- onClick: handleClick,
10452
- onMouseDown: enableCamera ? handleMouseDown : void 0,
10453
- onMouseMove: handleMouseMoveWithCamera,
10454
- onMouseUp: enableCamera ? handleMouseUp : void 0,
10455
- onMouseLeave: handleMouseLeaveWithCamera,
10682
+ onPointerDown: gestureHandlers.onPointerDown,
10683
+ onPointerMove: (e) => {
10684
+ gestureHandlers.onPointerMove(e);
10685
+ handleCanvasHover(e);
10686
+ },
10687
+ onPointerUp: gestureHandlers.onPointerUp,
10688
+ onPointerCancel: gestureHandlers.onPointerCancel,
10689
+ onPointerLeave: handleCanvasPointerLeave,
10690
+ onWheel: gestureHandlers.onWheel,
10456
10691
  onContextMenu: (e) => e.preventDefault(),
10457
- className: "cursor-pointer",
10692
+ className: "cursor-pointer touch-none",
10458
10693
  style: {
10459
10694
  width: viewportSize.width,
10460
10695
  height: viewportSize.height
@@ -10508,6 +10743,7 @@ var init_IsometricCanvas = __esm({
10508
10743
  init_ErrorState();
10509
10744
  init_useImageCache();
10510
10745
  init_useCamera();
10746
+ init_useCanvasGestures();
10511
10747
  init_useUnitSpriteAtlas();
10512
10748
  init_verificationRegistry();
10513
10749
  init_isometric();
@@ -13623,6 +13859,10 @@ var init_StateMachineView = __esm({
13623
13859
  const handleMouseLeave2 = () => {
13624
13860
  onHover(null, 0, 0);
13625
13861
  };
13862
+ const handlePointerDown2 = (e) => {
13863
+ if (e.pointerType === "mouse") return;
13864
+ handleMouseEnter2();
13865
+ };
13626
13866
  return /* @__PURE__ */ jsxRuntime.jsxs(
13627
13867
  "g",
13628
13868
  {
@@ -13632,6 +13872,7 @@ var init_StateMachineView = __esm({
13632
13872
  onClick: () => onClick?.(bundle),
13633
13873
  onMouseEnter: handleMouseEnter2,
13634
13874
  onMouseLeave: handleMouseLeave2,
13875
+ onPointerDown: handlePointerDown2,
13635
13876
  style: { pointerEvents: "auto" },
13636
13877
  children: [
13637
13878
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -13749,6 +13990,10 @@ var init_StateMachineView = __esm({
13749
13990
  const handleMouseLeave = React79.useCallback(() => {
13750
13991
  onHover(null, 0, 0);
13751
13992
  }, [onHover]);
13993
+ const handlePointerDown = React79.useCallback((e) => {
13994
+ if (e.pointerType === "mouse") return;
13995
+ handleMouseEnter();
13996
+ }, [handleMouseEnter]);
13752
13997
  const uniqueMarkerId = `arrow-${bundle.id}`;
13753
13998
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
13754
13999
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -13760,6 +14005,7 @@ var init_StateMachineView = __esm({
13760
14005
  onClick: () => onClick?.(bundle),
13761
14006
  onMouseEnter: handleMouseEnter,
13762
14007
  onMouseLeave: handleMouseLeave,
14008
+ onPointerDown: handlePointerDown,
13763
14009
  style: { pointerEvents: "auto" },
13764
14010
  children: [
13765
14011
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -22532,10 +22778,84 @@ function SubMenu({
22532
22778
  );
22533
22779
  return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
22534
22780
  }
22781
+ function MenuItemRow({
22782
+ item,
22783
+ itemId,
22784
+ hasSubMenu,
22785
+ isDanger,
22786
+ direction,
22787
+ isSubMenuOpen,
22788
+ activeSubMenuRef,
22789
+ eventBus,
22790
+ onItemClick,
22791
+ openSubMenu
22792
+ }) {
22793
+ const rowRef = React79.useRef(null);
22794
+ const { triggerProps } = useTapReveal({
22795
+ enabled: hasSubMenu,
22796
+ onReveal: () => openSubMenu(itemId, rowRef.current),
22797
+ refs: [rowRef]
22798
+ });
22799
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
22800
+ /* @__PURE__ */ jsxRuntime.jsx(
22801
+ exports.Box,
22802
+ {
22803
+ ref: rowRef,
22804
+ as: "button",
22805
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
22806
+ "aria-disabled": item.disabled || void 0,
22807
+ onMouseEnter: (e) => {
22808
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
22809
+ },
22810
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
22811
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22812
+ className: cn(
22813
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22814
+ "text-sm transition-colors",
22815
+ "hover:bg-muted",
22816
+ "focus:outline-none focus:bg-muted",
22817
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22818
+ item.disabled && "cursor-not-allowed",
22819
+ isDanger && "text-error hover:bg-error/10"
22820
+ ),
22821
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22822
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
22823
+ /* @__PURE__ */ jsxRuntime.jsx(
22824
+ exports.Typography,
22825
+ {
22826
+ variant: "small",
22827
+ className: cn("flex-1", isDanger && "text-red-600"),
22828
+ children: item.label
22829
+ }
22830
+ ),
22831
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", children: item.badge }),
22832
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
22833
+ exports.Icon,
22834
+ {
22835
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
22836
+ size: "sm",
22837
+ className: "flex-shrink-0"
22838
+ }
22839
+ )
22840
+ ] })
22841
+ }
22842
+ ),
22843
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22844
+ SubMenu,
22845
+ {
22846
+ items: item.subMenu,
22847
+ itemRef: activeSubMenuRef,
22848
+ direction,
22849
+ eventBus
22850
+ }
22851
+ )
22852
+ ] });
22853
+ }
22535
22854
  var MENU_GAP, menuContainerStyles; exports.Menu = void 0;
22536
22855
  var init_Menu = __esm({
22537
22856
  "components/core/molecules/Menu.tsx"() {
22538
22857
  "use client";
22858
+ init_useTapReveal();
22539
22859
  init_Box();
22540
22860
  init_Icon();
22541
22861
  init_Divider();
@@ -22590,6 +22910,10 @@ var init_Menu = __esm({
22590
22910
  setIsOpen(false);
22591
22911
  }
22592
22912
  };
22913
+ const openSubMenu = (itemId, el) => {
22914
+ setActiveSubMenu(itemId);
22915
+ setActiveSubMenuRef(el);
22916
+ };
22593
22917
  React79.useEffect(() => {
22594
22918
  if (isOpen) {
22595
22919
  updatePosition();
@@ -22633,61 +22957,22 @@ var init_Menu = __esm({
22633
22957
  if (isDivider) {
22634
22958
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { className: "my-1" }, `divider-${index}`);
22635
22959
  }
22636
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
22637
- /* @__PURE__ */ jsxRuntime.jsx(
22638
- exports.Box,
22639
- {
22640
- as: "button",
22641
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
22642
- "aria-disabled": item.disabled || void 0,
22643
- onMouseEnter: (e) => {
22644
- if (hasSubMenu) {
22645
- setActiveSubMenu(itemId);
22646
- setActiveSubMenuRef(e.currentTarget);
22647
- }
22648
- },
22649
- "data-testid": item.event ? `action-${item.event}` : void 0,
22650
- className: cn(
22651
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22652
- "text-sm transition-colors",
22653
- "hover:bg-muted",
22654
- "focus:outline-none focus:bg-muted",
22655
- "disabled:opacity-50 disabled:cursor-not-allowed",
22656
- item.disabled && "cursor-not-allowed",
22657
- isDanger && "text-error hover:bg-error/10"
22658
- ),
22659
- children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22660
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
22661
- /* @__PURE__ */ jsxRuntime.jsx(
22662
- exports.Typography,
22663
- {
22664
- variant: "small",
22665
- className: cn("flex-1", isDanger && "text-red-600"),
22666
- children: item.label
22667
- }
22668
- ),
22669
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", children: item.badge }),
22670
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
22671
- exports.Icon,
22672
- {
22673
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
22674
- size: "sm",
22675
- className: "flex-shrink-0"
22676
- }
22677
- )
22678
- ] })
22679
- }
22680
- ),
22681
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22682
- SubMenu,
22683
- {
22684
- items: item.subMenu,
22685
- itemRef: activeSubMenuRef,
22686
- direction,
22687
- eventBus
22688
- }
22689
- )
22690
- ] }, itemId);
22960
+ return /* @__PURE__ */ jsxRuntime.jsx(
22961
+ MenuItemRow,
22962
+ {
22963
+ item,
22964
+ itemId,
22965
+ hasSubMenu,
22966
+ isDanger,
22967
+ direction,
22968
+ isSubMenuOpen: activeSubMenu === itemId,
22969
+ activeSubMenuRef,
22970
+ eventBus,
22971
+ onItemClick: handleItemClick,
22972
+ openSubMenu
22973
+ },
22974
+ itemId
22975
+ );
22691
22976
  });
22692
22977
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
22693
22978
  "div",
@@ -25932,6 +26217,7 @@ var init_Popover = __esm({
25932
26217
  "use client";
25933
26218
  init_Typography();
25934
26219
  init_cn();
26220
+ init_useTapReveal();
25935
26221
  arrowClasses = {
25936
26222
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
25937
26223
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -26002,18 +26288,32 @@ var init_Popover = __esm({
26002
26288
  document.addEventListener("mousedown", handleClickOutside);
26003
26289
  return () => document.removeEventListener("mousedown", handleClickOutside);
26004
26290
  }, [isOpen, trigger]);
26005
- const triggerProps = trigger === "click" ? {
26291
+ const { triggerProps: tapTriggerProps } = useTapReveal({
26292
+ enabled: trigger === "hover",
26293
+ onReveal: handleOpen,
26294
+ onDismiss: handleClose,
26295
+ refs: [triggerRef, popoverRef]
26296
+ });
26297
+ const handlerProps = trigger === "click" ? {
26006
26298
  onClick: handleToggle
26007
26299
  } : {
26008
26300
  onMouseEnter: handleOpen,
26009
- onMouseLeave: handleClose
26301
+ onMouseLeave: handleClose,
26302
+ onPointerDown: tapTriggerProps.onPointerDown
26010
26303
  };
26011
26304
  const childElement = React79__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
26305
+ const childPointerDown = childElement.props.onPointerDown;
26012
26306
  const triggerElement = React79__namespace.default.cloneElement(
26013
26307
  childElement,
26014
26308
  {
26015
26309
  ref: triggerRef,
26016
- ...triggerProps
26310
+ ...handlerProps,
26311
+ ...trigger === "hover" ? {
26312
+ onPointerDown: (e) => {
26313
+ tapTriggerProps.onPointerDown(e);
26314
+ childPointerDown?.(e);
26315
+ }
26316
+ } : void 0
26017
26317
  }
26018
26318
  );
26019
26319
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -26794,6 +27094,7 @@ var init_Tooltip = __esm({
26794
27094
  "use client";
26795
27095
  init_Typography();
26796
27096
  init_cn();
27097
+ init_useTapReveal();
26797
27098
  TRIGGER_GAP2 = 8;
26798
27099
  arrowClasses2 = {
26799
27100
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -26838,6 +27139,11 @@ var init_Tooltip = __esm({
26838
27139
  setIsVisible(false);
26839
27140
  }, hideDelay);
26840
27141
  };
27142
+ const { triggerProps } = useTapReveal({
27143
+ onReveal: handleMouseEnter,
27144
+ onDismiss: handleMouseLeave,
27145
+ refs: [triggerRef, tooltipRef]
27146
+ });
26841
27147
  React79.useLayoutEffect(() => {
26842
27148
  if (isVisible) {
26843
27149
  updatePosition();
@@ -26850,12 +27156,17 @@ var init_Tooltip = __esm({
26850
27156
  };
26851
27157
  }, []);
26852
27158
  const triggerElement = React79__namespace.default.isValidElement(children) ? children : /* @__PURE__ */ jsxRuntime.jsx("span", { children });
27159
+ const childPointerDown = triggerElement.props.onPointerDown;
26853
27160
  const trigger = React79__namespace.default.cloneElement(triggerElement, {
26854
27161
  ref: triggerRef,
26855
27162
  onMouseEnter: handleMouseEnter,
26856
27163
  onMouseLeave: handleMouseLeave,
26857
27164
  onFocus: handleMouseEnter,
26858
- onBlur: handleMouseLeave
27165
+ onBlur: handleMouseLeave,
27166
+ onPointerDown: (e) => {
27167
+ triggerProps.onPointerDown(e);
27168
+ childPointerDown?.(e);
27169
+ }
26859
27170
  });
26860
27171
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
26861
27172
  "div",
@@ -30528,6 +30839,14 @@ var init_GraphView = __esm({
30528
30839
  },
30529
30840
  [onNodeClick]
30530
30841
  );
30842
+ const handleNodePointerDown = React79.useCallback(
30843
+ (e, node) => {
30844
+ if (e.pointerType === "mouse") return;
30845
+ handleNodeMouseEnter(node);
30846
+ handleNodeClickInternal(node);
30847
+ },
30848
+ [handleNodeMouseEnter, handleNodeClickInternal]
30849
+ );
30531
30850
  if (nodes.length === 0) {
30532
30851
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("flex items-center justify-center", className), style: { width: w, height: h }, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-muted-foreground text-sm", children: t("display.noGraphData") }) });
30533
30852
  }
@@ -30587,6 +30906,7 @@ var init_GraphView = __esm({
30587
30906
  onMouseEnter: () => handleNodeMouseEnter(node),
30588
30907
  onMouseLeave: handleNodeMouseLeave,
30589
30908
  onClick: () => handleNodeClickInternal(node),
30909
+ onPointerDown: (e) => handleNodePointerDown(e, node),
30590
30910
  children: [
30591
30911
  /* @__PURE__ */ jsxRuntime.jsx(
30592
30912
  "circle",
@@ -30667,13 +30987,13 @@ var init_MapView = __esm({
30667
30987
  shadowSize: [41, 41]
30668
30988
  });
30669
30989
  L.Marker.prototype.options.icon = defaultIcon;
30670
- const { useEffect: useEffect82, useRef: useRef81, useCallback: useCallback125, useState: useState111 } = React79__namespace.default;
30990
+ const { useEffect: useEffect83, useRef: useRef83, useCallback: useCallback127, useState: useState112 } = React79__namespace.default;
30671
30991
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
30672
30992
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
30673
30993
  function MapUpdater({ centerLat, centerLng, zoom }) {
30674
30994
  const map = useMap();
30675
- const prevRef = useRef81({ centerLat, centerLng, zoom });
30676
- useEffect82(() => {
30995
+ const prevRef = useRef83({ centerLat, centerLng, zoom });
30996
+ useEffect83(() => {
30677
30997
  const prev = prevRef.current;
30678
30998
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
30679
30999
  map.setView([centerLat, centerLng], zoom);
@@ -30684,7 +31004,7 @@ var init_MapView = __esm({
30684
31004
  }
30685
31005
  function MapClickHandler({ onMapClick }) {
30686
31006
  const map = useMap();
30687
- useEffect82(() => {
31007
+ useEffect83(() => {
30688
31008
  if (!onMapClick) return;
30689
31009
  const handler = (e) => {
30690
31010
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -30712,8 +31032,8 @@ var init_MapView = __esm({
30712
31032
  showAttribution = true
30713
31033
  }) {
30714
31034
  const eventBus = useEventBus3();
30715
- const [clickedPosition, setClickedPosition] = useState111(null);
30716
- const handleMapClick = useCallback125((lat, lng) => {
31035
+ const [clickedPosition, setClickedPosition] = useState112(null);
31036
+ const handleMapClick = useCallback127((lat, lng) => {
30717
31037
  if (showClickedPin) {
30718
31038
  setClickedPosition({ lat, lng });
30719
31039
  }
@@ -30722,7 +31042,7 @@ var init_MapView = __esm({
30722
31042
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
30723
31043
  }
30724
31044
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
30725
- const handleMarkerClick = useCallback125((marker) => {
31045
+ const handleMarkerClick = useCallback127((marker) => {
30726
31046
  onMarkerClick?.(marker);
30727
31047
  if (markerClickEvent) {
30728
31048
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -31063,7 +31383,7 @@ var init_StarRating = __esm({
31063
31383
  "aria-valuenow": value,
31064
31384
  tabIndex: readOnly ? void 0 : 0,
31065
31385
  onKeyDown: handleKeyDown,
31066
- onMouseLeave: () => setHoverValue(null),
31386
+ onPointerLeave: () => setHoverValue(null),
31067
31387
  children: Array.from({ length: max }, (_, i) => {
31068
31388
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
31069
31389
  const isFull = fillLevel >= 1;
@@ -31073,7 +31393,7 @@ var init_StarRating = __esm({
31073
31393
  {
31074
31394
  className: "relative inline-block",
31075
31395
  onClick: () => handleStarClick(i, false),
31076
- onMouseMove: (e) => {
31396
+ onPointerMove: (e) => {
31077
31397
  if (readOnly) return;
31078
31398
  const rect = e.currentTarget.getBoundingClientRect();
31079
31399
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -34963,7 +35283,7 @@ var init_PositionedCanvas = __esm({
34963
35283
  {
34964
35284
  ref: containerRef,
34965
35285
  "data-testid": "positioned-canvas",
34966
- className: "relative bg-background border border-border rounded-container overflow-hidden",
35286
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
34967
35287
  style: { width, height },
34968
35288
  onClick: handleContainerClick,
34969
35289
  children: items.map((item) => {
@@ -39132,6 +39452,7 @@ var init_GraphCanvas = __esm({
39132
39452
  init_ErrorState();
39133
39453
  init_EmptyState();
39134
39454
  init_useEventBus();
39455
+ init_useCanvasGestures();
39135
39456
  GROUP_COLORS2 = [
39136
39457
  "var(--color-primary)",
39137
39458
  "var(--color-success)",
@@ -39169,6 +39490,10 @@ var init_GraphCanvas = __esm({
39169
39490
  const animRef = React79.useRef(0);
39170
39491
  const [zoom, setZoom] = React79.useState(1);
39171
39492
  const [offset, setOffset] = React79.useState({ x: 0, y: 0 });
39493
+ const zoomRef = React79.useRef(zoom);
39494
+ zoomRef.current = zoom;
39495
+ const offsetRef = React79.useRef(offset);
39496
+ offsetRef.current = offset;
39172
39497
  const [hoveredNode, setHoveredNode] = React79.useState(null);
39173
39498
  const nodesRef = React79.useRef([]);
39174
39499
  const [, forceUpdate] = React79.useState(0);
@@ -39428,25 +39753,27 @@ var init_GraphCanvas = __esm({
39428
39753
  setZoom(1);
39429
39754
  setOffset({ x: 0, y: 0 });
39430
39755
  }, []);
39431
- const handleWheel = React79.useCallback(
39432
- (e) => {
39433
- if (!interactive) return;
39434
- e.preventDefault();
39435
- const coords = toCoords(e);
39436
- if (!coords) return;
39437
- const oldZoom = zoom;
39438
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
39439
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39440
- if (newZoom === oldZoom) return;
39441
- setOffset((o) => ({
39442
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
39443
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
39444
- }));
39445
- setZoom(newZoom);
39446
- },
39447
- [interactive, toCoords, zoom]
39448
- );
39449
- const handleMouseDown = React79.useCallback(
39756
+ const applyZoom = React79.useCallback((factor, cx, cy) => {
39757
+ if (!interactive) return;
39758
+ const oldZoom = zoomRef.current;
39759
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39760
+ if (newZoom === oldZoom) return;
39761
+ const o = offsetRef.current;
39762
+ setOffset({
39763
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
39764
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
39765
+ });
39766
+ setZoom(newZoom);
39767
+ }, [interactive]);
39768
+ const applyPanDelta = React79.useCallback((dx, dy) => {
39769
+ if (!interactive) return;
39770
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
39771
+ }, [interactive]);
39772
+ const cancelSinglePointer = React79.useCallback(() => {
39773
+ interactionRef.current.mode = "none";
39774
+ interactionRef.current.dragNodeId = null;
39775
+ }, []);
39776
+ const handlePointerDown = React79.useCallback(
39450
39777
  (e) => {
39451
39778
  const coords = toCoords(e);
39452
39779
  if (!coords) return;
@@ -39468,7 +39795,7 @@ var init_GraphCanvas = __esm({
39468
39795
  },
39469
39796
  [toCoords, nodeAt, draggable, interactive, offset]
39470
39797
  );
39471
- const handleMouseMove = React79.useCallback(
39798
+ const handlePointerMove = React79.useCallback(
39472
39799
  (e) => {
39473
39800
  const state = interactionRef.current;
39474
39801
  if (state.mode === "panning") {
@@ -39497,7 +39824,7 @@ var init_GraphCanvas = __esm({
39497
39824
  },
39498
39825
  [toCoords, nodeAt]
39499
39826
  );
39500
- const handleMouseUp = React79.useCallback(
39827
+ const handlePointerUp = React79.useCallback(
39501
39828
  (e) => {
39502
39829
  const state = interactionRef.current;
39503
39830
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -39514,11 +39841,19 @@ var init_GraphCanvas = __esm({
39514
39841
  },
39515
39842
  [toCoords, nodeAt, handleNodeClick]
39516
39843
  );
39517
- const handleMouseLeave = React79.useCallback(() => {
39518
- interactionRef.current.mode = "none";
39519
- interactionRef.current.dragNodeId = null;
39844
+ const handlePointerLeave = React79.useCallback(() => {
39520
39845
  setHoveredNode(null);
39521
39846
  }, []);
39847
+ const gestureHandlers = useCanvasGestures({
39848
+ canvasRef,
39849
+ enabled: interactive || draggable,
39850
+ onPointerDown: handlePointerDown,
39851
+ onPointerMove: handlePointerMove,
39852
+ onPointerUp: handlePointerUp,
39853
+ onZoom: applyZoom,
39854
+ onPanDelta: applyPanDelta,
39855
+ onMultiTouchStart: cancelSinglePointer
39856
+ });
39522
39857
  const handleDoubleClick = React79.useCallback(
39523
39858
  (e) => {
39524
39859
  const coords = toCoords(e);
@@ -39587,13 +39922,14 @@ var init_GraphCanvas = __esm({
39587
39922
  ref: canvasRef,
39588
39923
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39589
39924
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39590
- className: "w-full cursor-grab active:cursor-grabbing",
39925
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
39591
39926
  style: { height },
39592
- onWheel: handleWheel,
39593
- onMouseDown: handleMouseDown,
39594
- onMouseMove: handleMouseMove,
39595
- onMouseUp: handleMouseUp,
39596
- onMouseLeave: handleMouseLeave,
39927
+ onPointerDown: gestureHandlers.onPointerDown,
39928
+ onPointerMove: gestureHandlers.onPointerMove,
39929
+ onPointerUp: gestureHandlers.onPointerUp,
39930
+ onPointerCancel: gestureHandlers.onPointerCancel,
39931
+ onPointerLeave: handlePointerLeave,
39932
+ onWheel: gestureHandlers.onWheel,
39597
39933
  onDoubleClick: handleDoubleClick
39598
39934
  }
39599
39935
  ) }),
@@ -44392,6 +44728,9 @@ var init_GameCanvas3D2 = __esm({
44392
44728
  target: cameraTarget,
44393
44729
  enableDamping: true,
44394
44730
  dampingFactor: 0.05,
44731
+ enableZoom: true,
44732
+ enablePan: true,
44733
+ touches: { ONE: THREE3__namespace.TOUCH.ROTATE, TWO: THREE3__namespace.TOUCH.DOLLY_PAN },
44395
44734
  minDistance: 2,
44396
44735
  maxDistance: 100,
44397
44736
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -49877,20 +50216,21 @@ var init_SplitPane = __esm({
49877
50216
  const [ratio, setRatio] = React79.useState(initialRatio);
49878
50217
  const containerRef = React79.useRef(null);
49879
50218
  const isDragging = React79.useRef(false);
49880
- const handleMouseDown = React79.useCallback(
50219
+ const handlePointerDown = React79.useCallback(
49881
50220
  (e) => {
49882
50221
  if (!resizable) return;
49883
50222
  e.preventDefault();
49884
50223
  isDragging.current = true;
49885
- const handleMouseMove = (e2) => {
50224
+ e.currentTarget.setPointerCapture(e.pointerId);
50225
+ const handlePointerMove = (ev) => {
49886
50226
  if (!isDragging.current || !containerRef.current) return;
49887
50227
  const rect = containerRef.current.getBoundingClientRect();
49888
50228
  let newRatio;
49889
50229
  if (direction === "horizontal") {
49890
- const x = e2.clientX - rect.left;
50230
+ const x = ev.clientX - rect.left;
49891
50231
  newRatio = x / rect.width * 100;
49892
50232
  } else {
49893
- const y = e2.clientY - rect.top;
50233
+ const y = ev.clientY - rect.top;
49894
50234
  newRatio = y / rect.height * 100;
49895
50235
  }
49896
50236
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -49898,13 +50238,15 @@ var init_SplitPane = __esm({
49898
50238
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
49899
50239
  setRatio(newRatio);
49900
50240
  };
49901
- const handleMouseUp = () => {
50241
+ const handlePointerUp = () => {
49902
50242
  isDragging.current = false;
49903
- document.removeEventListener("mousemove", handleMouseMove);
49904
- document.removeEventListener("mouseup", handleMouseUp);
50243
+ document.removeEventListener("pointermove", handlePointerMove);
50244
+ document.removeEventListener("pointerup", handlePointerUp);
50245
+ document.removeEventListener("pointercancel", handlePointerUp);
49905
50246
  };
49906
- document.addEventListener("mousemove", handleMouseMove);
49907
- document.addEventListener("mouseup", handleMouseUp);
50247
+ document.addEventListener("pointermove", handlePointerMove);
50248
+ document.addEventListener("pointerup", handlePointerUp);
50249
+ document.addEventListener("pointercancel", handlePointerUp);
49908
50250
  },
49909
50251
  [direction, minSize, resizable]
49910
50252
  );
@@ -49933,9 +50275,9 @@ var init_SplitPane = __esm({
49933
50275
  resizable && /* @__PURE__ */ jsxRuntime.jsx(
49934
50276
  "div",
49935
50277
  {
49936
- onMouseDown: handleMouseDown,
50278
+ onPointerDown: handlePointerDown,
49937
50279
  className: cn(
49938
- "flex-shrink-0 bg-border transition-colors",
50280
+ "flex-shrink-0 bg-border transition-colors touch-none",
49939
50281
  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"
49940
50282
  )
49941
50283
  }