@buoy-gg/core 7.0.17 → 7.0.19

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 (59) hide show
  1. package/lib/commonjs/floatingMenu/AppHost.js +5 -3
  2. package/lib/commonjs/floatingMenu/AppHost.web.js +6 -1
  3. package/lib/commonjs/floatingMenu/AppIcon.js +91 -0
  4. package/lib/commonjs/floatingMenu/FloatingDevTools.js +15 -9
  5. package/lib/commonjs/floatingMenu/FloatingMenu.js +12 -6
  6. package/lib/commonjs/floatingMenu/ToolHost.js +13 -3
  7. package/lib/commonjs/floatingMenu/autoExternalSync.js +12 -2
  8. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +13 -6
  9. package/lib/commonjs/toolkit.js +201 -44
  10. package/lib/commonjs/toolkitComponents.js +241 -0
  11. package/lib/module/floatingMenu/AppHost.js +5 -3
  12. package/lib/module/floatingMenu/AppHost.web.js +6 -1
  13. package/lib/module/floatingMenu/AppIcon.js +84 -0
  14. package/lib/module/floatingMenu/FloatingDevTools.js +15 -9
  15. package/lib/module/floatingMenu/FloatingMenu.js +12 -6
  16. package/lib/module/floatingMenu/ToolHost.js +14 -4
  17. package/lib/module/floatingMenu/autoExternalSync.js +12 -2
  18. package/lib/module/floatingMenu/dial/DialDevTools.js +13 -6
  19. package/lib/module/toolkit.js +33 -16
  20. package/lib/module/toolkitComponents.js +230 -0
  21. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts +30 -0
  24. package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts.map +1 -0
  25. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts +5 -0
  28. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +8 -1
  30. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/floatingMenu/sync/appSyncAdapter.d.ts +1 -1
  33. package/lib/typescript/commonjs/floatingMenu/sync/appSyncAdapter.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/floatingMenu/types.d.ts +32 -1
  35. package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/toolkit.d.ts +19 -10
  37. package/lib/typescript/commonjs/toolkit.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/toolkitComponents.d.ts +61 -0
  39. package/lib/typescript/commonjs/toolkitComponents.d.ts.map +1 -0
  40. package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -1
  41. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +1 -1
  42. package/lib/typescript/module/floatingMenu/AppIcon.d.ts +30 -0
  43. package/lib/typescript/module/floatingMenu/AppIcon.d.ts.map +1 -0
  44. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  45. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
  46. package/lib/typescript/module/floatingMenu/ToolHost.d.ts +5 -0
  47. package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
  48. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +8 -1
  49. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  50. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  51. package/lib/typescript/module/floatingMenu/sync/appSyncAdapter.d.ts +1 -1
  52. package/lib/typescript/module/floatingMenu/sync/appSyncAdapter.d.ts.map +1 -1
  53. package/lib/typescript/module/floatingMenu/types.d.ts +32 -1
  54. package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -1
  55. package/lib/typescript/module/toolkit.d.ts +19 -10
  56. package/lib/typescript/module/toolkit.d.ts.map +1 -1
  57. package/lib/typescript/module/toolkitComponents.d.ts +61 -0
  58. package/lib/typescript/module/toolkitComponents.d.ts.map +1 -0
  59. package/package.json +25 -6
@@ -4,6 +4,7 @@ import { persistentStorage, setExpandableWindowControls } from "@buoy-gg/shared-
4
4
  import React, { createContext, useCallback, useContext, useMemo, useState, useEffect, useRef } from "react";
5
5
  import { BackHandler, Modal, StyleSheet, View } from "react-native";
6
6
  import { resolveOpenAppsState } from "./AppHostLogic.js";
7
+ import { renderAppIcon } from "./AppIcon.js";
7
8
  import { useMinimizedTools } from "./MinimizedToolsContext.js";
8
9
  import { ToolModalShell } from "./ToolHost.js";
9
10
  import { useDevToolsSettings } from "./DevToolsSettingsModal";
@@ -64,10 +65,11 @@ export const AppHostProvider = ({
64
65
  }) => {
65
66
  const appDef = installedAppsRef.current.find(app => app.id === appId);
66
67
  if (appDef) {
67
- const resolvedIcon = typeof appDef.icon === "function" ? appDef.icon({
68
+ const resolvedIcon = renderAppIcon(appDef.icon, {
68
69
  slot: "dial",
69
- size: 20
70
- }) : appDef.icon;
70
+ size: 20,
71
+ color: appDef.color
72
+ });
71
73
  open({
72
74
  id: appDef.id,
73
75
  title: appDef.name,
@@ -25,6 +25,7 @@
25
25
  import { persistentStorage } from "@buoy-gg/shared-ui";
26
26
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
27
27
  import { resolveOpenAppsState } from "./AppHostLogic.js";
28
+ import { renderAppIcon } from "./AppIcon.js";
28
29
  import { getBubbleAnchorPosition } from "./floatingTools.web.js";
29
30
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
30
31
  const AppHostContext = /*#__PURE__*/createContext(null);
@@ -81,7 +82,11 @@ export const AppHostProvider = ({
81
82
  }) => {
82
83
  const appDef = installedAppsRef.current.find(app => app.id === appId);
83
84
  if (appDef && appDef.component) {
84
- const resolvedIcon = typeof appDef.icon === "function" ? appDef.icon(20) : appDef.icon;
85
+ const resolvedIcon = renderAppIcon(appDef.icon, {
86
+ slot: "dial",
87
+ size: 20,
88
+ color: appDef.color
89
+ });
85
90
  open({
86
91
  id: appDef.id,
87
92
  title: appDef.name,
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Icon resolution for `InstalledApp.icon`.
5
+ *
6
+ * Custom tools may provide an icon three ways:
7
+ * - a component `({ slot, size }) => node` (dynamic, hook-capable)
8
+ * - a pre-built ReactNode
9
+ * - a PLAIN STRING — "AUTH", "KIT", "🗑️" — rendered as an auto-fitting text
10
+ * icon so simple tools never need an icon library.
11
+ *
12
+ * Every menu surface (dial, row, minimize stack, restore path) resolves icons
13
+ * through `renderAppIcon` so all three forms behave identically.
14
+ */
15
+ import React from "react";
16
+ import { Text, View } from "react-native";
17
+ import { toolTheme } from "@buoy-gg/shared-ui";
18
+ import { jsx as _jsx } from "react/jsx-runtime";
19
+ /** Visible glyphs, not UTF-16 units — "🗑️" is one glyph, not three. */
20
+ function glyphCount(label) {
21
+ // Strip variation selectors + zero-width joiners before counting.
22
+ const stripped = label.replace(/[\uFE00-\uFE0F\u200D]/g, "");
23
+ return Math.max(Array.from(stripped).length, 1);
24
+ }
25
+ export function AppTextIcon({
26
+ label,
27
+ size,
28
+ color = toolTheme.colors.text
29
+ }) {
30
+ const chars = glyphCount(label);
31
+ // Short labels stay square; longer ones ("AUTH") get a wider box — the
32
+ // dial/row layouts have breathing room and the label reads better than a
33
+ // squashed square would. The computed font size is what fits the box on
34
+ // web; adjustsFontSizeToFit covers native font-metric drift.
35
+ const maxWidth = chars <= 2 ? size : size * 1.6;
36
+ const fontSize = Math.max(7, Math.min(size * (chars === 1 ? 0.7 : 0.62), maxWidth / (0.62 * chars)));
37
+ return /*#__PURE__*/_jsx(View, {
38
+ style: {
39
+ minWidth: size,
40
+ maxWidth,
41
+ height: size,
42
+ alignItems: "center",
43
+ justifyContent: "center"
44
+ },
45
+ children: /*#__PURE__*/_jsx(Text, {
46
+ numberOfLines: 1,
47
+ adjustsFontSizeToFit: true,
48
+ minimumFontScale: 0.5,
49
+ style: {
50
+ color,
51
+ fontSize,
52
+ fontWeight: "800",
53
+ letterSpacing: 0.5,
54
+ textAlign: "center"
55
+ },
56
+ children: label
57
+ })
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Resolve any `InstalledApp.icon` form to a renderable node. Function icons
63
+ * become elements (createElement — hooks keep working); strings become
64
+ * auto-fitting text icons; nodes pass through.
65
+ */
66
+ export function renderAppIcon(icon, ctx) {
67
+ if (icon == null) return icon;
68
+ if (typeof icon === "function") {
69
+ return /*#__PURE__*/React.createElement(icon, {
70
+ slot: ctx.slot,
71
+ size: ctx.size,
72
+ state: ctx.state,
73
+ actions: ctx.actions
74
+ });
75
+ }
76
+ if (typeof icon === "string" || typeof icon === "number") {
77
+ return /*#__PURE__*/_jsx(AppTextIcon, {
78
+ label: String(icon),
79
+ size: ctx.size,
80
+ color: ctx.color
81
+ });
82
+ }
83
+ return icon;
84
+ }
@@ -6,6 +6,7 @@ import { AppHostProvider } from "./AppHost";
6
6
  import { FloatingMenu } from "./FloatingMenu.js";
7
7
  import { AppOverlay } from "./AppHost";
8
8
  import { autoDiscoverPresets, autoDiscoverPresetsWithCustom } from "./autoDiscoverPresets.js";
9
+ import { renderAppIcon } from "./AppIcon.js";
9
10
  import { DevToolsVisibilityProvider } from "./DevToolsVisibilityContext.js";
10
11
  import { reportOptionalRequireError } from "./optionalRequire.js";
11
12
  import { HintsProvider, LicenseEntryModal, normalizeEnvironment } from "@buoy-gg/shared-ui";
@@ -226,6 +227,11 @@ export const FloatingDevTools = ({
226
227
  // identity (and re-ran discovery) on every parent render.
227
228
  const finalApps = useMemo(() => userApps ? autoDiscoverPresetsWithCustom(userApps) : autoDiscoverPresets(), [userApps]);
228
229
 
230
+ // Custom tools that declared a sync adapter — registered with the broker as
231
+ // `custom:<id>` by AutoExternalSync. Memoized so the sync tools map (keyed
232
+ // by identity) doesn't rebuild every render.
233
+ const syncApps = useMemo(() => finalApps.filter(app => app.sync), [finalApps]);
234
+
229
235
  // Check if debug-borders is installed and auto-render the overlay
230
236
  const DebugBordersOverlay = useMemo(() => {
231
237
  try {
@@ -362,13 +368,11 @@ export const FloatingDevTools = ({
362
368
  const getToolIcon = useCallback(id => {
363
369
  const tool = finalApps.find(app => app.id === id);
364
370
  if (!tool) return null;
365
- if (typeof tool.icon === "function") {
366
- return tool.icon({
367
- slot: "dial",
368
- size: 20
369
- });
370
- }
371
- return tool.icon;
371
+ return renderAppIcon(tool.icon, {
372
+ slot: "dial",
373
+ size: 20,
374
+ color: tool.color
375
+ });
372
376
  }, [finalApps]);
373
377
 
374
378
  // Gate: Free tier only works in development mode
@@ -390,7 +394,8 @@ export const FloatingDevTools = ({
390
394
  options: typeof externalSync === "object" ? externalSync : undefined,
391
395
  requiredEnvVars: requiredEnvVars,
392
396
  isPro: isLicensed,
393
- licenseKey: licenseKey
397
+ licenseKey: licenseKey,
398
+ customApps: syncApps
394
399
  })]
395
400
  });
396
401
  }
@@ -430,7 +435,8 @@ export const FloatingDevTools = ({
430
435
  options: typeof externalSync === "object" ? externalSync : undefined,
431
436
  requiredEnvVars: requiredEnvVars,
432
437
  isPro: isLicensed,
433
- licenseKey: licenseKey
438
+ licenseKey: licenseKey,
439
+ customApps: syncApps
434
440
  })]
435
441
  })
436
442
  })
@@ -4,6 +4,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
4
4
  import { TouchableOpacity, StyleSheet, View } from "react-native";
5
5
  import { absoluteFill } from "@buoy-gg/shared-ui";
6
6
  import { FloatingTools, UserStatus } from "./floatingTools";
7
+ import { renderAppIcon } from "./AppIcon.js";
7
8
  import { DialDevTools } from "./dial/DialDevTools";
8
9
  import { EnvironmentIndicator, persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors } from "@buoy-gg/shared-ui";
9
10
  import { useDevToolsSettings } from "./DevToolsSettingsModal";
@@ -168,12 +169,13 @@ export const FloatingMenu = ({
168
169
 
169
170
  // Only open modal if not a toggle-only tool
170
171
  if (app.launchMode !== "toggle-only") {
171
- // Resolve the icon for minimize stack display
172
- // IMPORTANT: Use React.createElement for function components to preserve hooks
173
- const resolvedIcon = typeof app.icon === "function" ? /*#__PURE__*/React.createElement(app.icon, {
172
+ // Resolve the icon for minimize stack display (createElement keeps
173
+ // function-icon hooks working; strings become text icons).
174
+ const resolvedIcon = renderAppIcon(app.icon, {
174
175
  slot: "dial",
175
- size: 20
176
- }) : app.icon;
176
+ size: 20,
177
+ color: app.color
178
+ });
177
179
  open({
178
180
  id: app.id,
179
181
  title: app.name,
@@ -281,7 +283,11 @@ export const FloatingMenu = ({
281
283
  actions: mergedActions
282
284
  });
283
285
  }
284
- return app.icon;
286
+ return renderAppIcon(app.icon, {
287
+ slot: "row",
288
+ size: 16,
289
+ color: app.color
290
+ });
285
291
  })()
286
292
  }, `row-${app.id}`))]
287
293
  })
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
14
14
  import { Animated, StyleSheet, Text, View } from "react-native";
15
- import { JsModal, ModalHeader, HeaderSearchButton, devToolsStorageKeys, buoyColors, gameUIColors } from "@buoy-gg/shared-ui";
15
+ import { JsModal, ModalHeader, HeaderSearchButton, TickProvider, devToolsStorageKeys, buoyColors, gameUIColors } from "@buoy-gg/shared-ui";
16
16
 
17
17
  /** Per-screen header overrides a tool body can apply (see `useToolHeader`). */
18
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -160,6 +160,13 @@ export function ToolModalShell({
160
160
  const subtitle = headerOverride?.subtitle ?? config.subtitle;
161
161
  const onBack = headerOverride?.onBack;
162
162
  const actions = headerOverride?.actions ?? config.headerActions;
163
+ const searchable = headerOverride?.searchable ?? config.searchable;
164
+
165
+ // A screen that hides search while it's open would strand the input with
166
+ // no way to dismiss it — close it on entry instead.
167
+ useEffect(() => {
168
+ if (!searchable && isSearchActive) clearSearch();
169
+ }, [searchable, isSearchActive, clearSearch]);
163
170
 
164
171
  // Tool-scoped persistence via the custom namespace unless the user has the
165
172
  // shared-dimensions global setting on (all tools share one window size).
@@ -176,7 +183,8 @@ export function ToolModalShell({
176
183
  title: title,
177
184
  subtitle: subtitle
178
185
  }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
179
- children: [!isSearchActive && actions, config.searchable && !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
186
+ children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
187
+ testID: `tool-search-${app.id}`,
180
188
  onPress: () => setIsSearchActive(true)
181
189
  })]
182
190
  })]
@@ -204,8 +212,10 @@ export function ToolModalShell({
204
212
  styles: {},
205
213
  children: /*#__PURE__*/_jsxs(View, {
206
214
  style: styles.bodyRoot,
207
- children: [/*#__PURE__*/_jsx(Body, {
208
- ...bodyProps
215
+ children: [/*#__PURE__*/_jsx(TickProvider, {
216
+ children: /*#__PURE__*/_jsx(Body, {
217
+ ...bodyProps
218
+ })
209
219
  }), toast && /*#__PURE__*/_jsx(ToastView, {
210
220
  message: toast.message,
211
221
  type: toast.type,
@@ -175,7 +175,8 @@ export function AutoExternalSync({
175
175
  options,
176
176
  requiredEnvVars,
177
177
  isPro,
178
- licenseKey
178
+ licenseKey,
179
+ customApps
179
180
  }) {
180
181
  const {
181
182
  useExternalSyncSocket,
@@ -288,8 +289,17 @@ export function AutoExternalSync({
288
289
  if (assetsModule?.assetsSyncAdapter) {
289
290
  map.assets = assetsModule.assetsSyncAdapter;
290
291
  }
292
+
293
+ // Third-party custom tools that declared InstalledApp.sync. Namespaced so
294
+ // a custom tool named "network" can't shadow the first-party adapter on
295
+ // the broker, in MCP call_action, or in desktop watch state.
296
+ for (const app of customApps ?? []) {
297
+ if (app?.sync) {
298
+ map[`custom:${app.id}`] = app.sync;
299
+ }
300
+ }
291
301
  return map;
292
- }, [queryAdapter, requiredEnvVars]);
302
+ }, [queryAdapter, requiredEnvVars, customApps]);
293
303
  useExternalSync({
294
304
  tools,
295
305
  socket,
@@ -11,6 +11,7 @@ import { getRankedToolIds, isDialUsageLoaded, loadDialUsage, recordToolUsage } f
11
11
  import { persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors } from "@buoy-gg/shared-ui";
12
12
  import { DevToolsSettingsModal, useDevToolsSettings } from "../DevToolsSettingsModal";
13
13
  import { useProAccess } from "@buoy-gg/license";
14
+ import { renderAppIcon } from "../AppIcon.js";
14
15
  import { useAppHost } from "../AppHost";
15
16
  import { OnboardingTooltip } from "./OnboardingTooltip";
16
17
  import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, DIAL_ICON_GLYPH_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
@@ -172,9 +173,14 @@ export const DialDevTools = ({
172
173
  map.set(app.id, {
173
174
  id: app.id,
174
175
  name: app.name,
175
- // Pass both the pre-rendered icon (for non-function icons) and the
176
- // component (for dynamic rendering).
177
- icon: typeof app.icon === "function" ? null : app.icon,
176
+ // Pass both the pre-rendered icon (for non-function icons; strings
177
+ // resolve to auto-fitting text icons) and the component (for dynamic
178
+ // rendering).
179
+ icon: typeof app.icon === "function" ? null : renderAppIcon(app.icon, {
180
+ slot: "dial",
181
+ size: 20,
182
+ color: app.color
183
+ }),
178
184
  // Cast to the expected type - the signature is compatible at runtime.
179
185
  iconComponent: typeof app.icon === "function" ? app.icon : undefined,
180
186
  color: app.color ?? buoyColors.primary,
@@ -188,10 +194,11 @@ export const DialDevTools = ({
188
194
 
189
195
  // Only open modal if not a toggle-only tool.
190
196
  if (app.launchMode !== "toggle-only") {
191
- const resolvedIcon = typeof app.icon === "function" ? app.icon({
197
+ const resolvedIcon = renderAppIcon(app.icon, {
192
198
  slot: "dial",
193
- size: 20
194
- }) : app.icon;
199
+ size: 20,
200
+ color: app.color
201
+ });
195
202
  open({
196
203
  id: app.id,
197
204
  title: app.name,
@@ -6,6 +6,11 @@
6
6
  * one dependency and never touch `@buoy-gg/shared-ui` (which stays internal
7
7
  * and free to churn).
8
8
  *
9
+ * Design rules for this surface (keep it Tailwind-small):
10
+ * - Variants over components: a styling difference is a prop, not an export.
11
+ * - Behavior earns a component (CopyButton); a size preset does not.
12
+ * - The tool shell owns chrome — header/back/search buttons are not user API.
13
+ *
9
14
  * Everything here is considered API: additions are minor bumps, removals are
10
15
  * major. If a component you want is missing, that's deliberate — request it
11
16
  * rather than deep-importing.
@@ -14,32 +19,44 @@
14
19
  // ---- Theme tokens ---------------------------------------------------------
15
20
  export { toolTheme } from "@buoy-gg/shared-ui";
16
21
 
17
- // ---- Layout / lists -------------------------------------------------------
18
- export { CompactRow, SectionHeader, TabSelector, EmptyState, NoDataEmptyState, NoResultsEmptyState, NoSearchResultsEmptyState } from "@buoy-gg/shared-ui";
19
-
20
- // ---- Badges ---------------------------------------------------------------
21
- export { Badge, StatusBadge, CountBadge, TypeBadge, MethodBadge } from "@buoy-gg/shared-ui";
22
+ // ---- Badges & status ------------------------------------------------------
23
+ // One Badge for every chip job; color it with the semantic helpers.
24
+ export { Badge, StatusDot, statusColor, methodColor, typeColor } from "./toolkitComponents.js";
22
25
 
23
26
  // ---- Buttons --------------------------------------------------------------
24
- export { ToolButton, CopyButton, InlineCopyButton, ToolbarCopyButton, ToolbarClearButton, PowerToggleButton, HeaderSearchButton } from "@buoy-gg/shared-ui";
27
+ export { ToolButton as Button, CopyButton, PowerToggleButton as PowerToggle } from "@buoy-gg/shared-ui";
28
+
29
+ // ---- Rows & cards ---------------------------------------------------------
30
+ export { CompactRow as Row, DevToolsCard as Card } from "@buoy-gg/shared-ui";
31
+ export { InfoRow } from "./toolkitComponents.js";
25
32
 
26
- // ---- Detail / data display ------------------------------------------------
27
- export { EventStepperFooter } from "@buoy-gg/shared-ui";
33
+ // ---- Structure ------------------------------------------------------------
34
+ export { SectionHeader, CollapsibleSection, TabSelector, EmptyState, SearchBar, EventStepperFooter } from "@buoy-gg/shared-ui";
35
+
36
+ // ---- Data -----------------------------------------------------------------
28
37
  export { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
38
+ export { DiffViewer } from "./toolkitComponents.js";
39
+
40
+ // ---- Stores (event log / observable state for tool data) ------------------
41
+ export { BaseEventStore } from "@buoy-gg/shared-ui";
29
42
 
30
43
  // ---- Time -----------------------------------------------------------------
31
- export { RelativeTime, formatRelativeTime } from "@buoy-gg/shared-ui";
44
+ // The tool shell mounts the shared tick clock, so RelativeTime just works.
45
+ export { RelativeTime } from "@buoy-gg/shared-ui";
46
+
47
+ // ---- Formatting -----------------------------------------------------------
48
+ export { formatBytes, formatDuration, formatNumber, truncateMiddle, safeStringify } from "@buoy-gg/shared-ui";
32
49
 
33
50
  // ---- Persistence ----------------------------------------------------------
34
- export { persistentStorage, devToolsStorageKeys, usePersistedState } from "@buoy-gg/shared-ui";
51
+ export { usePersistedState, persistentStorage, devToolsStorageKeys } from "@buoy-gg/shared-ui";
35
52
 
36
53
  // ---- Hooks / utils --------------------------------------------------------
37
- export { useSafeAreaInsets, copyToClipboard, displayValue, safeStringify } from "@buoy-gg/shared-ui";
54
+ export { useSafeAreaInsets, copyToClipboard } from "@buoy-gg/shared-ui";
38
55
 
39
- // ---- Manual chrome (advanced) --------------------------------------------
40
- // Most tools should use `InstalledApp.modal` + `useToolHost` instead; these
41
- // exist for tools that need full control of their shell.
42
- export { JsModal, ModalHeader } from "@buoy-gg/shared-ui";
56
+ // ---- Manual chrome (advanced escape hatch) --------------------------------
57
+ // Tools should use `InstalledApp.modal` + `useToolHost` instead; JsModal is
58
+ // for the rare tool that needs full control of its shell.
59
+ export { JsModal } from "@buoy-gg/shared-ui";
43
60
 
44
61
  // ---- Icons (curated lucide set) ------------------------------------------
45
- export { Activity, AlertTriangle, Bug, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Copy, Database, Eye, EyeOff, FileJson, FileText, Filter, FlaskConical, Globe, Hash, Info, Key, Layers, Lock, Pause, Play, Plus, Power, RefreshCw, Search, Settings, Shield, Trash2, User, Wifi, X, Zap } from "@buoy-gg/shared-ui";
62
+ export { Activity, AlertTriangle, Bug, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Copy, Database, Eye, EyeOff, FileJson, FileText, Filter, FlaskConical, Globe, Hash, Info, Key, Layers, Lock, Pause, Play, Plus, Power, RefreshCw, Search, Settings, Shield, Trash2, User, Wifi, X, Zap, AlertCircle, BarChart3, Bookmark, Cloud, Download, Edit3, GitBranch, HardDrive, HelpCircle, Home, Image, Link, Maximize2, Minus, Palette, Pin, Route, Server, Smartphone, Square, CheckSquare, Timer, Unlock, Upload, Users, XCircle } from "@buoy-gg/shared-ui";
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Facade primitives for the public custom-tools toolkit (see toolkit.ts).
5
+ *
6
+ * shared-ui grew several overlapping treatments of the same jobs (seven badge
7
+ * variants, four copy buttons, two diff viewers...). The public surface merges
8
+ * each job into ONE component with variants, implemented here on toolTheme —
9
+ * shared-ui's internals stay unchanged for first-party tools.
10
+ */
11
+ import React from "react";
12
+ import { StyleSheet, Text, View } from "react-native";
13
+ import { CopyButton, toolTheme } from "@buoy-gg/shared-ui";
14
+ import { TreeDiffViewer, SplitDiffViewer } from "@buoy-gg/shared-ui/dataViewer";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Semantic color helpers — pair with <Badge>/<StatusDot> instead of exporting
18
+ // a preset component per domain.
19
+ // ---------------------------------------------------------------------------
20
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
21
+ const STATUS_COLORS = {
22
+ success: toolTheme.colors.success,
23
+ active: toolTheme.colors.success,
24
+ connected: toolTheme.colors.success,
25
+ error: toolTheme.colors.error,
26
+ failed: toolTheme.colors.error,
27
+ warning: toolTheme.colors.warning,
28
+ stale: toolTheme.colors.warning,
29
+ pending: toolTheme.colors.info,
30
+ loading: toolTheme.colors.info,
31
+ fetching: toolTheme.colors.info,
32
+ info: toolTheme.colors.info,
33
+ inactive: toolTheme.colors.textMuted,
34
+ idle: toolTheme.colors.textMuted
35
+ };
36
+
37
+ /** Map a status word ("success", "pending", "stale"...) to a semantic color. */
38
+ export function statusColor(status) {
39
+ return STATUS_COLORS[status.toLowerCase()] ?? toolTheme.colors.textMuted;
40
+ }
41
+ const METHOD_COLORS = {
42
+ GET: toolTheme.colors.info,
43
+ POST: toolTheme.colors.success,
44
+ PUT: toolTheme.colors.warning,
45
+ PATCH: toolTheme.colors.warning,
46
+ DELETE: toolTheme.colors.error
47
+ };
48
+
49
+ /** Map an HTTP method to its conventional color. */
50
+ export function methodColor(method) {
51
+ return METHOD_COLORS[method.toUpperCase()] ?? toolTheme.colors.textSecondary;
52
+ }
53
+ const TYPE_COLORS = {
54
+ string: toolTheme.colors.success,
55
+ number: toolTheme.colors.info,
56
+ boolean: toolTheme.colors.warning,
57
+ object: toolTheme.colors.accent,
58
+ array: toolTheme.colors.accent,
59
+ function: toolTheme.colors.error,
60
+ null: toolTheme.colors.textMuted,
61
+ undefined: toolTheme.colors.textMuted
62
+ };
63
+
64
+ /** Map a JS value-type name to its display color. */
65
+ export function typeColor(type) {
66
+ return TYPE_COLORS[type.toLowerCase()] ?? toolTheme.colors.textSecondary;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Badge — the one chip. Color it with statusColor()/methodColor()/typeColor()
71
+ // (or any hex); shape/typography are props, not sibling components.
72
+ // ---------------------------------------------------------------------------
73
+
74
+ export function Badge({
75
+ children,
76
+ color = toolTheme.colors.textSecondary,
77
+ icon,
78
+ size = "small",
79
+ pill = false,
80
+ mono = false,
81
+ style
82
+ }) {
83
+ const isText = typeof children === "string" || typeof children === "number";
84
+ return /*#__PURE__*/_jsxs(View, {
85
+ style: [badgeStyles.base, size === "medium" && badgeStyles.medium, pill && badgeStyles.pill, {
86
+ backgroundColor: color + "1A",
87
+ borderColor: color + "40"
88
+ }, style],
89
+ children: [icon, isText ? /*#__PURE__*/_jsx(Text, {
90
+ style: [badgeStyles.text, size === "medium" && badgeStyles.textMedium, mono && badgeStyles.textMono, {
91
+ color
92
+ }],
93
+ numberOfLines: 1,
94
+ children: children
95
+ }) : children]
96
+ });
97
+ }
98
+ const badgeStyles = StyleSheet.create({
99
+ base: {
100
+ flexDirection: "row",
101
+ alignItems: "center",
102
+ alignSelf: "flex-start",
103
+ gap: 4,
104
+ borderWidth: 1,
105
+ borderRadius: toolTheme.radius.sm,
106
+ paddingHorizontal: 6,
107
+ paddingVertical: 2
108
+ },
109
+ medium: {
110
+ paddingHorizontal: 8,
111
+ paddingVertical: 3
112
+ },
113
+ pill: {
114
+ borderRadius: toolTheme.radius.pill,
115
+ minWidth: 20,
116
+ justifyContent: "center"
117
+ },
118
+ text: {
119
+ fontSize: 10,
120
+ fontWeight: "600",
121
+ letterSpacing: 0.3
122
+ },
123
+ textMedium: {
124
+ fontSize: 12
125
+ },
126
+ textMono: {
127
+ fontFamily: toolTheme.text.mono.fontFamily
128
+ }
129
+ });
130
+
131
+ // ---------------------------------------------------------------------------
132
+ // StatusDot — the bare status circle (list rows, connection indicators).
133
+ // ---------------------------------------------------------------------------
134
+
135
+ export function StatusDot({
136
+ status,
137
+ color,
138
+ size = 8,
139
+ style
140
+ }) {
141
+ const dotColor = color ?? (status ? statusColor(status) : toolTheme.colors.textMuted);
142
+ return /*#__PURE__*/_jsx(View, {
143
+ style: [{
144
+ width: size,
145
+ height: size,
146
+ borderRadius: size / 2,
147
+ backgroundColor: dotColor
148
+ }, style]
149
+ });
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // InfoRow — the label/value line of every detail screen, with optional copy.
154
+ // ---------------------------------------------------------------------------
155
+
156
+ export function InfoRow({
157
+ label,
158
+ value,
159
+ copyable = false,
160
+ style
161
+ }) {
162
+ const isText = typeof value === "string" || typeof value === "number";
163
+ return /*#__PURE__*/_jsxs(View, {
164
+ style: [infoRowStyles.row, style],
165
+ children: [/*#__PURE__*/_jsx(Text, {
166
+ style: infoRowStyles.label,
167
+ numberOfLines: 1,
168
+ children: label
169
+ }), /*#__PURE__*/_jsx(View, {
170
+ style: infoRowStyles.value,
171
+ children: isText ? /*#__PURE__*/_jsx(Text, {
172
+ style: infoRowStyles.valueText,
173
+ numberOfLines: 2,
174
+ children: String(value)
175
+ }) : value
176
+ }), copyable && value != null && /*#__PURE__*/_jsx(CopyButton, {
177
+ value: value,
178
+ size: 12
179
+ })]
180
+ });
181
+ }
182
+ const infoRowStyles = StyleSheet.create({
183
+ row: {
184
+ flexDirection: "row",
185
+ alignItems: "center",
186
+ gap: toolTheme.spacing.sm,
187
+ paddingVertical: 3
188
+ },
189
+ label: {
190
+ ...toolTheme.text.label,
191
+ color: toolTheme.colors.textMuted,
192
+ minWidth: 72
193
+ },
194
+ value: {
195
+ flex: 1,
196
+ flexDirection: "row",
197
+ alignItems: "center"
198
+ },
199
+ valueText: {
200
+ ...toolTheme.text.mono,
201
+ color: toolTheme.colors.text
202
+ }
203
+ });
204
+
205
+ // ---------------------------------------------------------------------------
206
+ // DiffViewer — one before/after component; the rendering strategy is a mode.
207
+ // ---------------------------------------------------------------------------
208
+
209
+ export function DiffViewer({
210
+ oldValue,
211
+ newValue,
212
+ mode = "tree",
213
+ showUnchanged = true,
214
+ scrollEnabled = false,
215
+ height = 220
216
+ }) {
217
+ if (mode === "text") {
218
+ return /*#__PURE__*/_jsx(SplitDiffViewer, {
219
+ oldValue: oldValue,
220
+ newValue: newValue,
221
+ height: height
222
+ });
223
+ }
224
+ return /*#__PURE__*/_jsx(TreeDiffViewer, {
225
+ oldValue: oldValue,
226
+ newValue: newValue,
227
+ showUnchanged: showUnchanged,
228
+ scrollEnabled: scrollEnabled
229
+ });
230
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"AppHost.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/AppHost.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAQZ,SAAS,EACV,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,WAAW,EAIZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAIjC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,KAAK,MAAM,CAAC;IACvD,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IACrC,wDAAwD;IACxD,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mFAAmF;IACnF,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACxE,4CAA4C;IAC5C,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C,CAAC;AAaF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,sBA4NpE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,2BAgBtB,CAAC;AAkHF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,gCAsFtB,CAAC"}
1
+ {"version":3,"file":"AppHost.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/AppHost.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAQZ,SAAS,EACV,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,WAAW,EAIZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAEL,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAIjC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,KAAK,MAAM,CAAC;IACvD,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IACrC,wDAAwD;IACxD,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mFAAmF;IACnF,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACxE,4CAA4C;IAC5C,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C,CAAC;AAaF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,sBA6NpE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,2BAgBtB,CAAC;AAkHF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,gCAsFtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"AppHost.web.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/AppHost.web.tsx"],"names":[],"mappings":"AAuBA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,WAAW,EACX,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAGxB,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC;IACtC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IACpC,wDAAwD;IACxD,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,4CAA4C;IAC5C,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C,CAAC;AAuBF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,gCAsNpE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,QAAO,mBAgB7B,CAAC;AAmGF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,0CA8BtB,CAAC"}
1
+ {"version":3,"file":"AppHost.web.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/AppHost.web.tsx"],"names":[],"mappings":"AAuBA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,WAAW,EACX,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAIxB,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC;IACtC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IACpC,wDAAwD;IACxD,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,4CAA4C;IAC5C,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C,CAAC;AAuBF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,gCAuNpE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,QAAO,mBAgB7B,CAAC;AAmGF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,0CA8BtB,CAAC"}