@buoy-gg/core 7.0.32 → 7.0.34

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 (47) hide show
  1. package/lib/commonjs/floatingMenu/FloatingDevTools.js +24 -1
  2. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +21 -5
  3. package/lib/commonjs/floatingMenu/autoExternalSync.js +32 -17
  4. package/lib/commonjs/floatingMenu/deviceIdentity.js +173 -0
  5. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +5 -46
  6. package/lib/commonjs/floatingMenu/dial/DialIcon.js +2 -46
  7. package/lib/commonjs/version.js +1 -1
  8. package/lib/module/floatingMenu/FloatingDevTools.js +24 -1
  9. package/lib/module/floatingMenu/autoDiscoverPresets.js +21 -5
  10. package/lib/module/floatingMenu/autoExternalSync.js +32 -17
  11. package/lib/module/floatingMenu/deviceIdentity.js +163 -0
  12. package/lib/module/floatingMenu/dial/DialDevTools.js +6 -47
  13. package/lib/module/floatingMenu/dial/DialIcon.js +2 -46
  14. package/lib/module/version.js +1 -1
  15. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +38 -1
  16. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  17. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +12 -2
  19. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  21. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/floatingMenu/deviceIdentity.d.ts +66 -0
  23. package/lib/typescript/commonjs/floatingMenu/deviceIdentity.d.ts.map +1 -0
  24. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +0 -7
  25. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/version.d.ts +1 -1
  28. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +38 -1
  29. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  30. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  31. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +12 -2
  32. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  33. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  34. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
  35. package/lib/typescript/module/floatingMenu/deviceIdentity.d.ts +66 -0
  36. package/lib/typescript/module/floatingMenu/deviceIdentity.d.ts.map +1 -0
  37. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +0 -7
  38. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  39. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  40. package/lib/typescript/module/version.d.ts +1 -1
  41. package/package.json +5 -5
  42. package/lib/commonjs/floatingMenu/allToolsRegistry.js +0 -159
  43. package/lib/module/floatingMenu/allToolsRegistry.js +0 -155
  44. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts +0 -42
  45. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts.map +0 -1
  46. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts +0 -42
  47. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts.map +0 -1
@@ -12,13 +12,16 @@
12
12
  * package, discovered with the same guarded-require pattern as
13
13
  * autoDiscoverPresets. No <DevToolsSync> wiring needed in the app.
14
14
  *
15
- * Identity defaults come from expo-constants when available (app name →
16
- * "MyApp (ios)" / "myapp-ios"), overridable via the `externalSync` prop.
15
+ * Identity defaults come from deviceIdentity.ts: the app name (expo-constants)
16
+ * plus a persisted per-install tail, so two devices on one build register as
17
+ * two devices — "MyApp (iPhone 17 Pro · 2c1d)" / "myapp-ios-3f9a2c1d".
18
+ * Overridable via the `externalSync` prop.
17
19
  */
18
20
  import { useEffect, useMemo, useRef } from "react";
19
21
  import { Platform } from "react-native";
20
22
  import { reportOptionalRequireError } from "./optionalRequire.js";
21
23
  import { appSyncAdapter } from "./sync/appSyncAdapter.js";
24
+ import { useDeviceIdentity } from "./deviceIdentity.js";
22
25
  import { registerToolAdapters } from "@buoy-gg/shared-ui";
23
26
 
24
27
  /* eslint-disable @typescript-eslint/no-var-requires */
@@ -41,6 +44,7 @@ let zustandModule = null;
41
44
  let jotaiModule = null;
42
45
  let eventsModule = null;
43
46
  let consoleModule = null;
47
+ let sentryModule = null;
44
48
  let envModule = null;
45
49
  let highlightUpdatesModule = null;
46
50
  let debugBordersModule = null;
@@ -106,6 +110,11 @@ try {
106
110
  } catch (e) {
107
111
  reportOptionalRequireError("@buoy-gg/console", e);
108
112
  }
113
+ try {
114
+ sentryModule = require("@buoy-gg/sentry");
115
+ } catch (e) {
116
+ reportOptionalRequireError("@buoy-gg/sentry", e);
117
+ }
109
118
  try {
110
119
  envModule = require("@buoy-gg/env");
111
120
  } catch (e) {
@@ -171,13 +180,12 @@ function getAppName() {
171
180
  const constants = expoConstantsModule?.default ?? expoConstantsModule;
172
181
  return constants?.expoConfig?.name ?? constants?.manifest2?.extra?.expoClient?.name ?? null;
173
182
  }
174
- function defaultIdentity() {
175
- const appName = getAppName();
176
- const slug = (appName ?? "buoy").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
177
- return {
178
- deviceName: `${appName ?? "Buoy Device"} (${Platform.OS})`,
179
- deviceId: `${slug || "buoy"}-${Platform.OS}`
180
- };
183
+
184
+ /** expo-constants' `deviceName`: the simulator's name on a sim, the model on Android. */
185
+ function getHardwareName() {
186
+ const constants = expoConstantsModule?.default ?? expoConstantsModule;
187
+ const name = constants?.deviceName;
188
+ return typeof name === "string" && name.length > 0 ? name : null;
181
189
  }
182
190
  /**
183
191
  * Build the `toolId -> sync adapter` map from whatever tool packages the app
@@ -241,12 +249,16 @@ export function AutoExternalSync({
241
249
  requiredEnvVars,
242
250
  customApps
243
251
  });
244
- const identity = useMemo(defaultIdentity, []);
245
- const deviceId = options?.deviceId ?? identity.deviceId;
252
+ const identity = useDeviceIdentity(options, {
253
+ appName: getAppName(),
254
+ hardwareName: getHardwareName(),
255
+ platform: Platform.OS
256
+ });
257
+ const deviceId = identity?.deviceId ?? null;
246
258
  const {
247
259
  socket
248
260
  } = useExternalSyncSocket({
249
- deviceName: options?.deviceName ?? identity.deviceName,
261
+ deviceName: identity?.deviceName ?? "Buoy Device",
250
262
  // Older external-sync versions predate getDefaultSocketURL and require
251
263
  // an explicit URL — keep the literal fallback for them.
252
264
  socketURL: options?.socketURL ?? externalSyncModule.getDefaultSocketURL?.() ?? "http://localhost:42831",
@@ -335,6 +347,9 @@ function useToolAdapterMapInner(queryAdapter, requiredEnvVars, customApps) {
335
347
  if (consoleModule?.consoleSyncAdapter) {
336
348
  map.console = consoleModule.consoleSyncAdapter;
337
349
  }
350
+ if (sentryModule?.sentrySyncAdapter) {
351
+ map.sentry = sentryModule.sentrySyncAdapter;
352
+ }
338
353
  // Lazy: time-machine imports `@buoy-gg/core` (useToolHost), and this file
339
354
  // is loaded from core's index. Requiring it at module top-level is a
340
355
  // require cycle (LogBox on every reload). Waiting until first render
@@ -358,14 +373,14 @@ function useToolAdapterMapInner(queryAdapter, requiredEnvVars, customApps) {
358
373
  reportOptionalRequireError("@buoy-gg/scenarios", e);
359
374
  }
360
375
  // Lazy, same require-cycle reason as time-machine and scenarios: the
361
- // copilot package imports `@buoy-gg/core` for the tool-host hooks.
376
+ // ask-buoy package imports `@buoy-gg/core` for the tool-host hooks.
362
377
  try {
363
- const copilotModule = require("@buoy-gg/copilot");
364
- if (copilotModule?.copilotSyncAdapter) {
365
- map.copilot = copilotModule.copilotSyncAdapter;
378
+ const askBuoyModule = require("@buoy-gg/ask-buoy");
379
+ if (askBuoyModule?.askBuoySyncAdapter) {
380
+ map["ask-buoy"] = askBuoyModule.askBuoySyncAdapter;
366
381
  }
367
382
  } catch (e) {
368
- reportOptionalRequireError("@buoy-gg/copilot", e);
383
+ reportOptionalRequireError("@buoy-gg/ask-buoy", e);
369
384
  }
370
385
  if (envModule?.createEnvSyncAdapter) {
371
386
  map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * How a device introduces itself to the Buoy Desktop broker.
5
+ *
6
+ * The id used to be `<app-slug>-<platform>` — a BUILD identity, not a device
7
+ * identity. Every phone and simulator running the same app registered under
8
+ * the same id, and the broker (which deliberately lets one device hold
9
+ * several sockets for Fast Refresh) treated each newcomer as a reconnect and
10
+ * handed it the identity. Actions then landed on whichever device connected
11
+ * most recently, silently. See DEVICE_IDENTITY_COLLISION.md.
12
+ *
13
+ * Now the id carries a per-install tail minted once and persisted:
14
+ *
15
+ * deviceId = "<app-slug>-<platform>-<8 hex>" e.g. acme-app-ios-3f9a2c1d
16
+ * deviceName = "<App Name> (<hardware> · <last 4>)" e.g. Acme App (iPhone 17 Pro · 2c1d)
17
+ *
18
+ * The tail is what makes two devices on one build two devices. The name's
19
+ * hardware label comes from expo-constants' `deviceName` when available (the
20
+ * simulator's own name on a sim, "iPhone" on iOS 16+ hardware, the model on
21
+ * Android) and falls back to the platform; the 4-char tail is always on so
22
+ * two identical simulators never share a label.
23
+ *
24
+ * Persistence: native goes through shared's `persistentStorage` (file system
25
+ * → AsyncStorage → memory); web uses `sessionStorage` so every browser TAB
26
+ * is its own device — `localStorage` is shared across tabs and would collide
27
+ * exactly the way the old default did. The id does not survive a reinstall
28
+ * on purpose: it crosses an unauthenticated LAN socket, and deleting the app
29
+ * should reset it.
30
+ *
31
+ * An explicit `externalSync.deviceId` bypasses all of this; whoever pins an
32
+ * id owns its uniqueness.
33
+ */
34
+ import { useEffect, useMemo, useState } from "react";
35
+ import { Platform } from "react-native";
36
+ import { devToolsStorageKeys, persistentStorage } from "@buoy-gg/shared-ui";
37
+
38
+ /** Minimal getItem/setItem surface, so storage can be injected in tests. */
39
+
40
+ export const INSTALL_ID_LENGTH = 8;
41
+
42
+ /** 8 lowercase hex chars — short enough to read, random enough for a LAN. */
43
+ export function generateInstallId() {
44
+ let out = "";
45
+ while (out.length < INSTALL_ID_LENGTH) {
46
+ out += (Math.random() * 16 | 0).toString(16);
47
+ }
48
+ return out;
49
+ }
50
+ function slugify(name) {
51
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
52
+ }
53
+ /** Pure: turns the inputs into the id/name pair the broker sees. */
54
+ export function buildDeviceIdentity({
55
+ appName,
56
+ hardwareName,
57
+ platform,
58
+ installId,
59
+ deviceId
60
+ }) {
61
+ const slug = slugify(appName ?? "buoy") || "buoy";
62
+ const label = appName ?? "Buoy Device";
63
+ const hardware = hardwareName?.trim() || platform;
64
+ if (deviceId) {
65
+ // The app pinned its own id: it can also pin its own name, so keep the
66
+ // default name free of a tail that would refer to an id nobody sees.
67
+ return {
68
+ deviceId,
69
+ deviceName: `${label} (${hardware})`
70
+ };
71
+ }
72
+ const id = `${slug}-${platform}-${installId}`;
73
+ return {
74
+ deviceId: id,
75
+ deviceName: `${label} (${hardware} · ${installId.slice(-4)})`
76
+ };
77
+ }
78
+
79
+ // Web: every tab is a device, so the id lives in the tab's sessionStorage.
80
+ function sessionStore() {
81
+ const g = globalThis;
82
+ try {
83
+ return g.sessionStorage ?? null;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ /**
89
+ * The persisted per-install tail: read it, or mint and store it on the first
90
+ * dial. Storage failures degrade to a fresh in-memory id rather than blocking
91
+ * the socket — a per-launch id still beats the old colliding one.
92
+ */
93
+ export async function loadInstallId({
94
+ platform = Platform.OS,
95
+ store,
96
+ mint = generateInstallId
97
+ } = {}) {
98
+ const key = devToolsStorageKeys.sync.deviceId();
99
+ const target = store === undefined ? platform === "web" ? sessionStore() : persistentStorage : store;
100
+ if (!target) return mint();
101
+ try {
102
+ const existing = await target.getItem(key);
103
+ if (existing && existing.length > 0) return existing;
104
+ const fresh = mint();
105
+ await target.setItem(key, fresh);
106
+ return fresh;
107
+ } catch {
108
+ return mint();
109
+ }
110
+ }
111
+ let installIdPromise = null;
112
+
113
+ /**
114
+ * Process-wide memo so every mount (and every Fast Refresh of the component
115
+ * that calls it) shares one storage round-trip and one id.
116
+ */
117
+ export function getInstallId() {
118
+ if (!installIdPromise) installIdPromise = loadInstallId();
119
+ return installIdPromise;
120
+ }
121
+
122
+ /** Tests only. */
123
+ export function resetInstallIdForTests() {
124
+ installIdPromise = null;
125
+ }
126
+ /**
127
+ * The identity to dial with. Null until the per-install id is read from
128
+ * storage (one round-trip on first mount; memoized process-wide after) —
129
+ * both sync hooks no-op on a null id, so the socket simply dials a beat
130
+ * later. An explicit `deviceId` skips the read entirely; an explicit
131
+ * `deviceName` is used verbatim.
132
+ */
133
+ export function useDeviceIdentity(options, env) {
134
+ const pinnedId = options?.deviceId;
135
+ const pinnedName = options?.deviceName;
136
+ const loadId = env.loadId ?? getInstallId;
137
+ const platform = env.platform ?? Platform.OS;
138
+ const [installId, setInstallId] = useState(null);
139
+ useEffect(() => {
140
+ if (pinnedId) return;
141
+ let cancelled = false;
142
+ loadId().then(id => {
143
+ if (!cancelled) setInstallId(id);
144
+ });
145
+ return () => {
146
+ cancelled = true;
147
+ };
148
+ }, [pinnedId, loadId]);
149
+ return useMemo(() => {
150
+ if (!pinnedId && !installId) return null;
151
+ const identity = buildDeviceIdentity({
152
+ appName: env.appName,
153
+ hardwareName: env.hardwareName,
154
+ platform,
155
+ installId: installId ?? "",
156
+ deviceId: pinnedId
157
+ });
158
+ return pinnedName ? {
159
+ ...identity,
160
+ deviceName: pinnedName
161
+ } : identity;
162
+ }, [pinnedId, pinnedName, installId, env.appName, env.hardwareName, platform]);
163
+ }
@@ -6,7 +6,6 @@ import { absoluteFill } from "@buoy-gg/shared-ui";
6
6
  // Icons are provided by installedApps; no direct icon imports here.
7
7
  import { DialIcon } from "./DialIcon.js";
8
8
  import { DialHyperspace } from "./DialHyperspace.js";
9
- import { ALL_TOOLS_REGISTRY } from "../allToolsRegistry.js";
10
9
  import { DialPagination } from "./DialPagination.js";
11
10
  import { getRankedToolIds, isDialUsageLoaded, loadDialUsage, recordToolUsage } from "./dialUsageStore.js";
12
11
  import { persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors, ToolBackground, useToolBackground } from "@buoy-gg/shared-ui";
@@ -15,7 +14,7 @@ import { useProAccess } from "@buoy-gg/license";
15
14
  import { renderAppIcon } from "../AppIcon.js";
16
15
  import { useAppHost } from "../AppHost";
17
16
  import { OnboardingTooltip } from "./OnboardingTooltip";
18
- import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, DIAL_ICON_GLYPH_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
17
+ import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
19
18
 
20
19
  // The circle size depends on the live window width, so it's computed inside
21
20
  // the component via useWindowDimensions — a module-scope Dimensions.get
@@ -258,38 +257,6 @@ export const DialDevTools = ({
258
257
  return map;
259
258
  }, [dialApps, actions, open, onClose]);
260
259
 
261
- // Tools from the global registry that are NOT installed in this app.
262
- // These are appended after all available tools so paging "next" eventually
263
- // reveals them. They are display-only (real icon + real name + "Unavailable").
264
- const unavailableTools = useMemo(() => {
265
- const installedIds = new Set(dialApps.map(a => a.id));
266
- return ALL_TOOLS_REGISTRY.filter(t => !installedIds.has(t.id));
267
- }, [dialApps]);
268
-
269
- // Build stable IconType entries for each unavailable tool.
270
- const unavailableIconsById = useMemo(() => {
271
- const map = new Map();
272
- for (const tool of unavailableTools) {
273
- map.set(tool.id, {
274
- id: tool.id,
275
- name: tool.name,
276
- // Pre-render the icon at the standard GLYPH size (not the slot size —
277
- // the slot also holds the label + "Unavailable" caption, so a glyph
278
- // rendered at DIAL_ICON_SIZE overflows the slot and looks ~2x bigger
279
- // than installed tools' icons).
280
- icon: tool.renderIcon(DIAL_ICON_GLYPH_SIZE),
281
- iconComponent: undefined,
282
- color: tool.color,
283
- unavailable: true,
284
- // Tapping an unavailable tool is intentionally a no-op: the dial stays
285
- // open and no modal launches, signalling the tool is not installed.
286
- // Future: add a brief Toast with an install link here.
287
- onPress: () => {}
288
- });
289
- }
290
- return map;
291
- }, [unavailableTools]);
292
-
293
260
  // Snapshot the usage-ranked order when the dial opens. It stays stable while
294
261
  // open so icons don't jump positions mid-interaction.
295
262
  const [rankedIds, setRankedIds] = useState(() => getRankedToolIds(dialApps.map(a => a.id)));
@@ -309,8 +276,9 @@ export const DialDevTools = ({
309
276
  };
310
277
  }, [dialApps]);
311
278
 
312
- // Page count spans available tools (usage-ranked) + unavailable tools (appended).
313
- const pageCount = Math.max(1, Math.ceil((rankedIds.length + unavailableTools.length) / MAX_DIAL_SLOTS));
279
+ // Page count spans the installed tools (usage-ranked). Only tools the app
280
+ // actually has appear in the dial — nothing is shown for uninstalled ones.
281
+ const pageCount = Math.max(1, Math.ceil(rankedIds.length / MAX_DIAL_SLOTS));
314
282
  const [currentPage, setCurrentPage] = useState(0);
315
283
  const safePage = Math.min(currentPage, pageCount - 1);
316
284
 
@@ -318,18 +286,13 @@ export const DialDevTools = ({
318
286
  // snapshotted on open, so each icon's page and slot are fixed for the
319
287
  // session — which lets us mount all icons once and paginate purely by
320
288
  // toggling visibility (no remounts on page change).
321
- //
322
- // Order: usage-ranked available tools first, unavailable tools appended at
323
- // the very end. This guarantees unavailable items always appear on the last
324
- // pages and never displace available tools.
325
289
  const allDialIcons = useMemo(() => {
326
- const allIds = [...rankedIds, ...unavailableTools.map(t => t.id)];
327
- return allIds.map(id => iconsById.get(id) ?? unavailableIconsById.get(id)).filter(icon => Boolean(icon)).map((icon, i) => ({
290
+ return rankedIds.map(id => iconsById.get(id)).filter(icon => Boolean(icon)).map((icon, i) => ({
328
291
  icon,
329
292
  page: Math.floor(i / MAX_DIAL_SLOTS),
330
293
  slot: i % MAX_DIAL_SLOTS
331
294
  }));
332
- }, [rankedIds, iconsById, unavailableTools, unavailableIconsById]);
295
+ }, [rankedIds, iconsById]);
333
296
 
334
297
  // Empty slot indices for the current page (only the last page can be
335
298
  // partial). These are cheap placeholder dots.
@@ -532,10 +495,6 @@ export const DialDevTools = ({
532
495
  });
533
496
  };
534
497
  const handleIconPress = icon => {
535
- // Unavailable tools are display-only. Return early so the dial stays open
536
- // and no modal or animation is triggered. The dimmed appearance already
537
- // communicates that the tool is not installed.
538
- if (icon.unavailable) return;
539
498
  setSelectedIcon(1);
540
499
  const interaction = dialAnimationConfig?.interaction ?? {
541
500
  iconSelect: {
@@ -183,35 +183,6 @@ export const DialIcon = ({
183
183
  children: /*#__PURE__*/_jsx(View, {
184
184
  style: styles.emptyDot
185
185
  })
186
- }) : icon.unavailable ?
187
- /*#__PURE__*/
188
- // ── Unavailable tool ──────────────────────────────────────────────────
189
- // Renders the real icon + real name at NORMAL styling (visually identical
190
- // to an installed tool). The ONLY signal that it's unavailable is the small
191
- // muted "Unavailable" caption under the name. It's non-interactive: no
192
- // Pressable, and the dial stays open when tapped because
193
- // DialDevTools.handleIconPress returns early for unavailable icons.
194
- _jsxs(View, {
195
- style: styles.pressable,
196
- children: [/*#__PURE__*/_jsx(View, {
197
- style: styles.iconWrapper,
198
- children: /*#__PURE__*/_jsx(View, {
199
- style: styles.iconClamp,
200
- children: icon.icon
201
- })
202
- }), /*#__PURE__*/_jsx(Text, {
203
- style: styles.label,
204
- numberOfLines: 1,
205
- adjustsFontSizeToFit: true,
206
- minimumFontScale: 0.7,
207
- children: icon.name.toUpperCase()
208
- }), /*#__PURE__*/_jsx(Text, {
209
- style: styles.unavailableCaption,
210
- numberOfLines: 1,
211
- adjustsFontSizeToFit: true,
212
- minimumFontScale: 0.6,
213
- children: "Unavailable"
214
- })]
215
186
  }) : /*#__PURE__*/_jsxs(Pressable, {
216
187
  onPress: () => onPress(icon),
217
188
  onPressIn: handlePressIn,
@@ -289,8 +260,8 @@ const styles = StyleSheet.create({
289
260
  },
290
261
  /**
291
262
  * Hard clamp for PRE-RENDERED icon nodes (icon.icon) — the path where the
292
- * glyph's size is baked in by whoever created the node (unavailable-tool
293
- * registry entries, consumer custom tools) rather than controlled here. The
263
+ * glyph's size is baked in by whoever created the node (consumer custom
264
+ * tools) rather than controlled here. The
294
265
  * fixed overflow-hidden box guarantees an oversized node can never blow past
295
266
  * its slot and dwarf neighboring icons — worst case it center-crops. The
296
267
  * iconComponent path stays unclipped: we render it at DIAL_ICON_GLYPH_SIZE
@@ -324,20 +295,5 @@ const styles = StyleSheet.create({
324
295
  backgroundColor: dialColors.emptyDotBackground,
325
296
  borderWidth: dialStyles.emptySlot.borderWidth,
326
297
  borderColor: dialColors.emptyDotBorder
327
- },
328
- // ── Unavailable tool styles ────────────────────────────────────────────────
329
- /**
330
- * Small muted caption shown under the (normal-styled) tool name — the only
331
- * signal that the tool is not installed. Kept compact (smaller than the label)
332
- * so the extra line doesn't disturb the arc layout. Uses the shared muted
333
- * theme token rather than a hardcoded color.
334
- */
335
- unavailableCaption: {
336
- fontSize: dialStyles.icon.label.fontSize - 1,
337
- fontWeight: dialStyles.icon.label.fontWeight,
338
- letterSpacing: dialStyles.icon.label.letterSpacing,
339
- fontFamily: dialStyles.icon.label.fontFamily,
340
- color: dialColors.iconLabelMuted,
341
- textTransform: "uppercase"
342
298
  }
343
299
  });
@@ -11,4 +11,4 @@
11
11
  * Drift is prevented by a test that fails when this disagrees with
12
12
  * package.json, so a version bump that forgets this file cannot ship.
13
13
  */
14
- export const SDK_VERSION = "7.0.32";
14
+ export const SDK_VERSION = "7.0.34";
@@ -39,6 +39,25 @@ export interface StorageKeyConfig {
39
39
  description?: string;
40
40
  storageType: "async" | "secure" | "mmkv";
41
41
  }
42
+ /**
43
+ * Ask Buoy's config, mirrored structurally so core never depends on the
44
+ * optional `@buoy-gg/ask-buoy` package (it is required lazily, like every other
45
+ * tool). The object is handed to `createAskBuoyTool` untouched, so any field
46
+ * that package accepts passes through — the index signature is the seam.
47
+ */
48
+ export interface FloatingAskBuoyConfig {
49
+ /** Your company's own LLM gateway. Buoy never proxies or holds a key. */
50
+ endpoint: string;
51
+ /** Model id passed through to the endpoint. */
52
+ model: string;
53
+ /** Which request/response shape the endpoint speaks. @default "anthropic" */
54
+ protocol?: "anthropic" | "openai";
55
+ /** Resolved fresh per request — the app's own short-lived session token. */
56
+ headers?: () => Record<string, string> | Promise<Record<string, string>>;
57
+ /** DEV ONLY — compiled into the bundle in plaintext. Use `headers` + a gateway. */
58
+ apiKey?: string;
59
+ [key: string]: unknown;
60
+ }
42
61
  /**
43
62
  * Props for FloatingDevTools component.
44
63
  *
@@ -101,6 +120,24 @@ export interface FloatingDevToolsProps extends Omit<FloatingMenuProps, "apps"> {
101
120
  * ```
102
121
  */
103
122
  requiredStorageKeys?: StorageKeyConfig[];
123
+ /**
124
+ * Wire Ask Buoy (the in-app AI chat) to your team's AI endpoint — the
125
+ * documented one-prop setup. Requires `@buoy-gg/ask-buoy` to be installed;
126
+ * the config is exactly what `createAskBuoyTool` takes and passes through
127
+ * untouched.
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * <FloatingDevTools
132
+ * askBuoy={{
133
+ * endpoint: "https://ai.acme.com/v1/messages",
134
+ * model: "claude-sonnet-4-5",
135
+ * headers: async () => ({ Authorization: `Bearer ${await auth.getToken()}` }),
136
+ * }}
137
+ * />
138
+ * ```
139
+ */
140
+ askBuoy?: FloatingAskBuoyConfig;
104
141
  /**
105
142
  * Optional children to render within the DevToolsVisibilityProvider context.
106
143
  * Useful for tools that need to react to DevTools visibility (like DebugBordersStandaloneOverlay).
@@ -331,5 +368,5 @@ export interface FloatingDevToolsProps extends Omit<FloatingMenuProps, "apps"> {
331
368
  *
332
369
  * @param props - FloatingDevTools props
333
370
  */
334
- export declare const FloatingDevTools: ({ apps, requiredEnvVars, requiredStorageKeys, children, disableHints, defaultFloatingTools, defaultDialTools, requireLicense, licenseKey: licenseKeyProp, zustandStores, environment, externalSync, headless, ...props }: FloatingDevToolsProps) => React.JSX.Element | null;
371
+ export declare const FloatingDevTools: ({ apps, requiredEnvVars, requiredStorageKeys, askBuoy, children, disableHints, defaultFloatingTools, defaultDialTools, requireLicense, licenseKey: licenseKeyProp, zustandStores, environment, externalSync, headless, ...props }: FloatingDevToolsProps) => React.JSX.Element | null;
335
372
  //# sourceMappingURL=FloatingDevTools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingDevTools.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/FloatingDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+D,MAAM,OAAO,CAAC;AAGpF,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAOtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ5C,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAM5B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC5E;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEzC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;IAE7C;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IAErC;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,OAAO,oBAAoB,EAAE,WAAW,EAAE,CAAC;IAEnE;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,oBAAoB,EAAE,WAAW,KAAK,IAAI,CAAC;IAEtF;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAC;IAE7C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,gBAAgB,GAAI,0NAe9B,qBAAqB,6BAybvB,CAAC"}
1
+ {"version":3,"file":"FloatingDevTools.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/FloatingDevTools.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"autoDiscoverPresets.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/autoDiscoverPresets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA8TpD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,YAAY,EAAE,GAC1B,YAAY,EAAE,CAahB"}
1
+ {"version":3,"file":"autoDiscoverPresets.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/autoDiscoverPresets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA4UpD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,YAAY,EAAE,GAC1B,YAAY,EAAE,CAahB"}
@@ -12,9 +12,19 @@ export interface ExternalSyncOptions {
12
12
  * broker on another machine.
13
13
  */
14
14
  socketURL?: string;
15
- /** Shown in the desktop device list. @default "<app name> (<platform>)" */
15
+ /**
16
+ * Shown in the desktop device list.
17
+ * @default "<app name> (<hardware> · <last 4 of the id>)"
18
+ */
16
19
  deviceName?: string;
17
- /** Stable device identity across restarts. @default "<app-slug>-<platform>" */
20
+ /**
21
+ * Stable device identity across restarts. Leave unset: the default is
22
+ * `<app-slug>-<platform>-<per-install id>`, minted once and persisted, so
23
+ * every device running this build is its own entry in Buoy Desktop and
24
+ * `list_devices`. If you do pin one, it MUST be unique per device — two
25
+ * devices sharing an id look like one device to the broker, and actions
26
+ * land on whichever connected last.
27
+ */
18
28
  deviceId?: string;
19
29
  /**
20
30
  * Allow this build to dial the broker when `__DEV__` is false (a release /
@@ -1 +1 @@
1
- {"version":3,"file":"autoExternalSync.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/autoExternalSync.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAsJjD,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAuBD,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IACjC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,eAAe,EACf,UAAU,GACX,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,eAAe,EACf,UAAU,GACX,EAAE,mBAAmB,QAYrB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,eAAe,EACf,KAAK,EACL,IAAI,EACJ,UAAU,EACV,UAAU,GACX,EAAE,qBAAqB,QAuDvB"}
1
+ {"version":3,"file":"autoExternalSync.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/autoExternalSync.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA6JjD,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkBD,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IACjC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,eAAe,EACf,UAAU,GACX,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,eAAe,EACf,UAAU,GACX,EAAE,mBAAmB,QAYrB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,eAAe,EACf,KAAK,EACL,IAAI,EACJ,UAAU,EACV,UAAU,GACX,EAAE,qBAAqB,QA2DvB"}
@@ -19,7 +19,7 @@
19
19
  * All known tool IDs that can appear in the floating menu or dial menu.
20
20
  * This is a union type of all auto-discovered tool IDs.
21
21
  */
22
- export type BuiltInToolId = 'env' | 'network' | 'storage' | 'query' | 'query-wifi-toggle' | 'route-events' | 'debug-borders' | 'highlight-updates' | 'highlight-updates-modal' | 'redux' | 'zustand' | 'jotai' | 'events' | 'time-machine' | 'scenarios' | 'copilot' | 'image-overlay' | 'perf-monitor' | 'perf-monitor-modal';
22
+ export type BuiltInToolId = 'env' | 'network' | 'storage' | 'query' | 'query-wifi-toggle' | 'route-events' | 'debug-borders' | 'highlight-updates' | 'highlight-updates-modal' | 'redux' | 'zustand' | 'jotai' | 'events' | 'time-machine' | 'scenarios' | 'ask-buoy' | 'image-overlay' | 'perf-monitor' | 'perf-monitor-modal';
23
23
  /**
24
24
  * Special floating-only tool IDs that only appear in the floating bubble row.
25
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"defaultConfig.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/defaultConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,SAAS,GACT,SAAS,GACT,OAAO,GACP,mBAAmB,GACnB,cAAc,GACd,eAAe,GACf,mBAAmB,GACnB,yBAAyB,GACzB,OAAO,GACP,SAAS,GACT,OAAO,GACP,QAAQ,GACR,cAAc,GACd,WAAW,GACX,SAAS,GACT,eAAe,GACf,cAAc,GACd,oBAAoB,CAAC;AAEzB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,cAAc,EAAE,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,EAAE,CAAC;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAW9E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,SAAS,cAAc,EAAE,EAC1B,CAAC,SAAS,UAAU,EAAE,EACtB,MAAM,EAAE;IACR,QAAQ,CAAC,EAAE,CAAC,CAAC;IACb,IAAI,CAAC,EAAE,CAAC,CAAC;CACV,GAAG;IACF,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;CACrB,CAUA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,cAAc,CAcjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,UAAU,CAazD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,GAAG,EAAE,CAAC,EAAE,EACR,cAAc,EAAE,CAAC,EAAE,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
1
+ {"version":3,"file":"defaultConfig.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/defaultConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,SAAS,GACT,SAAS,GACT,OAAO,GACP,mBAAmB,GACnB,cAAc,GACd,eAAe,GACf,mBAAmB,GACnB,yBAAyB,GACzB,OAAO,GACP,SAAS,GACT,OAAO,GACP,QAAQ,GACR,cAAc,GACd,WAAW,GACX,UAAU,GACV,eAAe,GACf,cAAc,GACd,oBAAoB,CAAC;AAEzB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,cAAc,EAAE,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,EAAE,CAAC;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAW9E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,SAAS,cAAc,EAAE,EAC1B,CAAC,SAAS,UAAU,EAAE,EACtB,MAAM,EAAE;IACR,QAAQ,CAAC,EAAE,CAAC,CAAC;IACb,IAAI,CAAC,EAAE,CAAC,CAAC;CACV,GAAG;IACF,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;CACrB,CAUA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,cAAc,CAcjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,UAAU,CAazD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,GAAG,EAAE,CAAC,EAAE,EACR,cAAc,EAAE,CAAC,EAAE,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
@@ -0,0 +1,66 @@
1
+ export interface DeviceIdentity {
2
+ deviceName: string;
3
+ deviceId: string;
4
+ }
5
+ /** Minimal getItem/setItem surface, so storage can be injected in tests. */
6
+ export interface IdentityStore {
7
+ getItem(key: string): Promise<string | null> | string | null;
8
+ setItem(key: string, value: string): Promise<void> | void;
9
+ }
10
+ export declare const INSTALL_ID_LENGTH = 8;
11
+ /** 8 lowercase hex chars — short enough to read, random enough for a LAN. */
12
+ export declare function generateInstallId(): string;
13
+ export interface BuildIdentityInput {
14
+ /** Expo config name; null for bare RN. */
15
+ appName: string | null;
16
+ /** expo-constants `deviceName`; null when unavailable. */
17
+ hardwareName?: string | null;
18
+ platform: string;
19
+ /** The persisted per-install tail. */
20
+ installId: string;
21
+ /** An explicit `externalSync.deviceId` — used verbatim, no tail in the name. */
22
+ deviceId?: string;
23
+ }
24
+ /** Pure: turns the inputs into the id/name pair the broker sees. */
25
+ export declare function buildDeviceIdentity({ appName, hardwareName, platform, installId, deviceId, }: BuildIdentityInput): DeviceIdentity;
26
+ export interface LoadInstallIdOptions {
27
+ platform?: string;
28
+ /** Storage override (tests). Defaults to sessionStorage on web, persistentStorage elsewhere. */
29
+ store?: IdentityStore | null;
30
+ mint?: () => string;
31
+ }
32
+ /**
33
+ * The persisted per-install tail: read it, or mint and store it on the first
34
+ * dial. Storage failures degrade to a fresh in-memory id rather than blocking
35
+ * the socket — a per-launch id still beats the old colliding one.
36
+ */
37
+ export declare function loadInstallId({ platform, store, mint, }?: LoadInstallIdOptions): Promise<string>;
38
+ /**
39
+ * Process-wide memo so every mount (and every Fast Refresh of the component
40
+ * that calls it) shares one storage round-trip and one id.
41
+ */
42
+ export declare function getInstallId(): Promise<string>;
43
+ /** Tests only. */
44
+ export declare function resetInstallIdForTests(): void;
45
+ export interface DeviceIdentityOptions {
46
+ deviceId?: string;
47
+ deviceName?: string;
48
+ }
49
+ export interface DeviceIdentityEnvironment {
50
+ /** Expo config name; null for bare RN. */
51
+ appName: string | null;
52
+ /** expo-constants `deviceName`; null when unavailable. */
53
+ hardwareName: string | null;
54
+ platform?: string;
55
+ /** The install-id loader (tests inject one; production uses the memo). */
56
+ loadId?: () => Promise<string>;
57
+ }
58
+ /**
59
+ * The identity to dial with. Null until the per-install id is read from
60
+ * storage (one round-trip on first mount; memoized process-wide after) —
61
+ * both sync hooks no-op on a null id, so the socket simply dials a beat
62
+ * later. An explicit `deviceId` skips the read entirely; an explicit
63
+ * `deviceName` is used verbatim.
64
+ */
65
+ export declare function useDeviceIdentity(options: DeviceIdentityOptions | undefined, env: DeviceIdentityEnvironment): DeviceIdentity | null;
66
+ //# sourceMappingURL=deviceIdentity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviceIdentity.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/deviceIdentity.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC3D;AAED,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,6EAA6E;AAC7E,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C;AASD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,kBAAkB,GAAG,cAAc,CAcrC;AAYD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gGAAgG;IAChG,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAClC,QAAsB,EACtB,KAAK,EACL,IAAwB,GACzB,GAAE,oBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB7C;AAID;;;GAGG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAG9C;AAED,kBAAkB;AAClB,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,0CAA0C;IAC1C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,qBAAqB,GAAG,SAAS,EAC1C,GAAG,EAAE,yBAAyB,GAC7B,cAAc,GAAG,IAAI,CA4BvB"}