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