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