@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.
- package/lib/commonjs/icons/ask-buoy-icon.native.js +1 -0
- package/lib/commonjs/icons/bif-raster-icon.native.js +1 -0
- package/lib/commonjs/icons/console-icon.native.js +1 -0
- package/lib/commonjs/icons/generated/icon-data.g.js +69 -2
- package/lib/commonjs/icons/impersonate-icon.native.js +1 -0
- package/lib/commonjs/icons/jotai-icon.native.js +1 -0
- package/lib/commonjs/icons/notifications-icon.js +15 -0
- package/lib/commonjs/icons/query-icon.native.js +1 -0
- package/lib/commonjs/icons/redux-icon.native.js +1 -0
- package/lib/commonjs/icons/scenarios-icon.native.js +1 -0
- package/lib/commonjs/icons/sentry-icon.native.js +1 -0
- package/lib/commonjs/icons/time-machine-icon.native.js +1 -0
- package/lib/commonjs/icons/zustand-icon.native.js +1 -0
- package/lib/commonjs/index.js +17 -2
- package/lib/module/icons/ask-buoy-icon.native.js +1 -0
- package/lib/module/icons/bif-raster-icon.native.js +1 -0
- package/lib/module/icons/console-icon.native.js +1 -0
- package/lib/module/icons/generated/icon-data.g.js +67 -0
- package/lib/module/icons/impersonate-icon.native.js +1 -0
- package/lib/module/icons/jotai-icon.native.js +1 -0
- package/lib/module/icons/notifications-icon.js +6 -0
- package/lib/module/icons/query-icon.native.js +1 -0
- package/lib/module/icons/redux-icon.native.js +1 -0
- package/lib/module/icons/scenarios-icon.native.js +1 -0
- package/lib/module/icons/sentry-icon.native.js +1 -0
- package/lib/module/icons/time-machine-icon.native.js +1 -0
- package/lib/module/icons/zustand-icon.native.js +1 -0
- package/lib/module/index.js +2 -1
- package/lib/typescript/commonjs/icons/generated/icon-data.g.d.ts +2 -0
- package/lib/typescript/commonjs/icons/generated/icon-data.g.d.ts.map +1 -1
- package/lib/typescript/commonjs/icons/notifications-icon.d.ts +3 -0
- package/lib/typescript/commonjs/icons/notifications-icon.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/module/icons/generated/icon-data.g.d.ts +2 -0
- package/lib/typescript/module/icons/generated/icon-data.g.d.ts.map +1 -1
- package/lib/typescript/module/icons/notifications-icon.d.ts +3 -0
- package/lib/typescript/module/icons/notifications-icon.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/web/chunks/chunk-2K2AYORW.mjs +493 -0
- package/lib/web/chunks/chunk-G4F6TBFE.mjs +488 -0
- package/lib/web/chunks/chunk-PUCCT3HF.mjs +2 -0
- package/lib/web/floating.mjs +2 -0
- package/lib/web/index.mjs +20 -0
- 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
|
+
};
|