@almadar/ui 5.62.0 → 5.63.2

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 +526 -161
  2. package/dist/avl/index.js +526 -161
  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 +526 -162
  11. package/dist/components/index.js +526 -162
  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 +526 -161
  32. package/dist/providers/index.js +526 -161
  33. package/dist/runtime/index.cjs +526 -161
  34. package/dist/runtime/index.js +526 -161
  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) => {
@@ -40882,6 +41203,13 @@ var init_DocumentViewer = __esm({
40882
41203
  DocumentViewer.displayName = "DocumentViewer";
40883
41204
  }
40884
41205
  });
41206
+ function measureLabelWidth(text) {
41207
+ if (typeof document === "undefined") return text.length * 6;
41208
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
41209
+ if (!labelMeasureCtx) return text.length * 6;
41210
+ labelMeasureCtx.font = "10px system-ui";
41211
+ return labelMeasureCtx.measureText(text).width;
41212
+ }
40885
41213
  function getGroupColor(group, groups) {
40886
41214
  if (!group) return GROUP_COLORS2[0];
40887
41215
  const idx = groups.indexOf(group);
@@ -40893,7 +41221,7 @@ function resolveColor2(color, el) {
40893
41221
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
40894
41222
  return resolved || (m[2]?.trim() ?? "#888888");
40895
41223
  }
40896
- var GROUP_COLORS2, GraphCanvas;
41224
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
40897
41225
  var init_GraphCanvas = __esm({
40898
41226
  "components/core/molecules/GraphCanvas.tsx"() {
40899
41227
  "use client";
@@ -40904,6 +41232,7 @@ var init_GraphCanvas = __esm({
40904
41232
  init_ErrorState();
40905
41233
  init_EmptyState();
40906
41234
  init_useEventBus();
41235
+ init_useCanvasGestures();
40907
41236
  GROUP_COLORS2 = [
40908
41237
  "var(--color-primary)",
40909
41238
  "var(--color-success)",
@@ -40912,6 +41241,7 @@ var init_GraphCanvas = __esm({
40912
41241
  "var(--color-info)",
40913
41242
  "var(--color-accent)"
40914
41243
  ];
41244
+ labelMeasureCtx = null;
40915
41245
  GraphCanvas = ({
40916
41246
  title,
40917
41247
  nodes: propNodes = [],
@@ -40941,6 +41271,10 @@ var init_GraphCanvas = __esm({
40941
41271
  const animRef = React93.useRef(0);
40942
41272
  const [zoom, setZoom] = React93.useState(1);
40943
41273
  const [offset, setOffset] = React93.useState({ x: 0, y: 0 });
41274
+ const zoomRef = React93.useRef(zoom);
41275
+ zoomRef.current = zoom;
41276
+ const offsetRef = React93.useRef(offset);
41277
+ offsetRef.current = offset;
40944
41278
  const [hoveredNode, setHoveredNode] = React93.useState(null);
40945
41279
  const nodesRef = React93.useRef([]);
40946
41280
  const [, forceUpdate] = React93.useState(0);
@@ -41088,22 +41422,36 @@ var init_GraphCanvas = __esm({
41088
41422
  node.x = Math.max(30, Math.min(w - 30, node.x));
41089
41423
  node.y = Math.max(30, Math.min(h - 30, node.y));
41090
41424
  }
41425
+ const LABEL_GAP = 12;
41426
+ const LABEL_H = 12;
41427
+ const pad = nodeSpacing / 2;
41091
41428
  for (let i = 0; i < nodes.length; i++) {
41092
41429
  for (let j = i + 1; j < nodes.length; j++) {
41093
41430
  const a = nodes[i];
41094
41431
  const b = nodes[j];
41095
- const dx = b.x - a.x;
41096
- const dy = b.y - a.y;
41097
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
41098
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
41099
- if (dist < minDist) {
41100
- const push2 = (minDist - dist) / 2;
41101
- const ux = dx / dist;
41102
- const uy = dy / dist;
41103
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
41104
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
41105
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
41106
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
41432
+ const ar = a.size || 8;
41433
+ const br = b.size || 8;
41434
+ if (showLabels) {
41435
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
41436
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
41437
+ }
41438
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
41439
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
41440
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
41441
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
41442
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
41443
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
41444
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
41445
+ if (overlapX > 0 && overlapY > 0) {
41446
+ if (overlapX <= overlapY) {
41447
+ const push2 = overlapX / 2 * (b.x >= a.x ? 1 : -1);
41448
+ a.x = Math.max(30, Math.min(w - 30, a.x - push2));
41449
+ b.x = Math.max(30, Math.min(w - 30, b.x + push2));
41450
+ } else {
41451
+ const push2 = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
41452
+ a.y = Math.max(30, Math.min(h - 30, a.y - push2));
41453
+ b.y = Math.max(30, Math.min(h - 30, b.y + push2));
41454
+ }
41107
41455
  }
41108
41456
  }
41109
41457
  }
@@ -41120,7 +41468,7 @@ var init_GraphCanvas = __esm({
41120
41468
  return () => {
41121
41469
  cancelAnimationFrame(animRef.current);
41122
41470
  };
41123
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
41471
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
41124
41472
  React93.useEffect(() => {
41125
41473
  const canvas = canvasRef.current;
41126
41474
  if (!canvas) return;
@@ -41200,25 +41548,27 @@ var init_GraphCanvas = __esm({
41200
41548
  setZoom(1);
41201
41549
  setOffset({ x: 0, y: 0 });
41202
41550
  }, []);
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(
41551
+ const applyZoom = React93.useCallback((factor, cx, cy) => {
41552
+ if (!interactive) return;
41553
+ const oldZoom = zoomRef.current;
41554
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
41555
+ if (newZoom === oldZoom) return;
41556
+ const o = offsetRef.current;
41557
+ setOffset({
41558
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
41559
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
41560
+ });
41561
+ setZoom(newZoom);
41562
+ }, [interactive]);
41563
+ const applyPanDelta = React93.useCallback((dx, dy) => {
41564
+ if (!interactive) return;
41565
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
41566
+ }, [interactive]);
41567
+ const cancelSinglePointer = React93.useCallback(() => {
41568
+ interactionRef.current.mode = "none";
41569
+ interactionRef.current.dragNodeId = null;
41570
+ }, []);
41571
+ const handlePointerDown = React93.useCallback(
41222
41572
  (e) => {
41223
41573
  const coords = toCoords(e);
41224
41574
  if (!coords) return;
@@ -41240,7 +41590,7 @@ var init_GraphCanvas = __esm({
41240
41590
  },
41241
41591
  [toCoords, nodeAt, draggable, interactive, offset]
41242
41592
  );
41243
- const handleMouseMove = React93.useCallback(
41593
+ const handlePointerMove = React93.useCallback(
41244
41594
  (e) => {
41245
41595
  const state = interactionRef.current;
41246
41596
  if (state.mode === "panning") {
@@ -41269,7 +41619,7 @@ var init_GraphCanvas = __esm({
41269
41619
  },
41270
41620
  [toCoords, nodeAt]
41271
41621
  );
41272
- const handleMouseUp = React93.useCallback(
41622
+ const handlePointerUp = React93.useCallback(
41273
41623
  (e) => {
41274
41624
  const state = interactionRef.current;
41275
41625
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -41286,11 +41636,19 @@ var init_GraphCanvas = __esm({
41286
41636
  },
41287
41637
  [toCoords, nodeAt, handleNodeClick]
41288
41638
  );
41289
- const handleMouseLeave = React93.useCallback(() => {
41290
- interactionRef.current.mode = "none";
41291
- interactionRef.current.dragNodeId = null;
41639
+ const handlePointerLeave = React93.useCallback(() => {
41292
41640
  setHoveredNode(null);
41293
41641
  }, []);
41642
+ const gestureHandlers = useCanvasGestures({
41643
+ canvasRef,
41644
+ enabled: interactive || draggable,
41645
+ onPointerDown: handlePointerDown,
41646
+ onPointerMove: handlePointerMove,
41647
+ onPointerUp: handlePointerUp,
41648
+ onZoom: applyZoom,
41649
+ onPanDelta: applyPanDelta,
41650
+ onMultiTouchStart: cancelSinglePointer
41651
+ });
41294
41652
  const handleDoubleClick = React93.useCallback(
41295
41653
  (e) => {
41296
41654
  const coords = toCoords(e);
@@ -41359,13 +41717,14 @@ var init_GraphCanvas = __esm({
41359
41717
  ref: canvasRef,
41360
41718
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
41361
41719
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
41362
- className: "w-full cursor-grab active:cursor-grabbing",
41720
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
41363
41721
  style: { height },
41364
- onWheel: handleWheel,
41365
- onMouseDown: handleMouseDown,
41366
- onMouseMove: handleMouseMove,
41367
- onMouseUp: handleMouseUp,
41368
- onMouseLeave: handleMouseLeave,
41722
+ onPointerDown: gestureHandlers.onPointerDown,
41723
+ onPointerMove: gestureHandlers.onPointerMove,
41724
+ onPointerUp: gestureHandlers.onPointerUp,
41725
+ onPointerCancel: gestureHandlers.onPointerCancel,
41726
+ onPointerLeave: handlePointerLeave,
41727
+ onWheel: gestureHandlers.onWheel,
41369
41728
  onDoubleClick: handleDoubleClick
41370
41729
  }
41371
41730
  ) }),
@@ -45837,6 +46196,9 @@ var init_GameCanvas3D2 = __esm({
45837
46196
  target: cameraTarget,
45838
46197
  enableDamping: true,
45839
46198
  dampingFactor: 0.05,
46199
+ enableZoom: true,
46200
+ enablePan: true,
46201
+ touches: { ONE: THREE3__namespace.TOUCH.ROTATE, TWO: THREE3__namespace.TOUCH.DOLLY_PAN },
45840
46202
  minDistance: 2,
45841
46203
  maxDistance: 100,
45842
46204
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -51167,20 +51529,21 @@ var init_SplitPane = __esm({
51167
51529
  const [ratio, setRatio] = React93.useState(initialRatio);
51168
51530
  const containerRef = React93.useRef(null);
51169
51531
  const isDragging = React93.useRef(false);
51170
- const handleMouseDown = React93.useCallback(
51532
+ const handlePointerDown = React93.useCallback(
51171
51533
  (e) => {
51172
51534
  if (!resizable) return;
51173
51535
  e.preventDefault();
51174
51536
  isDragging.current = true;
51175
- const handleMouseMove = (e2) => {
51537
+ e.currentTarget.setPointerCapture(e.pointerId);
51538
+ const handlePointerMove = (ev) => {
51176
51539
  if (!isDragging.current || !containerRef.current) return;
51177
51540
  const rect = containerRef.current.getBoundingClientRect();
51178
51541
  let newRatio;
51179
51542
  if (direction === "horizontal") {
51180
- const x = e2.clientX - rect.left;
51543
+ const x = ev.clientX - rect.left;
51181
51544
  newRatio = x / rect.width * 100;
51182
51545
  } else {
51183
- const y = e2.clientY - rect.top;
51546
+ const y = ev.clientY - rect.top;
51184
51547
  newRatio = y / rect.height * 100;
51185
51548
  }
51186
51549
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -51188,13 +51551,15 @@ var init_SplitPane = __esm({
51188
51551
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
51189
51552
  setRatio(newRatio);
51190
51553
  };
51191
- const handleMouseUp = () => {
51554
+ const handlePointerUp = () => {
51192
51555
  isDragging.current = false;
51193
- document.removeEventListener("mousemove", handleMouseMove);
51194
- document.removeEventListener("mouseup", handleMouseUp);
51556
+ document.removeEventListener("pointermove", handlePointerMove);
51557
+ document.removeEventListener("pointerup", handlePointerUp);
51558
+ document.removeEventListener("pointercancel", handlePointerUp);
51195
51559
  };
51196
- document.addEventListener("mousemove", handleMouseMove);
51197
- document.addEventListener("mouseup", handleMouseUp);
51560
+ document.addEventListener("pointermove", handlePointerMove);
51561
+ document.addEventListener("pointerup", handlePointerUp);
51562
+ document.addEventListener("pointercancel", handlePointerUp);
51198
51563
  },
51199
51564
  [direction, minSize, resizable]
51200
51565
  );
@@ -51223,9 +51588,9 @@ var init_SplitPane = __esm({
51223
51588
  resizable && /* @__PURE__ */ jsxRuntime.jsx(
51224
51589
  "div",
51225
51590
  {
51226
- onMouseDown: handleMouseDown,
51591
+ onPointerDown: handlePointerDown,
51227
51592
  className: cn(
51228
- "flex-shrink-0 bg-border transition-colors",
51593
+ "flex-shrink-0 bg-border transition-colors touch-none",
51229
51594
  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
51595
  )
51231
51596
  }