@buoy-gg/floating-tools-core 7.0.39 → 7.0.41

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 (46) hide show
  1. package/lib/commonjs/icons/ask-buoy-icon.native.js +1 -0
  2. package/lib/commonjs/icons/bif-raster-icon.native.js +1 -0
  3. package/lib/commonjs/icons/console-icon.native.js +1 -0
  4. package/lib/commonjs/icons/generated/icon-data.g.js +69 -2
  5. package/lib/commonjs/icons/impersonate-icon.native.js +1 -0
  6. package/lib/commonjs/icons/jotai-icon.native.js +1 -0
  7. package/lib/commonjs/icons/notifications-icon.js +15 -0
  8. package/lib/commonjs/icons/query-icon.native.js +1 -0
  9. package/lib/commonjs/icons/redux-icon.native.js +1 -0
  10. package/lib/commonjs/icons/scenarios-icon.native.js +1 -0
  11. package/lib/commonjs/icons/sentry-icon.native.js +1 -0
  12. package/lib/commonjs/icons/time-machine-icon.native.js +1 -0
  13. package/lib/commonjs/icons/zustand-icon.native.js +1 -0
  14. package/lib/commonjs/index.js +17 -2
  15. package/lib/module/icons/ask-buoy-icon.native.js +1 -0
  16. package/lib/module/icons/bif-raster-icon.native.js +1 -0
  17. package/lib/module/icons/console-icon.native.js +1 -0
  18. package/lib/module/icons/generated/icon-data.g.js +67 -0
  19. package/lib/module/icons/impersonate-icon.native.js +1 -0
  20. package/lib/module/icons/jotai-icon.native.js +1 -0
  21. package/lib/module/icons/notifications-icon.js +6 -0
  22. package/lib/module/icons/query-icon.native.js +1 -0
  23. package/lib/module/icons/redux-icon.native.js +1 -0
  24. package/lib/module/icons/scenarios-icon.native.js +1 -0
  25. package/lib/module/icons/sentry-icon.native.js +1 -0
  26. package/lib/module/icons/time-machine-icon.native.js +1 -0
  27. package/lib/module/icons/zustand-icon.native.js +1 -0
  28. package/lib/module/index.js +2 -1
  29. package/lib/typescript/commonjs/icons/generated/icon-data.g.d.ts +2 -0
  30. package/lib/typescript/commonjs/icons/generated/icon-data.g.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/icons/notifications-icon.d.ts +3 -0
  32. package/lib/typescript/commonjs/icons/notifications-icon.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/index.d.ts +1 -0
  34. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  35. package/lib/typescript/module/icons/generated/icon-data.g.d.ts +2 -0
  36. package/lib/typescript/module/icons/generated/icon-data.g.d.ts.map +1 -1
  37. package/lib/typescript/module/icons/notifications-icon.d.ts +3 -0
  38. package/lib/typescript/module/icons/notifications-icon.d.ts.map +1 -0
  39. package/lib/typescript/module/index.d.ts +1 -0
  40. package/lib/typescript/module/index.d.ts.map +1 -1
  41. package/lib/web/chunks/chunk-2K2AYORW.mjs +493 -0
  42. package/lib/web/chunks/chunk-G4F6TBFE.mjs +488 -0
  43. package/lib/web/chunks/chunk-PUCCT3HF.mjs +2 -0
  44. package/lib/web/floating.mjs +2 -0
  45. package/lib/web/index.mjs +20 -0
  46. package/package.json +33 -8
@@ -0,0 +1,493 @@
1
+ "use client";
2
+
3
+ // ../../scripts/web-environment.js
4
+ if (typeof window !== "undefined" && typeof globalThis.global === "undefined") {
5
+ globalThis.global = globalThis;
6
+ }
7
+
8
+ // src/constants.ts
9
+ var VISIBLE_HANDLE_WIDTH = 32;
10
+ var DRAG_THRESHOLD = 5;
11
+ var ANIMATION_DURATION = 200;
12
+ var EDGE_PADDING = 10;
13
+ var SAVE_DEBOUNCE_MS = 500;
14
+ var STORAGE_KEYS = {
15
+ POSITION_X: "@react_buoy_bubble_position_x",
16
+ POSITION_Y: "@react_buoy_bubble_position_y"
17
+ };
18
+
19
+ // src/FloatingToolsStore.ts
20
+ var FloatingToolsStore = class {
21
+ constructor(options) {
22
+ this.isDragging = false;
23
+ this.isHidden = false;
24
+ // Drag tracking
25
+ this.dragStartPos = { x: 0, y: 0 };
26
+ this.dragStartMouse = { x: 0, y: 0 };
27
+ this.hasMoved = false;
28
+ this.savedPosition = null;
29
+ // Persistence
30
+ this.saveTimeout = null;
31
+ this.isInitialized = false;
32
+ // Subscribers (for useSyncExternalStore)
33
+ this.listeners = /* @__PURE__ */ new Set();
34
+ // =============================
35
+ // Subscription (for React hooks)
36
+ // =============================
37
+ this.subscribe = (listener) => {
38
+ this.listeners.add(listener);
39
+ return () => this.listeners.delete(listener);
40
+ };
41
+ this.getSnapshot = () => {
42
+ return this.stateSnapshot;
43
+ };
44
+ this.screenWidth = options.screenWidth;
45
+ this.screenHeight = options.screenHeight;
46
+ this.bubbleSize = options.initialBubbleSize ?? { width: 100, height: 32 };
47
+ this.minPosition = options.minPosition ?? { x: EDGE_PADDING, y: EDGE_PADDING };
48
+ this.visibleHandleWidth = options.visibleHandleWidth ?? VISIBLE_HANDLE_WIDTH;
49
+ this.dragThreshold = options.dragThreshold ?? DRAG_THRESHOLD;
50
+ this.edgePadding = options.edgePadding ?? EDGE_PADDING;
51
+ this.enablePositionPersistence = options.enablePositionPersistence ?? true;
52
+ this.storage = options.storage;
53
+ this.onPositionChange = options.onPositionChange;
54
+ this.onHiddenChange = options.onHiddenChange;
55
+ this.onDraggingChange = options.onDraggingChange;
56
+ this.position = options.initialPosition ?? {
57
+ x: this.screenWidth - this.bubbleSize.width - 20,
58
+ y: Math.max(this.minPosition.y, Math.min(100, this.screenHeight - this.bubbleSize.height - this.edgePadding))
59
+ };
60
+ this.stateSnapshot = this.createSnapshot();
61
+ }
62
+ createSnapshot() {
63
+ return {
64
+ position: { ...this.position },
65
+ isDragging: this.isDragging,
66
+ isHidden: this.isHidden,
67
+ bubbleSize: { ...this.bubbleSize }
68
+ };
69
+ }
70
+ notify() {
71
+ this.stateSnapshot = this.createSnapshot();
72
+ this.listeners.forEach((listener) => listener());
73
+ }
74
+ // =============================
75
+ // Initialization
76
+ // =============================
77
+ /**
78
+ * Initialize the store by loading persisted position.
79
+ * Call this after setting up storage adapter.
80
+ */
81
+ async initialize() {
82
+ if (this.isInitialized || !this.enablePositionPersistence || !this.storage) {
83
+ this.isInitialized = true;
84
+ return;
85
+ }
86
+ try {
87
+ const [xStr, yStr] = await Promise.all([
88
+ this.storage.getItem(STORAGE_KEYS.POSITION_X),
89
+ this.storage.getItem(STORAGE_KEYS.POSITION_Y)
90
+ ]);
91
+ if (xStr !== null && yStr !== null) {
92
+ const x = parseFloat(xStr);
93
+ const y = parseFloat(yStr);
94
+ if (!Number.isNaN(x) && !Number.isNaN(y)) {
95
+ const saved = { x, y };
96
+ const validated = this.validatePosition(saved);
97
+ if (Math.abs(saved.x - validated.x) > 5 || Math.abs(saved.y - validated.y) > 5) {
98
+ this.savePosition(validated.x, validated.y);
99
+ }
100
+ this.position = validated;
101
+ if (validated.x >= this.screenWidth - this.visibleHandleWidth - 5) {
102
+ this.isHidden = true;
103
+ }
104
+ this.notify();
105
+ }
106
+ }
107
+ } catch {
108
+ }
109
+ this.isInitialized = true;
110
+ }
111
+ // =============================
112
+ // Position Management
113
+ // =============================
114
+ /**
115
+ * Get current bounds for position validation.
116
+ */
117
+ getBounds() {
118
+ return {
119
+ minX: this.minPosition.x,
120
+ maxX: this.screenWidth - this.visibleHandleWidth,
121
+ minY: this.minPosition.y,
122
+ maxY: this.screenHeight - this.bubbleSize.height - this.edgePadding
123
+ };
124
+ }
125
+ /**
126
+ * Validate and clamp a position to screen boundaries.
127
+ */
128
+ validatePosition(pos) {
129
+ const bounds = this.getBounds();
130
+ return {
131
+ x: Math.max(bounds.minX, Math.min(pos.x, bounds.maxX)),
132
+ y: Math.max(bounds.minY, Math.min(pos.y, bounds.maxY))
133
+ };
134
+ }
135
+ /**
136
+ * Set position directly (for animation callbacks).
137
+ */
138
+ setPosition(pos) {
139
+ this.position = pos;
140
+ this.onPositionChange?.(pos);
141
+ this.notify();
142
+ }
143
+ /**
144
+ * Update bubble size (call on layout).
145
+ * Only notifies if size actually changed.
146
+ */
147
+ setBubbleSize(size) {
148
+ if (this.bubbleSize.width === size.width && this.bubbleSize.height === size.height) {
149
+ return;
150
+ }
151
+ this.bubbleSize = size;
152
+ if (!this.isDragging) this.reconcileBounds();
153
+ this.notify();
154
+ }
155
+ /**
156
+ * Update screen dimensions (call on resize).
157
+ */
158
+ setScreenSize(width, height) {
159
+ this.screenWidth = width;
160
+ this.screenHeight = height;
161
+ this.reconcileBounds();
162
+ this.notify();
163
+ }
164
+ /** Keep the whole visible tool reachable after its content or viewport changes. */
165
+ reconcileBounds() {
166
+ const clampVisible = (position) => ({
167
+ x: Math.max(this.minPosition.x, Math.min(position.x, this.screenWidth - this.bubbleSize.width - this.edgePadding)),
168
+ y: Math.max(this.minPosition.y, Math.min(position.y, this.screenHeight - this.bubbleSize.height - this.edgePadding))
169
+ });
170
+ const visible = clampVisible(this.position);
171
+ this.position = this.isHidden ? { ...visible, x: this.screenWidth - this.visibleHandleWidth } : visible;
172
+ if (this.savedPosition) this.savedPosition = clampVisible(this.savedPosition);
173
+ }
174
+ /**
175
+ * Update min position (e.g., for safe area insets).
176
+ */
177
+ setMinPosition(minPos) {
178
+ this.minPosition = minPos;
179
+ }
180
+ // =============================
181
+ // Persistence
182
+ // =============================
183
+ async savePosition(x, y) {
184
+ if (!this.enablePositionPersistence || !this.storage) return;
185
+ try {
186
+ await Promise.all([
187
+ this.storage.setItem(STORAGE_KEYS.POSITION_X, x.toString()),
188
+ this.storage.setItem(STORAGE_KEYS.POSITION_Y, y.toString())
189
+ ]);
190
+ } catch {
191
+ }
192
+ }
193
+ debouncedSavePosition(x, y) {
194
+ if (this.saveTimeout) clearTimeout(this.saveTimeout);
195
+ this.saveTimeout = setTimeout(() => this.savePosition(x, y), SAVE_DEBOUNCE_MS);
196
+ }
197
+ // =============================
198
+ // Hide/Show Toggle
199
+ // =============================
200
+ /**
201
+ * Toggle between hidden and visible states.
202
+ * Returns the target position for animation.
203
+ */
204
+ toggleHideShow() {
205
+ const isVisuallyOffScreen = this.position.x > this.screenWidth - this.bubbleSize.width / 2;
206
+ if (this.isHidden || isVisuallyOffScreen) {
207
+ let targetX;
208
+ let targetY;
209
+ if (this.savedPosition && this.savedPosition.x < this.screenWidth - this.bubbleSize.width / 2) {
210
+ targetX = this.savedPosition.x;
211
+ targetY = this.savedPosition.y;
212
+ } else {
213
+ targetX = this.screenWidth - this.bubbleSize.width - 20;
214
+ targetY = this.position.y;
215
+ }
216
+ this.isHidden = false;
217
+ this.onHiddenChange?.(false);
218
+ this.notify();
219
+ return { targetPosition: { x: targetX, y: targetY }, isHiding: false };
220
+ } else {
221
+ this.savedPosition = { ...this.position };
222
+ const hiddenX = this.screenWidth - this.visibleHandleWidth;
223
+ this.isHidden = true;
224
+ this.onHiddenChange?.(true);
225
+ this.notify();
226
+ return { targetPosition: { x: hiddenX, y: this.position.y }, isHiding: true };
227
+ }
228
+ }
229
+ /**
230
+ * Force hide (for pushToSide behavior).
231
+ */
232
+ forceHide() {
233
+ if (this.isHidden) {
234
+ return { targetPosition: this.position };
235
+ }
236
+ this.savedPosition = { ...this.position };
237
+ const hiddenX = this.screenWidth - this.visibleHandleWidth;
238
+ this.isHidden = true;
239
+ this.onHiddenChange?.(true);
240
+ this.notify();
241
+ return { targetPosition: { x: hiddenX, y: this.position.y } };
242
+ }
243
+ /**
244
+ * Force show (restore from pushToSide).
245
+ */
246
+ forceShow() {
247
+ if (!this.isHidden) {
248
+ return { targetPosition: this.position };
249
+ }
250
+ let targetX;
251
+ let targetY;
252
+ if (this.savedPosition) {
253
+ targetX = this.savedPosition.x;
254
+ targetY = this.savedPosition.y;
255
+ } else {
256
+ targetX = this.screenWidth - this.bubbleSize.width - 20;
257
+ targetY = this.position.y;
258
+ }
259
+ this.isHidden = false;
260
+ this.onHiddenChange?.(false);
261
+ this.notify();
262
+ return { targetPosition: { x: targetX, y: targetY } };
263
+ }
264
+ // =============================
265
+ // Drag Handling
266
+ // =============================
267
+ /**
268
+ * Start a drag operation.
269
+ */
270
+ handleDragStart(event) {
271
+ this.hasMoved = false;
272
+ this.dragStartPos = { ...this.position };
273
+ this.dragStartMouse = { x: event.clientX, y: event.clientY };
274
+ }
275
+ /**
276
+ * Process drag movement.
277
+ * Returns the new position (for animation) and whether drag threshold was exceeded.
278
+ */
279
+ handleDragMove(event) {
280
+ const dx = event.clientX - this.dragStartMouse.x;
281
+ const dy = event.clientY - this.dragStartMouse.y;
282
+ const totalDistance = Math.abs(dx) + Math.abs(dy);
283
+ if (!this.hasMoved && totalDistance > this.dragThreshold) {
284
+ this.hasMoved = true;
285
+ this.isDragging = true;
286
+ this.onDraggingChange?.(true);
287
+ this.notify();
288
+ }
289
+ if (this.hasMoved) {
290
+ const newX = this.dragStartPos.x + dx;
291
+ const newY = this.dragStartPos.y + dy;
292
+ const bounds = this.getBounds();
293
+ const clampedX = Math.max(bounds.minX, Math.min(newX, this.screenWidth - this.visibleHandleWidth + this.bubbleSize.width));
294
+ const clampedY = Math.max(bounds.minY, Math.min(newY, bounds.maxY));
295
+ this.position = { x: clampedX, y: clampedY };
296
+ this.onPositionChange?.(this.position);
297
+ this.notify();
298
+ return { position: this.position, isDragging: true };
299
+ }
300
+ return { position: this.position, isDragging: false };
301
+ }
302
+ /**
303
+ * End a drag operation.
304
+ * Returns the result including whether to animate to hidden state.
305
+ */
306
+ handleDragEnd(event) {
307
+ const wasTap = !this.hasMoved;
308
+ if (wasTap) {
309
+ this.isDragging = false;
310
+ this.onDraggingChange?.(false);
311
+ this.notify();
312
+ return { position: this.position, shouldHide: false, wasTap: true };
313
+ }
314
+ const dx = event.clientX - this.dragStartMouse.x;
315
+ const dy = event.clientY - this.dragStartMouse.y;
316
+ const currentX = this.dragStartPos.x + dx;
317
+ const currentY = this.dragStartPos.y + dy;
318
+ const bubbleMidpoint = currentX + this.bubbleSize.width / 2;
319
+ const shouldHide = bubbleMidpoint > this.screenWidth;
320
+ const bounds = this.getBounds();
321
+ if (shouldHide) {
322
+ const hiddenX = this.screenWidth - this.visibleHandleWidth;
323
+ const clampedY = Math.max(bounds.minY, Math.min(currentY, bounds.maxY));
324
+ this.isHidden = true;
325
+ this.onHiddenChange?.(true);
326
+ this.isDragging = false;
327
+ this.onDraggingChange?.(false);
328
+ this.notify();
329
+ return { position: { x: hiddenX, y: clampedY }, shouldHide: true, wasTap: false };
330
+ } else {
331
+ if (this.isHidden && currentX < this.screenWidth - this.visibleHandleWidth - 10) {
332
+ this.isHidden = false;
333
+ this.onHiddenChange?.(false);
334
+ }
335
+ const clampedX = Math.max(bounds.minX, Math.min(currentX, this.screenWidth - this.bubbleSize.width - this.edgePadding));
336
+ const clampedY = Math.max(bounds.minY, Math.min(currentY, bounds.maxY));
337
+ if (clampedX < this.screenWidth - this.bubbleSize.width / 2) {
338
+ this.savedPosition = { x: clampedX, y: clampedY };
339
+ }
340
+ this.position = { x: clampedX, y: clampedY };
341
+ this.isDragging = false;
342
+ this.onDraggingChange?.(false);
343
+ this.debouncedSavePosition(clampedX, clampedY);
344
+ this.notify();
345
+ return { position: { x: clampedX, y: clampedY }, shouldHide: false, wasTap: false };
346
+ }
347
+ }
348
+ /**
349
+ * Commit position after animation completes.
350
+ */
351
+ commitPosition(pos) {
352
+ this.position = pos;
353
+ this.savePosition(pos.x, pos.y);
354
+ this.notify();
355
+ }
356
+ // =============================
357
+ // Cleanup
358
+ // =============================
359
+ destroy() {
360
+ if (this.saveTimeout) {
361
+ clearTimeout(this.saveTimeout);
362
+ this.saveTimeout = null;
363
+ }
364
+ this.listeners.clear();
365
+ }
366
+ };
367
+
368
+ // src/utils.ts
369
+ function getUserStatusConfig(userRole, colors) {
370
+ switch (userRole) {
371
+ case "admin":
372
+ return {
373
+ label: "Admin",
374
+ dotColor: colors.success,
375
+ textColor: colors.success
376
+ };
377
+ case "internal":
378
+ return {
379
+ label: "Internal",
380
+ dotColor: colors.optional,
381
+ textColor: colors.optional
382
+ };
383
+ case "user":
384
+ default:
385
+ return {
386
+ label: "User",
387
+ dotColor: colors.muted,
388
+ textColor: colors.secondary
389
+ };
390
+ }
391
+ }
392
+ function getGripIconLayout(size) {
393
+ return {
394
+ dotSize: Math.max(2, Math.round(size / 6)),
395
+ columnGap: Math.max(2, Math.round(size / 12)),
396
+ rowGap: Math.max(2, Math.round(size / 12))
397
+ };
398
+ }
399
+ function filterValidChildren(children) {
400
+ return children.filter(
401
+ (child) => child != null && child !== false
402
+ );
403
+ }
404
+
405
+ // src/easing.ts
406
+ var easing = {
407
+ /**
408
+ * Linear - no easing.
409
+ */
410
+ linear: (t) => t,
411
+ /**
412
+ * Ease out cubic - decelerates towards end.
413
+ * Used for hide/show animations.
414
+ */
415
+ easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
416
+ /**
417
+ * Ease in cubic - accelerates from start.
418
+ */
419
+ easeInCubic: (t) => t * t * t,
420
+ /**
421
+ * Ease in-out cubic - smooth acceleration and deceleration.
422
+ */
423
+ easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
424
+ /**
425
+ * Ease out quad - lighter deceleration than cubic.
426
+ */
427
+ easeOutQuad: (t) => 1 - (1 - t) * (1 - t),
428
+ /**
429
+ * Ease out expo - strong deceleration (snappy feel).
430
+ */
431
+ easeOutExpo: (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t)
432
+ };
433
+ function getEasing(name) {
434
+ return easing[name];
435
+ }
436
+ function interpolate(from, to, progress, easingFn = easing.easeOutCubic) {
437
+ const eased = easingFn(Math.max(0, Math.min(1, progress)));
438
+ return from + (to - from) * eased;
439
+ }
440
+ function interpolatePosition(from, to, progress, easingFn = easing.easeOutCubic) {
441
+ return {
442
+ x: interpolate(from.x, to.x, progress, easingFn),
443
+ y: interpolate(from.y, to.y, progress, easingFn)
444
+ };
445
+ }
446
+
447
+ // src/colors.ts
448
+ var floatingToolsColors = {
449
+ /** Dark panel background */
450
+ panel: "#1A1A1A",
451
+ /** Muted gray for secondary elements */
452
+ muted: "#888888",
453
+ /** Primary text color */
454
+ secondary: "#E0E0E0",
455
+ /** Info/active state color (Buoy teal) */
456
+ info: "#20C997",
457
+ /** Success/admin color (Buoy teal) */
458
+ success: "#20C997",
459
+ /** Warning/optional color (Buoy orange) */
460
+ optional: "#FFA94D",
461
+ /** Error color (Buoy red) */
462
+ error: "#EF4444",
463
+ /** Primary accent color (Buoy teal) */
464
+ primary: "#20C997",
465
+ /** Color used when dragging the floating tools (Buoy teal) */
466
+ dragActive: "#20C997"
467
+ };
468
+ function withAlpha(color, alpha) {
469
+ if (typeof alpha === "number") {
470
+ const hex = Math.round(alpha * 255).toString(16).padStart(2, "0");
471
+ return `${color}${hex}`;
472
+ }
473
+ return `${color}${alpha}`;
474
+ }
475
+
476
+ export {
477
+ VISIBLE_HANDLE_WIDTH,
478
+ DRAG_THRESHOLD,
479
+ ANIMATION_DURATION,
480
+ EDGE_PADDING,
481
+ SAVE_DEBOUNCE_MS,
482
+ STORAGE_KEYS,
483
+ FloatingToolsStore,
484
+ getUserStatusConfig,
485
+ getGripIconLayout,
486
+ filterValidChildren,
487
+ easing,
488
+ getEasing,
489
+ interpolate,
490
+ interpolatePosition,
491
+ floatingToolsColors,
492
+ withAlpha
493
+ };