@cloudparse/up-miniapps-sdk 0.6.7 → 0.7.0

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.
package/dist/index.js CHANGED
@@ -72,6 +72,58 @@ function isRunningInsideNativeShell() {
72
72
  // src/bridge.ts
73
73
  var BridgeTimeoutError = class extends Error {
74
74
  };
75
+ var MOCK_ACHIEVEMENTS = [
76
+ {
77
+ completed: false,
78
+ description: "Discover 5 points of interest around Annecy.",
79
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=explore",
80
+ playerActionCount: 3,
81
+ slug: "explore-5-pois",
82
+ title: "Explorer",
83
+ totalActionCount: 5,
84
+ upReward: 50,
85
+ xpReward: 100
86
+ },
87
+ {
88
+ completed: true,
89
+ description: "Complete your first navigation journey.",
90
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=journey",
91
+ playerActionCount: 1,
92
+ slug: "first-journey",
93
+ title: "First Steps",
94
+ totalActionCount: 1,
95
+ upReward: 20,
96
+ xpReward: 40
97
+ },
98
+ {
99
+ completed: false,
100
+ description: "Scan 10 QR codes at partner venues.",
101
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=scan",
102
+ playerActionCount: 4,
103
+ slug: "scan-10-qrcodes",
104
+ title: "Code Breaker",
105
+ totalActionCount: 10,
106
+ upReward: 80,
107
+ xpReward: 150
108
+ }
109
+ ];
110
+ function mockAchievement(slug) {
111
+ const found = MOCK_ACHIEVEMENTS.find((a) => a.slug === slug);
112
+ if (found) return found;
113
+ return {
114
+ completed: false,
115
+ description: "Mocked achievement (no native shell attached).",
116
+ playerActionCount: 0,
117
+ slug: slug || "unknown-achievement",
118
+ title: slug || "Unknown Achievement",
119
+ totalActionCount: 1,
120
+ upReward: 0,
121
+ xpReward: 0
122
+ };
123
+ }
124
+ function mockAchievements() {
125
+ return MOCK_ACHIEVEMENTS;
126
+ }
75
127
  var Bridge = class {
76
128
  callbacks = /* @__PURE__ */ new Map();
77
129
  eventListeners = /* @__PURE__ */ new Map();
@@ -222,6 +274,12 @@ var Bridge = class {
222
274
  return this.mockSaveImage(
223
275
  payload
224
276
  );
277
+ case "GET_ACHIEVEMENT":
278
+ return Promise.resolve(
279
+ mockAchievement(payload?.slug)
280
+ );
281
+ case "GET_ACHIEVEMENTS":
282
+ return Promise.resolve(mockAchievements());
225
283
  default:
226
284
  console.warn(
227
285
  `[Bridge] Mock mode active: Unhandled message type ${type}`
@@ -408,420 +466,29 @@ var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @_
408
466
  SheetDescription.displayName = SheetPrimitive.Description.displayName;
409
467
 
410
468
  // src/components/Map/components/MapDrawer.tsx
411
- import {
412
- forwardRef as forwardRef3,
413
- useEffect,
414
- useImperativeHandle,
415
- useRef,
416
- useState
417
- } from "react";
469
+ import { forwardRef as forwardRef3, useImperativeHandle, useRef, useState } from "react";
418
470
  import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
419
- var resolveHeightToPixels = (height, containerHeight) => {
420
- if (typeof height === "number") {
421
- return height;
422
- }
423
- if (height.endsWith("%")) {
424
- return parseFloat(height) / 100 * containerHeight;
425
- }
426
- return parseFloat(height);
427
- };
428
- var CONTENT_EDGE_PULL_THRESHOLD_PX = 24;
429
- var HANDLE_BAR_HEIGHT_PX = 32;
430
- var HANDLE_TAP_CLICK_SUPPRESSION_MS = 400;
431
471
  var MapDrawer = forwardRef3(
432
472
  ({
433
473
  menuItems,
434
474
  state,
435
475
  onChangeState,
436
- side,
437
- etaBarHeight = 0,
438
- etaData,
439
- touchStartY,
440
- setTouchStartY,
441
- currentTouchY,
442
- setCurrentTouchY,
443
- isDragging,
444
- clampedDragOffset,
445
- drawerPeekHeight,
446
- drawerFullHeight,
447
- drawerAutoMaxHeight,
448
- containerHeight,
449
476
  menuContent,
450
- enableTouchDrag = false,
451
- handleVisible = true,
452
- onAutoContentHeightChange,
453
477
  showScrollToTopButton = true,
454
- scrollToTopThreshold = 300,
455
- safeAreaInsets = { bottom: 0, left: 0, right: 0, top: 0 }
478
+ scrollToTopThreshold = 300
456
479
  }, ref) => {
457
480
  const scrollContainerRef = useRef(null);
458
- const containerRef = useRef(null);
459
- const handleBarRef = useRef(null);
460
- const contentAreaRef = useRef(null);
461
481
  useImperativeHandle(
462
482
  ref,
463
483
  () => scrollContainerRef.current
464
484
  );
465
- const handleHandleClick = () => {
466
- if (side === "left") {
467
- onChangeState(state === "closed" ? "full" : "closed");
468
- } else {
469
- if (state === "closed") {
470
- onChangeState("peek");
471
- } else if (state === "peek") {
472
- onChangeState("full");
473
- } else if (state === "auto") {
474
- onChangeState("closed");
475
- } else {
476
- onChangeState("peek");
477
- }
478
- }
479
- };
480
- const handleHandleClickRef = useRef(handleHandleClick);
481
- handleHandleClickRef.current = handleHandleClick;
482
- const [autoContentPx, setAutoContentPx] = useState(0);
483
- useEffect(() => {
484
- if (state !== "auto") return;
485
- const scrollEl = scrollContainerRef.current;
486
- const contentAreaEl = contentAreaRef.current;
487
- if (!scrollEl || !contentAreaEl) return;
488
- const handleHeight = handleVisible ? handleBarRef.current?.offsetHeight ?? 0 : 0;
489
- const contentEl = scrollEl.firstElementChild;
490
- const measure = () => {
491
- const contentAreaStyle = window.getComputedStyle(contentAreaEl);
492
- const verticalPadding = parseFloat(contentAreaStyle.paddingTop || "0") + parseFloat(contentAreaStyle.paddingBottom || "0");
493
- const contentHeight = contentEl?.scrollHeight ?? scrollEl.scrollHeight;
494
- const px = handleHeight + verticalPadding + contentHeight;
495
- setAutoContentPx(px);
496
- onAutoContentHeightChange?.(px);
497
- };
498
- measure();
499
- const ro = new ResizeObserver(measure);
500
- ro.observe(contentEl ?? scrollEl);
501
- return () => ro.disconnect();
502
- }, [state, handleVisible, onAutoContentHeightChange]);
503
485
  const [showScrollTop, setShowScrollTop] = useState(false);
504
486
  const handleScroll = (e) => {
505
487
  const scrollTop = e.currentTarget.scrollTop;
506
488
  setShowScrollTop(state !== "closed" && scrollTop > scrollToTopThreshold);
507
489
  };
508
- useEffect(() => {
509
- if (state === "closed") {
510
- setShowScrollTop(false);
511
- } else {
512
- const scrollTop = scrollContainerRef.current?.scrollTop || 0;
513
- setShowScrollTop(scrollTop > scrollToTopThreshold);
514
- }
515
- }, [state, scrollToTopThreshold]);
516
- const isDraggingSheet = useRef(false);
517
- const hasDeterminedDirection = useRef(false);
518
- const isTouchOnContent = useRef(false);
519
- const isTouchOnHandle = useRef(false);
520
- const suppressNextClickRef = useRef(false);
521
- const contentAtTopSinceY = useRef(null);
522
- const contentAtBottomSinceY = useRef(null);
523
- const dragStartYRef = useRef(null);
524
- const dragBaseHeightPxRef = useRef(0);
525
- const stateRef = useRef(state);
526
- const touchStartYRef = useRef(touchStartY);
527
- const currentTouchYRef = useRef(currentTouchY);
528
- useEffect(() => {
529
- stateRef.current = state;
530
- }, [state]);
531
- useEffect(() => {
532
- touchStartYRef.current = touchStartY;
533
- }, [touchStartY]);
534
- useEffect(() => {
535
- currentTouchYRef.current = currentTouchY;
536
- }, [currentTouchY]);
537
- useEffect(() => {
538
- const container = containerRef.current;
539
- if (!container || side !== "bottom" || !enableTouchDrag) return;
540
- const handleNativeTouchStart = (e) => {
541
- const clientY = e.targetTouches[0].clientY;
542
- setTouchStartY(clientY);
543
- setCurrentTouchY(clientY);
544
- isDraggingSheet.current = false;
545
- hasDeterminedDirection.current = false;
546
- contentAtTopSinceY.current = null;
547
- contentAtBottomSinceY.current = null;
548
- dragStartYRef.current = null;
549
- const target = e.target;
550
- isTouchOnContent.current = scrollContainerRef.current ? scrollContainerRef.current.contains(target) : false;
551
- isTouchOnHandle.current = handleBarRef.current ? handleBarRef.current.contains(target) : false;
552
- };
553
- const handleNativeTouchMove = (e) => {
554
- const startY = touchStartYRef.current;
555
- if (startY === null) return;
556
- const currentY = e.targetTouches[0].clientY;
557
- const deltaY = startY - currentY;
558
- const scrollTop = scrollContainerRef.current ? scrollContainerRef.current.scrollTop : 0;
559
- const clientHeight = scrollContainerRef.current ? scrollContainerRef.current.clientHeight : 0;
560
- const scrollHeight = scrollContainerRef.current ? scrollContainerRef.current.scrollHeight : 0;
561
- const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
562
- const currentState = stateRef.current;
563
- const containerHForHeights = containerHeight || container.parentElement?.getBoundingClientRect().height || window.innerHeight;
564
- const fullPxNow = resolveHeightToPixels(
565
- drawerFullHeight || "85%",
566
- containerHForHeights
567
- );
568
- const peekPxNow = resolveHeightToPixels(
569
- drawerPeekHeight || "15%",
570
- containerHForHeights
571
- );
572
- const closedPxNow = (handleVisible ? HANDLE_BAR_HEIGHT_PX : 0) + (currentState === "closed" ? safeAreaInsets.bottom : 0);
573
- const autoMaxPxNow = resolveHeightToPixels(
574
- drawerAutoMaxHeight || "100%",
575
- containerHForHeights
576
- );
577
- const autoPxNow = Math.min(autoMaxPxNow, autoContentPx);
578
- const baselineHeightPxForState = (s) => {
579
- if (s === "full") return fullPxNow;
580
- if (s === "peek") return peekPxNow;
581
- if (s === "auto") return autoPxNow;
582
- return closedPxNow;
583
- };
584
- const commitDrag = () => {
585
- isDraggingSheet.current = true;
586
- dragStartYRef.current = currentY;
587
- dragBaseHeightPxRef.current = baselineHeightPxForState(currentState);
588
- };
589
- if (scrollTop <= 0) {
590
- if (contentAtTopSinceY.current === null) {
591
- contentAtTopSinceY.current = currentY;
592
- }
593
- } else {
594
- contentAtTopSinceY.current = null;
595
- }
596
- if (isAtBottom) {
597
- if (contentAtBottomSinceY.current === null) {
598
- contentAtBottomSinceY.current = currentY;
599
- }
600
- } else {
601
- contentAtBottomSinceY.current = null;
602
- }
603
- const pullPastTop = contentAtTopSinceY.current !== null ? currentY - contentAtTopSinceY.current : 0;
604
- const pullPastBottom = contentAtBottomSinceY.current !== null ? contentAtBottomSinceY.current - currentY : 0;
605
- if (!hasDeterminedDirection.current) {
606
- if (!isTouchOnContent.current) {
607
- if (Math.abs(deltaY) < CONTENT_EDGE_PULL_THRESHOLD_PX) {
608
- return;
609
- }
610
- commitDrag();
611
- hasDeterminedDirection.current = true;
612
- } else if (currentState !== "full") {
613
- if (deltaY > 0 && isAtBottom && pullPastBottom >= CONTENT_EDGE_PULL_THRESHOLD_PX) {
614
- commitDrag();
615
- hasDeterminedDirection.current = true;
616
- } else if (deltaY < 0 && scrollTop <= 0 && pullPastTop >= CONTENT_EDGE_PULL_THRESHOLD_PX) {
617
- commitDrag();
618
- hasDeterminedDirection.current = true;
619
- } else if (deltaY > 0 && isAtBottom || deltaY < 0 && scrollTop <= 0) {
620
- return;
621
- } else {
622
- isDraggingSheet.current = false;
623
- hasDeterminedDirection.current = true;
624
- }
625
- } else {
626
- if (deltaY < 0 && scrollTop <= 0 && pullPastTop >= CONTENT_EDGE_PULL_THRESHOLD_PX) {
627
- commitDrag();
628
- hasDeterminedDirection.current = true;
629
- } else if (deltaY < 0 && scrollTop <= 0) {
630
- return;
631
- } else {
632
- isDraggingSheet.current = false;
633
- hasDeterminedDirection.current = true;
634
- }
635
- }
636
- } else if (!isDraggingSheet.current && isTouchOnContent.current) {
637
- if (deltaY < 0 && scrollTop <= 0 && pullPastTop >= CONTENT_EDGE_PULL_THRESHOLD_PX) {
638
- commitDrag();
639
- return;
640
- }
641
- if (deltaY > 0 && isAtBottom && currentState !== "full" && pullPastBottom >= CONTENT_EDGE_PULL_THRESHOLD_PX) {
642
- commitDrag();
643
- return;
644
- }
645
- }
646
- if (isDraggingSheet.current) {
647
- if (e.cancelable) {
648
- e.preventDefault();
649
- }
650
- const parentContainer = container.parentElement;
651
- if (parentContainer) {
652
- const dragOffset = currentY - (dragStartYRef.current ?? currentY);
653
- let localClamped = dragOffset;
654
- if (currentState === "full") {
655
- localClamped = Math.max(
656
- 0,
657
- Math.min(fullPxNow - closedPxNow, dragOffset)
658
- );
659
- } else if (currentState === "peek") {
660
- localClamped = Math.max(
661
- -(fullPxNow - peekPxNow),
662
- Math.min(peekPxNow - closedPxNow, dragOffset)
663
- );
664
- } else if (currentState === "closed") {
665
- localClamped = Math.min(
666
- 0,
667
- Math.max(-(fullPxNow - closedPxNow), dragOffset)
668
- );
669
- } else if (currentState === "auto") {
670
- localClamped = Math.max(0, dragOffset);
671
- }
672
- parentContainer.style.setProperty(
673
- "--drag-offset",
674
- `${localClamped}px`
675
- );
676
- }
677
- }
678
- };
679
- const handleNativeTouchEnd = (e) => {
680
- const startY = touchStartYRef.current;
681
- const currentY = currentTouchYRef.current;
682
- const parentContainer = container.parentElement;
683
- if (startY === null || currentY === null) {
684
- if (parentContainer) {
685
- parentContainer.style.setProperty("--drag-offset", "0px");
686
- }
687
- return;
688
- }
689
- if (!isDraggingSheet.current) {
690
- if (isTouchOnHandle.current) {
691
- handleHandleClickRef.current();
692
- suppressNextClickRef.current = true;
693
- setTimeout(() => {
694
- suppressNextClickRef.current = false;
695
- }, HANDLE_TAP_CLICK_SUPPRESSION_MS);
696
- }
697
- setTouchStartY(null);
698
- setCurrentTouchY(null);
699
- if (parentContainer) {
700
- parentContainer.style.setProperty("--drag-offset", "0px");
701
- }
702
- return;
703
- }
704
- const touchEndY = e.changedTouches[0].clientY;
705
- const dragStartY = dragStartYRef.current ?? startY;
706
- const dragDistance = Math.abs(touchEndY - dragStartY);
707
- if (dragDistance < 10) {
708
- setTouchStartY(null);
709
- setCurrentTouchY(null);
710
- if (parentContainer) {
711
- parentContainer.style.setProperty("--drag-offset", "0px");
712
- }
713
- return;
714
- }
715
- const containerH2 = containerHeight || (parentContainer ? parentContainer.getBoundingClientRect().height : window.innerHeight);
716
- const fullPx2 = resolveHeightToPixels(
717
- drawerFullHeight || "85%",
718
- containerH2
719
- );
720
- const peekPx2 = resolveHeightToPixels(
721
- drawerPeekHeight || "15%",
722
- containerH2
723
- );
724
- const currentState = stateRef.current;
725
- const closedPx = (handleVisible ? HANDLE_BAR_HEIGHT_PX : 0) + (currentState === "closed" ? safeAreaInsets.bottom : 0);
726
- const dragDelta = touchEndY - dragStartY;
727
- const drawerHeightFromBottom = dragBaseHeightPxRef.current - dragDelta;
728
- const distToClosed = Math.abs(drawerHeightFromBottom - closedPx);
729
- let targetState;
730
- if (currentState === "auto") {
731
- const autoMaxPx2 = resolveHeightToPixels(
732
- drawerAutoMaxHeight || "100%",
733
- containerH2
734
- );
735
- const autoPx2 = Math.min(autoMaxPx2, autoContentPx);
736
- const distToAuto = Math.abs(drawerHeightFromBottom - autoPx2);
737
- targetState = distToAuto <= distToClosed ? "auto" : "closed";
738
- } else {
739
- const distToFull = Math.abs(drawerHeightFromBottom - fullPx2);
740
- const distToPeek = Math.abs(drawerHeightFromBottom - peekPx2);
741
- const minDist = Math.min(distToFull, distToPeek, distToClosed);
742
- if (minDist === distToFull) {
743
- targetState = "full";
744
- } else if (minDist === distToPeek) {
745
- targetState = "peek";
746
- } else {
747
- targetState = "closed";
748
- }
749
- }
750
- setTouchStartY(null);
751
- setCurrentTouchY(null);
752
- onChangeState(targetState);
753
- requestAnimationFrame(() => {
754
- if (parentContainer) {
755
- parentContainer.style.setProperty("--drag-offset", "0px");
756
- }
757
- });
758
- };
759
- const handleNativeClickCapture = (e) => {
760
- if (!suppressNextClickRef.current) return;
761
- suppressNextClickRef.current = false;
762
- e.preventDefault();
763
- e.stopPropagation();
764
- };
765
- container.addEventListener("touchstart", handleNativeTouchStart, {
766
- passive: true
767
- });
768
- container.addEventListener("touchmove", handleNativeTouchMove, {
769
- passive: false
770
- });
771
- container.addEventListener("touchend", handleNativeTouchEnd, {
772
- passive: true
773
- });
774
- container.addEventListener("click", handleNativeClickCapture, {
775
- capture: true
776
- });
777
- return () => {
778
- container.removeEventListener("touchstart", handleNativeTouchStart);
779
- container.removeEventListener("touchmove", handleNativeTouchMove);
780
- container.removeEventListener("touchend", handleNativeTouchEnd);
781
- container.removeEventListener("click", handleNativeClickCapture, {
782
- capture: true
783
- });
784
- };
785
- }, [
786
- side,
787
- enableTouchDrag,
788
- containerHeight,
789
- drawerFullHeight,
790
- drawerPeekHeight,
791
- drawerAutoMaxHeight,
792
- safeAreaInsets,
793
- onChangeState,
794
- setTouchStartY,
795
- setCurrentTouchY,
796
- autoContentPx,
797
- handleVisible
798
- ]);
799
- const isBackdropActive = state === "full" || state === "auto";
490
+ const isBackdropActive = state !== "closed";
800
491
  const backdropStyles = isBackdropActive ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none";
801
- const containerH = containerHeight || window.innerHeight;
802
- const fullPx = resolveHeightToPixels(drawerFullHeight || "85%", containerH);
803
- const peekPx = resolveHeightToPixels(drawerPeekHeight || "15%", containerH);
804
- const autoMaxPx = resolveHeightToPixels(
805
- drawerAutoMaxHeight || "100%",
806
- containerH
807
- );
808
- const shiftPx = fullPx - peekPx;
809
- const autoPx = Math.min(autoMaxPx, autoContentPx);
810
- const autoShiftPx = Math.max(fullPx - autoPx, 0);
811
- const getBottomDrawerTransform = () => {
812
- if (state === "full") {
813
- return "translateY(var(--drag-offset, 0px))";
814
- }
815
- if (state === "peek") {
816
- return `translateY(calc(${shiftPx}px + var(--drag-offset, 0px)))`;
817
- }
818
- if (state === "auto") {
819
- return `translateY(calc(${autoShiftPx}px + var(--drag-offset, 0px)))`;
820
- }
821
- const closedVisibleHeight = handleVisible ? HANDLE_BAR_HEIGHT_PX : 0;
822
- const closedShiftPx = fullPx - closedVisibleHeight;
823
- return `translateY(calc(${closedShiftPx}px + var(--drag-offset, 0px)))`;
824
- };
825
492
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
826
493
  /* @__PURE__ */ jsx3("style", { children: `
827
494
  .no-scrollbar {
@@ -839,233 +506,113 @@ var MapDrawer = forwardRef3(
839
506
  onClick: () => onChangeState("closed")
840
507
  }
841
508
  ),
842
- side === "left" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
843
- state === "closed" && /* @__PURE__ */ jsx3(
844
- Button,
845
- {
846
- className: "absolute top-4 left-4 z-40 w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all cursor-pointer hover:bg-zinc-900 hover:text-white p-0!",
847
- onClick: () => onChangeState("full"),
848
- variant: "ghost",
849
- children: /* @__PURE__ */ jsxs2(
850
- "svg",
851
- {
852
- fill: "none",
853
- height: "24",
854
- stroke: "currentColor",
855
- strokeLinecap: "round",
856
- strokeLinejoin: "round",
857
- strokeWidth: "2",
858
- viewBox: "0 0 24 24",
859
- width: "24",
860
- xmlns: "http://www.w3.org/2000/svg",
861
- children: [
862
- /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
863
- /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
864
- /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
865
- ]
866
- }
867
- )
868
- }
869
- ),
870
- /* @__PURE__ */ jsx3(
871
- Sheet,
872
- {
873
- onOpenChange: (open) => onChangeState(open ? "full" : "closed"),
874
- open: state !== "closed",
875
- children: /* @__PURE__ */ jsxs2(
876
- SheetContent,
877
- {
878
- className: "w-80 bg-zinc-950/85 backdrop-blur-md border-r border-y border-white/10 rounded-r-2xl shadow-2xl p-4 text-white flex flex-col h-full overflow-hidden",
879
- portal: false,
880
- side: "left",
881
- children: [
882
- /* @__PURE__ */ jsx3(SheetHeader, { className: "pb-3 border-b border-white/10 mb-2 text-left", children: /* @__PURE__ */ jsx3(SheetTitle, { className: "text-sm font-bold tracking-wider text-zinc-400 uppercase", children: "Menu" }) }),
883
- /* @__PURE__ */ jsx3(
884
- "div",
885
- {
886
- className: "flex-1 overflow-y-auto no-scrollbar pr-2",
887
- onScroll: handleScroll,
888
- ref: scrollContainerRef,
889
- children: menuContent ? menuContent : /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1.5", children: menuItems.map((item) => /* @__PURE__ */ jsxs2(
890
- Button,
891
- {
892
- className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 justify-start hover:bg-white/5 hover:text-white active:bg-white/10 text-white ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : ""}`,
893
- disabled: item.disabled,
894
- onClick: () => {
895
- if (!item.disabled) {
896
- item.onPress();
897
- onChangeState("closed");
898
- }
899
- },
900
- variant: "ghost",
901
- children: [
902
- /* @__PURE__ */ jsxs2("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
903
- item.icon,
904
- item.badge !== void 0 && /* @__PURE__ */ jsx3("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
905
- ] }),
906
- /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium tracking-wide flex-1", children: item.label })
907
- ]
908
- },
909
- item.id
910
- )) })
911
- }
912
- ),
913
- showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx3(
914
- Button,
915
- {
916
- className: "absolute bottom-6 right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black hover:text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer p-0!",
917
- onClick: () => {
918
- scrollContainerRef.current?.scrollTo({
919
- behavior: "smooth",
920
- top: 0
921
- });
922
- },
923
- variant: "ghost",
924
- children: /* @__PURE__ */ jsx3(
925
- "svg",
926
- {
927
- fill: "none",
928
- height: "20",
929
- stroke: "currentColor",
930
- strokeLinecap: "round",
931
- strokeLinejoin: "round",
932
- strokeWidth: "2.5",
933
- viewBox: "0 0 24 24",
934
- width: "20",
935
- xmlns: "http://www.w3.org/2000/svg",
936
- children: /* @__PURE__ */ jsx3("polyline", { points: "18 15 12 9 6 15" })
937
- }
938
- )
939
- }
940
- )
941
- ]
942
- }
943
- )
944
- }
945
- )
946
- ] }) : (
947
- /* Bottom Sheet Panel - Hardware accelerated translateY transitions */
948
- /* @__PURE__ */ jsxs2(
949
- "div",
950
- {
951
- className: `absolute w-full bg-zinc-950/85 backdrop-blur-md border border-white/10 rounded-t-2xl shadow-2xl flex flex-col z-40 select-none ${isDragging ? "transition-none" : "transition-transform duration-300 ease-out"}`,
952
- ref: containerRef,
953
- style: {
954
- bottom: "0px",
955
- // Box height must stay exactly fullPx for "closed"/"peek"/
956
- // "full" — every other transform calc in this component
957
- // assumes the box IS fullPx tall. Only "auto" grows it (up to
958
- // autoMaxPx) when content needs more room than fullPx offers;
959
- // getBottomDrawerTransform()'s auto branch is derived to match
960
- // (shift = max(fullPx - autoPx, 0), so a taller box needs zero
961
- // extra shift to fully reveal itself).
962
- height: `${state === "auto" ? Math.max(fullPx, autoPx) : fullPx}px`,
963
- marginBottom: state === "closed" ? `${safeAreaInsets.bottom}px` : "0px",
964
- transform: getBottomDrawerTransform(),
965
- // `height` is animated too (not just transform/margin-bottom):
966
- // entering/leaving "auto" with content taller than fullPx
967
- // changes the box's actual CSS height, and without this it
968
- // would snap instantly while transform eases in over 300ms —
969
- // a visible pop of content before the slide animation catches
970
- // up.
971
- transition: isDragging ? "none" : "transform 300ms ease-out, height 300ms ease-out, margin-bottom 250ms ease-in-out"
972
- },
973
- children: [
974
- handleVisible && /* @__PURE__ */ jsx3(
975
- "div",
976
- {
977
- className: "w-full h-8 flex flex-col items-center justify-center cursor-pointer hover:bg-white/5 rounded-t-2xl transition-colors border-b border-white/5",
978
- onClick: () => {
979
- if (suppressNextClickRef.current) return;
980
- handleHandleClick();
981
- },
982
- ref: handleBarRef,
983
- children: /* @__PURE__ */ jsx3("div", { className: "w-12 h-1 bg-white/25 rounded-full" })
984
- }
985
- ),
986
- /* @__PURE__ */ jsxs2(
987
- "div",
988
- {
989
- className: "flex-1 flex flex-col overflow-hidden px-4 pb-4 pt-2 transition-[margin-top] duration-250 ease-in-out relative",
990
- ref: contentAreaRef,
991
- style: {
992
- marginTop: state === "closed" ? `${safeAreaInsets.bottom}px` : "0px"
993
- },
994
- children: [
995
- /* @__PURE__ */ jsx3(
996
- "div",
997
- {
998
- className: "flex-1 overflow-y-auto no-scrollbar",
999
- onScroll: handleScroll,
1000
- ref: scrollContainerRef,
1001
- style: {
1002
- paddingBottom: state === "peek" ? `${shiftPx}px` : "0px"
1003
- },
1004
- children: menuContent ? menuContent : /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1", children: menuItems.map((item) => /* @__PURE__ */ jsxs2(
1005
- Button,
1006
- {
1007
- className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 justify-start hover:bg-white/5 hover:text-white active:bg-white/10 text-white ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : ""}`,
1008
- disabled: item.disabled,
1009
- onClick: () => {
1010
- if (!item.disabled) {
1011
- item.onPress();
1012
- onChangeState("closed");
1013
- }
1014
- },
1015
- variant: "ghost",
1016
- children: [
1017
- /* @__PURE__ */ jsxs2("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
1018
- item.icon,
1019
- item.badge !== void 0 && /* @__PURE__ */ jsx3("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
1020
- ] }),
1021
- /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium tracking-wide", children: item.label })
1022
- ]
1023
- },
1024
- item.id
1025
- )) })
1026
- }
1027
- ),
1028
- showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx3(
509
+ state === "closed" && /* @__PURE__ */ jsx3(
510
+ Button,
511
+ {
512
+ className: "absolute top-4 left-4 z-40 w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all cursor-pointer hover:bg-zinc-900 hover:text-white p-0!",
513
+ onClick: () => onChangeState("full"),
514
+ variant: "ghost",
515
+ children: /* @__PURE__ */ jsxs2(
516
+ "svg",
517
+ {
518
+ fill: "none",
519
+ height: "24",
520
+ stroke: "currentColor",
521
+ strokeLinecap: "round",
522
+ strokeLinejoin: "round",
523
+ strokeWidth: "2",
524
+ viewBox: "0 0 24 24",
525
+ width: "24",
526
+ xmlns: "http://www.w3.org/2000/svg",
527
+ children: [
528
+ /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
529
+ /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
530
+ /* @__PURE__ */ jsx3("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
531
+ ]
532
+ }
533
+ )
534
+ }
535
+ ),
536
+ /* @__PURE__ */ jsx3(
537
+ Sheet,
538
+ {
539
+ onOpenChange: (open) => onChangeState(open ? "full" : "closed"),
540
+ open: state !== "closed",
541
+ children: /* @__PURE__ */ jsxs2(
542
+ SheetContent,
543
+ {
544
+ className: "w-80 bg-zinc-950/85 backdrop-blur-md border-r border-y border-white/10 rounded-r-2xl shadow-2xl p-4 text-white flex flex-col h-full overflow-hidden",
545
+ portal: false,
546
+ side: "left",
547
+ children: [
548
+ /* @__PURE__ */ jsx3(SheetHeader, { className: "pb-3 border-b border-white/10 mb-2 text-left", children: /* @__PURE__ */ jsx3(SheetTitle, { className: "text-sm font-bold tracking-wider text-zinc-400 uppercase", children: "Menu" }) }),
549
+ /* @__PURE__ */ jsx3(
550
+ "div",
551
+ {
552
+ className: "flex-1 overflow-y-auto no-scrollbar pr-2",
553
+ onScroll: handleScroll,
554
+ ref: scrollContainerRef,
555
+ children: menuContent ? menuContent : /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1.5", children: menuItems.map((item) => /* @__PURE__ */ jsxs2(
1029
556
  Button,
1030
557
  {
1031
- className: "absolute right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black hover:text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer p-0!",
558
+ className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 justify-start hover:bg-white/5 hover:text-white active:bg-white/10 text-white ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : ""}`,
559
+ disabled: item.disabled,
1032
560
  onClick: () => {
1033
- scrollContainerRef.current?.scrollTo({
1034
- behavior: "smooth",
1035
- top: 0
1036
- });
1037
- },
1038
- style: {
1039
- bottom: state === "peek" ? `calc(${24 + shiftPx}px + var(--drag-offset, 0px))` : `calc(24px + var(--drag-offset, 0px))`
561
+ if (!item.disabled) {
562
+ item.onPress();
563
+ onChangeState("closed");
564
+ }
1040
565
  },
1041
566
  variant: "ghost",
1042
- children: /* @__PURE__ */ jsx3(
1043
- "svg",
1044
- {
1045
- fill: "none",
1046
- height: "20",
1047
- stroke: "currentColor",
1048
- strokeLinecap: "round",
1049
- strokeLinejoin: "round",
1050
- strokeWidth: "2.5",
1051
- viewBox: "0 0 24 24",
1052
- width: "20",
1053
- xmlns: "http://www.w3.org/2000/svg",
1054
- children: /* @__PURE__ */ jsx3("polyline", { points: "18 15 12 9 6 15" })
1055
- }
1056
- )
567
+ children: [
568
+ /* @__PURE__ */ jsxs2("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
569
+ item.icon,
570
+ item.badge !== void 0 && /* @__PURE__ */ jsx3("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
571
+ ] }),
572
+ /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium tracking-wide flex-1", children: item.label })
573
+ ]
574
+ },
575
+ item.id
576
+ )) })
577
+ }
578
+ ),
579
+ showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx3(
580
+ Button,
581
+ {
582
+ className: "absolute bottom-6 right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black hover:text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer p-0!",
583
+ onClick: () => {
584
+ scrollContainerRef.current?.scrollTo({
585
+ behavior: "smooth",
586
+ top: 0
587
+ });
588
+ },
589
+ variant: "ghost",
590
+ children: /* @__PURE__ */ jsx3(
591
+ "svg",
592
+ {
593
+ fill: "none",
594
+ height: "20",
595
+ stroke: "currentColor",
596
+ strokeLinecap: "round",
597
+ strokeLinejoin: "round",
598
+ strokeWidth: "2.5",
599
+ viewBox: "0 0 24 24",
600
+ width: "20",
601
+ xmlns: "http://www.w3.org/2000/svg",
602
+ children: /* @__PURE__ */ jsx3("polyline", { points: "18 15 12 9 6 15" })
1057
603
  }
1058
604
  )
1059
- ]
1060
- }
1061
- )
1062
- ]
1063
- }
1064
- )
605
+ }
606
+ )
607
+ ]
608
+ }
609
+ )
610
+ }
1065
611
  )
1066
612
  ] });
1067
613
  }
1068
614
  );
615
+ MapDrawer.displayName = "MapDrawer";
1069
616
 
1070
617
  // src/components/ui/card.tsx
1071
618
  import * as React3 from "react";
@@ -1516,40 +1063,348 @@ var MapRightControls = ({
1516
1063
  );
1517
1064
  };
1518
1065
 
1519
- // src/components/Map/MapContext.tsx
1066
+ // src/components/Map/components/MobileBottomDrawer.tsx
1067
+ import { interpolate, useTransform } from "motion/react";
1520
1068
  import {
1521
- createContext as createContext2,
1069
+ forwardRef as forwardRef5,
1522
1070
  useCallback,
1523
- useContext as useContext2,
1071
+ useEffect,
1072
+ useImperativeHandle as useImperativeHandle2,
1524
1073
  useMemo,
1525
1074
  useRef as useRef2,
1526
1075
  useState as useState2
1527
1076
  } from "react";
1528
- import { jsx as jsx7 } from "react/jsx-runtime";
1077
+ import { Sheet as Sheet2 } from "react-modal-sheet";
1078
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1079
+ var resolveHeightToPixels = (height, containerHeight) => {
1080
+ if (typeof height === "number") {
1081
+ return height;
1082
+ }
1083
+ if (height.endsWith("%")) {
1084
+ return parseFloat(height) / 100 * containerHeight;
1085
+ }
1086
+ return parseFloat(height);
1087
+ };
1088
+ var HANDLE_HEIGHT_PX = 32;
1089
+ var SHEET_TWEEN_CONFIG = {
1090
+ duration: 0.4,
1091
+ ease: [0.32, 0.72, 0, 1]
1092
+ };
1093
+ var SHEET_DRAG_VELOCITY_THRESHOLD = 1700;
1094
+ var MobileBottomDrawer = forwardRef5(
1095
+ ({
1096
+ menuItems,
1097
+ state,
1098
+ onChangeState,
1099
+ drawerPeekHeight,
1100
+ drawerFullHeight,
1101
+ drawerAutoMaxHeight,
1102
+ containerHeight,
1103
+ menuContent,
1104
+ enableTouchDrag = true,
1105
+ handleVisible = true,
1106
+ onAutoContentHeightChange,
1107
+ showScrollToTopButton = true,
1108
+ scrollToTopThreshold = 300,
1109
+ safeAreaInsets = { bottom: 0, left: 0, right: 0, top: 0 }
1110
+ }, ref) => {
1111
+ const scrollRef = useRef2(null);
1112
+ useImperativeHandle2(ref, () => scrollRef.current);
1113
+ const headerRef = useRef2(null);
1114
+ const contentWrapperRef = useRef2(null);
1115
+ const [viewportHeight, setViewportHeight] = useState2(
1116
+ () => containerHeight ?? (typeof window !== "undefined" ? window.innerHeight : 800)
1117
+ );
1118
+ useEffect(() => {
1119
+ if (containerHeight) {
1120
+ setViewportHeight(containerHeight);
1121
+ return;
1122
+ }
1123
+ if (typeof window === "undefined") return;
1124
+ const update = () => setViewportHeight(window.innerHeight);
1125
+ update();
1126
+ window.addEventListener("resize", update);
1127
+ return () => window.removeEventListener("resize", update);
1128
+ }, [containerHeight]);
1129
+ const [autoContentPx, setAutoContentPx] = useState2(0);
1130
+ useEffect(() => {
1131
+ const contentEl = contentWrapperRef.current;
1132
+ if (!contentEl) return;
1133
+ const measure = () => {
1134
+ const headerHeight = handleVisible ? headerRef.current?.offsetHeight ?? 0 : 0;
1135
+ const px = headerHeight + contentEl.scrollHeight;
1136
+ setAutoContentPx(px);
1137
+ onAutoContentHeightChange?.(px);
1138
+ };
1139
+ measure();
1140
+ const ro = new ResizeObserver(measure);
1141
+ ro.observe(contentEl);
1142
+ return () => ro.disconnect();
1143
+ }, [handleVisible, onAutoContentHeightChange]);
1144
+ const closedPx = (handleVisible ? HANDLE_HEIGHT_PX : 0) + safeAreaInsets.bottom;
1145
+ const peekPx = resolveHeightToPixels(
1146
+ drawerPeekHeight ?? "15%",
1147
+ viewportHeight
1148
+ );
1149
+ const autoMaxPx = resolveHeightToPixels(
1150
+ drawerAutoMaxHeight ?? "100%",
1151
+ viewportHeight
1152
+ );
1153
+ const autoPx = Math.min(autoMaxPx, Math.max(autoContentPx, closedPx + 1));
1154
+ const { snapPoints, snapLabels, backdropOutput } = useMemo(() => {
1155
+ const normalize = (raw) => raw > 0 && raw <= 1 ? raw * viewportHeight : raw;
1156
+ const unsorted = [
1157
+ { label: "closed", raw: 0 },
1158
+ { label: "closed", raw: closedPx },
1159
+ { label: "peek", raw: peekPx },
1160
+ { label: "auto", raw: autoPx },
1161
+ { label: "full", raw: 1 }
1162
+ ];
1163
+ const entries = unsorted.sort(
1164
+ (a, b) => normalize(a.raw) - normalize(b.raw)
1165
+ );
1166
+ const deduped = [];
1167
+ for (const entry of entries) {
1168
+ const prev = deduped[deduped.length - 1];
1169
+ if (prev && Math.abs(normalize(prev.raw) - normalize(entry.raw)) < 0.5) {
1170
+ deduped[deduped.length - 1] = entry;
1171
+ continue;
1172
+ }
1173
+ deduped.push(entry);
1174
+ }
1175
+ return {
1176
+ backdropOutput: deduped.map(
1177
+ (d) => d.label === "full" || d.label === "auto" ? 1 : 0
1178
+ ),
1179
+ snapLabels: deduped.map((d) => d.label),
1180
+ snapPoints: deduped.map((d) => d.raw)
1181
+ };
1182
+ }, [closedPx, peekPx, autoPx, viewportHeight]);
1183
+ const currentIndex = Math.max(0, snapLabels.lastIndexOf(state));
1184
+ const sheetRef = useRef2(null);
1185
+ useEffect(() => {
1186
+ let cancelled = false;
1187
+ let attempts = 0;
1188
+ const trySnap = () => {
1189
+ if (cancelled) return;
1190
+ const ref2 = sheetRef.current;
1191
+ if (ref2 && ref2.snapPoints.length > 0) {
1192
+ ref2.snapTo(currentIndex);
1193
+ return;
1194
+ }
1195
+ attempts += 1;
1196
+ if (attempts > 60) return;
1197
+ requestAnimationFrame(trySnap);
1198
+ };
1199
+ trySnap();
1200
+ return () => {
1201
+ cancelled = true;
1202
+ };
1203
+ }, [currentIndex]);
1204
+ const handleSnap = useCallback(
1205
+ (index) => {
1206
+ const label = snapLabels[index] ?? "closed";
1207
+ if (label !== state) onChangeState(label);
1208
+ },
1209
+ [snapLabels, state, onChangeState]
1210
+ );
1211
+ const handleHandleClick = () => {
1212
+ if (state === "closed") {
1213
+ onChangeState("peek");
1214
+ } else if (state === "peek") {
1215
+ onChangeState("full");
1216
+ } else if (state === "auto") {
1217
+ onChangeState("closed");
1218
+ } else {
1219
+ onChangeState("peek");
1220
+ }
1221
+ };
1222
+ const [showScrollTop, setShowScrollTop] = useState2(false);
1223
+ useEffect(() => {
1224
+ const el = scrollRef.current;
1225
+ if (!el) return;
1226
+ const onScroll = () => {
1227
+ setShowScrollTop(
1228
+ state !== "closed" && el.scrollTop > scrollToTopThreshold
1229
+ );
1230
+ };
1231
+ el.addEventListener("scroll", onScroll);
1232
+ return () => el.removeEventListener("scroll", onScroll);
1233
+ }, [state, scrollToTopThreshold]);
1234
+ useEffect(() => {
1235
+ if (state === "closed") setShowScrollTop(false);
1236
+ }, [state]);
1237
+ const dragDisabled = !enableTouchDrag;
1238
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1239
+ /* @__PURE__ */ jsx7("style", { children: `
1240
+ .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
1241
+ .no-scrollbar::-webkit-scrollbar { display: none; }
1242
+ ` }),
1243
+ /* @__PURE__ */ jsxs5(
1244
+ Sheet2,
1245
+ {
1246
+ disableDismiss: true,
1247
+ disableDrag: dragDisabled,
1248
+ dragVelocityThreshold: SHEET_DRAG_VELOCITY_THRESHOLD,
1249
+ initialSnap: currentIndex,
1250
+ isOpen: true,
1251
+ onClose: () => onChangeState("closed"),
1252
+ onSnap: handleSnap,
1253
+ ref: sheetRef,
1254
+ snapPoints,
1255
+ tweenConfig: SHEET_TWEEN_CONFIG,
1256
+ children: [
1257
+ /* @__PURE__ */ jsxs5(
1258
+ Sheet2.Container,
1259
+ {
1260
+ className: "!bg-zinc-950/85 !backdrop-blur-md !border !border-white/10 !border-b-0 !rounded-t-2xl !shadow-2xl ",
1261
+ style: { paddingBottom: safeAreaInsets.bottom },
1262
+ children: [
1263
+ handleVisible && /* @__PURE__ */ jsx7(
1264
+ Sheet2.Header,
1265
+ {
1266
+ className: "!bg-transparent cursor-pointer hover:!bg-white/5 transition-colors",
1267
+ onClick: handleHandleClick,
1268
+ ref: headerRef
1269
+ }
1270
+ ),
1271
+ /* @__PURE__ */ jsxs5(
1272
+ Sheet2.Content,
1273
+ {
1274
+ className: "!bg-transparent",
1275
+ disableDrag: (info) => dragDisabled || info.scrollPosition === "middle",
1276
+ disableScroll: (info) => !dragDisabled && info.currentSnap !== snapPoints.length - 1,
1277
+ scrollClassName: "no-scrollbar",
1278
+ scrollRef,
1279
+ children: [
1280
+ /* @__PURE__ */ jsx7("div", { className: "px-4 pb-4 pt-2 relative", ref: contentWrapperRef, children: menuContent ? menuContent : /* @__PURE__ */ jsx7("div", { className: "flex flex-col gap-1", children: menuItems.map((item) => /* @__PURE__ */ jsxs5(
1281
+ Button,
1282
+ {
1283
+ className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 justify-start hover:bg-white/5 hover:text-white active:bg-white/10 text-white ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : ""}`,
1284
+ disabled: item.disabled,
1285
+ onClick: () => {
1286
+ if (!item.disabled) {
1287
+ item.onPress();
1288
+ onChangeState("closed");
1289
+ }
1290
+ },
1291
+ variant: "ghost",
1292
+ children: [
1293
+ /* @__PURE__ */ jsxs5("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
1294
+ item.icon,
1295
+ item.badge !== void 0 && /* @__PURE__ */ jsx7("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
1296
+ ] }),
1297
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium tracking-wide", children: item.label })
1298
+ ]
1299
+ },
1300
+ item.id
1301
+ )) }) }),
1302
+ showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx7(
1303
+ Button,
1304
+ {
1305
+ className: "absolute bottom-4 right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black hover:text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer p-0!",
1306
+ onClick: () => {
1307
+ scrollRef.current?.scrollTo({ behavior: "smooth", top: 0 });
1308
+ },
1309
+ variant: "ghost",
1310
+ children: /* @__PURE__ */ jsx7(
1311
+ "svg",
1312
+ {
1313
+ fill: "none",
1314
+ height: "20",
1315
+ stroke: "currentColor",
1316
+ strokeLinecap: "round",
1317
+ strokeLinejoin: "round",
1318
+ strokeWidth: "2.5",
1319
+ viewBox: "0 0 24 24",
1320
+ width: "20",
1321
+ xmlns: "http://www.w3.org/2000/svg",
1322
+ children: /* @__PURE__ */ jsx7("polyline", { points: "18 15 12 9 6 15" })
1323
+ }
1324
+ )
1325
+ }
1326
+ )
1327
+ ]
1328
+ }
1329
+ )
1330
+ ]
1331
+ }
1332
+ ),
1333
+ /* @__PURE__ */ jsx7(
1334
+ DrawerBackdrop,
1335
+ {
1336
+ active: state === "full" || state === "auto",
1337
+ onTap: () => onChangeState("closed"),
1338
+ output: backdropOutput
1339
+ }
1340
+ )
1341
+ ]
1342
+ }
1343
+ )
1344
+ ] });
1345
+ }
1346
+ );
1347
+ MobileBottomDrawer.displayName = "MobileBottomDrawer";
1348
+ function DrawerBackdrop({
1349
+ active,
1350
+ output,
1351
+ onTap
1352
+ }) {
1353
+ const { y, snapPoints } = Sheet2.useContext();
1354
+ const opacity = useTransform(y, (value) => {
1355
+ if (snapPoints.length === 0 || output.length !== snapPoints.length) {
1356
+ return 0;
1357
+ }
1358
+ const mix = interpolate(
1359
+ snapPoints.map((point) => point.snapValueY),
1360
+ output
1361
+ );
1362
+ return mix(value) * 0.45;
1363
+ });
1364
+ return /* @__PURE__ */ jsx7(
1365
+ Sheet2.Backdrop,
1366
+ {
1367
+ className: "!bg-black !backdrop-blur-[1px]",
1368
+ onTap: active ? onTap : void 0,
1369
+ style: { opacity }
1370
+ }
1371
+ );
1372
+ }
1373
+
1374
+ // src/components/Map/MapContext.tsx
1375
+ import {
1376
+ createContext as createContext2,
1377
+ useCallback as useCallback2,
1378
+ useContext as useContext2,
1379
+ useMemo as useMemo2,
1380
+ useRef as useRef3,
1381
+ useState as useState3
1382
+ } from "react";
1383
+ import { jsx as jsx8 } from "react/jsx-runtime";
1529
1384
  var MapControlsContext = createContext2(
1530
1385
  null
1531
1386
  );
1532
1387
  var SDKMapProvider = ({
1533
1388
  children
1534
1389
  }) => {
1535
- const [isFollowing, setIsFollowing] = useState2(true);
1536
- const [bearing, setBearingState] = useState2(0);
1537
- const [pitch, setPitchState] = useState2(0);
1538
- const [drawerState, setDrawerStateInternal] = useState2("closed");
1539
- const drawerContentRef = useRef2(null);
1540
- const setDrawerState = useCallback((val) => {
1390
+ const [isFollowing, setIsFollowing] = useState3(true);
1391
+ const [bearing, setBearingState] = useState3(0);
1392
+ const [pitch, setPitchState] = useState3(0);
1393
+ const [drawerState, setDrawerStateInternal] = useState3("closed");
1394
+ const drawerContentRef = useRef3(null);
1395
+ const setDrawerState = useCallback2((val) => {
1541
1396
  setDrawerStateInternal(val);
1542
1397
  }, []);
1543
- const [drawerHandleVisible, setDrawerHandleVisibleState] = useState2(true);
1544
- const setDrawerHandleVisible = useCallback((val) => {
1398
+ const [drawerHandleVisible, setDrawerHandleVisibleState] = useState3(true);
1399
+ const setDrawerHandleVisible = useCallback2((val) => {
1545
1400
  setDrawerHandleVisibleState((prev) => prev === val ? prev : val);
1546
1401
  }, []);
1547
- const [hasControls, setHasControlsState] = useState2(false);
1548
- const setHasControls = useCallback((val) => {
1402
+ const [hasControls, setHasControlsState] = useState3(false);
1403
+ const setHasControls = useCallback2((val) => {
1549
1404
  setHasControlsState((prev) => prev === val ? prev : val);
1550
1405
  }, []);
1551
- const [etaData, setEtaDataState] = useState2(void 0);
1552
- const setEtaData = useCallback((newData) => {
1406
+ const [etaData, setEtaDataState] = useState3(void 0);
1407
+ const setEtaData = useCallback2((newData) => {
1553
1408
  setEtaDataState((prev) => {
1554
1409
  if (!prev && !newData) return prev;
1555
1410
  if (!prev || !newData) return newData;
@@ -1559,29 +1414,29 @@ var SDKMapProvider = ({
1559
1414
  return newData;
1560
1415
  });
1561
1416
  }, []);
1562
- const [insets, setInsetsState] = useState2({
1417
+ const [insets, setInsetsState] = useState3({
1563
1418
  bottom: 0,
1564
1419
  left: 0,
1565
1420
  right: 0,
1566
1421
  top: 0
1567
1422
  });
1568
- const setInsets = useCallback((newInsets) => {
1423
+ const setInsets = useCallback2((newInsets) => {
1569
1424
  setInsetsState((prev) => ({ ...prev, ...newInsets }));
1570
1425
  }, []);
1571
- const centerCallbackRef = useRef2(null);
1572
- const resetBearingCallbackRef = useRef2(null);
1573
- const flyToCallbackRef = useRef2(null);
1574
- const centerOnUser = useCallback((params) => {
1426
+ const centerCallbackRef = useRef3(null);
1427
+ const resetBearingCallbackRef = useRef3(null);
1428
+ const flyToCallbackRef = useRef3(null);
1429
+ const centerOnUser = useCallback2((params) => {
1575
1430
  if (centerCallbackRef.current) {
1576
1431
  centerCallbackRef.current(params);
1577
1432
  }
1578
1433
  }, []);
1579
- const resetBearing = useCallback((params) => {
1434
+ const resetBearing = useCallback2((params) => {
1580
1435
  if (resetBearingCallbackRef.current) {
1581
1436
  resetBearingCallbackRef.current(params);
1582
1437
  }
1583
1438
  }, []);
1584
- const flyTo = useCallback(
1439
+ const flyTo = useCallback2(
1585
1440
  (coords, params) => {
1586
1441
  if (flyToCallbackRef.current) {
1587
1442
  flyToCallbackRef.current(coords, params);
@@ -1589,31 +1444,31 @@ var SDKMapProvider = ({
1589
1444
  },
1590
1445
  []
1591
1446
  );
1592
- const _registerCenterCallback = useCallback(
1447
+ const _registerCenterCallback = useCallback2(
1593
1448
  (cb) => {
1594
1449
  centerCallbackRef.current = cb;
1595
1450
  },
1596
1451
  []
1597
1452
  );
1598
- const _registerResetBearingCallback = useCallback(
1453
+ const _registerResetBearingCallback = useCallback2(
1599
1454
  (cb) => {
1600
1455
  resetBearingCallbackRef.current = cb;
1601
1456
  },
1602
1457
  []
1603
1458
  );
1604
- const _registerFlyToCallback = useCallback(
1459
+ const _registerFlyToCallback = useCallback2(
1605
1460
  (cb) => {
1606
1461
  flyToCallbackRef.current = cb;
1607
1462
  },
1608
1463
  []
1609
1464
  );
1610
- const setBearing = useCallback((b) => {
1465
+ const setBearing = useCallback2((b) => {
1611
1466
  setBearingState(b);
1612
1467
  }, []);
1613
- const setPitch = useCallback((p) => {
1468
+ const setPitch = useCallback2((p) => {
1614
1469
  setPitchState(p);
1615
1470
  }, []);
1616
- const contextValue = useMemo(
1471
+ const contextValue = useMemo2(
1617
1472
  () => ({
1618
1473
  _registerCenterCallback,
1619
1474
  _registerFlyToCallback,
@@ -1663,7 +1518,7 @@ var SDKMapProvider = ({
1663
1518
  setInsets
1664
1519
  ]
1665
1520
  );
1666
- return /* @__PURE__ */ jsx7(MapControlsContext.Provider, { value: contextValue, children });
1521
+ return /* @__PURE__ */ jsx8(MapControlsContext.Provider, { value: contextValue, children });
1667
1522
  };
1668
1523
  var useMapControls = () => {
1669
1524
  const context = useContext2(MapControlsContext);
@@ -1674,8 +1529,8 @@ var useMapControls = () => {
1674
1529
  };
1675
1530
 
1676
1531
  // src/components/Map/components/MapControls.tsx
1677
- import { useCallback as useCallback2, useContext as useContext3, useEffect as useEffect2, useRef as useRef3, useState as useState3 } from "react";
1678
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1532
+ import { useCallback as useCallback3, useContext as useContext3, useEffect as useEffect2, useRef as useRef4, useState as useState4 } from "react";
1533
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1679
1534
  var resolveHeightToPixels2 = (height, containerHeight) => {
1680
1535
  if (typeof height === "number") {
1681
1536
  return height;
@@ -1713,7 +1568,7 @@ var MapControls = ({
1713
1568
  }) => {
1714
1569
  const mapContext = useContext3(MapControlsContext);
1715
1570
  const contextRef = mapContext?.drawerContentRef;
1716
- const mergedRef = useCallback2(
1571
+ const mergedRef = useCallback3(
1717
1572
  (node) => {
1718
1573
  if (drawerContentRef) {
1719
1574
  if (typeof drawerContentRef === "function") {
@@ -1728,11 +1583,11 @@ var MapControls = ({
1728
1583
  },
1729
1584
  [drawerContentRef, contextRef]
1730
1585
  );
1731
- const [localDrawerState, setLocalDrawerState] = useState3(
1586
+ const [localDrawerState, setLocalDrawerState] = useState4(
1732
1587
  defaultState ?? "closed"
1733
1588
  );
1734
1589
  const drawerState = mapContext ? mapContext.drawerState : localDrawerState;
1735
- const setDrawerState = useCallback2(
1590
+ const setDrawerState = useCallback3(
1736
1591
  (state) => {
1737
1592
  if (mapContext) {
1738
1593
  mapContext.setDrawerState(state);
@@ -1747,9 +1602,9 @@ var MapControls = ({
1747
1602
  mapContext.setDrawerState(defaultState);
1748
1603
  }
1749
1604
  }, []);
1750
- const [side, setSide] = useState3("left");
1751
- const containerRef = useRef3(null);
1752
- const [mapHeight, setMapHeight] = useState3(600);
1605
+ const [side, setSide] = useState4("left");
1606
+ const containerRef = useRef4(null);
1607
+ const [mapHeight, setMapHeight] = useState4(600);
1753
1608
  useEffect2(() => {
1754
1609
  const parent = containerRef.current?.parentElement;
1755
1610
  if (!parent) return;
@@ -1788,9 +1643,7 @@ var MapControls = ({
1788
1643
  };
1789
1644
  const mapPitch = mapContext ? mapContext.pitch : 0;
1790
1645
  const resolvedEtaData = etaData !== void 0 ? etaData : mapContext?.etaData;
1791
- const [touchStartY, setTouchStartY] = useState3(null);
1792
- const [currentTouchY, setCurrentTouchY] = useState3(null);
1793
- const [autoContentPx, setAutoContentPx] = useState3(0);
1646
+ const [autoContentPx, setAutoContentPx] = useState4(0);
1794
1647
  useEffect2(() => {
1795
1648
  if (typeof window === "undefined") return;
1796
1649
  const checkLayout = () => {
@@ -1808,8 +1661,8 @@ var MapControls = ({
1808
1661
  window.addEventListener("resize", checkLayout);
1809
1662
  return () => window.removeEventListener("resize", checkLayout);
1810
1663
  }, [drawerSide]);
1811
- const isDragging = touchStartY !== null && currentTouchY !== null;
1812
- const dragOffset = isDragging ? currentTouchY - touchStartY : 0;
1664
+ const isDragging = false;
1665
+ const clampedDragOffset = 0;
1813
1666
  const fullPx = resolveHeightToPixels2(drawerFullHeight || "85%", mapHeight);
1814
1667
  const peekPx = resolveHeightToPixels2(drawerPeekHeight || "15%", mapHeight);
1815
1668
  const autoMaxPx = resolveHeightToPixels2(
@@ -1819,22 +1672,6 @@ var MapControls = ({
1819
1672
  const autoPx = Math.min(autoMaxPx, autoContentPx);
1820
1673
  const bottomInset = mapContext?.insets.bottom || 0;
1821
1674
  const handleVisible = mapContext ? mapContext.drawerHandleVisible : true;
1822
- const closedPx = (handleVisible ? 32 : 0) + (drawerState === "closed" ? bottomInset : 0);
1823
- let clampedDragOffset = dragOffset;
1824
- if (drawerState === "full") {
1825
- clampedDragOffset = Math.max(0, Math.min(fullPx - closedPx, dragOffset));
1826
- } else if (drawerState === "peek") {
1827
- clampedDragOffset = Math.max(
1828
- -(fullPx - peekPx),
1829
- // up to full
1830
- Math.min(peekPx - closedPx, dragOffset)
1831
- // down to closed
1832
- );
1833
- } else if (drawerState === "closed") {
1834
- clampedDragOffset = Math.min(0, Math.max(-(fullPx - closedPx), dragOffset));
1835
- } else if (drawerState === "auto") {
1836
- clampedDragOffset = Math.max(0, dragOffset);
1837
- }
1838
1675
  let baseHeight = handleVisible ? 32 : 0;
1839
1676
  if (drawerState === "peek") {
1840
1677
  baseHeight = peekPx;
@@ -1843,7 +1680,7 @@ var MapControls = ({
1843
1680
  } else if (drawerState === "auto") {
1844
1681
  baseHeight = autoPx;
1845
1682
  }
1846
- const currentDrawerHeight = baseHeight - clampedDragOffset;
1683
+ const currentDrawerHeight = baseHeight;
1847
1684
  const startFadeHeight = peekPx + 20;
1848
1685
  const endFadeHeight = peekPx + (fullPx - peekPx) * 0.35;
1849
1686
  let etaOpacity = 1;
@@ -1863,22 +1700,17 @@ var MapControls = ({
1863
1700
  etaOpacity = drawerState === "full" ? 0 : 1;
1864
1701
  etaBottomStyle = `calc(1rem + ${bottomInset}px)`;
1865
1702
  }
1866
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1867
- /* @__PURE__ */ jsx8("div", { ref: containerRef, style: { display: "none" } }),
1868
- /* @__PURE__ */ jsx8(
1869
- MapDrawer,
1703
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
1704
+ /* @__PURE__ */ jsx9("div", { ref: containerRef, style: { display: "none" } }),
1705
+ side === "bottom" ? /* @__PURE__ */ jsx9(
1706
+ MobileBottomDrawer,
1870
1707
  {
1871
- clampedDragOffset,
1872
1708
  containerHeight: mapHeight,
1873
- currentTouchY,
1874
1709
  drawerAutoMaxHeight,
1875
1710
  drawerFullHeight,
1876
1711
  drawerPeekHeight,
1877
1712
  enableTouchDrag,
1878
- etaBarHeight,
1879
- etaData: resolvedEtaData,
1880
1713
  handleVisible,
1881
- isDragging,
1882
1714
  menuContent: children || menuContent,
1883
1715
  menuItems,
1884
1716
  onAutoContentHeightChange: setAutoContentPx,
@@ -1886,15 +1718,22 @@ var MapControls = ({
1886
1718
  ref: mergedRef,
1887
1719
  safeAreaInsets: mapContext?.insets,
1888
1720
  scrollToTopThreshold,
1889
- setCurrentTouchY,
1890
- setTouchStartY,
1891
1721
  showScrollToTopButton,
1892
- side,
1893
- state: drawerState,
1894
- touchStartY
1722
+ state: drawerState
1723
+ }
1724
+ ) : /* @__PURE__ */ jsx9(
1725
+ MapDrawer,
1726
+ {
1727
+ menuContent: children || menuContent,
1728
+ menuItems,
1729
+ onChangeState: setDrawerState,
1730
+ ref: mergedRef,
1731
+ scrollToTopThreshold,
1732
+ showScrollToTopButton,
1733
+ state: drawerState
1895
1734
  }
1896
1735
  ),
1897
- /* @__PURE__ */ jsx8(
1736
+ /* @__PURE__ */ jsx9(
1898
1737
  MapRightControls,
1899
1738
  {
1900
1739
  bottomOffset: controlsBottomOffset,
@@ -1917,7 +1756,7 @@ var MapControls = ({
1917
1756
  side
1918
1757
  }
1919
1758
  ),
1920
- resolvedEtaData && /* @__PURE__ */ jsx8(
1759
+ resolvedEtaData && /* @__PURE__ */ jsx9(
1921
1760
  MapEtaPanel,
1922
1761
  {
1923
1762
  etaData: resolvedEtaData,
@@ -1936,9 +1775,9 @@ var MapControls = ({
1936
1775
 
1937
1776
  // src/components/Map/components/MapMenu.tsx
1938
1777
  import { useContext as useContext4 } from "react";
1939
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1778
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1940
1779
  var MapMenu = ({ children }) => {
1941
- return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-1.5", children });
1780
+ return /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-1.5", children });
1942
1781
  };
1943
1782
  var MapMenuItem = ({
1944
1783
  id,
@@ -1957,7 +1796,7 @@ var MapMenuItem = ({
1957
1796
  context.setDrawerState("closed");
1958
1797
  }
1959
1798
  };
1960
- return /* @__PURE__ */ jsxs6(
1799
+ return /* @__PURE__ */ jsxs7(
1961
1800
  "button",
1962
1801
  {
1963
1802
  className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 ${disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : "hover:bg-white/5 active:bg-white/10 text-white"}`,
@@ -1965,12 +1804,12 @@ var MapMenuItem = ({
1965
1804
  onClick: handleClick,
1966
1805
  type: "button",
1967
1806
  children: [
1968
- icon && /* @__PURE__ */ jsxs6("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
1807
+ icon && /* @__PURE__ */ jsxs7("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
1969
1808
  icon,
1970
- badge !== void 0 && /* @__PURE__ */ jsx9("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: badge })
1809
+ badge !== void 0 && /* @__PURE__ */ jsx10("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: badge })
1971
1810
  ] }),
1972
- /* @__PURE__ */ jsxs6("div", { className: "flex-1 flex flex-col justify-center", children: [
1973
- /* @__PURE__ */ jsx9("span", { className: "text-sm font-medium tracking-wide flex-1", children: label }),
1811
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 flex flex-col justify-center", children: [
1812
+ /* @__PURE__ */ jsx10("span", { className: "text-sm font-medium tracking-wide flex-1", children: label }),
1974
1813
  children
1975
1814
  ] })
1976
1815
  ]
@@ -1984,7 +1823,7 @@ var MapMenuTitle = ({
1984
1823
  showDivider = true,
1985
1824
  className = ""
1986
1825
  }) => {
1987
- return /* @__PURE__ */ jsx9(
1826
+ return /* @__PURE__ */ jsx10(
1988
1827
  "div",
1989
1828
  {
1990
1829
  className: `flex items-center justify-between pb-3 first:mt-0 px-3.5 ${className} ${showDivider ? "border-b border-white/10 mb-2 mt-4" : ""}`,
@@ -1996,14 +1835,14 @@ MapMenuTitle.displayName = "MapMenuTitle";
1996
1835
  var MapMenuDivider = ({
1997
1836
  className = ""
1998
1837
  }) => {
1999
- return /* @__PURE__ */ jsx9("div", { className: `h-[1px] bg-white/10 my-3 mx-3.5 ${className}` });
1838
+ return /* @__PURE__ */ jsx10("div", { className: `h-[1px] bg-white/10 my-3 mx-3.5 ${className}` });
2000
1839
  };
2001
1840
  MapMenuDivider.displayName = "MapMenuDivider";
2002
1841
  var MapMenuFooter = ({
2003
1842
  children,
2004
1843
  className = ""
2005
1844
  }) => {
2006
- return /* @__PURE__ */ jsx9(
1845
+ return /* @__PURE__ */ jsx10(
2007
1846
  "div",
2008
1847
  {
2009
1848
  className: `mt-auto pt-4 border-t border-white/10 px-3.5 text-xs text-zinc-500 font-medium ${className}`,
@@ -2031,122 +1870,149 @@ var GlobalEvent = /* @__PURE__ */ ((GlobalEvent2) => {
2031
1870
  })(GlobalEvent || {});
2032
1871
 
2033
1872
  // src/components/Map/hooks/useUserLocation.ts
2034
- import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
1873
+ import { useEffect as useEffect3, useState as useState5 } from "react";
2035
1874
  var LERP_FACTOR = 0.12;
2036
- var useUserLocation = () => {
2037
- const [locationState, setLocationState] = useState4({
2038
- accuracy: null,
2039
- coords: null,
2040
- heading: null
2041
- });
2042
- const targetRef = useRef4({
2043
- accuracy: null,
2044
- coords: null,
2045
- heading: null
2046
- });
2047
- const currentRef = useRef4({
2048
- accuracy: null,
2049
- coords: null,
2050
- heading: null
2051
- });
2052
- const mapPayload = useCallback3((payload) => {
2053
- const coords = payload.location?.coords || payload.coords;
2054
- const headingData = payload.heading;
2055
- const newCoords = coords ? [coords.longitude, coords.latitude] : null;
2056
- let heading = null;
2057
- if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
2058
- heading = headingData.trueHeading;
2059
- } else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
2060
- heading = headingData.magneticHeading;
2061
- } else if (headingData?.heading !== null && headingData?.heading >= 0) {
2062
- heading = headingData.heading;
2063
- }
2064
- return {
2065
- accuracy: coords?.accuracy || null,
2066
- coords: newCoords,
2067
- heading
2068
- };
2069
- }, []);
2070
- useEffect3(() => {
2071
- let rafId;
2072
- const animate = () => {
2073
- let changed = false;
2074
- if (targetRef.current.coords) {
2075
- if (!currentRef.current.coords) {
2076
- currentRef.current.coords = [...targetRef.current.coords];
2077
- changed = true;
2078
- } else {
2079
- const [tLng, tLat] = targetRef.current.coords;
2080
- const [cLng, cLat] = currentRef.current.coords;
2081
- const nLng = cLng + (tLng - cLng) * LERP_FACTOR;
2082
- const nLat = cLat + (tLat - cLat) * LERP_FACTOR;
2083
- if (Math.abs(nLng - cLng) > 1e-7 || Math.abs(nLat - cLat) > 1e-7) {
2084
- currentRef.current.coords = [nLng, nLat];
2085
- changed = true;
2086
- }
2087
- }
2088
- }
2089
- if (targetRef.current.heading !== null) {
2090
- if (currentRef.current.heading === null) {
2091
- currentRef.current.heading = targetRef.current.heading;
2092
- changed = true;
2093
- } else {
2094
- const target = targetRef.current.heading;
2095
- const current = currentRef.current.heading;
2096
- let delta = target - current;
2097
- if (delta > 180) delta -= 360;
2098
- if (delta < -180) delta += 360;
2099
- const nHeading = current + delta * LERP_FACTOR;
2100
- const normalized = (nHeading % 360 + 360) % 360;
2101
- if (Math.abs(delta) > 0.1) {
2102
- currentRef.current.heading = normalized;
2103
- changed = true;
2104
- }
2105
- }
2106
- }
2107
- if (changed) {
2108
- setLocationState({
2109
- accuracy: targetRef.current.accuracy,
2110
- coords: currentRef.current.coords ? [...currentRef.current.coords] : null,
2111
- heading: currentRef.current.heading
2112
- });
2113
- }
2114
- rafId = requestAnimationFrame(animate);
2115
- };
2116
- rafId = requestAnimationFrame(animate);
2117
- sdk.location.getCurrentPosition().then((data) => {
2118
- const payload = mapPayload(data);
2119
- targetRef.current = payload;
2120
- }).catch((e) => console.warn("Initial location fetch failed", e));
2121
- const unsubscribeStream = sdk.on(
2122
- "LOCATION_STREAM_UPDATE" /* LOCATION_STREAM_UPDATE */,
2123
- (payload) => {
2124
- targetRef.current = mapPayload(payload);
1875
+ var EMPTY_STATE = {
1876
+ accuracy: null,
1877
+ coords: null,
1878
+ heading: null
1879
+ };
1880
+ function mapPayload(payload) {
1881
+ const coords = payload.location?.coords || payload.coords;
1882
+ const headingData = payload.heading;
1883
+ const newCoords = coords ? [coords.longitude, coords.latitude] : null;
1884
+ let heading = null;
1885
+ if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
1886
+ heading = headingData.trueHeading;
1887
+ } else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
1888
+ heading = headingData.magneticHeading;
1889
+ } else if (headingData?.heading !== null && headingData?.heading >= 0) {
1890
+ heading = headingData.heading;
1891
+ }
1892
+ return {
1893
+ accuracy: coords?.accuracy || null,
1894
+ coords: newCoords,
1895
+ heading
1896
+ };
1897
+ }
1898
+ var refCount = 0;
1899
+ var generation = 0;
1900
+ var subscribers = /* @__PURE__ */ new Set();
1901
+ var targetState = { ...EMPTY_STATE };
1902
+ var currentInterpolated = { coords: null, heading: null };
1903
+ var rafId = null;
1904
+ var unsubscribeStream = null;
1905
+ var unsubscribeLegacy = null;
1906
+ function getSnapshot() {
1907
+ return {
1908
+ accuracy: targetState.accuracy,
1909
+ coords: currentInterpolated.coords ? [...currentInterpolated.coords] : null,
1910
+ heading: currentInterpolated.heading
1911
+ };
1912
+ }
1913
+ function notify() {
1914
+ const snapshot = getSnapshot();
1915
+ for (const callback of subscribers) callback(snapshot);
1916
+ }
1917
+ function animate() {
1918
+ let changed = false;
1919
+ if (targetState.coords) {
1920
+ if (!currentInterpolated.coords) {
1921
+ currentInterpolated.coords = [...targetState.coords];
1922
+ changed = true;
1923
+ } else {
1924
+ const [tLng, tLat] = targetState.coords;
1925
+ const [cLng, cLat] = currentInterpolated.coords;
1926
+ const nLng = cLng + (tLng - cLng) * LERP_FACTOR;
1927
+ const nLat = cLat + (tLat - cLat) * LERP_FACTOR;
1928
+ if (Math.abs(nLng - cLng) > 1e-7 || Math.abs(nLat - cLat) > 1e-7) {
1929
+ currentInterpolated.coords = [nLng, nLat];
1930
+ changed = true;
2125
1931
  }
2126
- );
2127
- const unsubscribeLegacy = sdk.on(
2128
- "LOCATION_UPDATE" /* LOCATION_UPDATE */,
2129
- (payload) => {
2130
- targetRef.current = mapPayload(payload);
1932
+ }
1933
+ }
1934
+ if (targetState.heading !== null) {
1935
+ if (currentInterpolated.heading === null) {
1936
+ currentInterpolated.heading = targetState.heading;
1937
+ changed = true;
1938
+ } else {
1939
+ const target = targetState.heading;
1940
+ const current = currentInterpolated.heading;
1941
+ let delta = target - current;
1942
+ if (delta > 180) delta -= 360;
1943
+ if (delta < -180) delta += 360;
1944
+ const nHeading = current + delta * LERP_FACTOR;
1945
+ const normalized = (nHeading % 360 + 360) % 360;
1946
+ if (Math.abs(delta) > 0.1) {
1947
+ currentInterpolated.heading = normalized;
1948
+ changed = true;
2131
1949
  }
2132
- );
2133
- sdk.location.startStream({
2134
- distanceFilter: 1,
2135
- headingThreshold: 2
2136
- }).catch((e) => console.warn("Failed to start location stream", e));
1950
+ }
1951
+ }
1952
+ if (changed) notify();
1953
+ rafId = requestAnimationFrame(animate);
1954
+ }
1955
+ function startSharedStream() {
1956
+ rafId = requestAnimationFrame(animate);
1957
+ generation++;
1958
+ const thisGeneration = generation;
1959
+ sdk.location.getCurrentPosition().then((data) => {
1960
+ if (thisGeneration !== generation) return;
1961
+ targetState = mapPayload(data);
1962
+ }).catch((e) => console.warn("Initial location fetch failed", e));
1963
+ unsubscribeStream = sdk.on(
1964
+ "LOCATION_STREAM_UPDATE" /* LOCATION_STREAM_UPDATE */,
1965
+ (payload) => {
1966
+ targetState = mapPayload(payload);
1967
+ }
1968
+ );
1969
+ unsubscribeLegacy = sdk.on(
1970
+ "LOCATION_UPDATE" /* LOCATION_UPDATE */,
1971
+ (payload) => {
1972
+ targetState = mapPayload(payload);
1973
+ }
1974
+ );
1975
+ sdk.location.startStream({
1976
+ distanceFilter: 1,
1977
+ headingThreshold: 2
1978
+ }).catch((e) => console.warn("Failed to start location stream", e));
1979
+ }
1980
+ function stopSharedStream() {
1981
+ generation++;
1982
+ if (rafId !== null) cancelAnimationFrame(rafId);
1983
+ rafId = null;
1984
+ unsubscribeStream?.();
1985
+ unsubscribeLegacy?.();
1986
+ unsubscribeStream = null;
1987
+ unsubscribeLegacy = null;
1988
+ sdk.location.stopStream().catch(() => {
1989
+ });
1990
+ targetState = { ...EMPTY_STATE };
1991
+ currentInterpolated = { coords: null, heading: null };
1992
+ }
1993
+ var useUserLocation = () => {
1994
+ const [locationState, setLocationState] = useState5(getSnapshot);
1995
+ useEffect3(() => {
1996
+ refCount++;
1997
+ if (refCount === 1) {
1998
+ startSharedStream();
1999
+ } else {
2000
+ setLocationState(getSnapshot());
2001
+ }
2002
+ subscribers.add(setLocationState);
2137
2003
  return () => {
2138
- cancelAnimationFrame(rafId);
2139
- unsubscribeStream();
2140
- unsubscribeLegacy();
2141
- sdk.location.stopStream().catch(() => {
2142
- });
2004
+ subscribers.delete(setLocationState);
2005
+ refCount--;
2006
+ if (refCount === 0) {
2007
+ stopSharedStream();
2008
+ }
2143
2009
  };
2144
- }, [mapPayload]);
2010
+ }, []);
2145
2011
  return locationState;
2146
2012
  };
2147
2013
 
2148
2014
  // src/components/Map/hooks/useLiveRouting.ts
2149
- import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState5 } from "react";
2015
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef5, useState as useState6 } from "react";
2150
2016
  var useLiveRouting = ({
2151
2017
  origin: manualOrigin,
2152
2018
  destination,
@@ -2155,11 +2021,11 @@ var useLiveRouting = ({
2155
2021
  enabled = true,
2156
2022
  initialMode = "preview"
2157
2023
  }) => {
2158
- const [route, setRoute] = useState5(null);
2159
- const [totalDistance, setTotalDistance] = useState5(null);
2160
- const [isCalculating, setIsCalculating] = useState5(false);
2161
- const [error, setError] = useState5(null);
2162
- const [mode, setMode] = useState5(initialMode);
2024
+ const [route, setRoute] = useState6(null);
2025
+ const [totalDistance, setTotalDistance] = useState6(null);
2026
+ const [isCalculating, setIsCalculating] = useState6(false);
2027
+ const [error, setError] = useState6(null);
2028
+ const [mode, setMode] = useState6(initialMode);
2163
2029
  const userLocationState = useUserLocation();
2164
2030
  const calculationOrigin = manualOrigin || userLocationState.coords;
2165
2031
  const trackingLocation = userLocationState.coords || manualOrigin;
@@ -2180,7 +2046,7 @@ var useLiveRouting = ({
2180
2046
  },
2181
2047
  []
2182
2048
  );
2183
- const currentDistanceMeters = useMemo2(() => {
2049
+ const currentDistanceMeters = useMemo3(() => {
2184
2050
  if (!trackingLocation || !route || route.geometry.coordinates.length < 2) {
2185
2051
  if (trackingLocation && destination) {
2186
2052
  return getDistance(trackingLocation, destination);
@@ -2298,9 +2164,9 @@ var useLiveRouting = ({
2298
2164
  };
2299
2165
  };
2300
2166
  var useRoute = (origin, destination, profile = "driving") => {
2301
- const [route, setRoute] = useState5(null);
2302
- const [isCalculating, setIsCalculating] = useState5(false);
2303
- const [error, setError] = useState5(null);
2167
+ const [route, setRoute] = useState6(null);
2168
+ const [isCalculating, setIsCalculating] = useState6(false);
2169
+ const [error, setError] = useState6(null);
2304
2170
  useEffect4(() => {
2305
2171
  if (!origin || !destination) {
2306
2172
  setRoute(null);
@@ -2328,11 +2194,11 @@ var useRoute = (origin, destination, profile = "driving") => {
2328
2194
  };
2329
2195
 
2330
2196
  // src/components/Map/hooks/useJourney.ts
2331
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
2197
+ import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef6, useState as useState7 } from "react";
2332
2198
  var useJourney = (options = {}) => {
2333
2199
  const { navigation, onAbort, onModeChange } = options;
2334
- const [progNav, setProgNav] = useState6(null);
2335
- const [isAborted, setIsAborted] = useState6(false);
2200
+ const [progNav, setProgNav] = useState7(null);
2201
+ const [isAborted, setIsAborted] = useState7(false);
2336
2202
  useEffect5(() => {
2337
2203
  const unsubCreate = sdk.on("SDK_NAVIGATION_CREATE", (payload) => {
2338
2204
  setIsAborted(false);
@@ -2421,9 +2287,9 @@ var useJourney = (options = {}) => {
2421
2287
  };
2422
2288
 
2423
2289
  // src/components/Map/hooks/useTrimmedRoute.ts
2424
- import { useMemo as useMemo3 } from "react";
2290
+ import { useMemo as useMemo4 } from "react";
2425
2291
  var useTrimmedRoute = (route, userLocation) => {
2426
- return useMemo3(() => {
2292
+ return useMemo4(() => {
2427
2293
  if (!route || !userLocation || route.coordinates.length < 2) return route;
2428
2294
  const coords = route.coordinates;
2429
2295
  let minDistance = Infinity;
@@ -2448,7 +2314,7 @@ var useTrimmedRoute = (route, userLocation) => {
2448
2314
 
2449
2315
  // src/components/Map/layers/RouteLayer.tsx
2450
2316
  import { Layer, Source } from "@vis.gl/react-maplibre";
2451
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2317
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2452
2318
  var RouteLayer = ({
2453
2319
  geometry,
2454
2320
  color = "#3b82f6",
@@ -2461,8 +2327,8 @@ var RouteLayer = ({
2461
2327
  properties: {},
2462
2328
  type: "Feature"
2463
2329
  };
2464
- return /* @__PURE__ */ jsxs7(Source, { data, id: "sdk-route-source", type: "geojson", children: [
2465
- /* @__PURE__ */ jsx10(
2330
+ return /* @__PURE__ */ jsxs8(Source, { data, id: "sdk-route-source", type: "geojson", children: [
2331
+ /* @__PURE__ */ jsx11(
2466
2332
  Layer,
2467
2333
  {
2468
2334
  id: "sdk-route-layer-casing",
@@ -2475,7 +2341,7 @@ var RouteLayer = ({
2475
2341
  type: "line"
2476
2342
  }
2477
2343
  ),
2478
- /* @__PURE__ */ jsx10(
2344
+ /* @__PURE__ */ jsx11(
2479
2345
  Layer,
2480
2346
  {
2481
2347
  id: "sdk-route-layer",
@@ -2518,8 +2384,8 @@ var buildGeoCircle = (center, radiusMeters, points = 64) => {
2518
2384
 
2519
2385
  // src/components/Map/layers/MockRadiusLayer.tsx
2520
2386
  import { Layer as Layer2, Source as Source2 } from "@vis.gl/react-maplibre";
2521
- import { useMemo as useMemo4 } from "react";
2522
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2387
+ import { useMemo as useMemo5 } from "react";
2388
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2523
2389
  var MockRadiusLayer = ({
2524
2390
  center,
2525
2391
  radiusMeters,
@@ -2532,12 +2398,12 @@ var MockRadiusLayer = ({
2532
2398
  // pastel palette with no neon accents at all).
2533
2399
  color = "#e2e8f0"
2534
2400
  }) => {
2535
- const data = useMemo4(
2401
+ const data = useMemo5(
2536
2402
  () => buildGeoCircle(center, radiusMeters),
2537
2403
  [center, radiusMeters]
2538
2404
  );
2539
- return /* @__PURE__ */ jsxs8(Source2, { data, id: "sdk-mock-radius-source", type: "geojson", children: [
2540
- /* @__PURE__ */ jsx11(
2405
+ return /* @__PURE__ */ jsxs9(Source2, { data, id: "sdk-mock-radius-source", type: "geojson", children: [
2406
+ /* @__PURE__ */ jsx12(
2541
2407
  Layer2,
2542
2408
  {
2543
2409
  id: "sdk-mock-radius-fill",
@@ -2548,7 +2414,7 @@ var MockRadiusLayer = ({
2548
2414
  type: "fill"
2549
2415
  }
2550
2416
  ),
2551
- /* @__PURE__ */ jsx11(
2417
+ /* @__PURE__ */ jsx12(
2552
2418
  Layer2,
2553
2419
  {
2554
2420
  id: "sdk-mock-radius-outline",
@@ -2565,8 +2431,8 @@ var MockRadiusLayer = ({
2565
2431
 
2566
2432
  // src/components/Map/layers/POILayers.tsx
2567
2433
  import { Layer as Layer3, Source as Source3, useMap } from "@vis.gl/react-maplibre";
2568
- import { useEffect as useEffect6, useMemo as useMemo5, useState as useState7 } from "react";
2569
- import { jsx as jsx12 } from "react/jsx-runtime";
2434
+ import { useEffect as useEffect6, useMemo as useMemo6, useState as useState8 } from "react";
2435
+ import { jsx as jsx13 } from "react/jsx-runtime";
2570
2436
  function createEmojiIcon(emoji, size = 64) {
2571
2437
  const canvas = document.createElement("canvas");
2572
2438
  canvas.width = size;
@@ -2579,9 +2445,9 @@ function createEmojiIcon(emoji, size = 64) {
2579
2445
  return ctx.getImageData(0, 0, size, size);
2580
2446
  }
2581
2447
  var POILayers = ({ pois }) => {
2582
- const [loadedImages, setLoadedImages] = useState7(0);
2448
+ const [loadedImages, setLoadedImages] = useState8(0);
2583
2449
  const { current: map } = useMap();
2584
- const glPois = useMemo5(
2450
+ const glPois = useMemo6(
2585
2451
  () => pois.filter((p) => !p.displayMode || p.displayMode === "gl"),
2586
2452
  [pois]
2587
2453
  );
@@ -2625,7 +2491,7 @@ var POILayers = ({ pois }) => {
2625
2491
  setLoadedImages((prev) => prev + updates);
2626
2492
  }
2627
2493
  }, [map, glPois]);
2628
- const geojson = useMemo5(
2494
+ const geojson = useMemo6(
2629
2495
  () => ({
2630
2496
  features: glPois.map((poi) => ({
2631
2497
  geometry: {
@@ -2643,7 +2509,7 @@ var POILayers = ({ pois }) => {
2643
2509
  }),
2644
2510
  [glPois]
2645
2511
  );
2646
- return /* @__PURE__ */ jsx12(Source3, { data: geojson, id: "sdk-pois", type: "geojson", children: /* @__PURE__ */ jsx12(
2512
+ return /* @__PURE__ */ jsx13(Source3, { data: geojson, id: "sdk-pois", type: "geojson", children: /* @__PURE__ */ jsx13(
2647
2513
  Layer3,
2648
2514
  {
2649
2515
  id: "sdk-poi-layer",
@@ -2680,11 +2546,11 @@ var POILayers = ({ pois }) => {
2680
2546
 
2681
2547
  // src/components/Map/layers/POIMarkers.tsx
2682
2548
  import { Marker } from "@vis.gl/react-maplibre";
2683
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
2549
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2684
2550
  var POIMarkers = ({ pois, onClick }) => {
2685
2551
  const markerPois = pois.filter((p) => p.displayMode === "marker");
2686
2552
  if (markerPois.length === 0) return null;
2687
- return /* @__PURE__ */ jsx13(Fragment3, { children: markerPois.map((poi) => /* @__PURE__ */ jsx13(
2553
+ return /* @__PURE__ */ jsx14(Fragment4, { children: markerPois.map((poi) => /* @__PURE__ */ jsx14(
2688
2554
  Marker,
2689
2555
  {
2690
2556
  anchor: "bottom",
@@ -2694,7 +2560,7 @@ var POIMarkers = ({ pois, onClick }) => {
2694
2560
  e.originalEvent.stopPropagation();
2695
2561
  if (onClick) onClick(poi);
2696
2562
  },
2697
- children: poi.markerComponent ? poi.markerComponent : /* @__PURE__ */ jsxs9(
2563
+ children: poi.markerComponent ? poi.markerComponent : /* @__PURE__ */ jsxs10(
2698
2564
  "div",
2699
2565
  {
2700
2566
  style: {
@@ -2703,7 +2569,7 @@ var POIMarkers = ({ pois, onClick }) => {
2703
2569
  transform: "transition-transform duration-200 hover:scale-110"
2704
2570
  },
2705
2571
  children: [
2706
- /* @__PURE__ */ jsx13(
2572
+ /* @__PURE__ */ jsx14(
2707
2573
  "div",
2708
2574
  {
2709
2575
  style: {
@@ -2716,7 +2582,7 @@ var POIMarkers = ({ pois, onClick }) => {
2716
2582
  justifyContent: "center",
2717
2583
  width: "32px"
2718
2584
  },
2719
- children: poi.emoji ? /* @__PURE__ */ jsx13("span", { style: { fontSize: "16px" }, children: poi.emoji }) : /* @__PURE__ */ jsx13(
2585
+ children: poi.emoji ? /* @__PURE__ */ jsx14("span", { style: { fontSize: "16px" }, children: poi.emoji }) : /* @__PURE__ */ jsx14(
2720
2586
  "div",
2721
2587
  {
2722
2588
  style: {
@@ -2729,7 +2595,7 @@ var POIMarkers = ({ pois, onClick }) => {
2729
2595
  )
2730
2596
  }
2731
2597
  ),
2732
- /* @__PURE__ */ jsx13(
2598
+ /* @__PURE__ */ jsx14(
2733
2599
  "div",
2734
2600
  {
2735
2601
  style: {
@@ -2752,19 +2618,19 @@ var POIMarkers = ({ pois, onClick }) => {
2752
2618
 
2753
2619
  // src/components/Map/layers/UserLocationLayer.tsx
2754
2620
  import { Marker as Marker2 } from "@vis.gl/react-maplibre";
2755
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2621
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
2756
2622
  var UserLocationLayer = ({
2757
2623
  forcedLocation,
2758
2624
  heading
2759
2625
  }) => {
2760
2626
  if (!forcedLocation) return null;
2761
- return /* @__PURE__ */ jsx14(
2627
+ return /* @__PURE__ */ jsx15(
2762
2628
  Marker2,
2763
2629
  {
2764
2630
  anchor: "center",
2765
2631
  latitude: forcedLocation[1],
2766
2632
  longitude: forcedLocation[0],
2767
- children: /* @__PURE__ */ jsxs10(
2633
+ children: /* @__PURE__ */ jsxs11(
2768
2634
  "div",
2769
2635
  {
2770
2636
  style: {
@@ -2774,7 +2640,7 @@ var UserLocationLayer = ({
2774
2640
  position: "relative"
2775
2641
  },
2776
2642
  children: [
2777
- /* @__PURE__ */ jsx14(
2643
+ /* @__PURE__ */ jsx15(
2778
2644
  "div",
2779
2645
  {
2780
2646
  style: {
@@ -2787,7 +2653,7 @@ var UserLocationLayer = ({
2787
2653
  }
2788
2654
  }
2789
2655
  ),
2790
- heading !== void 0 && heading !== null && /* @__PURE__ */ jsx14(
2656
+ heading !== void 0 && heading !== null && /* @__PURE__ */ jsx15(
2791
2657
  "div",
2792
2658
  {
2793
2659
  style: {
@@ -2797,13 +2663,13 @@ var UserLocationLayer = ({
2797
2663
  width: "60px",
2798
2664
  zIndex: 0
2799
2665
  },
2800
- children: /* @__PURE__ */ jsxs10(
2666
+ children: /* @__PURE__ */ jsxs11(
2801
2667
  "svg",
2802
2668
  {
2803
2669
  style: { height: "100%", width: "100%" },
2804
2670
  viewBox: "0 0 100 100",
2805
2671
  children: [
2806
- /* @__PURE__ */ jsx14("defs", { children: /* @__PURE__ */ jsxs10(
2672
+ /* @__PURE__ */ jsx15("defs", { children: /* @__PURE__ */ jsxs11(
2807
2673
  "radialGradient",
2808
2674
  {
2809
2675
  cx: "50%",
@@ -2813,7 +2679,7 @@ var UserLocationLayer = ({
2813
2679
  id: "grad1",
2814
2680
  r: "50%",
2815
2681
  children: [
2816
- /* @__PURE__ */ jsx14(
2682
+ /* @__PURE__ */ jsx15(
2817
2683
  "stop",
2818
2684
  {
2819
2685
  offset: "0%",
@@ -2823,7 +2689,7 @@ var UserLocationLayer = ({
2823
2689
  }
2824
2690
  }
2825
2691
  ),
2826
- /* @__PURE__ */ jsx14(
2692
+ /* @__PURE__ */ jsx15(
2827
2693
  "stop",
2828
2694
  {
2829
2695
  offset: "100%",
@@ -2836,7 +2702,7 @@ var UserLocationLayer = ({
2836
2702
  ]
2837
2703
  }
2838
2704
  ) }),
2839
- /* @__PURE__ */ jsx14(
2705
+ /* @__PURE__ */ jsx15(
2840
2706
  "path",
2841
2707
  {
2842
2708
  d: "M 50 50 L 35 0 A 50 50 0 0 1 65 0 Z",
@@ -2848,7 +2714,7 @@ var UserLocationLayer = ({
2848
2714
  )
2849
2715
  }
2850
2716
  ),
2851
- /* @__PURE__ */ jsx14(
2717
+ /* @__PURE__ */ jsx15(
2852
2718
  "div",
2853
2719
  {
2854
2720
  style: {
@@ -2862,7 +2728,7 @@ var UserLocationLayer = ({
2862
2728
  width: "20px",
2863
2729
  zIndex: 1
2864
2730
  },
2865
- children: /* @__PURE__ */ jsx14(
2731
+ children: /* @__PURE__ */ jsx15(
2866
2732
  "div",
2867
2733
  {
2868
2734
  style: {
@@ -2876,7 +2742,7 @@ var UserLocationLayer = ({
2876
2742
  )
2877
2743
  }
2878
2744
  ),
2879
- /* @__PURE__ */ jsx14("style", { children: `@keyframes pulse { 0% { transform: scale(0.95); opacity: 0.7; } 70% { transform: scale(1.5); opacity: 0; } 100% { transform: scale(0.95); opacity: 0; } }` })
2745
+ /* @__PURE__ */ jsx15("style", { children: `@keyframes pulse { 0% { transform: scale(0.95); opacity: 0.7; } 70% { transform: scale(1.5); opacity: 0; } 100% { transform: scale(0.95); opacity: 0; } }` })
2880
2746
  ]
2881
2747
  }
2882
2748
  )
@@ -2890,12 +2756,12 @@ import React4, {
2890
2756
  useCallback as useCallback6,
2891
2757
  useContext as useContext5,
2892
2758
  useEffect as useEffect7,
2893
- useMemo as useMemo6,
2759
+ useMemo as useMemo7,
2894
2760
  useRef as useRef7,
2895
- useState as useState8
2761
+ useState as useState9
2896
2762
  } from "react";
2897
2763
  import "maplibre-gl/dist/maplibre-gl.css";
2898
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
2764
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
2899
2765
  var MapInternal = ({
2900
2766
  initialViewState = {
2901
2767
  latitude: 45.90583599233422,
@@ -2965,7 +2831,7 @@ var MapInternal = ({
2965
2831
  isFlyingRef.current = false;
2966
2832
  }
2967
2833
  }, []);
2968
- const [localIsFollowingUI, setLocalIsFollowingUI] = useState8(
2834
+ const [localIsFollowingUI, setLocalIsFollowingUI] = useState9(
2969
2835
  resolvedInitialFollow
2970
2836
  );
2971
2837
  const isFollowingUI = mapControls ? mapControls.isFollowing : localIsFollowingUI;
@@ -3128,7 +2994,7 @@ var MapInternal = ({
3128
2994
  }
3129
2995
  }, [propDrawerState, setDrawerStateCtx]);
3130
2996
  const resolvedMenuItems = menuItems ?? [];
3131
- const { mapLibreChildren, menuContentFromChildren } = useMemo6(() => {
2997
+ const { mapLibreChildren, menuContentFromChildren } = useMemo7(() => {
3132
2998
  const mapLibreChildren2 = [];
3133
2999
  const extractedMenuItems = [];
3134
3000
  React4.Children.forEach(children, (child) => {
@@ -3160,7 +3026,7 @@ var MapInternal = ({
3160
3026
  mapLibreChildren2.push(child);
3161
3027
  }
3162
3028
  });
3163
- const menuContentFromChildren2 = extractedMenuItems.length > 0 ? /* @__PURE__ */ jsx15(MapMenu, { children: extractedMenuItems }) : null;
3029
+ const menuContentFromChildren2 = extractedMenuItems.length > 0 ? /* @__PURE__ */ jsx16(MapMenu, { children: extractedMenuItems }) : null;
3164
3030
  return { mapLibreChildren: mapLibreChildren2, menuContentFromChildren: menuContentFromChildren2 };
3165
3031
  }, [children]);
3166
3032
  const handleMapClick = (e) => {
@@ -3265,13 +3131,13 @@ var MapInternal = ({
3265
3131
  }
3266
3132
  }
3267
3133
  };
3268
- return /* @__PURE__ */ jsxs11(
3134
+ return /* @__PURE__ */ jsxs12(
3269
3135
  "div",
3270
3136
  {
3271
3137
  className: `w-full h-full overflow-hidden relative ${mockLocation?.isPlacingLocation ? "cursor-crosshair" : ""} ${className}`,
3272
3138
  style,
3273
3139
  children: [
3274
- /* @__PURE__ */ jsxs11(
3140
+ /* @__PURE__ */ jsxs12(
3275
3141
  MapLibre,
3276
3142
  {
3277
3143
  attributionControl: false,
@@ -3301,30 +3167,30 @@ var MapInternal = ({
3301
3167
  showUserLocation && userLocationState.coords && (renderUserLocation ? renderUserLocation({
3302
3168
  coords: userLocationState.coords,
3303
3169
  heading: userLocationState.heading
3304
- }) : /* @__PURE__ */ jsx15(
3170
+ }) : /* @__PURE__ */ jsx16(
3305
3171
  UserLocationLayer,
3306
3172
  {
3307
3173
  forcedLocation: userLocationState.coords,
3308
3174
  heading: userLocationState.heading
3309
3175
  }
3310
3176
  )),
3311
- mockLocation && mockLocation.radiusMeters > 0 && userLocationState.coords && /* @__PURE__ */ jsx15(
3177
+ mockLocation && mockLocation.radiusMeters > 0 && userLocationState.coords && /* @__PURE__ */ jsx16(
3312
3178
  MockRadiusLayer,
3313
3179
  {
3314
3180
  center: userLocationState.coords,
3315
3181
  radiusMeters: mockLocation.radiusMeters
3316
3182
  }
3317
3183
  ),
3318
- displayRoute && /* @__PURE__ */ jsx15(RouteLayer, { geometry: displayRoute }),
3319
- pois.length > 0 && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3320
- /* @__PURE__ */ jsx15(POILayers, { pois }),
3321
- /* @__PURE__ */ jsx15(POIMarkers, { onClick: onPoiClick, pois })
3184
+ displayRoute && /* @__PURE__ */ jsx16(RouteLayer, { geometry: displayRoute }),
3185
+ pois.length > 0 && /* @__PURE__ */ jsxs12(Fragment5, { children: [
3186
+ /* @__PURE__ */ jsx16(POILayers, { pois }),
3187
+ /* @__PURE__ */ jsx16(POIMarkers, { onClick: onPoiClick, pois })
3322
3188
  ] }),
3323
3189
  mapLibreChildren
3324
3190
  ]
3325
3191
  }
3326
3192
  ),
3327
- showControls && mapControls && !mapControls.hasControls && /* @__PURE__ */ jsx15(
3193
+ showControls && mapControls && !mapControls.hasControls && /* @__PURE__ */ jsx16(
3328
3194
  MapControls,
3329
3195
  {
3330
3196
  drawerContentRef,
@@ -3346,12 +3212,12 @@ var SDKMapProviderWrapper = ({
3346
3212
  }) => {
3347
3213
  const context = useContext5(MapControlsContext);
3348
3214
  if (context) {
3349
- return /* @__PURE__ */ jsx15(Fragment4, { children });
3215
+ return /* @__PURE__ */ jsx16(Fragment5, { children });
3350
3216
  }
3351
- return /* @__PURE__ */ jsx15(SDKMapProvider, { children });
3217
+ return /* @__PURE__ */ jsx16(SDKMapProvider, { children });
3352
3218
  };
3353
3219
  var Map2 = (props) => {
3354
- return /* @__PURE__ */ jsx15(SDKMapProviderWrapper, { children: /* @__PURE__ */ jsx15(MapProvider, { children: /* @__PURE__ */ jsx15(MapInternal, { ...props }) }) });
3220
+ return /* @__PURE__ */ jsx16(SDKMapProviderWrapper, { children: /* @__PURE__ */ jsx16(MapProvider, { children: /* @__PURE__ */ jsx16(MapInternal, { ...props }) }) });
3355
3221
  };
3356
3222
 
3357
3223
  // src/components/MapWrapper.tsx
@@ -3540,7 +3406,7 @@ var loadImage2 = (src) => new Promise((resolve, reject) => {
3540
3406
  });
3541
3407
 
3542
3408
  // package.json
3543
- var version = "0.6.7";
3409
+ var version = "0.7.0";
3544
3410
 
3545
3411
  // src/sdk.ts
3546
3412
  function deepFreeze(obj) {
@@ -3886,6 +3752,8 @@ var MiniAppSDK = class {
3886
3752
  );
3887
3753
  this.initialize = this.initialize.bind(this);
3888
3754
  this.getEnv = this.getEnv.bind(this);
3755
+ this.achievement = this.achievement.bind(this);
3756
+ this.achievements = this.achievements.bind(this);
3889
3757
  this.on = this.on.bind(this);
3890
3758
  this.off = this.off.bind(this);
3891
3759
  }
@@ -3926,6 +3794,23 @@ var MiniAppSDK = class {
3926
3794
  getEnv() {
3927
3795
  return lastKnownEnv;
3928
3796
  }
3797
+ /**
3798
+ * Fetches a single Achievement's current progress by slug — read-only, for
3799
+ * rendering things like a progress bar. Progress is reported server-side
3800
+ * by the mini-app's own backend calling the Hub's M2M endpoint directly;
3801
+ * there is no corresponding "report progress" method on this SDK.
3802
+ * @param slug Unique slug identifying the Achievement.
3803
+ */
3804
+ achievement(slug) {
3805
+ return this.bridge.send("GET_ACHIEVEMENT", { slug });
3806
+ }
3807
+ /**
3808
+ * Fetches every Achievement (with current progress) visible to this
3809
+ * mini-app — read-only, same boundary as {@link MiniAppSDK.achievement}.
3810
+ */
3811
+ achievements() {
3812
+ return this.bridge.send("GET_ACHIEVEMENTS");
3813
+ }
3929
3814
  /**
3930
3815
  * Registers a callback for a specific event from the native shell.
3931
3816
  * @param event The name of the event to listen for.
@@ -3947,11 +3832,45 @@ var MiniAppSDK = class {
3947
3832
  var sdk = deepFreeze(new MiniAppSDK());
3948
3833
 
3949
3834
  // src/components/BridgeMocker.tsx
3950
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
3951
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3835
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo8, useRef as useRef8, useState as useState10 } from "react";
3836
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3837
+ var MOCK_ACHIEVEMENTS2 = [
3838
+ {
3839
+ completed: false,
3840
+ description: "Discover 5 points of interest around Annecy.",
3841
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=explore",
3842
+ playerActionCount: 3,
3843
+ slug: "explore-5-pois",
3844
+ title: "Explorer",
3845
+ totalActionCount: 5,
3846
+ upReward: 50,
3847
+ xpReward: 100
3848
+ },
3849
+ {
3850
+ completed: true,
3851
+ description: "Complete your first navigation journey.",
3852
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=journey",
3853
+ playerActionCount: 1,
3854
+ slug: "first-journey",
3855
+ title: "First Steps",
3856
+ totalActionCount: 1,
3857
+ upReward: 20,
3858
+ xpReward: 40
3859
+ },
3860
+ {
3861
+ completed: false,
3862
+ description: "Scan 10 QR codes at partner venues.",
3863
+ imageUrl: "https://api.dicebear.com/9.x/icons/svg?seed=scan",
3864
+ playerActionCount: 4,
3865
+ slug: "scan-10-qrcodes",
3866
+ title: "Code Breaker",
3867
+ totalActionCount: 10,
3868
+ upReward: 80,
3869
+ xpReward: 150
3870
+ }
3871
+ ];
3952
3872
  var AVATAR_MOUTH_VARIANTS = [
3953
3873
  "variant03",
3954
- "variant06",
3955
3874
  "variant07",
3956
3875
  "variant08",
3957
3876
  "variant09",
@@ -4036,15 +3955,15 @@ var BridgeMocker = ({
4036
3955
  mockJWTToken,
4037
3956
  children
4038
3957
  }) => {
4039
- const [autoMove, setAutoMove] = useState9(isMoving);
4040
- const [location, setLocation] = useState9(initialLocation);
4041
- const [heading, setHeading] = useState9(0);
4042
- const [speedMultiplier, setSpeedMultiplier] = useState9(simulationSpeed);
4043
- const [isReady, setIsReady] = useState9(false);
4044
- const [isMinimized, setIsMinimized] = useState9(true);
4045
- const [radiusMeters, setRadiusMeters] = useState9(0);
4046
- const [radiusUnit, setRadiusUnit] = useState9("m");
4047
- const [isPlacingLocation, setIsPlacingLocation] = useState9(false);
3958
+ const [autoMove, setAutoMove] = useState10(isMoving);
3959
+ const [location, setLocation] = useState10(initialLocation);
3960
+ const [heading, setHeading] = useState10(0);
3961
+ const [speedMultiplier, setSpeedMultiplier] = useState10(simulationSpeed);
3962
+ const [isReady, setIsReady] = useState10(false);
3963
+ const [isMinimized, setIsMinimized] = useState10(true);
3964
+ const [radiusMeters, setRadiusMeters] = useState10(0);
3965
+ const [radiusUnit, setRadiusUnit] = useState10("m");
3966
+ const [isPlacingLocation, setIsPlacingLocation] = useState10(false);
4048
3967
  const streamIntervalRef = useRef8(null);
4049
3968
  const pathIndexRef = useRef8(0);
4050
3969
  const activeConfigRef = useRef8(null);
@@ -4306,6 +4225,24 @@ var BridgeMocker = ({
4306
4225
  if (type === "GET_FIDELITY_BALANCE") {
4307
4226
  return Promise.resolve({ balance: 1337, currency: "FPoints" });
4308
4227
  }
4228
+ if (type === "GET_ACHIEVEMENTS") {
4229
+ return Promise.resolve(MOCK_ACHIEVEMENTS2);
4230
+ }
4231
+ if (type === "GET_ACHIEVEMENT") {
4232
+ const slug = payload?.slug;
4233
+ return Promise.resolve(
4234
+ MOCK_ACHIEVEMENTS2.find((a) => a.slug === slug) ?? {
4235
+ completed: false,
4236
+ description: "Mocked achievement (no native shell attached).",
4237
+ playerActionCount: 0,
4238
+ slug: slug || "unknown-achievement",
4239
+ title: slug || "Unknown Achievement",
4240
+ totalActionCount: 1,
4241
+ upReward: 0,
4242
+ xpReward: 0
4243
+ }
4244
+ );
4245
+ }
4309
4246
  if (type === "PAYMENT_REQUEST") {
4310
4247
  return Promise.resolve({
4311
4248
  success: true,
@@ -4324,7 +4261,7 @@ var BridgeMocker = ({
4324
4261
  };
4325
4262
  }, [appId]);
4326
4263
  const radiusInMeters = radiusUnit === "km" ? radiusMeters * 1e3 : radiusMeters;
4327
- const mockLocationContextValue = useMemo7(
4264
+ const mockLocationContextValue = useMemo8(
4328
4265
  () => ({
4329
4266
  isPlacingLocation,
4330
4267
  placeLocation,
@@ -4333,9 +4270,9 @@ var BridgeMocker = ({
4333
4270
  [isPlacingLocation, placeLocation, radiusInMeters]
4334
4271
  );
4335
4272
  if (!isReady) return null;
4336
- return /* @__PURE__ */ jsxs12("div", { className: "relative w-full h-full", children: [
4337
- /* @__PURE__ */ jsx16("div", { className: "w-full h-full", children: /* @__PURE__ */ jsx16(MockLocationContext.Provider, { value: mockLocationContextValue, children }) }),
4338
- isMinimized ? /* @__PURE__ */ jsx16(
4273
+ return /* @__PURE__ */ jsxs13("div", { className: "relative w-full h-full", children: [
4274
+ /* @__PURE__ */ jsx17("div", { className: "w-full h-full", children: /* @__PURE__ */ jsx17(MockLocationContext.Provider, { value: mockLocationContextValue, children }) }),
4275
+ isMinimized ? /* @__PURE__ */ jsx17(
4339
4276
  Button,
4340
4277
  {
4341
4278
  className: "fixed bottom-4 right-4 w-8 h-8 rounded-full bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 flex items-center justify-center text-slate-400 hover:text-blue-400 hover:bg-slate-900/80 z-[9999] transition-all shadow-lg group p-0!",
@@ -4343,7 +4280,7 @@ var BridgeMocker = ({
4343
4280
  title: "Open Simulation Controls",
4344
4281
  type: "button",
4345
4282
  variant: "ghost",
4346
- children: /* @__PURE__ */ jsxs12(
4283
+ children: /* @__PURE__ */ jsxs13(
4347
4284
  "svg",
4348
4285
  {
4349
4286
  className: "group-hover:scale-110 transition-transform",
@@ -4357,34 +4294,34 @@ var BridgeMocker = ({
4357
4294
  width: "14",
4358
4295
  xmlns: "http://www.w3.org/2000/svg",
4359
4296
  children: [
4360
- /* @__PURE__ */ jsx16("path", { d: "M21 4H3" }),
4361
- /* @__PURE__ */ jsx16("path", { d: "M10 8H3" }),
4362
- /* @__PURE__ */ jsx16("path", { d: "M21 12H3" }),
4363
- /* @__PURE__ */ jsx16("path", { d: "M10 16H3" }),
4364
- /* @__PURE__ */ jsx16("path", { d: "M21 20H3" }),
4365
- /* @__PURE__ */ jsx16("circle", { cx: "14", cy: "8", r: "2" }),
4366
- /* @__PURE__ */ jsx16("circle", { cx: "14", cy: "16", r: "2" })
4297
+ /* @__PURE__ */ jsx17("path", { d: "M21 4H3" }),
4298
+ /* @__PURE__ */ jsx17("path", { d: "M10 8H3" }),
4299
+ /* @__PURE__ */ jsx17("path", { d: "M21 12H3" }),
4300
+ /* @__PURE__ */ jsx17("path", { d: "M10 16H3" }),
4301
+ /* @__PURE__ */ jsx17("path", { d: "M21 20H3" }),
4302
+ /* @__PURE__ */ jsx17("circle", { cx: "14", cy: "8", r: "2" }),
4303
+ /* @__PURE__ */ jsx17("circle", { cx: "14", cy: "16", r: "2" })
4367
4304
  ]
4368
4305
  }
4369
4306
  )
4370
4307
  }
4371
- ) : /* @__PURE__ */ jsxs12(
4308
+ ) : /* @__PURE__ */ jsxs13(
4372
4309
  "div",
4373
4310
  {
4374
4311
  className: "fixed bottom-4 right-4 bg-slate-900 text-slate-300 font-mono text-xs rounded-xl shadow-2xl border border-slate-700 pointer-events-auto flex flex-col w-80 overflow-hidden animate-in slide-in-from-right duration-300",
4375
4312
  style: { zIndex: 9998 },
4376
4313
  children: [
4377
- /* @__PURE__ */ jsxs12(
4314
+ /* @__PURE__ */ jsxs13(
4378
4315
  "div",
4379
4316
  {
4380
4317
  className: "flex justify-between items-center p-3 border-b border-slate-800 cursor-pointer bg-slate-800/50 hover:bg-slate-800 transition-colors",
4381
4318
  onClick: () => setIsMinimized(true),
4382
4319
  children: [
4383
- /* @__PURE__ */ jsxs12("span", { className: "font-bold text-slate-100 flex items-center gap-2", children: [
4384
- /* @__PURE__ */ jsx16("span", { className: "text-lg", children: "\u{1F6E0}\uFE0F" }),
4320
+ /* @__PURE__ */ jsxs13("span", { className: "font-bold text-slate-100 flex items-center gap-2", children: [
4321
+ /* @__PURE__ */ jsx17("span", { className: "text-lg", children: "\u{1F6E0}\uFE0F" }),
4385
4322
  " Simulation Controls"
4386
4323
  ] }),
4387
- /* @__PURE__ */ jsx16(
4324
+ /* @__PURE__ */ jsx17(
4388
4325
  Button,
4389
4326
  {
4390
4327
  className: "text-slate-500 hover:text-white p-0 h-auto hover:bg-transparent cursor-pointer",
@@ -4396,10 +4333,10 @@ var BridgeMocker = ({
4396
4333
  ]
4397
4334
  }
4398
4335
  ),
4399
- /* @__PURE__ */ jsxs12("div", { className: "p-4 flex flex-col gap-3", children: [
4400
- /* @__PURE__ */ jsxs12("div", { className: "flex justify-between items-center", children: [
4401
- /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 cursor-pointer", children: [
4402
- /* @__PURE__ */ jsx16(
4336
+ /* @__PURE__ */ jsxs13("div", { className: "p-4 flex flex-col gap-3", children: [
4337
+ /* @__PURE__ */ jsxs13("div", { className: "flex justify-between items-center", children: [
4338
+ /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 cursor-pointer", children: [
4339
+ /* @__PURE__ */ jsx17(
4403
4340
  "input",
4404
4341
  {
4405
4342
  checked: autoMove,
@@ -4408,20 +4345,20 @@ var BridgeMocker = ({
4408
4345
  type: "checkbox"
4409
4346
  }
4410
4347
  ),
4411
- /* @__PURE__ */ jsxs12("span", { className: "text-slate-200 font-bold", children: [
4348
+ /* @__PURE__ */ jsxs13("span", { className: "text-slate-200 font-bold", children: [
4412
4349
  "Auto-Move ",
4413
4350
  simulationPath ? "(Path)" : "(Random)"
4414
4351
  ] })
4415
4352
  ] }),
4416
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
4417
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] text-slate-500", children: "Speed:" }),
4418
- /* @__PURE__ */ jsx16(
4353
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
4354
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] text-slate-500", children: "Speed:" }),
4355
+ /* @__PURE__ */ jsx17(
4419
4356
  "select",
4420
4357
  {
4421
4358
  className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5 text-white focus:outline-none focus:border-blue-500 text-[10px]",
4422
4359
  onChange: (e) => setSpeedMultiplier(Number(e.target.value)),
4423
4360
  value: speedMultiplier,
4424
- children: [1, 2, 5, 10, 50, 100, 250].map((s) => /* @__PURE__ */ jsxs12("option", { value: s, children: [
4361
+ children: [1, 2, 5, 10, 50, 100, 250].map((s) => /* @__PURE__ */ jsxs13("option", { value: s, children: [
4425
4362
  s,
4426
4363
  "x"
4427
4364
  ] }, s))
@@ -4429,10 +4366,10 @@ var BridgeMocker = ({
4429
4366
  )
4430
4367
  ] })
4431
4368
  ] }),
4432
- /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2 gap-2", children: [
4433
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1", children: [
4434
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] text-slate-500 text-left", children: "Lng" }),
4435
- /* @__PURE__ */ jsx16(
4369
+ /* @__PURE__ */ jsxs13("div", { className: "grid grid-cols-2 gap-2", children: [
4370
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1", children: [
4371
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] text-slate-500 text-left", children: "Lng" }),
4372
+ /* @__PURE__ */ jsx17(
4436
4373
  "input",
4437
4374
  {
4438
4375
  className: "bg-slate-800 border border-slate-700 rounded px-2 py-1 text-white focus:outline-none focus:border-blue-500",
@@ -4447,9 +4384,9 @@ var BridgeMocker = ({
4447
4384
  }
4448
4385
  )
4449
4386
  ] }),
4450
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1", children: [
4451
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] text-slate-500 text-left", children: "Lat" }),
4452
- /* @__PURE__ */ jsx16(
4387
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1", children: [
4388
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] text-slate-500 text-left", children: "Lat" }),
4389
+ /* @__PURE__ */ jsx17(
4453
4390
  "input",
4454
4391
  {
4455
4392
  className: "bg-slate-800 border border-slate-700 rounded px-2 py-1 text-white focus:outline-none focus:border-blue-500",
@@ -4464,9 +4401,9 @@ var BridgeMocker = ({
4464
4401
  }
4465
4402
  )
4466
4403
  ] }),
4467
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1 col-span-2", children: [
4468
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] text-slate-500", children: "Heading (Degrees)" }),
4469
- /* @__PURE__ */ jsx16(
4404
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1 col-span-2", children: [
4405
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] text-slate-500", children: "Heading (Degrees)" }),
4406
+ /* @__PURE__ */ jsx17(
4470
4407
  "input",
4471
4408
  {
4472
4409
  className: "w-full accent-blue-500",
@@ -4480,13 +4417,13 @@ var BridgeMocker = ({
4480
4417
  value: heading
4481
4418
  }
4482
4419
  ),
4483
- /* @__PURE__ */ jsxs12("div", { className: "text-right text-slate-400", children: [
4420
+ /* @__PURE__ */ jsxs13("div", { className: "text-right text-slate-400", children: [
4484
4421
  heading,
4485
4422
  "\xB0"
4486
4423
  ] })
4487
4424
  ] })
4488
4425
  ] }),
4489
- /* @__PURE__ */ jsx16(
4426
+ /* @__PURE__ */ jsx17(
4490
4427
  Button,
4491
4428
  {
4492
4429
  className: `w-full rounded px-2 py-1.5 text-[10px] font-bold border cursor-pointer ${isPlacingLocation ? "bg-blue-500/20 border-blue-500 text-blue-300" : "bg-slate-800 border-slate-700 text-slate-200 hover:bg-slate-700"}`,
@@ -4496,11 +4433,11 @@ var BridgeMocker = ({
4496
4433
  children: isPlacingLocation ? "\u{1F4CD} Click the map to drop location\u2026" : "\u{1F4CD} Drop location on map"
4497
4434
  }
4498
4435
  ),
4499
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1 border-t border-slate-800 pt-3", children: [
4500
- /* @__PURE__ */ jsxs12("div", { className: "flex justify-between items-center", children: [
4501
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] text-slate-500", children: "Scan Radius" }),
4502
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
4503
- /* @__PURE__ */ jsx16(
4436
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1 border-t border-slate-800 pt-3", children: [
4437
+ /* @__PURE__ */ jsxs13("div", { className: "flex justify-between items-center", children: [
4438
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] text-slate-500", children: "Scan Radius" }),
4439
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
4440
+ /* @__PURE__ */ jsx17(
4504
4441
  "input",
4505
4442
  {
4506
4443
  className: "w-16 bg-slate-800 border border-slate-700 rounded px-1.5 py-0.5 text-white text-[10px] focus:outline-none focus:border-blue-500",
@@ -4510,21 +4447,21 @@ var BridgeMocker = ({
4510
4447
  value: radiusMeters
4511
4448
  }
4512
4449
  ),
4513
- /* @__PURE__ */ jsxs12(
4450
+ /* @__PURE__ */ jsxs13(
4514
4451
  "select",
4515
4452
  {
4516
4453
  className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5 text-white text-[10px] focus:outline-none focus:border-blue-500",
4517
4454
  onChange: (e) => setRadiusUnit(e.target.value),
4518
4455
  value: radiusUnit,
4519
4456
  children: [
4520
- /* @__PURE__ */ jsx16("option", { value: "m", children: "m" }),
4521
- /* @__PURE__ */ jsx16("option", { value: "km", children: "km" })
4457
+ /* @__PURE__ */ jsx17("option", { value: "m", children: "m" }),
4458
+ /* @__PURE__ */ jsx17("option", { value: "km", children: "km" })
4522
4459
  ]
4523
4460
  }
4524
4461
  )
4525
4462
  ] })
4526
4463
  ] }),
4527
- /* @__PURE__ */ jsx16(
4464
+ /* @__PURE__ */ jsx17(
4528
4465
  "input",
4529
4466
  {
4530
4467
  className: "w-full accent-blue-500",
@@ -4536,7 +4473,7 @@ var BridgeMocker = ({
4536
4473
  value: radiusMeters
4537
4474
  }
4538
4475
  ),
4539
- /* @__PURE__ */ jsx16("div", { className: "text-right text-slate-400", children: radiusMeters === 0 ? "hidden" : `${radiusMeters}${radiusUnit} radius around user` })
4476
+ /* @__PURE__ */ jsx17("div", { className: "text-right text-slate-400", children: radiusMeters === 0 ? "hidden" : `${radiusMeters}${radiusUnit} radius around user` })
4540
4477
  ] })
4541
4478
  ] })
4542
4479
  ]
@@ -4546,14 +4483,14 @@ var BridgeMocker = ({
4546
4483
  };
4547
4484
 
4548
4485
  // src/components/DebugConsole.tsx
4549
- import { useCallback as useCallback8, useEffect as useEffect9, useRef as useRef9, useState as useState10 } from "react";
4550
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
4486
+ import { useCallback as useCallback8, useEffect as useEffect9, useRef as useRef9, useState as useState11 } from "react";
4487
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
4551
4488
  var DebugConsole = ({
4552
4489
  isOpen: initialOpen = false,
4553
4490
  onClose
4554
4491
  }) => {
4555
- const [showConsole, setShowConsole] = useState10(initialOpen);
4556
- const [logs, setLogs] = useState10([]);
4492
+ const [showConsole, setShowConsole] = useState11(initialOpen);
4493
+ const [logs, setLogs] = useState11([]);
4557
4494
  const scrollRef = useRef9(null);
4558
4495
  useEffect9(() => {
4559
4496
  setShowConsole(initialOpen);
@@ -4591,7 +4528,7 @@ var DebugConsole = ({
4591
4528
  };
4592
4529
  }, [addLog]);
4593
4530
  if (!showConsole) {
4594
- return /* @__PURE__ */ jsx17(
4531
+ return /* @__PURE__ */ jsx18(
4595
4532
  Button,
4596
4533
  {
4597
4534
  className: "fixed bottom-4 left-4 w-8 h-8 rounded-full bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 flex items-center justify-center text-slate-400 hover:text-emerald-400 hover:bg-slate-900/80 transition-all z-[9998] shadow-lg group p-0!",
@@ -4599,7 +4536,7 @@ var DebugConsole = ({
4599
4536
  title: "Open Debug Console",
4600
4537
  type: "button",
4601
4538
  variant: "ghost",
4602
- children: /* @__PURE__ */ jsxs13(
4539
+ children: /* @__PURE__ */ jsxs14(
4603
4540
  "svg",
4604
4541
  {
4605
4542
  className: "group-hover:scale-110 transition-transform",
@@ -4613,31 +4550,31 @@ var DebugConsole = ({
4613
4550
  width: "14",
4614
4551
  xmlns: "http://www.w3.org/2000/svg",
4615
4552
  children: [
4616
- /* @__PURE__ */ jsx17("path", { d: "m8 2 1.88 1.88" }),
4617
- /* @__PURE__ */ jsx17("path", { d: "M14.12 3.88 16 2" }),
4618
- /* @__PURE__ */ jsx17("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
4619
- /* @__PURE__ */ jsx17("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
4620
- /* @__PURE__ */ jsx17("path", { d: "M12 20v-9" }),
4621
- /* @__PURE__ */ jsx17("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
4622
- /* @__PURE__ */ jsx17("path", { d: "M6 13H2" }),
4623
- /* @__PURE__ */ jsx17("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
4624
- /* @__PURE__ */ jsx17("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
4625
- /* @__PURE__ */ jsx17("path", { d: "M22 13h-4" }),
4626
- /* @__PURE__ */ jsx17("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
4553
+ /* @__PURE__ */ jsx18("path", { d: "m8 2 1.88 1.88" }),
4554
+ /* @__PURE__ */ jsx18("path", { d: "M14.12 3.88 16 2" }),
4555
+ /* @__PURE__ */ jsx18("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
4556
+ /* @__PURE__ */ jsx18("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
4557
+ /* @__PURE__ */ jsx18("path", { d: "M12 20v-9" }),
4558
+ /* @__PURE__ */ jsx18("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
4559
+ /* @__PURE__ */ jsx18("path", { d: "M6 13H2" }),
4560
+ /* @__PURE__ */ jsx18("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
4561
+ /* @__PURE__ */ jsx18("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
4562
+ /* @__PURE__ */ jsx18("path", { d: "M22 13h-4" }),
4563
+ /* @__PURE__ */ jsx18("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
4627
4564
  ]
4628
4565
  }
4629
4566
  )
4630
4567
  }
4631
4568
  );
4632
4569
  }
4633
- return /* @__PURE__ */ jsxs13("div", { className: "fixed inset-x-0 bottom-0 w-full bg-[#0a080f] border-t border-slate-800 z-[9998] p-4 shadow-2xl flex flex-col animate-in slide-in-from-bottom duration-300", children: [
4634
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between mb-2", children: [
4635
- /* @__PURE__ */ jsxs13("span", { className: "text-[10px] font-bold uppercase tracking-widest text-slate-400 flex items-center gap-2", children: [
4636
- /* @__PURE__ */ jsx17("span", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
4570
+ return /* @__PURE__ */ jsxs14("div", { className: "fixed inset-x-0 bottom-0 w-full bg-[#0a080f] border-t border-slate-800 z-[9998] p-4 shadow-2xl flex flex-col animate-in slide-in-from-bottom duration-300", children: [
4571
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between mb-2", children: [
4572
+ /* @__PURE__ */ jsxs14("span", { className: "text-[10px] font-bold uppercase tracking-widest text-slate-400 flex items-center gap-2", children: [
4573
+ /* @__PURE__ */ jsx18("span", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
4637
4574
  "UP SDK Debug Console"
4638
4575
  ] }),
4639
- /* @__PURE__ */ jsxs13("div", { className: "flex gap-4", children: [
4640
- /* @__PURE__ */ jsx17(
4576
+ /* @__PURE__ */ jsxs14("div", { className: "flex gap-4", children: [
4577
+ /* @__PURE__ */ jsx18(
4641
4578
  Button,
4642
4579
  {
4643
4580
  className: "text-[10px] text-slate-500 font-bold uppercase hover:text-slate-300 hover:bg-transparent transition-colors p-0 h-auto cursor-pointer",
@@ -4647,7 +4584,7 @@ var DebugConsole = ({
4647
4584
  children: "Clear"
4648
4585
  }
4649
4586
  ),
4650
- /* @__PURE__ */ jsx17(
4587
+ /* @__PURE__ */ jsx18(
4651
4588
  Button,
4652
4589
  {
4653
4590
  className: "text-[10px] text-pink-500 font-bold uppercase hover:text-pink-400 hover:bg-transparent transition-colors p-0 h-auto cursor-pointer",
@@ -4662,21 +4599,21 @@ var DebugConsole = ({
4662
4599
  )
4663
4600
  ] })
4664
4601
  ] }),
4665
- /* @__PURE__ */ jsx17(
4602
+ /* @__PURE__ */ jsx18(
4666
4603
  "div",
4667
4604
  {
4668
4605
  className: "bg-black/80 rounded-xl p-3 font-mono text-[10px] text-emerald-400 min-h-[120px] max-h-[240px] overflow-y-auto border border-slate-900 custom-scrollbar",
4669
4606
  ref: scrollRef,
4670
- children: logs.length === 0 ? /* @__PURE__ */ jsx17("div", { className: "text-slate-600 italic text-center py-8", children: "No logs yet. Interacting with the SDK will show events here." }) : logs.map((log) => /* @__PURE__ */ jsxs13(
4607
+ children: logs.length === 0 ? /* @__PURE__ */ jsx18("div", { className: "text-slate-600 italic text-center py-8", children: "No logs yet. Interacting with the SDK will show events here." }) : logs.map((log) => /* @__PURE__ */ jsxs14(
4671
4608
  "div",
4672
4609
  {
4673
4610
  className: "mb-3 pb-3 border-b border-slate-900/50 last:border-0 last:mb-0 last:pb-0",
4674
4611
  children: [
4675
- /* @__PURE__ */ jsxs13("div", { className: "flex gap-2 text-slate-500 mb-1 items-center", children: [
4676
- /* @__PURE__ */ jsx17("span", { className: "text-[8px]", children: log.time }),
4677
- /* @__PURE__ */ jsx17("span", { className: "font-bold text-white uppercase text-[8px] bg-slate-800 px-1.5 py-0.5 rounded leading-none", children: log.label })
4612
+ /* @__PURE__ */ jsxs14("div", { className: "flex gap-2 text-slate-500 mb-1 items-center", children: [
4613
+ /* @__PURE__ */ jsx18("span", { className: "text-[8px]", children: log.time }),
4614
+ /* @__PURE__ */ jsx18("span", { className: "font-bold text-white uppercase text-[8px] bg-slate-800 px-1.5 py-0.5 rounded leading-none", children: log.label })
4678
4615
  ] }),
4679
- /* @__PURE__ */ jsx17("pre", { className: "whitespace-pre-wrap break-all opacity-90", children: log.content })
4616
+ /* @__PURE__ */ jsx18("pre", { className: "whitespace-pre-wrap break-all opacity-90", children: log.content })
4680
4617
  ]
4681
4618
  },
4682
4619
  log.id
@@ -4687,8 +4624,8 @@ var DebugConsole = ({
4687
4624
  };
4688
4625
 
4689
4626
  // src/components/PullToRefresh.tsx
4690
- import { useEffect as useEffect10, useRef as useRef10, useState as useState11 } from "react";
4691
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
4627
+ import { useEffect as useEffect10, useRef as useRef10, useState as useState12 } from "react";
4628
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
4692
4629
  var DEFAULT_PULL_THRESHOLD = 64;
4693
4630
  var DEFAULT_MAX_PULL = 96;
4694
4631
  var RESISTANCE = 0.5;
@@ -4699,7 +4636,7 @@ function DefaultIndicator({
4699
4636
  progress,
4700
4637
  isRefreshing
4701
4638
  }) {
4702
- return /* @__PURE__ */ jsxs14(
4639
+ return /* @__PURE__ */ jsxs15(
4703
4640
  "div",
4704
4641
  {
4705
4642
  style: {
@@ -4714,7 +4651,7 @@ function DefaultIndicator({
4714
4651
  width: 28
4715
4652
  },
4716
4653
  children: [
4717
- /* @__PURE__ */ jsxs14(
4654
+ /* @__PURE__ */ jsxs15(
4718
4655
  "svg",
4719
4656
  {
4720
4657
  height: 14,
@@ -4726,8 +4663,8 @@ function DefaultIndicator({
4726
4663
  viewBox: "0 0 24 24",
4727
4664
  width: 14,
4728
4665
  children: [
4729
- /* @__PURE__ */ jsx18("title", { children: "Refreshing" }),
4730
- /* @__PURE__ */ jsx18(
4666
+ /* @__PURE__ */ jsx19("title", { children: "Refreshing" }),
4667
+ /* @__PURE__ */ jsx19(
4731
4668
  "path",
4732
4669
  {
4733
4670
  d: "M21 12a9 9 0 1 1-2.64-6.36",
@@ -4740,7 +4677,7 @@ function DefaultIndicator({
4740
4677
  ]
4741
4678
  }
4742
4679
  ),
4743
- /* @__PURE__ */ jsx18("style", { children: "@keyframes up-sdk-ptr-spin { to { transform: rotate(360deg); } }" })
4680
+ /* @__PURE__ */ jsx19("style", { children: "@keyframes up-sdk-ptr-spin { to { transform: rotate(360deg); } }" })
4744
4681
  ]
4745
4682
  }
4746
4683
  );
@@ -4763,9 +4700,9 @@ var PullToRefresh = ({
4763
4700
  const isPullingRef = useRef10(false);
4764
4701
  const pullDistanceRef = useRef10(0);
4765
4702
  const flingRafRef = useRef10(null);
4766
- const [pullDistance, setPullDistance] = useState11(0);
4767
- const [isRefreshing, setIsRefreshing] = useState11(false);
4768
- const [isTracking, setIsTracking] = useState11(false);
4703
+ const [pullDistance, setPullDistance] = useState12(0);
4704
+ const [isRefreshing, setIsRefreshing] = useState12(false);
4705
+ const [isTracking, setIsTracking] = useState12(false);
4769
4706
  const disabledRef = useRef10(disabled);
4770
4707
  disabledRef.current = disabled;
4771
4708
  const onRefreshRef = useRef10(onRefresh);
@@ -4774,6 +4711,25 @@ var PullToRefresh = ({
4774
4711
  pullThresholdRef.current = pullThreshold;
4775
4712
  const maxPullRef = useRef10(maxPull);
4776
4713
  maxPullRef.current = maxPull;
4714
+ useEffect10(() => {
4715
+ const el = containerRef.current;
4716
+ if (!el || typeof window === "undefined") return;
4717
+ const checkScrollChain = () => {
4718
+ const noInternalOverflow = el.scrollHeight <= el.clientHeight;
4719
+ const tallerThanViewport = el.clientHeight > window.innerHeight;
4720
+ if (noInternalOverflow && tallerThanViewport) {
4721
+ console.warn(
4722
+ "[PullToRefresh] This container is taller than the viewport but has no internal scroll (scrollHeight <= clientHeight). Because this component disables native touch scrolling entirely (touch-action: none), scrolling is now completely broken here. This almost always means either: (1) an ancestor's flex-1 item is missing `min-h-0` (a flex item defaults to min-height:auto and refuses to shrink below its content size), or (2) an ancestor uses `min-h-screen`/`min-h-full` (a floor, not a ceiling) instead of `h-full`/`h-screen`/`h-dvh`. See the vault skill js/pull-to-refresh-scroll-chain.md for the fix pattern."
4723
+ );
4724
+ }
4725
+ };
4726
+ const raf = requestAnimationFrame(checkScrollChain);
4727
+ window.addEventListener("resize", checkScrollChain);
4728
+ return () => {
4729
+ cancelAnimationFrame(raf);
4730
+ window.removeEventListener("resize", checkScrollChain);
4731
+ };
4732
+ }, []);
4777
4733
  useEffect10(() => {
4778
4734
  const el = containerRef.current;
4779
4735
  if (!el) return;
@@ -4905,7 +4861,7 @@ var PullToRefresh = ({
4905
4861
  const indicatorHeight = isRefreshing ? pullThreshold : pullDistance;
4906
4862
  const progress = Math.min(indicatorHeight / pullThreshold, 1);
4907
4863
  const isSettling = pullDistance === 0 && !isRefreshing && !isTracking;
4908
- return /* @__PURE__ */ jsxs14(
4864
+ return /* @__PURE__ */ jsxs15(
4909
4865
  "div",
4910
4866
  {
4911
4867
  className: `up-sdk-pull-to-refresh ${className}`,
@@ -4916,8 +4872,8 @@ var PullToRefresh = ({
4916
4872
  touchAction: "none"
4917
4873
  },
4918
4874
  children: [
4919
- /* @__PURE__ */ jsx18("style", { children: ".up-sdk-pull-to-refresh::-webkit-scrollbar { display: none; } .up-sdk-pull-to-refresh { scrollbar-width: none; -ms-overflow-style: none; }" }),
4920
- /* @__PURE__ */ jsx18(
4875
+ /* @__PURE__ */ jsx19("style", { children: ".up-sdk-pull-to-refresh::-webkit-scrollbar { display: none; } .up-sdk-pull-to-refresh { scrollbar-width: none; -ms-overflow-style: none; }" }),
4876
+ /* @__PURE__ */ jsx19(
4921
4877
  "div",
4922
4878
  {
4923
4879
  style: {
@@ -4930,7 +4886,7 @@ var PullToRefresh = ({
4930
4886
  userSelect: "none",
4931
4887
  width: "100%"
4932
4888
  },
4933
- children: renderIndicator ? renderIndicator({ isRefreshing, progress }) : /* @__PURE__ */ jsx18(DefaultIndicator, { isRefreshing, progress })
4889
+ children: renderIndicator ? renderIndicator({ isRefreshing, progress }) : /* @__PURE__ */ jsx19(DefaultIndicator, { isRefreshing, progress })
4934
4890
  }
4935
4891
  ),
4936
4892
  children
@@ -4941,15 +4897,15 @@ var PullToRefresh = ({
4941
4897
 
4942
4898
  // src/components/QRScanner.tsx
4943
4899
  import jsQR2 from "jsqr";
4944
- import { useEffect as useEffect11, useRef as useRef11, useState as useState12 } from "react";
4945
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
4900
+ import { useEffect as useEffect11, useRef as useRef11, useState as useState13 } from "react";
4901
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
4946
4902
  function QRScanner({ onScan, onError, className = "" }) {
4947
4903
  const videoRef = useRef11(null);
4948
4904
  const canvasRef = useRef11(null);
4949
4905
  const streamRef = useRef11(null);
4950
4906
  const tickRequestRef = useRef11(null);
4951
4907
  const hasScannedRef = useRef11(false);
4952
- const [isReady, setIsReady] = useState12(false);
4908
+ const [isReady, setIsReady] = useState13(false);
4953
4909
  useEffect11(() => {
4954
4910
  let cancelled = false;
4955
4911
  hasScannedRef.current = false;
@@ -5033,7 +4989,7 @@ function QRScanner({ onScan, onError, className = "" }) {
5033
4989
  cleanup();
5034
4990
  };
5035
4991
  }, []);
5036
- return /* @__PURE__ */ jsxs15(
4992
+ return /* @__PURE__ */ jsxs16(
5037
4993
  "div",
5038
4994
  {
5039
4995
  className,
@@ -5045,7 +5001,7 @@ function QRScanner({ onScan, onError, className = "" }) {
5045
5001
  position: "relative"
5046
5002
  },
5047
5003
  children: [
5048
- /* @__PURE__ */ jsx19(
5004
+ /* @__PURE__ */ jsx20(
5049
5005
  "video",
5050
5006
  {
5051
5007
  playsInline: true,
@@ -5053,8 +5009,8 @@ function QRScanner({ onScan, onError, className = "" }) {
5053
5009
  style: { height: "100%", objectFit: "cover", width: "100%" }
5054
5010
  }
5055
5011
  ),
5056
- /* @__PURE__ */ jsx19("canvas", { ref: canvasRef, style: { display: "none" } }),
5057
- isReady && /* @__PURE__ */ jsx19(
5012
+ /* @__PURE__ */ jsx20("canvas", { ref: canvasRef, style: { display: "none" } }),
5013
+ isReady && /* @__PURE__ */ jsx20(
5058
5014
  "div",
5059
5015
  {
5060
5016
  style: {
@@ -5075,8 +5031,8 @@ function QRScanner({ onScan, onError, className = "" }) {
5075
5031
  }
5076
5032
 
5077
5033
  // src/components/SDKProvider.tsx
5078
- import { createContext as createContext3, useContext as useContext6, useMemo as useMemo8 } from "react";
5079
- import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
5034
+ import { createContext as createContext3, useContext as useContext6, useMemo as useMemo9 } from "react";
5035
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
5080
5036
  var SDKContext = createContext3({ isMocked: false });
5081
5037
  var useIsMocked = () => useContext6(SDKContext).isMocked;
5082
5038
  var SDKProvider = ({
@@ -5085,15 +5041,15 @@ var SDKProvider = ({
5085
5041
  ...mockerProps
5086
5042
  }) => {
5087
5043
  const isMocked = typeof window !== "undefined" && !(window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView);
5088
- const value = useMemo8(() => ({ isMocked }), [isMocked]);
5089
- const content = /* @__PURE__ */ jsxs16(Fragment5, { children: [
5044
+ const value = useMemo9(() => ({ isMocked }), [isMocked]);
5045
+ const content = /* @__PURE__ */ jsxs17(Fragment6, { children: [
5090
5046
  children,
5091
- (debug || isMocked) && /* @__PURE__ */ jsx20(DebugConsole, {})
5047
+ (debug || isMocked) && /* @__PURE__ */ jsx21(DebugConsole, {})
5092
5048
  ] });
5093
5049
  if (isMocked) {
5094
- return /* @__PURE__ */ jsx20(SDKContext.Provider, { value, children: /* @__PURE__ */ jsx20(BridgeMocker, { ...mockerProps, children: content }) });
5050
+ return /* @__PURE__ */ jsx21(SDKContext.Provider, { value, children: /* @__PURE__ */ jsx21(BridgeMocker, { ...mockerProps, children: content }) });
5095
5051
  }
5096
- return /* @__PURE__ */ jsx20(SDKContext.Provider, { value, children: content });
5052
+ return /* @__PURE__ */ jsx21(SDKContext.Provider, { value, children: content });
5097
5053
  };
5098
5054
 
5099
5055
  // src/components/SwipeCarousel.tsx
@@ -5101,9 +5057,9 @@ import {
5101
5057
  useEffect as useEffect12,
5102
5058
  useLayoutEffect,
5103
5059
  useRef as useRef12,
5104
- useState as useState13
5060
+ useState as useState14
5105
5061
  } from "react";
5106
- import { jsx as jsx21 } from "react/jsx-runtime";
5062
+ import { jsx as jsx22 } from "react/jsx-runtime";
5107
5063
  var DIRECTION_LOCK_THRESHOLD_PX = 8;
5108
5064
  var SWIPE_DISTANCE_THRESHOLD_PX = 40;
5109
5065
  var SWIPE_VELOCITY_THRESHOLD = 0.4;
@@ -5131,20 +5087,20 @@ var SwipeCarousel = ({
5131
5087
  visibleOverscan = DEFAULT_VISIBLE_OVERSCAN
5132
5088
  }) => {
5133
5089
  const containerRef = useRef12(null);
5134
- const [containerWidth, setContainerWidth] = useState13(0);
5135
- const [dragX, setDragX] = useState13(0);
5136
- const [dragY, setDragY] = useState13(0);
5137
- const [isDraggingX, setIsDraggingX] = useState13(false);
5138
- const [isDraggingY, setIsDraggingY] = useState13(false);
5090
+ const [containerWidth, setContainerWidth] = useState14(0);
5091
+ const [dragX, setDragX] = useState14(0);
5092
+ const [dragY, setDragY] = useState14(0);
5093
+ const [isDraggingX, setIsDraggingX] = useState14(false);
5094
+ const [isDraggingY, setIsDraggingY] = useState14(false);
5139
5095
  const touchStateRef = useRef12(null);
5140
- const [internalIndex, setInternalIndex] = useState13(
5096
+ const [internalIndex, setInternalIndex] = useState14(
5141
5097
  () => Math.max(
5142
5098
  0,
5143
5099
  items.findIndex((item) => item.id === activeId)
5144
5100
  )
5145
5101
  );
5146
5102
  const prevActiveIdRef = useRef12(activeId);
5147
- const [timingWaiting, setTimingWaiting] = useState13(true);
5103
+ const [timingWaiting, setTimingWaiting] = useState14(true);
5148
5104
  useEffect12(() => {
5149
5105
  const timer = setTimeout(() => {
5150
5106
  setTimingWaiting(false);
@@ -5280,7 +5236,7 @@ var SwipeCarousel = ({
5280
5236
  };
5281
5237
  const windowStart = Math.max(0, activeIndex - overscan);
5282
5238
  const windowEnd = Math.min(items.length - 1, activeIndex + overscan);
5283
- return /* @__PURE__ */ jsx21(
5239
+ return /* @__PURE__ */ jsx22(
5284
5240
  "div",
5285
5241
  {
5286
5242
  className: `relative overflow-hidden touch-pan-y ${className ?? ""}`,
@@ -5289,7 +5245,7 @@ var SwipeCarousel = ({
5289
5245
  onTouchMove: handleTouchMove,
5290
5246
  onTouchStart: handleTouchStart,
5291
5247
  ref: containerRef,
5292
- children: /* @__PURE__ */ jsx21(
5248
+ children: /* @__PURE__ */ jsx22(
5293
5249
  "div",
5294
5250
  {
5295
5251
  className: "relative h-full",
@@ -5304,7 +5260,7 @@ var SwipeCarousel = ({
5304
5260
  const verticalOffset = isActive ? dragY : 0;
5305
5261
  const distance = Math.abs(index - activeIndex);
5306
5262
  const isVisible = distance <= visibleOverscan;
5307
- return /* @__PURE__ */ jsx21(
5263
+ return /* @__PURE__ */ jsx22(
5308
5264
  "div",
5309
5265
  {
5310
5266
  className: `absolute top-0 left-0 h-full shrink-0 ${itemClassName ?? ""} ${item.className ?? ""}`,
@@ -5332,6 +5288,40 @@ var SwipeCarousel = ({
5332
5288
  );
5333
5289
  };
5334
5290
 
5291
+ // src/hooks/useTapAction.ts
5292
+ import { useRef as useRef13 } from "react";
5293
+ var TAP_MOVE_TOLERANCE_PX = 10;
5294
+ var CLICK_SUPPRESS_WINDOW_MS = 500;
5295
+ var useTapAction = (action) => {
5296
+ const startRef = useRef13(null);
5297
+ const handledByTouchRef = useRef13(false);
5298
+ const onTouchStart = (e) => {
5299
+ const touch = e.targetTouches[0];
5300
+ startRef.current = touch ? { x: touch.clientX, y: touch.clientY } : null;
5301
+ };
5302
+ const onTouchEnd = (e) => {
5303
+ const start = startRef.current;
5304
+ const touch = e.changedTouches[0];
5305
+ startRef.current = null;
5306
+ if (!start || !touch) return;
5307
+ const moved = Math.hypot(touch.clientX - start.x, touch.clientY - start.y);
5308
+ if (moved > TAP_MOVE_TOLERANCE_PX) return;
5309
+ handledByTouchRef.current = true;
5310
+ setTimeout(() => {
5311
+ handledByTouchRef.current = false;
5312
+ }, CLICK_SUPPRESS_WINDOW_MS);
5313
+ action();
5314
+ };
5315
+ const onClick = () => {
5316
+ if (handledByTouchRef.current) {
5317
+ handledByTouchRef.current = false;
5318
+ return;
5319
+ }
5320
+ action();
5321
+ };
5322
+ return { onClick, onTouchEnd, onTouchStart };
5323
+ };
5324
+
5335
5325
  // src/services/apiService.ts
5336
5326
  function buildUrl(baseUrl, path) {
5337
5327
  if (path.startsWith("http://") || path.startsWith("https://")) {
@@ -5600,6 +5590,7 @@ export {
5600
5590
  useMapControls,
5601
5591
  useMockLocation,
5602
5592
  useRoute,
5593
+ useTapAction,
5603
5594
  useTrimmedRoute,
5604
5595
  useUserLocation
5605
5596
  };