@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
@@ -3202,12 +3202,60 @@ var init_Avatar = __esm({
3202
3202
  Avatar.displayName = "Avatar";
3203
3203
  }
3204
3204
  });
3205
+ function useTapReveal(options = {}) {
3206
+ const { onReveal, onDismiss, refs, enabled = true } = options;
3207
+ const [revealed, setRevealed] = useState(false);
3208
+ const onRevealRef = useRef(onReveal);
3209
+ const onDismissRef = useRef(onDismiss);
3210
+ onRevealRef.current = onReveal;
3211
+ onDismissRef.current = onDismiss;
3212
+ const reveal = useCallback(() => {
3213
+ setRevealed((wasRevealed) => {
3214
+ if (!wasRevealed) onRevealRef.current?.();
3215
+ return true;
3216
+ });
3217
+ }, []);
3218
+ const dismiss = useCallback(() => {
3219
+ setRevealed((wasRevealed) => {
3220
+ if (wasRevealed) onDismissRef.current?.();
3221
+ return false;
3222
+ });
3223
+ }, []);
3224
+ const onPointerDown = useCallback(
3225
+ (e) => {
3226
+ if (!enabled || e.pointerType === "mouse") return;
3227
+ if (revealed) dismiss();
3228
+ else reveal();
3229
+ },
3230
+ [enabled, revealed, reveal, dismiss]
3231
+ );
3232
+ useEffect(() => {
3233
+ if (!revealed || typeof document === "undefined") return;
3234
+ const onDocDown = (ev) => {
3235
+ const target = ev.target;
3236
+ if (!(target instanceof Node)) return;
3237
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
3238
+ if (!inside) dismiss();
3239
+ };
3240
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
3241
+ return () => {
3242
+ clearTimeout(id);
3243
+ document.removeEventListener("pointerdown", onDocDown);
3244
+ };
3245
+ }, [revealed, refs, dismiss]);
3246
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
3247
+ }
3248
+ var init_useTapReveal = __esm({
3249
+ "hooks/useTapReveal.ts"() {
3250
+ }
3251
+ });
3205
3252
  var paddingStyles2, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles2, displayStyles, overflowStyles, positionStyles, Box;
3206
3253
  var init_Box = __esm({
3207
3254
  "components/core/atoms/Box.tsx"() {
3208
3255
  "use client";
3209
3256
  init_cn();
3210
3257
  init_useEventBus();
3258
+ init_useTapReveal();
3211
3259
  paddingStyles2 = {
3212
3260
  none: "p-0",
3213
3261
  xs: "p-1",
@@ -3333,10 +3381,12 @@ var init_Box = __esm({
3333
3381
  action,
3334
3382
  actionPayload,
3335
3383
  hoverEvent,
3384
+ tapReveal = true,
3336
3385
  maxWidth,
3337
3386
  onClick,
3338
3387
  onMouseEnter,
3339
3388
  onMouseLeave,
3389
+ onPointerDown,
3340
3390
  ...rest
3341
3391
  }, ref) => {
3342
3392
  const eventBus = useEventBus();
@@ -3359,6 +3409,19 @@ var init_Box = __esm({
3359
3409
  }
3360
3410
  onMouseLeave?.(e);
3361
3411
  }, [hoverEvent, eventBus, onMouseLeave]);
3412
+ const { triggerProps } = useTapReveal({
3413
+ enabled: tapReveal && !!hoverEvent,
3414
+ onReveal: useCallback(() => {
3415
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
3416
+ }, [hoverEvent, eventBus]),
3417
+ onDismiss: useCallback(() => {
3418
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
3419
+ }, [hoverEvent, eventBus])
3420
+ });
3421
+ const handlePointerDown = useCallback((e) => {
3422
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
3423
+ onPointerDown?.(e);
3424
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
3362
3425
  const isClickable = action || onClick;
3363
3426
  return React79__default.createElement(
3364
3427
  Component2,
@@ -3386,6 +3449,7 @@ var init_Box = __esm({
3386
3449
  onClick: isClickable ? handleClick : void 0,
3387
3450
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
3388
3451
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
3452
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
3389
3453
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3390
3454
  ...rest
3391
3455
  },
@@ -4142,6 +4206,11 @@ var init_TextHighlight = __esm({
4142
4206
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
4143
4207
  onMouseLeave?.();
4144
4208
  },
4209
+ onPointerDown: (e) => {
4210
+ if (e.pointerType === "mouse") return;
4211
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
4212
+ onMouseEnter?.();
4213
+ },
4145
4214
  role: "button",
4146
4215
  tabIndex: 0,
4147
4216
  onKeyDown: (e) => {
@@ -4640,6 +4709,7 @@ var init_LawReferenceTooltip = __esm({
4640
4709
  init_Typography();
4641
4710
  init_Divider();
4642
4711
  init_cn();
4712
+ init_useTapReveal();
4643
4713
  positionStyles2 = {
4644
4714
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
4645
4715
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -4661,6 +4731,7 @@ var init_LawReferenceTooltip = __esm({
4661
4731
  const { t } = useTranslate();
4662
4732
  const [isVisible, setIsVisible] = React79__default.useState(false);
4663
4733
  const timeoutRef = React79__default.useRef(null);
4734
+ const triggerRef = React79__default.useRef(null);
4664
4735
  const handleMouseEnter = () => {
4665
4736
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4666
4737
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -4669,6 +4740,8 @@ var init_LawReferenceTooltip = __esm({
4669
4740
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4670
4741
  setIsVisible(false);
4671
4742
  };
4743
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
4744
+ const open = isVisible || revealed;
4672
4745
  React79__default.useEffect(() => {
4673
4746
  return () => {
4674
4747
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -4677,6 +4750,7 @@ var init_LawReferenceTooltip = __esm({
4677
4750
  return /* @__PURE__ */ jsxs(
4678
4751
  Box,
4679
4752
  {
4753
+ ref: triggerRef,
4680
4754
  as: "span",
4681
4755
  position: "relative",
4682
4756
  display: "inline-block",
@@ -4685,9 +4759,10 @@ var init_LawReferenceTooltip = __esm({
4685
4759
  onMouseLeave: handleMouseLeave,
4686
4760
  onFocus: handleMouseEnter,
4687
4761
  onBlur: handleMouseLeave,
4762
+ onPointerDown: triggerProps.onPointerDown,
4688
4763
  children: [
4689
4764
  children,
4690
- isVisible && /* @__PURE__ */ jsxs(
4765
+ open && /* @__PURE__ */ jsxs(
4691
4766
  Box,
4692
4767
  {
4693
4768
  padding: "sm",
@@ -9332,6 +9407,36 @@ function useCamera() {
9332
9407
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
9333
9408
  drawFn?.();
9334
9409
  }, []);
9410
+ const handlePointerDown = useCallback((e) => {
9411
+ handleMouseDown(e);
9412
+ }, [handleMouseDown]);
9413
+ const handlePointerUp = useCallback(() => {
9414
+ handleMouseUp();
9415
+ }, [handleMouseUp]);
9416
+ const handlePointerMove = useCallback((e, drawFn) => {
9417
+ return handleMouseMove(e, drawFn);
9418
+ }, [handleMouseMove]);
9419
+ const panBy = useCallback((dx, dy, drawFn) => {
9420
+ cameraRef.current.x -= dx;
9421
+ cameraRef.current.y -= dy;
9422
+ targetCameraRef.current = null;
9423
+ drawFn?.();
9424
+ }, []);
9425
+ const zoomAtPoint = useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
9426
+ const cam = cameraRef.current;
9427
+ const oldZoom = cam.zoom;
9428
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
9429
+ if (newZoom === oldZoom) {
9430
+ drawFn?.();
9431
+ return;
9432
+ }
9433
+ const inv = 1 / oldZoom - 1 / newZoom;
9434
+ cam.x += (centerX - viewportSize.width / 2) * inv;
9435
+ cam.y += (centerY - viewportSize.height / 2) * inv;
9436
+ cam.zoom = newZoom;
9437
+ targetCameraRef.current = null;
9438
+ drawFn?.();
9439
+ }, []);
9335
9440
  const screenToWorld = useCallback((clientX, clientY, canvas, viewportSize) => {
9336
9441
  const rect = canvas.getBoundingClientRect();
9337
9442
  const screenX = clientX - rect.left;
@@ -9363,6 +9468,11 @@ function useCamera() {
9363
9468
  handleMouseMove,
9364
9469
  handleMouseLeave,
9365
9470
  handleWheel,
9471
+ handlePointerDown,
9472
+ handlePointerUp,
9473
+ handlePointerMove,
9474
+ panBy,
9475
+ zoomAtPoint,
9366
9476
  screenToWorld,
9367
9477
  lerpToTarget
9368
9478
  };
@@ -9372,6 +9482,113 @@ var init_useCamera = __esm({
9372
9482
  "use client";
9373
9483
  }
9374
9484
  });
9485
+ function localPoint(canvas, clientX, clientY) {
9486
+ if (!canvas) return { x: clientX, y: clientY };
9487
+ const rect = canvas.getBoundingClientRect();
9488
+ return { x: clientX - rect.left, y: clientY - rect.top };
9489
+ }
9490
+ function useCanvasGestures(options) {
9491
+ const {
9492
+ canvasRef,
9493
+ enabled = true,
9494
+ wheelStep = 1.1,
9495
+ onPointerDown,
9496
+ onPointerMove,
9497
+ onPointerUp,
9498
+ onZoom,
9499
+ onPanDelta,
9500
+ onMultiTouchStart
9501
+ } = options;
9502
+ const pointers = useRef(/* @__PURE__ */ new Map());
9503
+ const pinch = useRef(null);
9504
+ const computePinch = useCallback(() => {
9505
+ const pts = [...pointers.current.values()];
9506
+ const dx = pts[1].x - pts[0].x;
9507
+ const dy = pts[1].y - pts[0].y;
9508
+ return {
9509
+ distance: Math.hypot(dx, dy) || 1,
9510
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
9511
+ };
9512
+ }, []);
9513
+ const handlePointerDown = useCallback(
9514
+ (e) => {
9515
+ if (!enabled) return;
9516
+ e.currentTarget.setPointerCapture(e.pointerId);
9517
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
9518
+ if (pointers.current.size === 2) {
9519
+ onMultiTouchStart?.();
9520
+ pinch.current = computePinch();
9521
+ } else if (pointers.current.size === 1) {
9522
+ onPointerDown?.(e);
9523
+ }
9524
+ },
9525
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
9526
+ );
9527
+ const handlePointerMove = useCallback(
9528
+ (e) => {
9529
+ if (!enabled) return;
9530
+ if (pointers.current.has(e.pointerId)) {
9531
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
9532
+ }
9533
+ if (pointers.current.size >= 2 && pinch.current) {
9534
+ const next = computePinch();
9535
+ const prev = pinch.current;
9536
+ if (next.distance !== prev.distance) {
9537
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
9538
+ }
9539
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
9540
+ pinch.current = next;
9541
+ return;
9542
+ }
9543
+ onPointerMove?.(e);
9544
+ },
9545
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
9546
+ );
9547
+ const endPointer = useCallback(
9548
+ (e, fireUp) => {
9549
+ const wasMulti = pointers.current.size >= 2;
9550
+ pointers.current.delete(e.pointerId);
9551
+ if (pointers.current.size < 2) pinch.current = null;
9552
+ if (wasMulti && pointers.current.size === 1) return;
9553
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
9554
+ },
9555
+ [onPointerUp]
9556
+ );
9557
+ const handlePointerUp = useCallback(
9558
+ (e) => {
9559
+ if (!enabled) return;
9560
+ endPointer(e, true);
9561
+ },
9562
+ [enabled, endPointer]
9563
+ );
9564
+ const handlePointerCancel = useCallback(
9565
+ (e) => {
9566
+ if (!enabled) return;
9567
+ endPointer(e, false);
9568
+ },
9569
+ [enabled, endPointer]
9570
+ );
9571
+ const handleWheel = useCallback(
9572
+ (e) => {
9573
+ if (!enabled) return;
9574
+ e.preventDefault();
9575
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
9576
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
9577
+ },
9578
+ [enabled, canvasRef, wheelStep, onZoom]
9579
+ );
9580
+ return {
9581
+ onPointerDown: handlePointerDown,
9582
+ onPointerMove: handlePointerMove,
9583
+ onPointerUp: handlePointerUp,
9584
+ onPointerCancel: handlePointerCancel,
9585
+ onWheel: handleWheel
9586
+ };
9587
+ }
9588
+ var init_useCanvasGestures = __esm({
9589
+ "hooks/useCanvasGestures.ts"() {
9590
+ }
9591
+ });
9375
9592
 
9376
9593
  // components/game/organisms/utils/spriteSheetConstants.ts
9377
9594
  var SHEET_COLUMNS, SPRITE_SHEET_LAYOUT;
@@ -9877,13 +10094,13 @@ function IsometricCanvas({
9877
10094
  const {
9878
10095
  cameraRef,
9879
10096
  targetCameraRef,
9880
- isDragging,
9881
10097
  dragDistance,
9882
- handleMouseDown,
9883
- handleMouseUp,
9884
- handleMouseMove,
9885
10098
  handleMouseLeave,
9886
- handleWheel,
10099
+ handlePointerDown,
10100
+ handlePointerUp,
10101
+ handlePointerMove,
10102
+ panBy,
10103
+ zoomAtPoint,
9887
10104
  screenToWorld,
9888
10105
  lerpToTarget
9889
10106
  } = useCamera();
@@ -10318,11 +10535,16 @@ function IsometricCanvas({
10318
10535
  cancelAnimationFrame(rafIdRef.current);
10319
10536
  };
10320
10537
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
10321
- const handleMouseMoveWithCamera = useCallback((e) => {
10322
- if (enableCamera) {
10323
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
10324
- if (wasPanning) return;
10325
- }
10538
+ const singlePointerActiveRef = useRef(false);
10539
+ const handleCanvasPointerDown = useCallback((e) => {
10540
+ singlePointerActiveRef.current = true;
10541
+ if (enableCamera) handlePointerDown(e);
10542
+ }, [enableCamera, handlePointerDown]);
10543
+ const handleCanvasPointerMove = useCallback((e) => {
10544
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
10545
+ }, [enableCamera, handlePointerMove, draw]);
10546
+ const handleCanvasHover = useCallback((e) => {
10547
+ if (singlePointerActiveRef.current) return;
10326
10548
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
10327
10549
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
10328
10550
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -10333,26 +10555,10 @@ function IsometricCanvas({
10333
10555
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
10334
10556
  onTileHover?.(isoPos.x, isoPos.y);
10335
10557
  }
10336
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
10337
- const handleMouseLeaveWithCamera = useCallback(() => {
10338
- handleMouseLeave();
10339
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
10340
- onTileLeave?.();
10341
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
10342
- const handleWheelWithCamera = useCallback((e) => {
10343
- if (enableCamera) {
10344
- handleWheel(e, () => draw(animTimeRef.current));
10345
- }
10346
- }, [enableCamera, handleWheel, draw]);
10347
- useEffect(() => {
10348
- const canvas = canvasRef.current;
10349
- if (!canvas) return;
10350
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
10351
- return () => {
10352
- canvas.removeEventListener("wheel", handleWheelWithCamera);
10353
- };
10354
- }, [handleWheelWithCamera]);
10355
- const handleClick = useCallback((e) => {
10558
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
10559
+ const handleCanvasPointerUp = useCallback((e) => {
10560
+ singlePointerActiveRef.current = false;
10561
+ if (enableCamera) handlePointerUp();
10356
10562
  if (dragDistance() > 5) return;
10357
10563
  if (!canvasRef.current) return;
10358
10564
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -10370,7 +10576,32 @@ function IsometricCanvas({
10370
10576
  onTileClick?.(isoPos.x, isoPos.y);
10371
10577
  }
10372
10578
  }
10373
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
10579
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
10580
+ const handleCanvasPointerLeave = useCallback(() => {
10581
+ handleMouseLeave();
10582
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
10583
+ onTileLeave?.();
10584
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
10585
+ const applyZoom = useCallback((factor, centerX, centerY) => {
10586
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
10587
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
10588
+ const applyPanDelta = useCallback((dx, dy) => {
10589
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
10590
+ }, [enableCamera, panBy, draw]);
10591
+ const cancelSinglePointer = useCallback(() => {
10592
+ singlePointerActiveRef.current = false;
10593
+ if (enableCamera) handlePointerUp();
10594
+ }, [enableCamera, handlePointerUp]);
10595
+ const gestureHandlers = useCanvasGestures({
10596
+ canvasRef,
10597
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
10598
+ onPointerDown: handleCanvasPointerDown,
10599
+ onPointerMove: handleCanvasPointerMove,
10600
+ onPointerUp: handleCanvasPointerUp,
10601
+ onZoom: applyZoom,
10602
+ onPanDelta: applyPanDelta,
10603
+ onMultiTouchStart: cancelSinglePointer
10604
+ });
10374
10605
  if (error) {
10375
10606
  return /* @__PURE__ */ jsx(ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
10376
10607
  }
@@ -10402,13 +10633,17 @@ function IsometricCanvas({
10402
10633
  {
10403
10634
  ref: canvasRef,
10404
10635
  "data-testid": "game-canvas",
10405
- onClick: handleClick,
10406
- onMouseDown: enableCamera ? handleMouseDown : void 0,
10407
- onMouseMove: handleMouseMoveWithCamera,
10408
- onMouseUp: enableCamera ? handleMouseUp : void 0,
10409
- onMouseLeave: handleMouseLeaveWithCamera,
10636
+ onPointerDown: gestureHandlers.onPointerDown,
10637
+ onPointerMove: (e) => {
10638
+ gestureHandlers.onPointerMove(e);
10639
+ handleCanvasHover(e);
10640
+ },
10641
+ onPointerUp: gestureHandlers.onPointerUp,
10642
+ onPointerCancel: gestureHandlers.onPointerCancel,
10643
+ onPointerLeave: handleCanvasPointerLeave,
10644
+ onWheel: gestureHandlers.onWheel,
10410
10645
  onContextMenu: (e) => e.preventDefault(),
10411
- className: "cursor-pointer",
10646
+ className: "cursor-pointer touch-none",
10412
10647
  style: {
10413
10648
  width: viewportSize.width,
10414
10649
  height: viewportSize.height
@@ -10462,6 +10697,7 @@ var init_IsometricCanvas = __esm({
10462
10697
  init_ErrorState();
10463
10698
  init_useImageCache();
10464
10699
  init_useCamera();
10700
+ init_useCanvasGestures();
10465
10701
  init_useUnitSpriteAtlas();
10466
10702
  init_verificationRegistry();
10467
10703
  init_isometric();
@@ -13577,6 +13813,10 @@ var init_StateMachineView = __esm({
13577
13813
  const handleMouseLeave2 = () => {
13578
13814
  onHover(null, 0, 0);
13579
13815
  };
13816
+ const handlePointerDown2 = (e) => {
13817
+ if (e.pointerType === "mouse") return;
13818
+ handleMouseEnter2();
13819
+ };
13580
13820
  return /* @__PURE__ */ jsxs(
13581
13821
  "g",
13582
13822
  {
@@ -13586,6 +13826,7 @@ var init_StateMachineView = __esm({
13586
13826
  onClick: () => onClick?.(bundle),
13587
13827
  onMouseEnter: handleMouseEnter2,
13588
13828
  onMouseLeave: handleMouseLeave2,
13829
+ onPointerDown: handlePointerDown2,
13589
13830
  style: { pointerEvents: "auto" },
13590
13831
  children: [
13591
13832
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -13703,6 +13944,10 @@ var init_StateMachineView = __esm({
13703
13944
  const handleMouseLeave = useCallback(() => {
13704
13945
  onHover(null, 0, 0);
13705
13946
  }, [onHover]);
13947
+ const handlePointerDown = useCallback((e) => {
13948
+ if (e.pointerType === "mouse") return;
13949
+ handleMouseEnter();
13950
+ }, [handleMouseEnter]);
13706
13951
  const uniqueMarkerId = `arrow-${bundle.id}`;
13707
13952
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
13708
13953
  return /* @__PURE__ */ jsxs(
@@ -13714,6 +13959,7 @@ var init_StateMachineView = __esm({
13714
13959
  onClick: () => onClick?.(bundle),
13715
13960
  onMouseEnter: handleMouseEnter,
13716
13961
  onMouseLeave: handleMouseLeave,
13962
+ onPointerDown: handlePointerDown,
13717
13963
  style: { pointerEvents: "auto" },
13718
13964
  children: [
13719
13965
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -22486,10 +22732,84 @@ function SubMenu({
22486
22732
  );
22487
22733
  return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
22488
22734
  }
22735
+ function MenuItemRow({
22736
+ item,
22737
+ itemId,
22738
+ hasSubMenu,
22739
+ isDanger,
22740
+ direction,
22741
+ isSubMenuOpen,
22742
+ activeSubMenuRef,
22743
+ eventBus,
22744
+ onItemClick,
22745
+ openSubMenu
22746
+ }) {
22747
+ const rowRef = useRef(null);
22748
+ const { triggerProps } = useTapReveal({
22749
+ enabled: hasSubMenu,
22750
+ onReveal: () => openSubMenu(itemId, rowRef.current),
22751
+ refs: [rowRef]
22752
+ });
22753
+ return /* @__PURE__ */ jsxs(Box, { children: [
22754
+ /* @__PURE__ */ jsx(
22755
+ Box,
22756
+ {
22757
+ ref: rowRef,
22758
+ as: "button",
22759
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
22760
+ "aria-disabled": item.disabled || void 0,
22761
+ onMouseEnter: (e) => {
22762
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
22763
+ },
22764
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
22765
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22766
+ className: cn(
22767
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22768
+ "text-sm transition-colors",
22769
+ "hover:bg-muted",
22770
+ "focus:outline-none focus:bg-muted",
22771
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22772
+ item.disabled && "cursor-not-allowed",
22773
+ isDanger && "text-error hover:bg-error/10"
22774
+ ),
22775
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22776
+ 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" })),
22777
+ /* @__PURE__ */ jsx(
22778
+ Typography,
22779
+ {
22780
+ variant: "small",
22781
+ className: cn("flex-1", isDanger && "text-red-600"),
22782
+ children: item.label
22783
+ }
22784
+ ),
22785
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22786
+ hasSubMenu && /* @__PURE__ */ jsx(
22787
+ Icon,
22788
+ {
22789
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
22790
+ size: "sm",
22791
+ className: "flex-shrink-0"
22792
+ }
22793
+ )
22794
+ ] })
22795
+ }
22796
+ ),
22797
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsx(
22798
+ SubMenu,
22799
+ {
22800
+ items: item.subMenu,
22801
+ itemRef: activeSubMenuRef,
22802
+ direction,
22803
+ eventBus
22804
+ }
22805
+ )
22806
+ ] });
22807
+ }
22489
22808
  var MENU_GAP, menuContainerStyles, Menu;
22490
22809
  var init_Menu = __esm({
22491
22810
  "components/core/molecules/Menu.tsx"() {
22492
22811
  "use client";
22812
+ init_useTapReveal();
22493
22813
  init_Box();
22494
22814
  init_Icon();
22495
22815
  init_Divider();
@@ -22544,6 +22864,10 @@ var init_Menu = __esm({
22544
22864
  setIsOpen(false);
22545
22865
  }
22546
22866
  };
22867
+ const openSubMenu = (itemId, el) => {
22868
+ setActiveSubMenu(itemId);
22869
+ setActiveSubMenuRef(el);
22870
+ };
22547
22871
  useEffect(() => {
22548
22872
  if (isOpen) {
22549
22873
  updatePosition();
@@ -22587,61 +22911,22 @@ var init_Menu = __esm({
22587
22911
  if (isDivider) {
22588
22912
  return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
22589
22913
  }
22590
- return /* @__PURE__ */ jsxs(Box, { children: [
22591
- /* @__PURE__ */ jsx(
22592
- Box,
22593
- {
22594
- as: "button",
22595
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
22596
- "aria-disabled": item.disabled || void 0,
22597
- onMouseEnter: (e) => {
22598
- if (hasSubMenu) {
22599
- setActiveSubMenu(itemId);
22600
- setActiveSubMenuRef(e.currentTarget);
22601
- }
22602
- },
22603
- "data-testid": item.event ? `action-${item.event}` : void 0,
22604
- className: cn(
22605
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22606
- "text-sm transition-colors",
22607
- "hover:bg-muted",
22608
- "focus:outline-none focus:bg-muted",
22609
- "disabled:opacity-50 disabled:cursor-not-allowed",
22610
- item.disabled && "cursor-not-allowed",
22611
- isDanger && "text-error hover:bg-error/10"
22612
- ),
22613
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22614
- 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" })),
22615
- /* @__PURE__ */ jsx(
22616
- Typography,
22617
- {
22618
- variant: "small",
22619
- className: cn("flex-1", isDanger && "text-red-600"),
22620
- children: item.label
22621
- }
22622
- ),
22623
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22624
- hasSubMenu && /* @__PURE__ */ jsx(
22625
- Icon,
22626
- {
22627
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
22628
- size: "sm",
22629
- className: "flex-shrink-0"
22630
- }
22631
- )
22632
- ] })
22633
- }
22634
- ),
22635
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
22636
- SubMenu,
22637
- {
22638
- items: item.subMenu,
22639
- itemRef: activeSubMenuRef,
22640
- direction,
22641
- eventBus
22642
- }
22643
- )
22644
- ] }, itemId);
22914
+ return /* @__PURE__ */ jsx(
22915
+ MenuItemRow,
22916
+ {
22917
+ item,
22918
+ itemId,
22919
+ hasSubMenu,
22920
+ isDanger,
22921
+ direction,
22922
+ isSubMenuOpen: activeSubMenu === itemId,
22923
+ activeSubMenuRef,
22924
+ eventBus,
22925
+ onItemClick: handleItemClick,
22926
+ openSubMenu
22927
+ },
22928
+ itemId
22929
+ );
22645
22930
  });
22646
22931
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
22647
22932
  "div",
@@ -25886,6 +26171,7 @@ var init_Popover = __esm({
25886
26171
  "use client";
25887
26172
  init_Typography();
25888
26173
  init_cn();
26174
+ init_useTapReveal();
25889
26175
  arrowClasses = {
25890
26176
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
25891
26177
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -25956,18 +26242,32 @@ var init_Popover = __esm({
25956
26242
  document.addEventListener("mousedown", handleClickOutside);
25957
26243
  return () => document.removeEventListener("mousedown", handleClickOutside);
25958
26244
  }, [isOpen, trigger]);
25959
- const triggerProps = trigger === "click" ? {
26245
+ const { triggerProps: tapTriggerProps } = useTapReveal({
26246
+ enabled: trigger === "hover",
26247
+ onReveal: handleOpen,
26248
+ onDismiss: handleClose,
26249
+ refs: [triggerRef, popoverRef]
26250
+ });
26251
+ const handlerProps = trigger === "click" ? {
25960
26252
  onClick: handleToggle
25961
26253
  } : {
25962
26254
  onMouseEnter: handleOpen,
25963
- onMouseLeave: handleClose
26255
+ onMouseLeave: handleClose,
26256
+ onPointerDown: tapTriggerProps.onPointerDown
25964
26257
  };
25965
26258
  const childElement = React79__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26259
+ const childPointerDown = childElement.props.onPointerDown;
25966
26260
  const triggerElement = React79__default.cloneElement(
25967
26261
  childElement,
25968
26262
  {
25969
26263
  ref: triggerRef,
25970
- ...triggerProps
26264
+ ...handlerProps,
26265
+ ...trigger === "hover" ? {
26266
+ onPointerDown: (e) => {
26267
+ tapTriggerProps.onPointerDown(e);
26268
+ childPointerDown?.(e);
26269
+ }
26270
+ } : void 0
25971
26271
  }
25972
26272
  );
25973
26273
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
@@ -26748,6 +27048,7 @@ var init_Tooltip = __esm({
26748
27048
  "use client";
26749
27049
  init_Typography();
26750
27050
  init_cn();
27051
+ init_useTapReveal();
26751
27052
  TRIGGER_GAP2 = 8;
26752
27053
  arrowClasses2 = {
26753
27054
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -26792,6 +27093,11 @@ var init_Tooltip = __esm({
26792
27093
  setIsVisible(false);
26793
27094
  }, hideDelay);
26794
27095
  };
27096
+ const { triggerProps } = useTapReveal({
27097
+ onReveal: handleMouseEnter,
27098
+ onDismiss: handleMouseLeave,
27099
+ refs: [triggerRef, tooltipRef]
27100
+ });
26795
27101
  useLayoutEffect(() => {
26796
27102
  if (isVisible) {
26797
27103
  updatePosition();
@@ -26804,12 +27110,17 @@ var init_Tooltip = __esm({
26804
27110
  };
26805
27111
  }, []);
26806
27112
  const triggerElement = React79__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
27113
+ const childPointerDown = triggerElement.props.onPointerDown;
26807
27114
  const trigger = React79__default.cloneElement(triggerElement, {
26808
27115
  ref: triggerRef,
26809
27116
  onMouseEnter: handleMouseEnter,
26810
27117
  onMouseLeave: handleMouseLeave,
26811
27118
  onFocus: handleMouseEnter,
26812
- onBlur: handleMouseLeave
27119
+ onBlur: handleMouseLeave,
27120
+ onPointerDown: (e) => {
27121
+ triggerProps.onPointerDown(e);
27122
+ childPointerDown?.(e);
27123
+ }
26813
27124
  });
26814
27125
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxs(
26815
27126
  "div",
@@ -30482,6 +30793,14 @@ var init_GraphView = __esm({
30482
30793
  },
30483
30794
  [onNodeClick]
30484
30795
  );
30796
+ const handleNodePointerDown = useCallback(
30797
+ (e, node) => {
30798
+ if (e.pointerType === "mouse") return;
30799
+ handleNodeMouseEnter(node);
30800
+ handleNodeClickInternal(node);
30801
+ },
30802
+ [handleNodeMouseEnter, handleNodeClickInternal]
30803
+ );
30485
30804
  if (nodes.length === 0) {
30486
30805
  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") }) });
30487
30806
  }
@@ -30541,6 +30860,7 @@ var init_GraphView = __esm({
30541
30860
  onMouseEnter: () => handleNodeMouseEnter(node),
30542
30861
  onMouseLeave: handleNodeMouseLeave,
30543
30862
  onClick: () => handleNodeClickInternal(node),
30863
+ onPointerDown: (e) => handleNodePointerDown(e, node),
30544
30864
  children: [
30545
30865
  /* @__PURE__ */ jsx(
30546
30866
  "circle",
@@ -30621,13 +30941,13 @@ var init_MapView = __esm({
30621
30941
  shadowSize: [41, 41]
30622
30942
  });
30623
30943
  L.Marker.prototype.options.icon = defaultIcon;
30624
- const { useEffect: useEffect82, useRef: useRef81, useCallback: useCallback125, useState: useState111 } = React79__default;
30944
+ const { useEffect: useEffect83, useRef: useRef83, useCallback: useCallback127, useState: useState112 } = React79__default;
30625
30945
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
30626
30946
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
30627
30947
  function MapUpdater({ centerLat, centerLng, zoom }) {
30628
30948
  const map = useMap();
30629
- const prevRef = useRef81({ centerLat, centerLng, zoom });
30630
- useEffect82(() => {
30949
+ const prevRef = useRef83({ centerLat, centerLng, zoom });
30950
+ useEffect83(() => {
30631
30951
  const prev = prevRef.current;
30632
30952
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
30633
30953
  map.setView([centerLat, centerLng], zoom);
@@ -30638,7 +30958,7 @@ var init_MapView = __esm({
30638
30958
  }
30639
30959
  function MapClickHandler({ onMapClick }) {
30640
30960
  const map = useMap();
30641
- useEffect82(() => {
30961
+ useEffect83(() => {
30642
30962
  if (!onMapClick) return;
30643
30963
  const handler = (e) => {
30644
30964
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -30666,8 +30986,8 @@ var init_MapView = __esm({
30666
30986
  showAttribution = true
30667
30987
  }) {
30668
30988
  const eventBus = useEventBus3();
30669
- const [clickedPosition, setClickedPosition] = useState111(null);
30670
- const handleMapClick = useCallback125((lat, lng) => {
30989
+ const [clickedPosition, setClickedPosition] = useState112(null);
30990
+ const handleMapClick = useCallback127((lat, lng) => {
30671
30991
  if (showClickedPin) {
30672
30992
  setClickedPosition({ lat, lng });
30673
30993
  }
@@ -30676,7 +30996,7 @@ var init_MapView = __esm({
30676
30996
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
30677
30997
  }
30678
30998
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
30679
- const handleMarkerClick = useCallback125((marker) => {
30999
+ const handleMarkerClick = useCallback127((marker) => {
30680
31000
  onMarkerClick?.(marker);
30681
31001
  if (markerClickEvent) {
30682
31002
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -31017,7 +31337,7 @@ var init_StarRating = __esm({
31017
31337
  "aria-valuenow": value,
31018
31338
  tabIndex: readOnly ? void 0 : 0,
31019
31339
  onKeyDown: handleKeyDown,
31020
- onMouseLeave: () => setHoverValue(null),
31340
+ onPointerLeave: () => setHoverValue(null),
31021
31341
  children: Array.from({ length: max }, (_, i) => {
31022
31342
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
31023
31343
  const isFull = fillLevel >= 1;
@@ -31027,7 +31347,7 @@ var init_StarRating = __esm({
31027
31347
  {
31028
31348
  className: "relative inline-block",
31029
31349
  onClick: () => handleStarClick(i, false),
31030
- onMouseMove: (e) => {
31350
+ onPointerMove: (e) => {
31031
31351
  if (readOnly) return;
31032
31352
  const rect = e.currentTarget.getBoundingClientRect();
31033
31353
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -34917,7 +35237,7 @@ var init_PositionedCanvas = __esm({
34917
35237
  {
34918
35238
  ref: containerRef,
34919
35239
  "data-testid": "positioned-canvas",
34920
- className: "relative bg-background border border-border rounded-container overflow-hidden",
35240
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
34921
35241
  style: { width, height },
34922
35242
  onClick: handleContainerClick,
34923
35243
  children: items.map((item) => {
@@ -39064,6 +39384,13 @@ var init_DocumentViewer = __esm({
39064
39384
  DocumentViewer.displayName = "DocumentViewer";
39065
39385
  }
39066
39386
  });
39387
+ function measureLabelWidth(text) {
39388
+ if (typeof document === "undefined") return text.length * 6;
39389
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
39390
+ if (!labelMeasureCtx) return text.length * 6;
39391
+ labelMeasureCtx.font = "10px system-ui";
39392
+ return labelMeasureCtx.measureText(text).width;
39393
+ }
39067
39394
  function getGroupColor(group, groups) {
39068
39395
  if (!group) return GROUP_COLORS2[0];
39069
39396
  const idx = groups.indexOf(group);
@@ -39075,7 +39402,7 @@ function resolveColor2(color, el) {
39075
39402
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
39076
39403
  return resolved || (m[2]?.trim() ?? "#888888");
39077
39404
  }
39078
- var GROUP_COLORS2, GraphCanvas;
39405
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
39079
39406
  var init_GraphCanvas = __esm({
39080
39407
  "components/core/molecules/GraphCanvas.tsx"() {
39081
39408
  "use client";
@@ -39086,6 +39413,7 @@ var init_GraphCanvas = __esm({
39086
39413
  init_ErrorState();
39087
39414
  init_EmptyState();
39088
39415
  init_useEventBus();
39416
+ init_useCanvasGestures();
39089
39417
  GROUP_COLORS2 = [
39090
39418
  "var(--color-primary)",
39091
39419
  "var(--color-success)",
@@ -39094,6 +39422,7 @@ var init_GraphCanvas = __esm({
39094
39422
  "var(--color-info)",
39095
39423
  "var(--color-accent)"
39096
39424
  ];
39425
+ labelMeasureCtx = null;
39097
39426
  GraphCanvas = ({
39098
39427
  title,
39099
39428
  nodes: propNodes = [],
@@ -39123,6 +39452,10 @@ var init_GraphCanvas = __esm({
39123
39452
  const animRef = useRef(0);
39124
39453
  const [zoom, setZoom] = useState(1);
39125
39454
  const [offset, setOffset] = useState({ x: 0, y: 0 });
39455
+ const zoomRef = useRef(zoom);
39456
+ zoomRef.current = zoom;
39457
+ const offsetRef = useRef(offset);
39458
+ offsetRef.current = offset;
39126
39459
  const [hoveredNode, setHoveredNode] = useState(null);
39127
39460
  const nodesRef = useRef([]);
39128
39461
  const [, forceUpdate] = useState(0);
@@ -39270,22 +39603,36 @@ var init_GraphCanvas = __esm({
39270
39603
  node.x = Math.max(30, Math.min(w - 30, node.x));
39271
39604
  node.y = Math.max(30, Math.min(h - 30, node.y));
39272
39605
  }
39606
+ const LABEL_GAP = 12;
39607
+ const LABEL_H = 12;
39608
+ const pad = nodeSpacing / 2;
39273
39609
  for (let i = 0; i < nodes.length; i++) {
39274
39610
  for (let j = i + 1; j < nodes.length; j++) {
39275
39611
  const a = nodes[i];
39276
39612
  const b = nodes[j];
39277
- const dx = b.x - a.x;
39278
- const dy = b.y - a.y;
39279
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39280
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39281
- if (dist < minDist) {
39282
- const push = (minDist - dist) / 2;
39283
- const ux = dx / dist;
39284
- const uy = dy / dist;
39285
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39286
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39287
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39288
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39613
+ const ar = a.size || 8;
39614
+ const br = b.size || 8;
39615
+ if (showLabels) {
39616
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
39617
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
39618
+ }
39619
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
39620
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
39621
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
39622
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
39623
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
39624
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
39625
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
39626
+ if (overlapX > 0 && overlapY > 0) {
39627
+ if (overlapX <= overlapY) {
39628
+ const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
39629
+ a.x = Math.max(30, Math.min(w - 30, a.x - push));
39630
+ b.x = Math.max(30, Math.min(w - 30, b.x + push));
39631
+ } else {
39632
+ const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
39633
+ a.y = Math.max(30, Math.min(h - 30, a.y - push));
39634
+ b.y = Math.max(30, Math.min(h - 30, b.y + push));
39635
+ }
39289
39636
  }
39290
39637
  }
39291
39638
  }
@@ -39302,7 +39649,7 @@ var init_GraphCanvas = __esm({
39302
39649
  return () => {
39303
39650
  cancelAnimationFrame(animRef.current);
39304
39651
  };
39305
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
39652
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
39306
39653
  useEffect(() => {
39307
39654
  const canvas = canvasRef.current;
39308
39655
  if (!canvas) return;
@@ -39382,25 +39729,27 @@ var init_GraphCanvas = __esm({
39382
39729
  setZoom(1);
39383
39730
  setOffset({ x: 0, y: 0 });
39384
39731
  }, []);
39385
- const handleWheel = useCallback(
39386
- (e) => {
39387
- if (!interactive) return;
39388
- e.preventDefault();
39389
- const coords = toCoords(e);
39390
- if (!coords) return;
39391
- const oldZoom = zoom;
39392
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
39393
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39394
- if (newZoom === oldZoom) return;
39395
- setOffset((o) => ({
39396
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
39397
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
39398
- }));
39399
- setZoom(newZoom);
39400
- },
39401
- [interactive, toCoords, zoom]
39402
- );
39403
- const handleMouseDown = useCallback(
39732
+ const applyZoom = useCallback((factor, cx, cy) => {
39733
+ if (!interactive) return;
39734
+ const oldZoom = zoomRef.current;
39735
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39736
+ if (newZoom === oldZoom) return;
39737
+ const o = offsetRef.current;
39738
+ setOffset({
39739
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
39740
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
39741
+ });
39742
+ setZoom(newZoom);
39743
+ }, [interactive]);
39744
+ const applyPanDelta = useCallback((dx, dy) => {
39745
+ if (!interactive) return;
39746
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
39747
+ }, [interactive]);
39748
+ const cancelSinglePointer = useCallback(() => {
39749
+ interactionRef.current.mode = "none";
39750
+ interactionRef.current.dragNodeId = null;
39751
+ }, []);
39752
+ const handlePointerDown = useCallback(
39404
39753
  (e) => {
39405
39754
  const coords = toCoords(e);
39406
39755
  if (!coords) return;
@@ -39422,7 +39771,7 @@ var init_GraphCanvas = __esm({
39422
39771
  },
39423
39772
  [toCoords, nodeAt, draggable, interactive, offset]
39424
39773
  );
39425
- const handleMouseMove = useCallback(
39774
+ const handlePointerMove = useCallback(
39426
39775
  (e) => {
39427
39776
  const state = interactionRef.current;
39428
39777
  if (state.mode === "panning") {
@@ -39451,7 +39800,7 @@ var init_GraphCanvas = __esm({
39451
39800
  },
39452
39801
  [toCoords, nodeAt]
39453
39802
  );
39454
- const handleMouseUp = useCallback(
39803
+ const handlePointerUp = useCallback(
39455
39804
  (e) => {
39456
39805
  const state = interactionRef.current;
39457
39806
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -39468,11 +39817,19 @@ var init_GraphCanvas = __esm({
39468
39817
  },
39469
39818
  [toCoords, nodeAt, handleNodeClick]
39470
39819
  );
39471
- const handleMouseLeave = useCallback(() => {
39472
- interactionRef.current.mode = "none";
39473
- interactionRef.current.dragNodeId = null;
39820
+ const handlePointerLeave = useCallback(() => {
39474
39821
  setHoveredNode(null);
39475
39822
  }, []);
39823
+ const gestureHandlers = useCanvasGestures({
39824
+ canvasRef,
39825
+ enabled: interactive || draggable,
39826
+ onPointerDown: handlePointerDown,
39827
+ onPointerMove: handlePointerMove,
39828
+ onPointerUp: handlePointerUp,
39829
+ onZoom: applyZoom,
39830
+ onPanDelta: applyPanDelta,
39831
+ onMultiTouchStart: cancelSinglePointer
39832
+ });
39476
39833
  const handleDoubleClick = useCallback(
39477
39834
  (e) => {
39478
39835
  const coords = toCoords(e);
@@ -39541,13 +39898,14 @@ var init_GraphCanvas = __esm({
39541
39898
  ref: canvasRef,
39542
39899
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39543
39900
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39544
- className: "w-full cursor-grab active:cursor-grabbing",
39901
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
39545
39902
  style: { height },
39546
- onWheel: handleWheel,
39547
- onMouseDown: handleMouseDown,
39548
- onMouseMove: handleMouseMove,
39549
- onMouseUp: handleMouseUp,
39550
- onMouseLeave: handleMouseLeave,
39903
+ onPointerDown: gestureHandlers.onPointerDown,
39904
+ onPointerMove: gestureHandlers.onPointerMove,
39905
+ onPointerUp: gestureHandlers.onPointerUp,
39906
+ onPointerCancel: gestureHandlers.onPointerCancel,
39907
+ onPointerLeave: handlePointerLeave,
39908
+ onWheel: gestureHandlers.onWheel,
39551
39909
  onDoubleClick: handleDoubleClick
39552
39910
  }
39553
39911
  ) }),
@@ -44346,6 +44704,9 @@ var init_GameCanvas3D2 = __esm({
44346
44704
  target: cameraTarget,
44347
44705
  enableDamping: true,
44348
44706
  dampingFactor: 0.05,
44707
+ enableZoom: true,
44708
+ enablePan: true,
44709
+ touches: { ONE: THREE3.TOUCH.ROTATE, TWO: THREE3.TOUCH.DOLLY_PAN },
44349
44710
  minDistance: 2,
44350
44711
  maxDistance: 100,
44351
44712
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -49831,20 +50192,21 @@ var init_SplitPane = __esm({
49831
50192
  const [ratio, setRatio] = useState(initialRatio);
49832
50193
  const containerRef = useRef(null);
49833
50194
  const isDragging = useRef(false);
49834
- const handleMouseDown = useCallback(
50195
+ const handlePointerDown = useCallback(
49835
50196
  (e) => {
49836
50197
  if (!resizable) return;
49837
50198
  e.preventDefault();
49838
50199
  isDragging.current = true;
49839
- const handleMouseMove = (e2) => {
50200
+ e.currentTarget.setPointerCapture(e.pointerId);
50201
+ const handlePointerMove = (ev) => {
49840
50202
  if (!isDragging.current || !containerRef.current) return;
49841
50203
  const rect = containerRef.current.getBoundingClientRect();
49842
50204
  let newRatio;
49843
50205
  if (direction === "horizontal") {
49844
- const x = e2.clientX - rect.left;
50206
+ const x = ev.clientX - rect.left;
49845
50207
  newRatio = x / rect.width * 100;
49846
50208
  } else {
49847
- const y = e2.clientY - rect.top;
50209
+ const y = ev.clientY - rect.top;
49848
50210
  newRatio = y / rect.height * 100;
49849
50211
  }
49850
50212
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -49852,13 +50214,15 @@ var init_SplitPane = __esm({
49852
50214
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
49853
50215
  setRatio(newRatio);
49854
50216
  };
49855
- const handleMouseUp = () => {
50217
+ const handlePointerUp = () => {
49856
50218
  isDragging.current = false;
49857
- document.removeEventListener("mousemove", handleMouseMove);
49858
- document.removeEventListener("mouseup", handleMouseUp);
50219
+ document.removeEventListener("pointermove", handlePointerMove);
50220
+ document.removeEventListener("pointerup", handlePointerUp);
50221
+ document.removeEventListener("pointercancel", handlePointerUp);
49859
50222
  };
49860
- document.addEventListener("mousemove", handleMouseMove);
49861
- document.addEventListener("mouseup", handleMouseUp);
50223
+ document.addEventListener("pointermove", handlePointerMove);
50224
+ document.addEventListener("pointerup", handlePointerUp);
50225
+ document.addEventListener("pointercancel", handlePointerUp);
49862
50226
  },
49863
50227
  [direction, minSize, resizable]
49864
50228
  );
@@ -49887,9 +50251,9 @@ var init_SplitPane = __esm({
49887
50251
  resizable && /* @__PURE__ */ jsx(
49888
50252
  "div",
49889
50253
  {
49890
- onMouseDown: handleMouseDown,
50254
+ onPointerDown: handlePointerDown,
49891
50255
  className: cn(
49892
- "flex-shrink-0 bg-border transition-colors",
50256
+ "flex-shrink-0 bg-border transition-colors touch-none",
49893
50257
  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"
49894
50258
  )
49895
50259
  }