@almadar/ui 5.62.0 → 5.63.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/avl/index.cjs +490 -147
  2. package/dist/avl/index.js +490 -147
  3. package/dist/components/core/atoms/Box.d.ts +2 -0
  4. package/dist/components/core/molecules/CalendarGrid.d.ts +9 -1
  5. package/dist/components/core/molecules/PositionedCanvas.d.ts +14 -1
  6. package/dist/components/core/molecules/ReplyTree.d.ts +13 -1
  7. package/dist/components/game/molecules/three/index.cjs +64 -0
  8. package/dist/components/game/molecules/three/index.js +64 -0
  9. package/dist/components/game/organisms/hooks/useCamera.d.ts +16 -0
  10. package/dist/components/index.cjs +490 -148
  11. package/dist/components/index.js +490 -148
  12. package/dist/components/marketing/organisms/CaseStudyOrganism.d.ts +10 -1
  13. package/dist/components/marketing/organisms/FeatureGridOrganism.d.ts +10 -1
  14. package/dist/components/marketing/organisms/HeroOrganism.d.ts +24 -1
  15. package/dist/components/marketing/organisms/PricingOrganism.d.ts +14 -1
  16. package/dist/components/marketing/organisms/ShowcaseOrganism.d.ts +14 -1
  17. package/dist/components/marketing/organisms/StatsOrganism.d.ts +6 -1
  18. package/dist/components/marketing/organisms/StepFlowOrganism.d.ts +9 -1
  19. package/dist/components/marketing/organisms/TeamOrganism.d.ts +10 -1
  20. package/dist/docs/index.cjs +64 -0
  21. package/dist/docs/index.d.cts +2 -0
  22. package/dist/docs/index.js +64 -0
  23. package/dist/hooks/index.cjs +148 -0
  24. package/dist/hooks/index.d.ts +2 -0
  25. package/dist/hooks/index.js +147 -1
  26. package/dist/hooks/useCanvasGestures.d.ts +44 -0
  27. package/dist/hooks/useTapReveal.d.ts +32 -0
  28. package/dist/marketing/index.cjs +59 -0
  29. package/dist/marketing/index.d.cts +2 -0
  30. package/dist/marketing/index.js +59 -0
  31. package/dist/providers/index.cjs +490 -147
  32. package/dist/providers/index.js +490 -147
  33. package/dist/runtime/index.cjs +490 -147
  34. package/dist/runtime/index.js +490 -147
  35. package/package.json +2 -2
@@ -246,12 +246,60 @@ var init_cn = __esm({
246
246
  "lib/cn.ts"() {
247
247
  }
248
248
  });
249
+ function useTapReveal(options = {}) {
250
+ const { onReveal, onDismiss, refs, enabled = true } = options;
251
+ const [revealed, setRevealed] = useState(false);
252
+ const onRevealRef = useRef(onReveal);
253
+ const onDismissRef = useRef(onDismiss);
254
+ onRevealRef.current = onReveal;
255
+ onDismissRef.current = onDismiss;
256
+ const reveal = useCallback(() => {
257
+ setRevealed((wasRevealed) => {
258
+ if (!wasRevealed) onRevealRef.current?.();
259
+ return true;
260
+ });
261
+ }, []);
262
+ const dismiss = useCallback(() => {
263
+ setRevealed((wasRevealed) => {
264
+ if (wasRevealed) onDismissRef.current?.();
265
+ return false;
266
+ });
267
+ }, []);
268
+ const onPointerDown = useCallback(
269
+ (e) => {
270
+ if (!enabled || e.pointerType === "mouse") return;
271
+ if (revealed) dismiss();
272
+ else reveal();
273
+ },
274
+ [enabled, revealed, reveal, dismiss]
275
+ );
276
+ useEffect(() => {
277
+ if (!revealed || typeof document === "undefined") return;
278
+ const onDocDown = (ev) => {
279
+ const target = ev.target;
280
+ if (!(target instanceof Node)) return;
281
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
282
+ if (!inside) dismiss();
283
+ };
284
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
285
+ return () => {
286
+ clearTimeout(id);
287
+ document.removeEventListener("pointerdown", onDocDown);
288
+ };
289
+ }, [revealed, refs, dismiss]);
290
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
291
+ }
292
+ var init_useTapReveal = __esm({
293
+ "hooks/useTapReveal.ts"() {
294
+ }
295
+ });
249
296
  var paddingStyles, paddingXStyles, paddingYStyles, marginStyles, marginXStyles, marginYStyles, bgStyles, roundedStyles, shadowStyles, displayStyles, overflowStyles, positionStyles, Box;
250
297
  var init_Box = __esm({
251
298
  "components/core/atoms/Box.tsx"() {
252
299
  "use client";
253
300
  init_cn();
254
301
  init_useEventBus();
302
+ init_useTapReveal();
255
303
  paddingStyles = {
256
304
  none: "p-0",
257
305
  xs: "p-1",
@@ -377,10 +425,12 @@ var init_Box = __esm({
377
425
  action,
378
426
  actionPayload,
379
427
  hoverEvent,
428
+ tapReveal = true,
380
429
  maxWidth,
381
430
  onClick,
382
431
  onMouseEnter,
383
432
  onMouseLeave,
433
+ onPointerDown,
384
434
  ...rest
385
435
  }, ref) => {
386
436
  const eventBus = useEventBus();
@@ -403,6 +453,19 @@ var init_Box = __esm({
403
453
  }
404
454
  onMouseLeave?.(e);
405
455
  }, [hoverEvent, eventBus, onMouseLeave]);
456
+ const { triggerProps } = useTapReveal({
457
+ enabled: tapReveal && !!hoverEvent,
458
+ onReveal: useCallback(() => {
459
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
460
+ }, [hoverEvent, eventBus]),
461
+ onDismiss: useCallback(() => {
462
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
463
+ }, [hoverEvent, eventBus])
464
+ });
465
+ const handlePointerDown = useCallback((e) => {
466
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
467
+ onPointerDown?.(e);
468
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
406
469
  const isClickable = action || onClick;
407
470
  return React85__default.createElement(
408
471
  Component2,
@@ -430,6 +493,7 @@ var init_Box = __esm({
430
493
  onClick: isClickable ? handleClick : void 0,
431
494
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
432
495
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
496
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
433
497
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
434
498
  ...rest
435
499
  },
@@ -4840,6 +4904,11 @@ var init_TextHighlight = __esm({
4840
4904
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false, annotationId });
4841
4905
  onMouseLeave?.();
4842
4906
  },
4907
+ onPointerDown: (e) => {
4908
+ if (e.pointerType === "mouse") return;
4909
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true, annotationId });
4910
+ onMouseEnter?.();
4911
+ },
4843
4912
  role: "button",
4844
4913
  tabIndex: 0,
4845
4914
  onKeyDown: (e) => {
@@ -5015,6 +5084,7 @@ var init_LawReferenceTooltip = __esm({
5015
5084
  init_Typography();
5016
5085
  init_Divider();
5017
5086
  init_cn();
5087
+ init_useTapReveal();
5018
5088
  positionStyles2 = {
5019
5089
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
5020
5090
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
@@ -5036,6 +5106,7 @@ var init_LawReferenceTooltip = __esm({
5036
5106
  const { t } = useTranslate();
5037
5107
  const [isVisible, setIsVisible] = React85__default.useState(false);
5038
5108
  const timeoutRef = React85__default.useRef(null);
5109
+ const triggerRef = React85__default.useRef(null);
5039
5110
  const handleMouseEnter = () => {
5040
5111
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5041
5112
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -5044,6 +5115,8 @@ var init_LawReferenceTooltip = __esm({
5044
5115
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5045
5116
  setIsVisible(false);
5046
5117
  };
5118
+ const { revealed, triggerProps } = useTapReveal({ refs: [triggerRef] });
5119
+ const open = isVisible || revealed;
5047
5120
  React85__default.useEffect(() => {
5048
5121
  return () => {
5049
5122
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -5052,6 +5125,7 @@ var init_LawReferenceTooltip = __esm({
5052
5125
  return /* @__PURE__ */ jsxs(
5053
5126
  Box,
5054
5127
  {
5128
+ ref: triggerRef,
5055
5129
  as: "span",
5056
5130
  position: "relative",
5057
5131
  display: "inline-block",
@@ -5060,9 +5134,10 @@ var init_LawReferenceTooltip = __esm({
5060
5134
  onMouseLeave: handleMouseLeave,
5061
5135
  onFocus: handleMouseEnter,
5062
5136
  onBlur: handleMouseLeave,
5137
+ onPointerDown: triggerProps.onPointerDown,
5063
5138
  children: [
5064
5139
  children,
5065
- isVisible && /* @__PURE__ */ jsxs(
5140
+ open && /* @__PURE__ */ jsxs(
5066
5141
  Box,
5067
5142
  {
5068
5143
  padding: "sm",
@@ -10822,6 +10897,36 @@ function useCamera() {
10822
10897
  cameraRef.current.zoom = Math.max(0.5, Math.min(3, cameraRef.current.zoom * zoomDelta));
10823
10898
  drawFn?.();
10824
10899
  }, []);
10900
+ const handlePointerDown = useCallback((e) => {
10901
+ handleMouseDown(e);
10902
+ }, [handleMouseDown]);
10903
+ const handlePointerUp = useCallback(() => {
10904
+ handleMouseUp();
10905
+ }, [handleMouseUp]);
10906
+ const handlePointerMove = useCallback((e, drawFn) => {
10907
+ return handleMouseMove(e, drawFn);
10908
+ }, [handleMouseMove]);
10909
+ const panBy = useCallback((dx, dy, drawFn) => {
10910
+ cameraRef.current.x -= dx;
10911
+ cameraRef.current.y -= dy;
10912
+ targetCameraRef.current = null;
10913
+ drawFn?.();
10914
+ }, []);
10915
+ const zoomAtPoint = useCallback((factor, centerX, centerY, viewportSize, drawFn) => {
10916
+ const cam = cameraRef.current;
10917
+ const oldZoom = cam.zoom;
10918
+ const newZoom = Math.max(0.5, Math.min(3, oldZoom * factor));
10919
+ if (newZoom === oldZoom) {
10920
+ drawFn?.();
10921
+ return;
10922
+ }
10923
+ const inv = 1 / oldZoom - 1 / newZoom;
10924
+ cam.x += (centerX - viewportSize.width / 2) * inv;
10925
+ cam.y += (centerY - viewportSize.height / 2) * inv;
10926
+ cam.zoom = newZoom;
10927
+ targetCameraRef.current = null;
10928
+ drawFn?.();
10929
+ }, []);
10825
10930
  const screenToWorld = useCallback((clientX, clientY, canvas, viewportSize) => {
10826
10931
  const rect = canvas.getBoundingClientRect();
10827
10932
  const screenX = clientX - rect.left;
@@ -10853,6 +10958,11 @@ function useCamera() {
10853
10958
  handleMouseMove,
10854
10959
  handleMouseLeave,
10855
10960
  handleWheel,
10961
+ handlePointerDown,
10962
+ handlePointerUp,
10963
+ handlePointerMove,
10964
+ panBy,
10965
+ zoomAtPoint,
10856
10966
  screenToWorld,
10857
10967
  lerpToTarget
10858
10968
  };
@@ -10862,6 +10972,113 @@ var init_useCamera = __esm({
10862
10972
  "use client";
10863
10973
  }
10864
10974
  });
10975
+ function localPoint(canvas, clientX, clientY) {
10976
+ if (!canvas) return { x: clientX, y: clientY };
10977
+ const rect = canvas.getBoundingClientRect();
10978
+ return { x: clientX - rect.left, y: clientY - rect.top };
10979
+ }
10980
+ function useCanvasGestures(options) {
10981
+ const {
10982
+ canvasRef,
10983
+ enabled = true,
10984
+ wheelStep = 1.1,
10985
+ onPointerDown,
10986
+ onPointerMove,
10987
+ onPointerUp,
10988
+ onZoom,
10989
+ onPanDelta,
10990
+ onMultiTouchStart
10991
+ } = options;
10992
+ const pointers = useRef(/* @__PURE__ */ new Map());
10993
+ const pinch = useRef(null);
10994
+ const computePinch = useCallback(() => {
10995
+ const pts = [...pointers.current.values()];
10996
+ const dx = pts[1].x - pts[0].x;
10997
+ const dy = pts[1].y - pts[0].y;
10998
+ return {
10999
+ distance: Math.hypot(dx, dy) || 1,
11000
+ centroid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }
11001
+ };
11002
+ }, []);
11003
+ const handlePointerDown = useCallback(
11004
+ (e) => {
11005
+ if (!enabled) return;
11006
+ e.currentTarget.setPointerCapture(e.pointerId);
11007
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
11008
+ if (pointers.current.size === 2) {
11009
+ onMultiTouchStart?.();
11010
+ pinch.current = computePinch();
11011
+ } else if (pointers.current.size === 1) {
11012
+ onPointerDown?.(e);
11013
+ }
11014
+ },
11015
+ [enabled, canvasRef, onMultiTouchStart, computePinch, onPointerDown]
11016
+ );
11017
+ const handlePointerMove = useCallback(
11018
+ (e) => {
11019
+ if (!enabled) return;
11020
+ if (pointers.current.has(e.pointerId)) {
11021
+ pointers.current.set(e.pointerId, localPoint(canvasRef.current, e.clientX, e.clientY));
11022
+ }
11023
+ if (pointers.current.size >= 2 && pinch.current) {
11024
+ const next = computePinch();
11025
+ const prev = pinch.current;
11026
+ if (next.distance !== prev.distance) {
11027
+ onZoom?.(next.distance / prev.distance, next.centroid.x, next.centroid.y);
11028
+ }
11029
+ onPanDelta?.(next.centroid.x - prev.centroid.x, next.centroid.y - prev.centroid.y);
11030
+ pinch.current = next;
11031
+ return;
11032
+ }
11033
+ onPointerMove?.(e);
11034
+ },
11035
+ [enabled, canvasRef, computePinch, onZoom, onPanDelta, onPointerMove]
11036
+ );
11037
+ const endPointer = useCallback(
11038
+ (e, fireUp) => {
11039
+ const wasMulti = pointers.current.size >= 2;
11040
+ pointers.current.delete(e.pointerId);
11041
+ if (pointers.current.size < 2) pinch.current = null;
11042
+ if (wasMulti && pointers.current.size === 1) return;
11043
+ if (pointers.current.size === 0 && fireUp) onPointerUp?.(e);
11044
+ },
11045
+ [onPointerUp]
11046
+ );
11047
+ const handlePointerUp = useCallback(
11048
+ (e) => {
11049
+ if (!enabled) return;
11050
+ endPointer(e, true);
11051
+ },
11052
+ [enabled, endPointer]
11053
+ );
11054
+ const handlePointerCancel = useCallback(
11055
+ (e) => {
11056
+ if (!enabled) return;
11057
+ endPointer(e, false);
11058
+ },
11059
+ [enabled, endPointer]
11060
+ );
11061
+ const handleWheel = useCallback(
11062
+ (e) => {
11063
+ if (!enabled) return;
11064
+ e.preventDefault();
11065
+ const { x, y } = localPoint(canvasRef.current, e.clientX, e.clientY);
11066
+ onZoom?.(e.deltaY < 0 ? wheelStep : 1 / wheelStep, x, y);
11067
+ },
11068
+ [enabled, canvasRef, wheelStep, onZoom]
11069
+ );
11070
+ return {
11071
+ onPointerDown: handlePointerDown,
11072
+ onPointerMove: handlePointerMove,
11073
+ onPointerUp: handlePointerUp,
11074
+ onPointerCancel: handlePointerCancel,
11075
+ onWheel: handleWheel
11076
+ };
11077
+ }
11078
+ var init_useCanvasGestures = __esm({
11079
+ "hooks/useCanvasGestures.ts"() {
11080
+ }
11081
+ });
10865
11082
  function unitAtlasUrl(unit) {
10866
11083
  if (unit.spriteSheet) return unit.spriteSheet;
10867
11084
  const sprite = unit.sprite;
@@ -11240,11 +11457,12 @@ function IsometricCanvas({
11240
11457
  cameraRef,
11241
11458
  targetCameraRef,
11242
11459
  dragDistance,
11243
- handleMouseDown,
11244
- handleMouseUp,
11245
- handleMouseMove,
11246
11460
  handleMouseLeave,
11247
- handleWheel,
11461
+ handlePointerDown,
11462
+ handlePointerUp,
11463
+ handlePointerMove,
11464
+ panBy,
11465
+ zoomAtPoint,
11248
11466
  screenToWorld,
11249
11467
  lerpToTarget
11250
11468
  } = useCamera();
@@ -11679,11 +11897,16 @@ function IsometricCanvas({
11679
11897
  cancelAnimationFrame(rafIdRef.current);
11680
11898
  };
11681
11899
  }, [draw, units.length, validMoves.length, attackTargets.length, selectedUnitId, hasActiveEffects2, pendingCount, atlasPending, lerpToTarget, targetCameraRef]);
11682
- const handleMouseMoveWithCamera = useCallback((e) => {
11683
- if (enableCamera) {
11684
- const wasPanning = handleMouseMove(e, () => draw(animTimeRef.current));
11685
- if (wasPanning) return;
11686
- }
11900
+ const singlePointerActiveRef = useRef(false);
11901
+ const handleCanvasPointerDown = useCallback((e) => {
11902
+ singlePointerActiveRef.current = true;
11903
+ if (enableCamera) handlePointerDown(e);
11904
+ }, [enableCamera, handlePointerDown]);
11905
+ const handleCanvasPointerMove = useCallback((e) => {
11906
+ if (enableCamera) handlePointerMove(e, () => draw(animTimeRef.current));
11907
+ }, [enableCamera, handlePointerMove, draw]);
11908
+ const handleCanvasHover = useCallback((e) => {
11909
+ if (singlePointerActiveRef.current) return;
11687
11910
  if (!onTileHover && !tileHoverEvent || !canvasRef.current) return;
11688
11911
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
11689
11912
  const adjustedX = world.x - scaledTileWidth / 2;
@@ -11694,26 +11917,10 @@ function IsometricCanvas({
11694
11917
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
11695
11918
  onTileHover?.(isoPos.x, isoPos.y);
11696
11919
  }
11697
- }, [enableCamera, handleMouseMove, draw, onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11698
- const handleMouseLeaveWithCamera = useCallback(() => {
11699
- handleMouseLeave();
11700
- if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11701
- onTileLeave?.();
11702
- }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11703
- const handleWheelWithCamera = useCallback((e) => {
11704
- if (enableCamera) {
11705
- handleWheel(e, () => draw(animTimeRef.current));
11706
- }
11707
- }, [enableCamera, handleWheel, draw]);
11708
- useEffect(() => {
11709
- const canvas = canvasRef.current;
11710
- if (!canvas) return;
11711
- canvas.addEventListener("wheel", handleWheelWithCamera, { passive: false });
11712
- return () => {
11713
- canvas.removeEventListener("wheel", handleWheelWithCamera);
11714
- };
11715
- }, [handleWheelWithCamera]);
11716
- const handleClick = useCallback((e) => {
11920
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11921
+ const handleCanvasPointerUp = useCallback((e) => {
11922
+ singlePointerActiveRef.current = false;
11923
+ if (enableCamera) handlePointerUp();
11717
11924
  if (dragDistance() > 5) return;
11718
11925
  if (!canvasRef.current) return;
11719
11926
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
@@ -11731,7 +11938,32 @@ function IsometricCanvas({
11731
11938
  onTileClick?.(isoPos.x, isoPos.y);
11732
11939
  }
11733
11940
  }
11734
- }, [dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11941
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11942
+ const handleCanvasPointerLeave = useCallback(() => {
11943
+ handleMouseLeave();
11944
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
11945
+ onTileLeave?.();
11946
+ }, [handleMouseLeave, onTileLeave, tileLeaveEvent, eventBus]);
11947
+ const applyZoom = useCallback((factor, centerX, centerY) => {
11948
+ if (enableCamera) zoomAtPoint(factor, centerX, centerY, viewportSize, () => draw(animTimeRef.current));
11949
+ }, [enableCamera, zoomAtPoint, viewportSize, draw]);
11950
+ const applyPanDelta = useCallback((dx, dy) => {
11951
+ if (enableCamera) panBy(dx, dy, () => draw(animTimeRef.current));
11952
+ }, [enableCamera, panBy, draw]);
11953
+ const cancelSinglePointer = useCallback(() => {
11954
+ singlePointerActiveRef.current = false;
11955
+ if (enableCamera) handlePointerUp();
11956
+ }, [enableCamera, handlePointerUp]);
11957
+ const gestureHandlers = useCanvasGestures({
11958
+ canvasRef,
11959
+ enabled: enableCamera || !!onTileHover || !!tileHoverEvent || !!onTileClick || !!tileClickEvent || !!onUnitClick || !!unitClickEvent,
11960
+ onPointerDown: handleCanvasPointerDown,
11961
+ onPointerMove: handleCanvasPointerMove,
11962
+ onPointerUp: handleCanvasPointerUp,
11963
+ onZoom: applyZoom,
11964
+ onPanDelta: applyPanDelta,
11965
+ onMultiTouchStart: cancelSinglePointer
11966
+ });
11735
11967
  if (error) {
11736
11968
  return /* @__PURE__ */ jsx(ErrorState, { title: t("canvas.errorTitle"), message: error.message, className });
11737
11969
  }
@@ -11763,13 +11995,17 @@ function IsometricCanvas({
11763
11995
  {
11764
11996
  ref: canvasRef,
11765
11997
  "data-testid": "game-canvas",
11766
- onClick: handleClick,
11767
- onMouseDown: enableCamera ? handleMouseDown : void 0,
11768
- onMouseMove: handleMouseMoveWithCamera,
11769
- onMouseUp: enableCamera ? handleMouseUp : void 0,
11770
- onMouseLeave: handleMouseLeaveWithCamera,
11998
+ onPointerDown: gestureHandlers.onPointerDown,
11999
+ onPointerMove: (e) => {
12000
+ gestureHandlers.onPointerMove(e);
12001
+ handleCanvasHover(e);
12002
+ },
12003
+ onPointerUp: gestureHandlers.onPointerUp,
12004
+ onPointerCancel: gestureHandlers.onPointerCancel,
12005
+ onPointerLeave: handleCanvasPointerLeave,
12006
+ onWheel: gestureHandlers.onWheel,
11771
12007
  onContextMenu: (e) => e.preventDefault(),
11772
- className: "cursor-pointer",
12008
+ className: "cursor-pointer touch-none",
11773
12009
  style: {
11774
12010
  width: viewportSize.width,
11775
12011
  height: viewportSize.height
@@ -11823,6 +12059,7 @@ var init_IsometricCanvas = __esm({
11823
12059
  init_ErrorState();
11824
12060
  init_useImageCache();
11825
12061
  init_useCamera();
12062
+ init_useCanvasGestures();
11826
12063
  init_useUnitSpriteAtlas();
11827
12064
  init_verificationRegistry();
11828
12065
  init_isometric();
@@ -14938,6 +15175,10 @@ var init_StateMachineView = __esm({
14938
15175
  const handleMouseLeave2 = () => {
14939
15176
  onHover(null, 0, 0);
14940
15177
  };
15178
+ const handlePointerDown2 = (e) => {
15179
+ if (e.pointerType === "mouse") return;
15180
+ handleMouseEnter2();
15181
+ };
14941
15182
  return /* @__PURE__ */ jsxs(
14942
15183
  "g",
14943
15184
  {
@@ -14947,6 +15188,7 @@ var init_StateMachineView = __esm({
14947
15188
  onClick: () => onClick?.(bundle),
14948
15189
  onMouseEnter: handleMouseEnter2,
14949
15190
  onMouseLeave: handleMouseLeave2,
15191
+ onPointerDown: handlePointerDown2,
14950
15192
  style: { pointerEvents: "auto" },
14951
15193
  children: [
14952
15194
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -15064,6 +15306,10 @@ var init_StateMachineView = __esm({
15064
15306
  const handleMouseLeave = useCallback(() => {
15065
15307
  onHover(null, 0, 0);
15066
15308
  }, [onHover]);
15309
+ const handlePointerDown = useCallback((e) => {
15310
+ if (e.pointerType === "mouse") return;
15311
+ handleMouseEnter();
15312
+ }, [handleMouseEnter]);
15067
15313
  const uniqueMarkerId = `arrow-${bundle.id}`;
15068
15314
  const hasDetails = bundle.labels.some((l) => l.hasDetails);
15069
15315
  return /* @__PURE__ */ jsxs(
@@ -15075,6 +15321,7 @@ var init_StateMachineView = __esm({
15075
15321
  onClick: () => onClick?.(bundle),
15076
15322
  onMouseEnter: handleMouseEnter,
15077
15323
  onMouseLeave: handleMouseLeave,
15324
+ onPointerDown: handlePointerDown,
15078
15325
  style: { pointerEvents: "auto" },
15079
15326
  children: [
15080
15327
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
@@ -23573,10 +23820,84 @@ function SubMenu({
23573
23820
  );
23574
23821
  return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
23575
23822
  }
23823
+ function MenuItemRow({
23824
+ item,
23825
+ itemId,
23826
+ hasSubMenu,
23827
+ isDanger,
23828
+ direction,
23829
+ isSubMenuOpen,
23830
+ activeSubMenuRef,
23831
+ eventBus,
23832
+ onItemClick,
23833
+ openSubMenu
23834
+ }) {
23835
+ const rowRef = useRef(null);
23836
+ const { triggerProps } = useTapReveal({
23837
+ enabled: hasSubMenu,
23838
+ onReveal: () => openSubMenu(itemId, rowRef.current),
23839
+ refs: [rowRef]
23840
+ });
23841
+ return /* @__PURE__ */ jsxs(Box, { children: [
23842
+ /* @__PURE__ */ jsx(
23843
+ Box,
23844
+ {
23845
+ ref: rowRef,
23846
+ as: "button",
23847
+ onClick: () => onItemClick({ ...item, id: itemId }, itemId),
23848
+ "aria-disabled": item.disabled || void 0,
23849
+ onMouseEnter: (e) => {
23850
+ if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
23851
+ },
23852
+ onPointerDown: hasSubMenu ? triggerProps.onPointerDown : void 0,
23853
+ "data-testid": item.event ? `action-${item.event}` : void 0,
23854
+ className: cn(
23855
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23856
+ "text-sm transition-colors",
23857
+ "hover:bg-muted",
23858
+ "focus:outline-none focus:bg-muted",
23859
+ "disabled:opacity-50 disabled:cursor-not-allowed",
23860
+ item.disabled && "cursor-not-allowed",
23861
+ isDanger && "text-error hover:bg-error/10"
23862
+ ),
23863
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23864
+ 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" })),
23865
+ /* @__PURE__ */ jsx(
23866
+ Typography,
23867
+ {
23868
+ variant: "small",
23869
+ className: cn("flex-1", isDanger && "text-red-600"),
23870
+ children: item.label
23871
+ }
23872
+ ),
23873
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23874
+ hasSubMenu && /* @__PURE__ */ jsx(
23875
+ Icon,
23876
+ {
23877
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
23878
+ size: "sm",
23879
+ className: "flex-shrink-0"
23880
+ }
23881
+ )
23882
+ ] })
23883
+ }
23884
+ ),
23885
+ hasSubMenu && isSubMenuOpen && item.subMenu && /* @__PURE__ */ jsx(
23886
+ SubMenu,
23887
+ {
23888
+ items: item.subMenu,
23889
+ itemRef: activeSubMenuRef,
23890
+ direction,
23891
+ eventBus
23892
+ }
23893
+ )
23894
+ ] });
23895
+ }
23576
23896
  var MENU_GAP, menuContainerStyles, Menu;
23577
23897
  var init_Menu = __esm({
23578
23898
  "components/core/molecules/Menu.tsx"() {
23579
23899
  "use client";
23900
+ init_useTapReveal();
23580
23901
  init_Box();
23581
23902
  init_Icon();
23582
23903
  init_Divider();
@@ -23631,6 +23952,10 @@ var init_Menu = __esm({
23631
23952
  setIsOpen(false);
23632
23953
  }
23633
23954
  };
23955
+ const openSubMenu = (itemId, el) => {
23956
+ setActiveSubMenu(itemId);
23957
+ setActiveSubMenuRef(el);
23958
+ };
23634
23959
  useEffect(() => {
23635
23960
  if (isOpen) {
23636
23961
  updatePosition();
@@ -23674,61 +23999,22 @@ var init_Menu = __esm({
23674
23999
  if (isDivider) {
23675
24000
  return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
23676
24001
  }
23677
- return /* @__PURE__ */ jsxs(Box, { children: [
23678
- /* @__PURE__ */ jsx(
23679
- Box,
23680
- {
23681
- as: "button",
23682
- onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
23683
- "aria-disabled": item.disabled || void 0,
23684
- onMouseEnter: (e) => {
23685
- if (hasSubMenu) {
23686
- setActiveSubMenu(itemId);
23687
- setActiveSubMenuRef(e.currentTarget);
23688
- }
23689
- },
23690
- "data-testid": item.event ? `action-${item.event}` : void 0,
23691
- className: cn(
23692
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
23693
- "text-sm transition-colors",
23694
- "hover:bg-muted",
23695
- "focus:outline-none focus:bg-muted",
23696
- "disabled:opacity-50 disabled:cursor-not-allowed",
23697
- item.disabled && "cursor-not-allowed",
23698
- isDanger && "text-error hover:bg-error/10"
23699
- ),
23700
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
23701
- 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" })),
23702
- /* @__PURE__ */ jsx(
23703
- Typography,
23704
- {
23705
- variant: "small",
23706
- className: cn("flex-1", isDanger && "text-red-600"),
23707
- children: item.label
23708
- }
23709
- ),
23710
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
23711
- hasSubMenu && /* @__PURE__ */ jsx(
23712
- Icon,
23713
- {
23714
- name: direction === "rtl" ? "chevron-left" : "chevron-right",
23715
- size: "sm",
23716
- className: "flex-shrink-0"
23717
- }
23718
- )
23719
- ] })
23720
- }
23721
- ),
23722
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
23723
- SubMenu,
23724
- {
23725
- items: item.subMenu,
23726
- itemRef: activeSubMenuRef,
23727
- direction,
23728
- eventBus
23729
- }
23730
- )
23731
- ] }, itemId);
24002
+ return /* @__PURE__ */ jsx(
24003
+ MenuItemRow,
24004
+ {
24005
+ item,
24006
+ itemId,
24007
+ hasSubMenu,
24008
+ isDanger,
24009
+ direction,
24010
+ isSubMenuOpen: activeSubMenu === itemId,
24011
+ activeSubMenuRef,
24012
+ eventBus,
24013
+ onItemClick: handleItemClick,
24014
+ openSubMenu
24015
+ },
24016
+ itemId
24017
+ );
23732
24018
  });
23733
24019
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
23734
24020
  "div",
@@ -26031,6 +26317,7 @@ var init_Popover = __esm({
26031
26317
  "use client";
26032
26318
  init_Typography();
26033
26319
  init_cn();
26320
+ init_useTapReveal();
26034
26321
  arrowClasses = {
26035
26322
  top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
26036
26323
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
@@ -26101,18 +26388,32 @@ var init_Popover = __esm({
26101
26388
  document.addEventListener("mousedown", handleClickOutside);
26102
26389
  return () => document.removeEventListener("mousedown", handleClickOutside);
26103
26390
  }, [isOpen, trigger]);
26104
- const triggerProps = trigger === "click" ? {
26391
+ const { triggerProps: tapTriggerProps } = useTapReveal({
26392
+ enabled: trigger === "hover",
26393
+ onReveal: handleOpen,
26394
+ onDismiss: handleClose,
26395
+ refs: [triggerRef, popoverRef]
26396
+ });
26397
+ const handlerProps = trigger === "click" ? {
26105
26398
  onClick: handleToggle
26106
26399
  } : {
26107
26400
  onMouseEnter: handleOpen,
26108
- onMouseLeave: handleClose
26401
+ onMouseLeave: handleClose,
26402
+ onPointerDown: tapTriggerProps.onPointerDown
26109
26403
  };
26110
26404
  const childElement = React85__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26405
+ const childPointerDown = childElement.props.onPointerDown;
26111
26406
  const triggerElement = React85__default.cloneElement(
26112
26407
  childElement,
26113
26408
  {
26114
26409
  ref: triggerRef,
26115
- ...triggerProps
26410
+ ...handlerProps,
26411
+ ...trigger === "hover" ? {
26412
+ onPointerDown: (e) => {
26413
+ tapTriggerProps.onPointerDown(e);
26414
+ childPointerDown?.(e);
26415
+ }
26416
+ } : void 0
26116
26417
  }
26117
26418
  );
26118
26419
  const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
@@ -26643,6 +26944,7 @@ var init_Tooltip = __esm({
26643
26944
  "use client";
26644
26945
  init_Typography();
26645
26946
  init_cn();
26947
+ init_useTapReveal();
26646
26948
  TRIGGER_GAP2 = 8;
26647
26949
  arrowClasses2 = {
26648
26950
  top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
@@ -26687,6 +26989,11 @@ var init_Tooltip = __esm({
26687
26989
  setIsVisible(false);
26688
26990
  }, hideDelay);
26689
26991
  };
26992
+ const { triggerProps } = useTapReveal({
26993
+ onReveal: handleMouseEnter,
26994
+ onDismiss: handleMouseLeave,
26995
+ refs: [triggerRef, tooltipRef]
26996
+ });
26690
26997
  useLayoutEffect(() => {
26691
26998
  if (isVisible) {
26692
26999
  updatePosition();
@@ -26699,12 +27006,17 @@ var init_Tooltip = __esm({
26699
27006
  };
26700
27007
  }, []);
26701
27008
  const triggerElement = React85__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
27009
+ const childPointerDown = triggerElement.props.onPointerDown;
26702
27010
  const trigger = React85__default.cloneElement(triggerElement, {
26703
27011
  ref: triggerRef,
26704
27012
  onMouseEnter: handleMouseEnter,
26705
27013
  onMouseLeave: handleMouseLeave,
26706
27014
  onFocus: handleMouseEnter,
26707
- onBlur: handleMouseLeave
27015
+ onBlur: handleMouseLeave,
27016
+ onPointerDown: (e) => {
27017
+ triggerProps.onPointerDown(e);
27018
+ childPointerDown?.(e);
27019
+ }
26708
27020
  });
26709
27021
  const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxs(
26710
27022
  "div",
@@ -30054,6 +30366,14 @@ var init_GraphView = __esm({
30054
30366
  },
30055
30367
  [onNodeClick]
30056
30368
  );
30369
+ const handleNodePointerDown = useCallback(
30370
+ (e, node) => {
30371
+ if (e.pointerType === "mouse") return;
30372
+ handleNodeMouseEnter(node);
30373
+ handleNodeClickInternal(node);
30374
+ },
30375
+ [handleNodeMouseEnter, handleNodeClickInternal]
30376
+ );
30057
30377
  if (nodes.length === 0) {
30058
30378
  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") }) });
30059
30379
  }
@@ -30113,6 +30433,7 @@ var init_GraphView = __esm({
30113
30433
  onMouseEnter: () => handleNodeMouseEnter(node),
30114
30434
  onMouseLeave: handleNodeMouseLeave,
30115
30435
  onClick: () => handleNodeClickInternal(node),
30436
+ onPointerDown: (e) => handleNodePointerDown(e, node),
30116
30437
  children: [
30117
30438
  /* @__PURE__ */ jsx(
30118
30439
  "circle",
@@ -30193,13 +30514,13 @@ var init_MapView = __esm({
30193
30514
  shadowSize: [41, 41]
30194
30515
  });
30195
30516
  L.Marker.prototype.options.icon = defaultIcon;
30196
- const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback123, useState: useState110 } = React85__default;
30517
+ const { useEffect: useEffect82, useRef: useRef80, useCallback: useCallback125, useState: useState111 } = React85__default;
30197
30518
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
30198
30519
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
30199
30520
  function MapUpdater({ centerLat, centerLng, zoom }) {
30200
30521
  const map = useMap();
30201
- const prevRef = useRef78({ centerLat, centerLng, zoom });
30202
- useEffect81(() => {
30522
+ const prevRef = useRef80({ centerLat, centerLng, zoom });
30523
+ useEffect82(() => {
30203
30524
  const prev = prevRef.current;
30204
30525
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
30205
30526
  map.setView([centerLat, centerLng], zoom);
@@ -30210,7 +30531,7 @@ var init_MapView = __esm({
30210
30531
  }
30211
30532
  function MapClickHandler({ onMapClick }) {
30212
30533
  const map = useMap();
30213
- useEffect81(() => {
30534
+ useEffect82(() => {
30214
30535
  if (!onMapClick) return;
30215
30536
  const handler = (e) => {
30216
30537
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -30238,8 +30559,8 @@ var init_MapView = __esm({
30238
30559
  showAttribution = true
30239
30560
  }) {
30240
30561
  const eventBus = useEventBus3();
30241
- const [clickedPosition, setClickedPosition] = useState110(null);
30242
- const handleMapClick = useCallback123((lat, lng) => {
30562
+ const [clickedPosition, setClickedPosition] = useState111(null);
30563
+ const handleMapClick = useCallback125((lat, lng) => {
30243
30564
  if (showClickedPin) {
30244
30565
  setClickedPosition({ lat, lng });
30245
30566
  }
@@ -30248,7 +30569,7 @@ var init_MapView = __esm({
30248
30569
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
30249
30570
  }
30250
30571
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
30251
- const handleMarkerClick = useCallback123((marker) => {
30572
+ const handleMarkerClick = useCallback125((marker) => {
30252
30573
  onMarkerClick?.(marker);
30253
30574
  if (markerClickEvent) {
30254
30575
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30589,7 +30910,7 @@ var init_StarRating = __esm({
30589
30910
  "aria-valuenow": value,
30590
30911
  tabIndex: readOnly ? void 0 : 0,
30591
30912
  onKeyDown: handleKeyDown,
30592
- onMouseLeave: () => setHoverValue(null),
30913
+ onPointerLeave: () => setHoverValue(null),
30593
30914
  children: Array.from({ length: max }, (_, i) => {
30594
30915
  const fillLevel = Math.max(0, Math.min(1, displayValue - i));
30595
30916
  const isFull = fillLevel >= 1;
@@ -30599,7 +30920,7 @@ var init_StarRating = __esm({
30599
30920
  {
30600
30921
  className: "relative inline-block",
30601
30922
  onClick: () => handleStarClick(i, false),
30602
- onMouseMove: (e) => {
30923
+ onPointerMove: (e) => {
30603
30924
  if (readOnly) return;
30604
30925
  const rect = e.currentTarget.getBoundingClientRect();
30605
30926
  const isLeftHalf = e.clientX - rect.left < rect.width / 2;
@@ -34489,7 +34810,7 @@ var init_PositionedCanvas = __esm({
34489
34810
  {
34490
34811
  ref: containerRef,
34491
34812
  "data-testid": "positioned-canvas",
34492
- className: "relative bg-background border border-border rounded-container overflow-hidden",
34813
+ className: "relative bg-background border border-border rounded-container overflow-hidden touch-none",
34493
34814
  style: { width, height },
34494
34815
  onClick: handleContainerClick,
34495
34816
  children: items.map((item) => {
@@ -38658,6 +38979,7 @@ var init_GraphCanvas = __esm({
38658
38979
  init_ErrorState();
38659
38980
  init_EmptyState();
38660
38981
  init_useEventBus();
38982
+ init_useCanvasGestures();
38661
38983
  GROUP_COLORS2 = [
38662
38984
  "var(--color-primary)",
38663
38985
  "var(--color-success)",
@@ -38695,6 +39017,10 @@ var init_GraphCanvas = __esm({
38695
39017
  const animRef = useRef(0);
38696
39018
  const [zoom, setZoom] = useState(1);
38697
39019
  const [offset, setOffset] = useState({ x: 0, y: 0 });
39020
+ const zoomRef = useRef(zoom);
39021
+ zoomRef.current = zoom;
39022
+ const offsetRef = useRef(offset);
39023
+ offsetRef.current = offset;
38698
39024
  const [hoveredNode, setHoveredNode] = useState(null);
38699
39025
  const nodesRef = useRef([]);
38700
39026
  const [, forceUpdate] = useState(0);
@@ -38954,25 +39280,27 @@ var init_GraphCanvas = __esm({
38954
39280
  setZoom(1);
38955
39281
  setOffset({ x: 0, y: 0 });
38956
39282
  }, []);
38957
- const handleWheel = useCallback(
38958
- (e) => {
38959
- if (!interactive) return;
38960
- e.preventDefault();
38961
- const coords = toCoords(e);
38962
- if (!coords) return;
38963
- const oldZoom = zoom;
38964
- const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38965
- const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38966
- if (newZoom === oldZoom) return;
38967
- setOffset((o) => ({
38968
- x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38969
- y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38970
- }));
38971
- setZoom(newZoom);
38972
- },
38973
- [interactive, toCoords, zoom]
38974
- );
38975
- const handleMouseDown = useCallback(
39283
+ const applyZoom = useCallback((factor, cx, cy) => {
39284
+ if (!interactive) return;
39285
+ const oldZoom = zoomRef.current;
39286
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
39287
+ if (newZoom === oldZoom) return;
39288
+ const o = offsetRef.current;
39289
+ setOffset({
39290
+ x: cx - (cx - o.x) * (newZoom / oldZoom),
39291
+ y: cy - (cy - o.y) * (newZoom / oldZoom)
39292
+ });
39293
+ setZoom(newZoom);
39294
+ }, [interactive]);
39295
+ const applyPanDelta = useCallback((dx, dy) => {
39296
+ if (!interactive) return;
39297
+ setOffset((o) => ({ x: o.x + dx, y: o.y + dy }));
39298
+ }, [interactive]);
39299
+ const cancelSinglePointer = useCallback(() => {
39300
+ interactionRef.current.mode = "none";
39301
+ interactionRef.current.dragNodeId = null;
39302
+ }, []);
39303
+ const handlePointerDown = useCallback(
38976
39304
  (e) => {
38977
39305
  const coords = toCoords(e);
38978
39306
  if (!coords) return;
@@ -38994,7 +39322,7 @@ var init_GraphCanvas = __esm({
38994
39322
  },
38995
39323
  [toCoords, nodeAt, draggable, interactive, offset]
38996
39324
  );
38997
- const handleMouseMove = useCallback(
39325
+ const handlePointerMove = useCallback(
38998
39326
  (e) => {
38999
39327
  const state = interactionRef.current;
39000
39328
  if (state.mode === "panning") {
@@ -39023,7 +39351,7 @@ var init_GraphCanvas = __esm({
39023
39351
  },
39024
39352
  [toCoords, nodeAt]
39025
39353
  );
39026
- const handleMouseUp = useCallback(
39354
+ const handlePointerUp = useCallback(
39027
39355
  (e) => {
39028
39356
  const state = interactionRef.current;
39029
39357
  const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
@@ -39040,11 +39368,19 @@ var init_GraphCanvas = __esm({
39040
39368
  },
39041
39369
  [toCoords, nodeAt, handleNodeClick]
39042
39370
  );
39043
- const handleMouseLeave = useCallback(() => {
39044
- interactionRef.current.mode = "none";
39045
- interactionRef.current.dragNodeId = null;
39371
+ const handlePointerLeave = useCallback(() => {
39046
39372
  setHoveredNode(null);
39047
39373
  }, []);
39374
+ const gestureHandlers = useCanvasGestures({
39375
+ canvasRef,
39376
+ enabled: interactive || draggable,
39377
+ onPointerDown: handlePointerDown,
39378
+ onPointerMove: handlePointerMove,
39379
+ onPointerUp: handlePointerUp,
39380
+ onZoom: applyZoom,
39381
+ onPanDelta: applyPanDelta,
39382
+ onMultiTouchStart: cancelSinglePointer
39383
+ });
39048
39384
  const handleDoubleClick = useCallback(
39049
39385
  (e) => {
39050
39386
  const coords = toCoords(e);
@@ -39113,13 +39449,14 @@ var init_GraphCanvas = __esm({
39113
39449
  ref: canvasRef,
39114
39450
  width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39115
39451
  height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39116
- className: "w-full cursor-grab active:cursor-grabbing",
39452
+ className: "w-full cursor-grab active:cursor-grabbing touch-none",
39117
39453
  style: { height },
39118
- onWheel: handleWheel,
39119
- onMouseDown: handleMouseDown,
39120
- onMouseMove: handleMouseMove,
39121
- onMouseUp: handleMouseUp,
39122
- onMouseLeave: handleMouseLeave,
39454
+ onPointerDown: gestureHandlers.onPointerDown,
39455
+ onPointerMove: gestureHandlers.onPointerMove,
39456
+ onPointerUp: gestureHandlers.onPointerUp,
39457
+ onPointerCancel: gestureHandlers.onPointerCancel,
39458
+ onPointerLeave: handlePointerLeave,
39459
+ onWheel: gestureHandlers.onWheel,
39123
39460
  onDoubleClick: handleDoubleClick
39124
39461
  }
39125
39462
  ) }),
@@ -43591,6 +43928,9 @@ var init_GameCanvas3D2 = __esm({
43591
43928
  target: cameraTarget,
43592
43929
  enableDamping: true,
43593
43930
  dampingFactor: 0.05,
43931
+ enableZoom: true,
43932
+ enablePan: true,
43933
+ touches: { ONE: THREE3.TOUCH.ROTATE, TWO: THREE3.TOUCH.DOLLY_PAN },
43594
43934
  minDistance: 2,
43595
43935
  maxDistance: 100,
43596
43936
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -48902,20 +49242,21 @@ var init_SplitPane = __esm({
48902
49242
  const [ratio, setRatio] = useState(initialRatio);
48903
49243
  const containerRef = useRef(null);
48904
49244
  const isDragging = useRef(false);
48905
- const handleMouseDown = useCallback(
49245
+ const handlePointerDown = useCallback(
48906
49246
  (e) => {
48907
49247
  if (!resizable) return;
48908
49248
  e.preventDefault();
48909
49249
  isDragging.current = true;
48910
- const handleMouseMove = (e2) => {
49250
+ e.currentTarget.setPointerCapture(e.pointerId);
49251
+ const handlePointerMove = (ev) => {
48911
49252
  if (!isDragging.current || !containerRef.current) return;
48912
49253
  const rect = containerRef.current.getBoundingClientRect();
48913
49254
  let newRatio;
48914
49255
  if (direction === "horizontal") {
48915
- const x = e2.clientX - rect.left;
49256
+ const x = ev.clientX - rect.left;
48916
49257
  newRatio = x / rect.width * 100;
48917
49258
  } else {
48918
- const y = e2.clientY - rect.top;
49259
+ const y = ev.clientY - rect.top;
48919
49260
  newRatio = y / rect.height * 100;
48920
49261
  }
48921
49262
  const minRatio = minSize / (direction === "horizontal" ? rect.width : rect.height) * 100;
@@ -48923,13 +49264,15 @@ var init_SplitPane = __esm({
48923
49264
  newRatio = Math.max(minRatio, Math.min(maxRatio, newRatio));
48924
49265
  setRatio(newRatio);
48925
49266
  };
48926
- const handleMouseUp = () => {
49267
+ const handlePointerUp = () => {
48927
49268
  isDragging.current = false;
48928
- document.removeEventListener("mousemove", handleMouseMove);
48929
- document.removeEventListener("mouseup", handleMouseUp);
49269
+ document.removeEventListener("pointermove", handlePointerMove);
49270
+ document.removeEventListener("pointerup", handlePointerUp);
49271
+ document.removeEventListener("pointercancel", handlePointerUp);
48930
49272
  };
48931
- document.addEventListener("mousemove", handleMouseMove);
48932
- document.addEventListener("mouseup", handleMouseUp);
49273
+ document.addEventListener("pointermove", handlePointerMove);
49274
+ document.addEventListener("pointerup", handlePointerUp);
49275
+ document.addEventListener("pointercancel", handlePointerUp);
48933
49276
  },
48934
49277
  [direction, minSize, resizable]
48935
49278
  );
@@ -48958,9 +49301,9 @@ var init_SplitPane = __esm({
48958
49301
  resizable && /* @__PURE__ */ jsx(
48959
49302
  "div",
48960
49303
  {
48961
- onMouseDown: handleMouseDown,
49304
+ onPointerDown: handlePointerDown,
48962
49305
  className: cn(
48963
- "flex-shrink-0 bg-border transition-colors",
49306
+ "flex-shrink-0 bg-border transition-colors touch-none",
48964
49307
  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"
48965
49308
  )
48966
49309
  }