@buoy-gg/shared-ui 2.1.11 → 2.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  2. package/lib/commonjs/ui/components/DraggableHeader.js +147 -95
  3. package/lib/commonjs/ui/components/GripVerticalIcon.js +65 -0
  4. package/lib/commonjs/ui/components/index.js +7 -0
  5. package/lib/commonjs/utils/persistentStorage.js +14 -2
  6. package/lib/module/hooks/safe-area-impl.js +1 -1
  7. package/lib/module/ui/components/DraggableHeader.js +148 -96
  8. package/lib/module/ui/components/GripVerticalIcon.js +61 -0
  9. package/lib/module/ui/components/index.js +1 -0
  10. package/lib/module/utils/persistentStorage.js +14 -2
  11. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  12. package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts +25 -5
  13. package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts.map +1 -1
  14. package/lib/typescript/commonjs/ui/components/GripVerticalIcon.d.ts +14 -0
  15. package/lib/typescript/commonjs/ui/components/GripVerticalIcon.d.ts.map +1 -0
  16. package/lib/typescript/commonjs/ui/components/index.d.ts +1 -0
  17. package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/utils/persistentStorage.d.ts.map +1 -1
  19. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  20. package/lib/typescript/module/ui/components/DraggableHeader.d.ts +25 -5
  21. package/lib/typescript/module/ui/components/DraggableHeader.d.ts.map +1 -1
  22. package/lib/typescript/module/ui/components/GripVerticalIcon.d.ts +14 -0
  23. package/lib/typescript/module/ui/components/GripVerticalIcon.d.ts.map +1 -0
  24. package/lib/typescript/module/ui/components/index.d.ts +1 -0
  25. package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
  26. package/lib/typescript/module/utils/persistentStorage.d.ts.map +1 -1
  27. package/package.json +13 -14
@@ -7,7 +7,7 @@ exports.useNativeSafeAreaInsets = exports.safeAreaType = exports.hasSafeAreaPack
7
7
  /**
8
8
  * Auto-generated safe area implementation
9
9
  * Detected: none
10
- * Generated at: 2026-04-18T22:04:30.014Z
10
+ * Generated at: 2026-04-30T19:48:10.470Z
11
11
  *
12
12
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
13
13
  *
@@ -8,10 +8,18 @@ var _react = require("react");
8
8
  var _reactNative = require("react-native");
9
9
  var _jsxRuntime = require("react/jsx-runtime");
10
10
  /**
11
- * DraggableHeader - Reusable draggable component based on JsModal's working implementation
11
+ * DraggableHeader the canonical floating-tools drag handle.
12
12
  *
13
- * This component provides smooth drag functionality with proper boundary checking.
14
- * It uses the same proven pattern from JsModal that works reliably.
13
+ * Used by FloatingDevTools, JsModal's floating mode, and the perf-monitor
14
+ * bubble. Tracks drag via relative `dx`/`dy` (so parent padding/margins
15
+ * don't affect motion), discriminates tap-vs-drag by total distance, and
16
+ * clamps the final position to the container bounds.
17
+ *
18
+ * IMPORTANT: the PanResponder is created exactly ONCE per mount. All
19
+ * volatile props (callbacks, bounds, sizing) flow through `propsRef` so
20
+ * a parent re-render mid-gesture does NOT re-create the responder. Re-
21
+ * creating it during a drag detaches the active gesture and snaps the
22
+ * element back — that bug burned us once already.
15
23
  */
16
24
  const DraggableHeader = exports.DraggableHeader = /*#__PURE__*/(0, _react.memo)(function DraggableHeader({
17
25
  children,
@@ -19,6 +27,8 @@ const DraggableHeader = exports.DraggableHeader = /*#__PURE__*/(0, _react.memo)(
19
27
  onDragStart,
20
28
  onDragEnd,
21
29
  onTap,
30
+ onLongPress,
31
+ longPressDelay = 350,
22
32
  containerBounds = _reactNative.Dimensions.get("window"),
23
33
  elementSize = {
24
34
  width: 100,
@@ -29,113 +39,155 @@ const DraggableHeader = exports.DraggableHeader = /*#__PURE__*/(0, _react.memo)(
29
39
  y: 0
30
40
  },
31
41
  style,
32
- enabled = true
42
+ enabled = true,
43
+ maxOverflowX = 0
33
44
  }) {
34
45
  const isDraggingRef = (0, _react.useRef)(false);
35
46
  const dragDistanceRef = (0, _react.useRef)(0);
36
- const touchOffsetRef = (0, _react.useRef)({
47
+ const startPositionRef = (0, _react.useRef)({
37
48
  x: 0,
38
49
  y: 0
39
50
  });
40
- const panResponder = (0, _react.useMemo)(() => _reactNative.PanResponder.create({
41
- onStartShouldSetPanResponder: () => enabled,
42
- onMoveShouldSetPanResponder: (_, g) => enabled && (Math.abs(g.dx) > 1 || Math.abs(g.dy) > 1),
43
- onPanResponderTerminationRequest: () => false,
44
- // Resist touch steal
45
-
46
- onPanResponderGrant: evt => {
47
- isDraggingRef.current = false; // Start as not dragging
48
- dragDistanceRef.current = 0;
49
- // Don't call onDragStart immediately - wait to see if it's actually a drag
51
+ const longPressTimerRef = (0, _react.useRef)(null);
52
+ const longPressFiredRef = (0, _react.useRef)(false);
50
53
 
51
- // Record where inside the bubble the user touched
52
- touchOffsetRef.current = {
53
- x: evt.nativeEvent.locationX,
54
- y: evt.nativeEvent.locationY
55
- };
54
+ // Clear any pending long-press timer if the component unmounts mid-press,
55
+ // so the timeout never fires after we're gone.
56
+ (0, _react.useEffect)(() => {
57
+ return () => {
58
+ if (longPressTimerRef.current != null) {
59
+ clearTimeout(longPressTimerRef.current);
60
+ longPressTimerRef.current = null;
61
+ }
62
+ };
63
+ }, []);
56
64
 
57
- // Stop any running timing/spring and capture final XY
58
- position.stopAnimation(({
59
- x,
60
- y
61
- }) => {
62
- // Use that exact final value as the new offset for the gesture
63
- position.setOffset({
65
+ // Latest props in a ref. Keeps the PanResponder closure stable across
66
+ // re-renders so an active drag never gets its handlers swapped.
67
+ const propsRef = (0, _react.useRef)({
68
+ onDragStart,
69
+ onDragEnd,
70
+ onTap,
71
+ onLongPress,
72
+ longPressDelay,
73
+ containerBounds,
74
+ elementSize,
75
+ minPosition,
76
+ enabled,
77
+ maxOverflowX
78
+ });
79
+ propsRef.current = {
80
+ onDragStart,
81
+ onDragEnd,
82
+ onTap,
83
+ onLongPress,
84
+ longPressDelay,
85
+ containerBounds,
86
+ elementSize,
87
+ minPosition,
88
+ enabled,
89
+ maxOverflowX
90
+ };
91
+ const panResponder = (0, _react.useMemo)(() => {
92
+ const cancelLongPress = () => {
93
+ if (longPressTimerRef.current != null) {
94
+ clearTimeout(longPressTimerRef.current);
95
+ longPressTimerRef.current = null;
96
+ }
97
+ };
98
+ return _reactNative.PanResponder.create({
99
+ onStartShouldSetPanResponder: () => propsRef.current.enabled,
100
+ onMoveShouldSetPanResponder: (_, g) => propsRef.current.enabled && (Math.abs(g.dx) > 1 || Math.abs(g.dy) > 1),
101
+ onPanResponderTerminationRequest: () => false,
102
+ onPanResponderGrant: () => {
103
+ isDraggingRef.current = false;
104
+ dragDistanceRef.current = 0;
105
+ longPressFiredRef.current = false;
106
+ // Read position synchronously so the first onPanResponderMove
107
+ // has the correct startPosition. stopAnimation's callback can
108
+ // be async on iOS and races the first move event.
109
+ const sx = position.x.__getValue();
110
+ const sy = position.y.__getValue();
111
+ startPositionRef.current = {
112
+ x: sx,
113
+ y: sy
114
+ };
115
+ const {
116
+ onLongPress: lp,
117
+ longPressDelay: lpDelay
118
+ } = propsRef.current;
119
+ if (lp) {
120
+ longPressTimerRef.current = setTimeout(() => {
121
+ longPressTimerRef.current = null;
122
+ if (!isDraggingRef.current && dragDistanceRef.current <= 5) {
123
+ longPressFiredRef.current = true;
124
+ lp();
125
+ }
126
+ }, lpDelay);
127
+ }
128
+ },
129
+ onPanResponderMove: (_evt, gestureState) => {
130
+ const totalDistance = Math.abs(gestureState.dx) + Math.abs(gestureState.dy);
131
+ dragDistanceRef.current = totalDistance;
132
+ if (totalDistance > 5 && !isDraggingRef.current) {
133
+ isDraggingRef.current = true;
134
+ cancelLongPress();
135
+ propsRef.current.onDragStart?.();
136
+ }
137
+ // Use dx/dy relative movement so parent transforms don't affect
138
+ // the drag.
139
+ const x = startPositionRef.current.x + gestureState.dx;
140
+ const y = startPositionRef.current.y + gestureState.dy;
141
+ position.setValue({
64
142
  x,
65
143
  y
66
144
  });
67
- position.setValue({
68
- x: 0,
69
- y: 0
70
- });
71
- });
72
- },
73
- onPanResponderMove: (evt, gestureState) => {
74
- // Track total drag distance
75
- const totalDistance = Math.abs(gestureState.dx) + Math.abs(gestureState.dy);
76
- dragDistanceRef.current = totalDistance;
145
+ },
146
+ onPanResponderRelease: () => {
147
+ cancelLongPress();
148
+ const longPressed = longPressFiredRef.current;
149
+ longPressFiredRef.current = false;
150
+ const wasTap = dragDistanceRef.current <= 5 && !isDraggingRef.current;
151
+ if (wasTap) {
152
+ if (!longPressed) propsRef.current.onTap?.();
153
+ isDraggingRef.current = false;
154
+ return;
155
+ }
77
156
 
78
- // Mark as dragging if moved more than 5 pixels
79
- if (totalDistance > 5 && !isDraggingRef.current) {
80
- isDraggingRef.current = true;
81
- onDragStart?.(); // Call onDragStart only when we confirm it's a drag
82
- }
83
-
84
- // Use absolute finger anchoring for better grip feel
85
- const x = evt.nativeEvent.pageX - touchOffsetRef.current.x;
86
- const y = evt.nativeEvent.pageY - touchOffsetRef.current.y;
87
-
88
- // When using absolute follow, use the value directly (no offset on move)
89
- position.setOffset({
90
- x: 0,
91
- y: 0
92
- });
93
- position.setValue({
94
- x,
95
- y
96
- });
97
- },
98
- onPanResponderRelease: () => {
99
- // Get current position before any operations
100
- const currentX = Number(JSON.stringify(position.x));
101
- const currentY = Number(JSON.stringify(position.y));
102
-
103
- // Check if it was a tap (minimal movement)
104
- if (dragDistanceRef.current <= 5 && !isDraggingRef.current) {
105
- // Reset position to current values without offset for tap
106
- position.setOffset({
107
- x: 0,
108
- y: 0
109
- });
157
+ // Read the final position synchronously and clamp.
158
+ const cx = position.x.__getValue();
159
+ const cy = position.y.__getValue();
160
+ const {
161
+ containerBounds: bounds,
162
+ elementSize: size,
163
+ minPosition: minPos,
164
+ maxOverflowX: maxOX,
165
+ onDragEnd: dragEnd
166
+ } = propsRef.current;
167
+ const maxX = bounds.width - size.width + maxOX;
168
+ const clampedX = Math.max(minPos.x, Math.min(cx, maxX));
169
+ const clampedY = Math.max(minPos.y, Math.min(cy, bounds.height - size.height));
110
170
  position.setValue({
111
- x: currentX,
112
- y: currentY
171
+ x: clampedX,
172
+ y: clampedY
173
+ });
174
+ dragEnd?.({
175
+ x: clampedX,
176
+ y: clampedY
113
177
  });
114
- onTap?.();
115
- // No need to call onDragEnd since onDragStart was never called for a tap
116
- return;
178
+ isDraggingRef.current = false;
179
+ },
180
+ onPanResponderTerminate: () => {
181
+ cancelLongPress();
182
+ longPressFiredRef.current = false;
183
+ isDraggingRef.current = false;
117
184
  }
118
-
119
- // Apply boundary constraints
120
- const clampedX = Math.max(minPosition.x, Math.min(currentX, containerBounds.width - elementSize.width));
121
- const clampedY = Math.max(minPosition.y, Math.min(currentY, containerBounds.height - elementSize.height));
122
-
123
- // Set to clamped position
124
- position.setValue({
125
- x: clampedX,
126
- y: clampedY
127
- });
128
- onDragEnd?.({
129
- x: clampedX,
130
- y: clampedY
131
- });
132
- isDraggingRef.current = false;
133
- },
134
- onPanResponderTerminate: () => {
135
- isDraggingRef.current = false;
136
- // No need to flattenOffset since we're using absolute positioning
137
- }
138
- }), [enabled, position, onDragStart, onDragEnd, onTap, containerBounds, elementSize, minPosition]);
185
+ });
186
+ // The responder is stable for the component's lifetime — props are
187
+ // read from a ref. Position is the only Animated.Value reference and
188
+ // is itself stable (created via useRef in the parent hook).
189
+ // eslint-disable-next-line react-hooks/exhaustive-deps
190
+ }, [position]);
139
191
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
140
192
  style: style,
141
193
  ...panResponder.panHandlers,
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.GripVerticalIcon = GripVerticalIcon;
7
+ var _reactNative = require("react-native");
8
+ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
9
+ var _gameUIColors = require("../gameUI/constants/gameUIColors.js");
10
+ var _jsxRuntime = require("react/jsx-runtime");
11
+ /**
12
+ * Two columns of three dots — the standard "grabber" affordance used by
13
+ * floating bubbles in this suite (FloatingDevTools, perf-monitor HUD).
14
+ *
15
+ * Sizing comes from `getGripIconLayout` in `@buoy-gg/floating-tools-core`
16
+ * so all bubbles share identical proportions.
17
+ */
18
+ function GripVerticalIcon({
19
+ size = 24,
20
+ color = _gameUIColors.buoyColors.textSecondary + "CC"
21
+ }) {
22
+ const layout = (0, _floatingToolsCore.getGripIconLayout)(size);
23
+ const containerStyle = {
24
+ width: size,
25
+ height: size,
26
+ flexDirection: "row",
27
+ alignItems: "center",
28
+ justifyContent: "center"
29
+ };
30
+ const columnStyle = {
31
+ flexDirection: "column",
32
+ alignItems: "center",
33
+ justifyContent: "center",
34
+ marginHorizontal: layout.columnGap / 2
35
+ };
36
+ const dotStyle = {
37
+ width: layout.dotSize,
38
+ height: layout.dotSize,
39
+ borderRadius: layout.dotSize / 2,
40
+ backgroundColor: color,
41
+ marginVertical: layout.rowGap / 2
42
+ };
43
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
44
+ style: containerStyle,
45
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
46
+ style: columnStyle,
47
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
48
+ style: dotStyle
49
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
50
+ style: dotStyle
51
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
52
+ style: dotStyle
53
+ })]
54
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
55
+ style: columnStyle,
56
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
57
+ style: dotStyle
58
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
59
+ style: dotStyle
60
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
61
+ style: dotStyle
62
+ })]
63
+ })]
64
+ });
65
+ }
@@ -177,6 +177,12 @@ Object.defineProperty(exports, "FilterSection", {
177
177
  return _FilterComponents.FilterSection;
178
178
  }
179
179
  });
180
+ Object.defineProperty(exports, "GripVerticalIcon", {
181
+ enumerable: true,
182
+ get: function () {
183
+ return _GripVerticalIcon.GripVerticalIcon;
184
+ }
185
+ });
180
186
  Object.defineProperty(exports, "HeaderSearchButton", {
181
187
  enumerable: true,
182
188
  get: function () {
@@ -356,6 +362,7 @@ var _StatusIndicator = require("./StatusIndicator.js");
356
362
  var _TimeDisplay = require("./TimeDisplay.js");
357
363
  var _DetailView = require("./DetailView.js");
358
364
  var _DraggableHeader = require("./DraggableHeader.js");
365
+ var _GripVerticalIcon = require("./GripVerticalIcon.js");
359
366
  var _CollapsibleSection = require("./CollapsibleSection.js");
360
367
  var _DataInspector = require("./DataInspector.js");
361
368
  var _SearchBar = require("./SearchBar.js");
@@ -51,10 +51,22 @@ function getStorageDir() {
51
51
  if (!FileSystem?.documentDirectory) return null;
52
52
  return `${FileSystem.documentDirectory}${STORAGE_DIR_NAME}/`;
53
53
  }
54
+
55
+ // Android's expo-file-system resolves write paths as URIs and decodes %2F back
56
+ // to /, which turns slashed keys into non-existent subdirectories. Escape /
57
+ // before encodeURIComponent so the filename stays flat. JSON-Pointer-style
58
+ // escape is collision-free: ~ -> ~0, then / -> ~1. Decode reverses ~1 -> /
59
+ // before ~0 -> ~ so a key containing the literal "~1" round-trips correctly.
60
+ function encodeKeyForFs(key) {
61
+ return encodeURIComponent(key.replace(/~/g, "~0").replace(/\//g, "~1"));
62
+ }
63
+ function decodeKeyFromFs(encoded) {
64
+ return decodeURIComponent(encoded).replace(/~1/g, "/").replace(/~0/g, "~");
65
+ }
54
66
  function getFilePath(key) {
55
67
  const dir = getStorageDir();
56
68
  if (!dir) return null;
57
- return `${dir}${encodeURIComponent(key)}.json`;
69
+ return `${dir}${encodeKeyForFs(key)}.json`;
58
70
  }
59
71
 
60
72
  /**
@@ -244,7 +256,7 @@ const fileSystemStorage = {
244
256
  const dirInfo = await fs.getInfoAsync(storageDir);
245
257
  if (!dirInfo.exists) return [];
246
258
  const files = await fs.readDirectoryAsync(storageDir);
247
- return files.filter(f => f.endsWith(".json")).map(f => decodeURIComponent(f.replace(".json", "")));
259
+ return files.filter(f => f.endsWith(".json")).map(f => decodeKeyFromFs(f.replace(".json", "")));
248
260
  } catch {
249
261
  return [];
250
262
  }
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Auto-generated safe area implementation
5
5
  * Detected: none
6
- * Generated at: 2026-04-18T22:04:30.014Z
6
+ * Generated at: 2026-04-30T19:48:10.470Z
7
7
  *
8
8
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
9
9
  *
@@ -1,13 +1,21 @@
1
1
  "use strict";
2
2
 
3
- import { useRef, useMemo, memo } from "react";
3
+ import { useRef, useMemo, useEffect, memo } from "react";
4
4
  import { View, PanResponder, Dimensions } from "react-native";
5
5
  import { jsx as _jsx } from "react/jsx-runtime";
6
6
  /**
7
- * DraggableHeader - Reusable draggable component based on JsModal's working implementation
7
+ * DraggableHeader the canonical floating-tools drag handle.
8
8
  *
9
- * This component provides smooth drag functionality with proper boundary checking.
10
- * It uses the same proven pattern from JsModal that works reliably.
9
+ * Used by FloatingDevTools, JsModal's floating mode, and the perf-monitor
10
+ * bubble. Tracks drag via relative `dx`/`dy` (so parent padding/margins
11
+ * don't affect motion), discriminates tap-vs-drag by total distance, and
12
+ * clamps the final position to the container bounds.
13
+ *
14
+ * IMPORTANT: the PanResponder is created exactly ONCE per mount. All
15
+ * volatile props (callbacks, bounds, sizing) flow through `propsRef` so
16
+ * a parent re-render mid-gesture does NOT re-create the responder. Re-
17
+ * creating it during a drag detaches the active gesture and snaps the
18
+ * element back — that bug burned us once already.
11
19
  */
12
20
  export const DraggableHeader = /*#__PURE__*/memo(function DraggableHeader({
13
21
  children,
@@ -15,6 +23,8 @@ export const DraggableHeader = /*#__PURE__*/memo(function DraggableHeader({
15
23
  onDragStart,
16
24
  onDragEnd,
17
25
  onTap,
26
+ onLongPress,
27
+ longPressDelay = 350,
18
28
  containerBounds = Dimensions.get("window"),
19
29
  elementSize = {
20
30
  width: 100,
@@ -25,113 +35,155 @@ export const DraggableHeader = /*#__PURE__*/memo(function DraggableHeader({
25
35
  y: 0
26
36
  },
27
37
  style,
28
- enabled = true
38
+ enabled = true,
39
+ maxOverflowX = 0
29
40
  }) {
30
41
  const isDraggingRef = useRef(false);
31
42
  const dragDistanceRef = useRef(0);
32
- const touchOffsetRef = useRef({
43
+ const startPositionRef = useRef({
33
44
  x: 0,
34
45
  y: 0
35
46
  });
36
- const panResponder = useMemo(() => PanResponder.create({
37
- onStartShouldSetPanResponder: () => enabled,
38
- onMoveShouldSetPanResponder: (_, g) => enabled && (Math.abs(g.dx) > 1 || Math.abs(g.dy) > 1),
39
- onPanResponderTerminationRequest: () => false,
40
- // Resist touch steal
41
-
42
- onPanResponderGrant: evt => {
43
- isDraggingRef.current = false; // Start as not dragging
44
- dragDistanceRef.current = 0;
45
- // Don't call onDragStart immediately - wait to see if it's actually a drag
47
+ const longPressTimerRef = useRef(null);
48
+ const longPressFiredRef = useRef(false);
46
49
 
47
- // Record where inside the bubble the user touched
48
- touchOffsetRef.current = {
49
- x: evt.nativeEvent.locationX,
50
- y: evt.nativeEvent.locationY
51
- };
50
+ // Clear any pending long-press timer if the component unmounts mid-press,
51
+ // so the timeout never fires after we're gone.
52
+ useEffect(() => {
53
+ return () => {
54
+ if (longPressTimerRef.current != null) {
55
+ clearTimeout(longPressTimerRef.current);
56
+ longPressTimerRef.current = null;
57
+ }
58
+ };
59
+ }, []);
52
60
 
53
- // Stop any running timing/spring and capture final XY
54
- position.stopAnimation(({
55
- x,
56
- y
57
- }) => {
58
- // Use that exact final value as the new offset for the gesture
59
- position.setOffset({
61
+ // Latest props in a ref. Keeps the PanResponder closure stable across
62
+ // re-renders so an active drag never gets its handlers swapped.
63
+ const propsRef = useRef({
64
+ onDragStart,
65
+ onDragEnd,
66
+ onTap,
67
+ onLongPress,
68
+ longPressDelay,
69
+ containerBounds,
70
+ elementSize,
71
+ minPosition,
72
+ enabled,
73
+ maxOverflowX
74
+ });
75
+ propsRef.current = {
76
+ onDragStart,
77
+ onDragEnd,
78
+ onTap,
79
+ onLongPress,
80
+ longPressDelay,
81
+ containerBounds,
82
+ elementSize,
83
+ minPosition,
84
+ enabled,
85
+ maxOverflowX
86
+ };
87
+ const panResponder = useMemo(() => {
88
+ const cancelLongPress = () => {
89
+ if (longPressTimerRef.current != null) {
90
+ clearTimeout(longPressTimerRef.current);
91
+ longPressTimerRef.current = null;
92
+ }
93
+ };
94
+ return PanResponder.create({
95
+ onStartShouldSetPanResponder: () => propsRef.current.enabled,
96
+ onMoveShouldSetPanResponder: (_, g) => propsRef.current.enabled && (Math.abs(g.dx) > 1 || Math.abs(g.dy) > 1),
97
+ onPanResponderTerminationRequest: () => false,
98
+ onPanResponderGrant: () => {
99
+ isDraggingRef.current = false;
100
+ dragDistanceRef.current = 0;
101
+ longPressFiredRef.current = false;
102
+ // Read position synchronously so the first onPanResponderMove
103
+ // has the correct startPosition. stopAnimation's callback can
104
+ // be async on iOS and races the first move event.
105
+ const sx = position.x.__getValue();
106
+ const sy = position.y.__getValue();
107
+ startPositionRef.current = {
108
+ x: sx,
109
+ y: sy
110
+ };
111
+ const {
112
+ onLongPress: lp,
113
+ longPressDelay: lpDelay
114
+ } = propsRef.current;
115
+ if (lp) {
116
+ longPressTimerRef.current = setTimeout(() => {
117
+ longPressTimerRef.current = null;
118
+ if (!isDraggingRef.current && dragDistanceRef.current <= 5) {
119
+ longPressFiredRef.current = true;
120
+ lp();
121
+ }
122
+ }, lpDelay);
123
+ }
124
+ },
125
+ onPanResponderMove: (_evt, gestureState) => {
126
+ const totalDistance = Math.abs(gestureState.dx) + Math.abs(gestureState.dy);
127
+ dragDistanceRef.current = totalDistance;
128
+ if (totalDistance > 5 && !isDraggingRef.current) {
129
+ isDraggingRef.current = true;
130
+ cancelLongPress();
131
+ propsRef.current.onDragStart?.();
132
+ }
133
+ // Use dx/dy relative movement so parent transforms don't affect
134
+ // the drag.
135
+ const x = startPositionRef.current.x + gestureState.dx;
136
+ const y = startPositionRef.current.y + gestureState.dy;
137
+ position.setValue({
60
138
  x,
61
139
  y
62
140
  });
63
- position.setValue({
64
- x: 0,
65
- y: 0
66
- });
67
- });
68
- },
69
- onPanResponderMove: (evt, gestureState) => {
70
- // Track total drag distance
71
- const totalDistance = Math.abs(gestureState.dx) + Math.abs(gestureState.dy);
72
- dragDistanceRef.current = totalDistance;
141
+ },
142
+ onPanResponderRelease: () => {
143
+ cancelLongPress();
144
+ const longPressed = longPressFiredRef.current;
145
+ longPressFiredRef.current = false;
146
+ const wasTap = dragDistanceRef.current <= 5 && !isDraggingRef.current;
147
+ if (wasTap) {
148
+ if (!longPressed) propsRef.current.onTap?.();
149
+ isDraggingRef.current = false;
150
+ return;
151
+ }
73
152
 
74
- // Mark as dragging if moved more than 5 pixels
75
- if (totalDistance > 5 && !isDraggingRef.current) {
76
- isDraggingRef.current = true;
77
- onDragStart?.(); // Call onDragStart only when we confirm it's a drag
78
- }
79
-
80
- // Use absolute finger anchoring for better grip feel
81
- const x = evt.nativeEvent.pageX - touchOffsetRef.current.x;
82
- const y = evt.nativeEvent.pageY - touchOffsetRef.current.y;
83
-
84
- // When using absolute follow, use the value directly (no offset on move)
85
- position.setOffset({
86
- x: 0,
87
- y: 0
88
- });
89
- position.setValue({
90
- x,
91
- y
92
- });
93
- },
94
- onPanResponderRelease: () => {
95
- // Get current position before any operations
96
- const currentX = Number(JSON.stringify(position.x));
97
- const currentY = Number(JSON.stringify(position.y));
98
-
99
- // Check if it was a tap (minimal movement)
100
- if (dragDistanceRef.current <= 5 && !isDraggingRef.current) {
101
- // Reset position to current values without offset for tap
102
- position.setOffset({
103
- x: 0,
104
- y: 0
105
- });
153
+ // Read the final position synchronously and clamp.
154
+ const cx = position.x.__getValue();
155
+ const cy = position.y.__getValue();
156
+ const {
157
+ containerBounds: bounds,
158
+ elementSize: size,
159
+ minPosition: minPos,
160
+ maxOverflowX: maxOX,
161
+ onDragEnd: dragEnd
162
+ } = propsRef.current;
163
+ const maxX = bounds.width - size.width + maxOX;
164
+ const clampedX = Math.max(minPos.x, Math.min(cx, maxX));
165
+ const clampedY = Math.max(minPos.y, Math.min(cy, bounds.height - size.height));
106
166
  position.setValue({
107
- x: currentX,
108
- y: currentY
167
+ x: clampedX,
168
+ y: clampedY
169
+ });
170
+ dragEnd?.({
171
+ x: clampedX,
172
+ y: clampedY
109
173
  });
110
- onTap?.();
111
- // No need to call onDragEnd since onDragStart was never called for a tap
112
- return;
174
+ isDraggingRef.current = false;
175
+ },
176
+ onPanResponderTerminate: () => {
177
+ cancelLongPress();
178
+ longPressFiredRef.current = false;
179
+ isDraggingRef.current = false;
113
180
  }
114
-
115
- // Apply boundary constraints
116
- const clampedX = Math.max(minPosition.x, Math.min(currentX, containerBounds.width - elementSize.width));
117
- const clampedY = Math.max(minPosition.y, Math.min(currentY, containerBounds.height - elementSize.height));
118
-
119
- // Set to clamped position
120
- position.setValue({
121
- x: clampedX,
122
- y: clampedY
123
- });
124
- onDragEnd?.({
125
- x: clampedX,
126
- y: clampedY
127
- });
128
- isDraggingRef.current = false;
129
- },
130
- onPanResponderTerminate: () => {
131
- isDraggingRef.current = false;
132
- // No need to flattenOffset since we're using absolute positioning
133
- }
134
- }), [enabled, position, onDragStart, onDragEnd, onTap, containerBounds, elementSize, minPosition]);
181
+ });
182
+ // The responder is stable for the component's lifetime — props are
183
+ // read from a ref. Position is the only Animated.Value reference and
184
+ // is itself stable (created via useRef in the parent hook).
185
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
+ }, [position]);
135
187
  return /*#__PURE__*/_jsx(View, {
136
188
  style: style,
137
189
  ...panResponder.panHandlers,
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ import { View } from "react-native";
4
+ import { getGripIconLayout } from "@buoy-gg/floating-tools-core";
5
+ import { buoyColors } from "../gameUI/constants/gameUIColors.js";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ /**
8
+ * Two columns of three dots — the standard "grabber" affordance used by
9
+ * floating bubbles in this suite (FloatingDevTools, perf-monitor HUD).
10
+ *
11
+ * Sizing comes from `getGripIconLayout` in `@buoy-gg/floating-tools-core`
12
+ * so all bubbles share identical proportions.
13
+ */
14
+ export function GripVerticalIcon({
15
+ size = 24,
16
+ color = buoyColors.textSecondary + "CC"
17
+ }) {
18
+ const layout = getGripIconLayout(size);
19
+ const containerStyle = {
20
+ width: size,
21
+ height: size,
22
+ flexDirection: "row",
23
+ alignItems: "center",
24
+ justifyContent: "center"
25
+ };
26
+ const columnStyle = {
27
+ flexDirection: "column",
28
+ alignItems: "center",
29
+ justifyContent: "center",
30
+ marginHorizontal: layout.columnGap / 2
31
+ };
32
+ const dotStyle = {
33
+ width: layout.dotSize,
34
+ height: layout.dotSize,
35
+ borderRadius: layout.dotSize / 2,
36
+ backgroundColor: color,
37
+ marginVertical: layout.rowGap / 2
38
+ };
39
+ return /*#__PURE__*/_jsxs(View, {
40
+ style: containerStyle,
41
+ children: [/*#__PURE__*/_jsxs(View, {
42
+ style: columnStyle,
43
+ children: [/*#__PURE__*/_jsx(View, {
44
+ style: dotStyle
45
+ }), /*#__PURE__*/_jsx(View, {
46
+ style: dotStyle
47
+ }), /*#__PURE__*/_jsx(View, {
48
+ style: dotStyle
49
+ })]
50
+ }), /*#__PURE__*/_jsxs(View, {
51
+ style: columnStyle,
52
+ children: [/*#__PURE__*/_jsx(View, {
53
+ style: dotStyle
54
+ }), /*#__PURE__*/_jsx(View, {
55
+ style: dotStyle
56
+ }), /*#__PURE__*/_jsx(View, {
57
+ style: dotStyle
58
+ })]
59
+ })]
60
+ });
61
+ }
@@ -23,6 +23,7 @@ export { StatusIndicator } from "./StatusIndicator.js";
23
23
  export { TimeDisplay } from "./TimeDisplay.js";
24
24
  export { DetailView } from "./DetailView.js";
25
25
  export { DraggableHeader } from "./DraggableHeader.js";
26
+ export { GripVerticalIcon } from "./GripVerticalIcon.js";
26
27
  export { CollapsibleSection } from "./CollapsibleSection.js";
27
28
  export { DataInspector } from "./DataInspector.js";
28
29
  export { SearchBar } from "./SearchBar.js";
@@ -45,10 +45,22 @@ function getStorageDir() {
45
45
  if (!FileSystem?.documentDirectory) return null;
46
46
  return `${FileSystem.documentDirectory}${STORAGE_DIR_NAME}/`;
47
47
  }
48
+
49
+ // Android's expo-file-system resolves write paths as URIs and decodes %2F back
50
+ // to /, which turns slashed keys into non-existent subdirectories. Escape /
51
+ // before encodeURIComponent so the filename stays flat. JSON-Pointer-style
52
+ // escape is collision-free: ~ -> ~0, then / -> ~1. Decode reverses ~1 -> /
53
+ // before ~0 -> ~ so a key containing the literal "~1" round-trips correctly.
54
+ function encodeKeyForFs(key) {
55
+ return encodeURIComponent(key.replace(/~/g, "~0").replace(/\//g, "~1"));
56
+ }
57
+ function decodeKeyFromFs(encoded) {
58
+ return decodeURIComponent(encoded).replace(/~1/g, "/").replace(/~0/g, "~");
59
+ }
48
60
  function getFilePath(key) {
49
61
  const dir = getStorageDir();
50
62
  if (!dir) return null;
51
- return `${dir}${encodeURIComponent(key)}.json`;
63
+ return `${dir}${encodeKeyForFs(key)}.json`;
52
64
  }
53
65
 
54
66
  /**
@@ -238,7 +250,7 @@ const fileSystemStorage = {
238
250
  const dirInfo = await fs.getInfoAsync(storageDir);
239
251
  if (!dirInfo.exists) return [];
240
252
  const files = await fs.readDirectoryAsync(storageDir);
241
- return files.filter(f => f.endsWith(".json")).map(f => decodeURIComponent(f.replace(".json", "")));
253
+ return files.filter(f => f.endsWith(".json")).map(f => decodeKeyFromFs(f.replace(".json", "")));
242
254
  } catch {
243
255
  return [];
244
256
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auto-generated safe area implementation
3
3
  * Detected: none
4
- * Generated at: 2026-04-18T22:04:30.014Z
4
+ * Generated at: 2026-04-30T19:48:10.470Z
5
5
  *
6
6
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
7
7
  *
@@ -1,5 +1,5 @@
1
- import { ReactNode } from "react";
2
- import { Animated, ViewStyle, StyleProp } from "react-native";
1
+ import { type ReactNode } from "react";
2
+ import { Animated, type ViewStyle, type StyleProp } from "react-native";
3
3
  interface DraggableHeaderProps {
4
4
  children: ReactNode;
5
5
  position: Animated.ValueXY;
@@ -9,6 +9,12 @@ interface DraggableHeaderProps {
9
9
  y: number;
10
10
  }) => void;
11
11
  onTap?: () => void;
12
+ /**
13
+ * Fired when the user holds the surface without moving for
14
+ * `longPressDelay` ms. Cancelled by any drag motion.
15
+ */
16
+ onLongPress?: () => void;
17
+ longPressDelay?: number;
12
18
  containerBounds?: {
13
19
  width: number;
14
20
  height: number;
@@ -23,12 +29,26 @@ interface DraggableHeaderProps {
23
29
  };
24
30
  style?: StyleProp<ViewStyle>;
25
31
  enabled?: boolean;
32
+ /**
33
+ * Allow the element to drift past the right edge by this many pixels.
34
+ * Used to support drag-past-edge "hide" gestures where the bubble can
35
+ * sit mostly off-screen with only a peek visible.
36
+ */
37
+ maxOverflowX?: number;
26
38
  }
27
39
  /**
28
- * DraggableHeader - Reusable draggable component based on JsModal's working implementation
40
+ * DraggableHeader the canonical floating-tools drag handle.
29
41
  *
30
- * This component provides smooth drag functionality with proper boundary checking.
31
- * It uses the same proven pattern from JsModal that works reliably.
42
+ * Used by FloatingDevTools, JsModal's floating mode, and the perf-monitor
43
+ * bubble. Tracks drag via relative `dx`/`dy` (so parent padding/margins
44
+ * don't affect motion), discriminates tap-vs-drag by total distance, and
45
+ * clamps the final position to the container bounds.
46
+ *
47
+ * IMPORTANT: the PanResponder is created exactly ONCE per mount. All
48
+ * volatile props (callbacks, bounds, sizing) flow through `propsRef` so
49
+ * a parent re-render mid-gesture does NOT re-create the responder. Re-
50
+ * creating it during a drag detaches the active gesture and snaps the
51
+ * element back — that bug burned us once already.
32
52
  */
33
53
  export declare const DraggableHeader: import("react").NamedExoticComponent<DraggableHeaderProps>;
34
54
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"DraggableHeader.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/DraggableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAGL,QAAQ,EAER,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,4DAsH1B,CAAC"}
1
+ {"version":3,"file":"DraggableHeader.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/DraggableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzE,OAAO,EAGL,QAAQ,EAER,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,4DA0K1B,CAAC"}
@@ -0,0 +1,14 @@
1
+ interface GripVerticalIconProps {
2
+ size?: number;
3
+ color?: string;
4
+ }
5
+ /**
6
+ * Two columns of three dots — the standard "grabber" affordance used by
7
+ * floating bubbles in this suite (FloatingDevTools, perf-monitor HUD).
8
+ *
9
+ * Sizing comes from `getGripIconLayout` in `@buoy-gg/floating-tools-core`
10
+ * so all bubbles share identical proportions.
11
+ */
12
+ export declare function GripVerticalIcon({ size, color, }: GripVerticalIconProps): import("react").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=GripVerticalIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GripVerticalIcon.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/GripVerticalIcon.tsx"],"names":[],"mappings":"AAIA,UAAU,qBAAqB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,IAAS,EACT,KAAuC,GACxC,EAAE,qBAAqB,+BAwCvB"}
@@ -23,6 +23,7 @@ export { StatusIndicator } from "./StatusIndicator";
23
23
  export { TimeDisplay } from "./TimeDisplay";
24
24
  export { DetailView } from "./DetailView";
25
25
  export { DraggableHeader } from "./DraggableHeader";
26
+ export { GripVerticalIcon } from "./GripVerticalIcon";
26
27
  export { CollapsibleSection } from "./CollapsibleSection";
27
28
  export { DataInspector } from "./DataInspector";
28
29
  export { SearchBar } from "./SearchBar";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistentStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/persistentStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAGD,KAAK,cAAc,GAAG,YAAY,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAmjBhF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAmC/B,CAAC;AAEF;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,cAAc,CAAC,CAGrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
1
+ {"version":3,"file":"persistentStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/persistentStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAGD,KAAK,cAAc,GAAG,YAAY,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAgkBhF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAmC/B,CAAC;AAEF;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,cAAc,CAAC,CAGrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auto-generated safe area implementation
3
3
  * Detected: none
4
- * Generated at: 2026-04-18T22:04:30.014Z
4
+ * Generated at: 2026-04-30T19:48:10.470Z
5
5
  *
6
6
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
7
7
  *
@@ -1,5 +1,5 @@
1
- import { ReactNode } from "react";
2
- import { Animated, ViewStyle, StyleProp } from "react-native";
1
+ import { type ReactNode } from "react";
2
+ import { Animated, type ViewStyle, type StyleProp } from "react-native";
3
3
  interface DraggableHeaderProps {
4
4
  children: ReactNode;
5
5
  position: Animated.ValueXY;
@@ -9,6 +9,12 @@ interface DraggableHeaderProps {
9
9
  y: number;
10
10
  }) => void;
11
11
  onTap?: () => void;
12
+ /**
13
+ * Fired when the user holds the surface without moving for
14
+ * `longPressDelay` ms. Cancelled by any drag motion.
15
+ */
16
+ onLongPress?: () => void;
17
+ longPressDelay?: number;
12
18
  containerBounds?: {
13
19
  width: number;
14
20
  height: number;
@@ -23,12 +29,26 @@ interface DraggableHeaderProps {
23
29
  };
24
30
  style?: StyleProp<ViewStyle>;
25
31
  enabled?: boolean;
32
+ /**
33
+ * Allow the element to drift past the right edge by this many pixels.
34
+ * Used to support drag-past-edge "hide" gestures where the bubble can
35
+ * sit mostly off-screen with only a peek visible.
36
+ */
37
+ maxOverflowX?: number;
26
38
  }
27
39
  /**
28
- * DraggableHeader - Reusable draggable component based on JsModal's working implementation
40
+ * DraggableHeader the canonical floating-tools drag handle.
29
41
  *
30
- * This component provides smooth drag functionality with proper boundary checking.
31
- * It uses the same proven pattern from JsModal that works reliably.
42
+ * Used by FloatingDevTools, JsModal's floating mode, and the perf-monitor
43
+ * bubble. Tracks drag via relative `dx`/`dy` (so parent padding/margins
44
+ * don't affect motion), discriminates tap-vs-drag by total distance, and
45
+ * clamps the final position to the container bounds.
46
+ *
47
+ * IMPORTANT: the PanResponder is created exactly ONCE per mount. All
48
+ * volatile props (callbacks, bounds, sizing) flow through `propsRef` so
49
+ * a parent re-render mid-gesture does NOT re-create the responder. Re-
50
+ * creating it during a drag detaches the active gesture and snaps the
51
+ * element back — that bug burned us once already.
32
52
  */
33
53
  export declare const DraggableHeader: import("react").NamedExoticComponent<DraggableHeaderProps>;
34
54
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"DraggableHeader.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/DraggableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAGL,QAAQ,EAER,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,4DAsH1B,CAAC"}
1
+ {"version":3,"file":"DraggableHeader.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/DraggableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzE,OAAO,EAGL,QAAQ,EAER,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,4DA0K1B,CAAC"}
@@ -0,0 +1,14 @@
1
+ interface GripVerticalIconProps {
2
+ size?: number;
3
+ color?: string;
4
+ }
5
+ /**
6
+ * Two columns of three dots — the standard "grabber" affordance used by
7
+ * floating bubbles in this suite (FloatingDevTools, perf-monitor HUD).
8
+ *
9
+ * Sizing comes from `getGripIconLayout` in `@buoy-gg/floating-tools-core`
10
+ * so all bubbles share identical proportions.
11
+ */
12
+ export declare function GripVerticalIcon({ size, color, }: GripVerticalIconProps): import("react").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=GripVerticalIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GripVerticalIcon.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/GripVerticalIcon.tsx"],"names":[],"mappings":"AAIA,UAAU,qBAAqB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,IAAS,EACT,KAAuC,GACxC,EAAE,qBAAqB,+BAwCvB"}
@@ -23,6 +23,7 @@ export { StatusIndicator } from "./StatusIndicator";
23
23
  export { TimeDisplay } from "./TimeDisplay";
24
24
  export { DetailView } from "./DetailView";
25
25
  export { DraggableHeader } from "./DraggableHeader";
26
+ export { GripVerticalIcon } from "./GripVerticalIcon";
26
27
  export { CollapsibleSection } from "./CollapsibleSection";
27
28
  export { DataInspector } from "./DataInspector";
28
29
  export { SearchBar } from "./SearchBar";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistentStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/persistentStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAGD,KAAK,cAAc,GAAG,YAAY,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAmjBhF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAmC/B,CAAC;AAEF;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,cAAc,CAAC,CAGrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
1
+ {"version":3,"file":"persistentStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/persistentStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAGD,KAAK,cAAc,GAAG,YAAY,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAgkBhF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAmC/B,CAAC;AAEF;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,cAAc,CAAC,CAGrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/shared-ui",
3
- "version": "2.1.11",
3
+ "version": "2.1.13",
4
4
  "description": "Shared UI components, hooks, and utilities",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -114,19 +114,11 @@
114
114
  "scripts"
115
115
  ],
116
116
  "sideEffects": false,
117
- "scripts": {
118
- "postinstall": "node scripts/detect-clipboard.js && node scripts/detect-safe-area.js",
119
- "build": "bob build",
120
- "typecheck": "tsc --noEmit",
121
- "prepublishOnly": "REACT_BUOY_FALLBACK=1 node scripts/detect-safe-area.js && bob build",
122
- "clean": "rimraf lib",
123
- "test": "pnpm run typecheck"
124
- },
125
117
  "dependencies": {
126
- "@buoy-gg/floating-tools-core": "workspace:*"
118
+ "@buoy-gg/floating-tools-core": "2.1.13"
127
119
  },
128
120
  "peerDependencies": {
129
- "@buoy-gg/license": "2.1.11",
121
+ "@buoy-gg/license": "2.1.13",
130
122
  "@react-native-clipboard/clipboard": "*",
131
123
  "expo-clipboard": "*",
132
124
  "expo-file-system": "*",
@@ -149,12 +141,12 @@
149
141
  }
150
142
  },
151
143
  "devDependencies": {
152
- "@buoy-gg/license": "workspace:*",
153
144
  "@types/react": "^19.1.0",
154
145
  "@types/react-native": "^0.73.0",
155
146
  "expo-clipboard": "~7.1.5",
156
147
  "react-native-safe-area-context": "^5.6.2",
157
- "typescript": "~5.8.3"
148
+ "typescript": "~5.8.3",
149
+ "@buoy-gg/license": "2.1.13"
158
150
  },
159
151
  "react-native-builder-bob": {
160
152
  "source": "src",
@@ -189,5 +181,12 @@
189
181
  "publishConfig": {
190
182
  "access": "public",
191
183
  "tag": "latest"
184
+ },
185
+ "scripts": {
186
+ "postinstall": "node scripts/detect-clipboard.js && node scripts/detect-safe-area.js",
187
+ "build": "bob build",
188
+ "typecheck": "tsc --noEmit",
189
+ "clean": "rimraf lib",
190
+ "test": "pnpm run typecheck"
192
191
  }
193
- }
192
+ }