@buoy-gg/core 7.0.17 → 7.0.18
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/floatingMenu/AppHost.js +5 -3
- package/lib/commonjs/floatingMenu/AppHost.web.js +6 -1
- package/lib/commonjs/floatingMenu/AppIcon.js +91 -0
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +15 -9
- package/lib/commonjs/floatingMenu/FloatingMenu.js +12 -6
- package/lib/commonjs/floatingMenu/ToolHost.js +13 -3
- package/lib/commonjs/floatingMenu/autoExternalSync.js +12 -2
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +13 -6
- package/lib/commonjs/toolkit.js +201 -44
- package/lib/commonjs/toolkitComponents.js +241 -0
- package/lib/module/floatingMenu/AppHost.js +5 -3
- package/lib/module/floatingMenu/AppHost.web.js +6 -1
- package/lib/module/floatingMenu/AppIcon.js +84 -0
- package/lib/module/floatingMenu/FloatingDevTools.js +15 -9
- package/lib/module/floatingMenu/FloatingMenu.js +12 -6
- package/lib/module/floatingMenu/ToolHost.js +14 -4
- package/lib/module/floatingMenu/autoExternalSync.js +12 -2
- package/lib/module/floatingMenu/dial/DialDevTools.js +13 -6
- package/lib/module/toolkit.js +33 -16
- package/lib/module/toolkitComponents.js +230 -0
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts +30 -0
- package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts +5 -0
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +8 -1
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/types.d.ts +32 -1
- package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/toolkit.d.ts +19 -10
- package/lib/typescript/commonjs/toolkit.d.ts.map +1 -1
- package/lib/typescript/commonjs/toolkitComponents.d.ts +61 -0
- package/lib/typescript/commonjs/toolkitComponents.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/AppIcon.d.ts +30 -0
- package/lib/typescript/module/floatingMenu/AppIcon.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts +5 -0
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +8 -1
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/types.d.ts +32 -1
- package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -1
- package/lib/typescript/module/toolkit.d.ts +19 -10
- package/lib/typescript/module/toolkit.d.ts.map +1 -1
- package/lib/typescript/module/toolkitComponents.d.ts +61 -0
- package/lib/typescript/module/toolkitComponents.d.ts.map +1 -0
- package/package.json +19 -5
|
@@ -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 =
|
|
68
|
+
const resolvedIcon = renderAppIcon(appDef.icon, {
|
|
68
69
|
slot: "dial",
|
|
69
|
-
size: 20
|
|
70
|
-
|
|
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 =
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
//
|
|
173
|
-
const resolvedIcon =
|
|
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
|
-
|
|
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,
|
|
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(
|
|
208
|
-
|
|
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
|
|
176
|
-
// component (for dynamic
|
|
177
|
-
|
|
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 =
|
|
197
|
+
const resolvedIcon = renderAppIcon(app.icon, {
|
|
192
198
|
slot: "dial",
|
|
193
|
-
size: 20
|
|
194
|
-
|
|
199
|
+
size: 20,
|
|
200
|
+
color: app.color
|
|
201
|
+
});
|
|
195
202
|
open({
|
|
196
203
|
id: app.id,
|
|
197
204
|
title: app.name,
|
package/lib/module/toolkit.js
CHANGED
|
@@ -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
|
-
// ----
|
|
18
|
-
|
|
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
|
|
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
|
-
// ----
|
|
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
|
-
|
|
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
|
|
51
|
+
export { usePersistedState, persistentStorage, devToolsStorageKeys } from "@buoy-gg/shared-ui";
|
|
35
52
|
|
|
36
53
|
// ---- Hooks / utils --------------------------------------------------------
|
|
37
|
-
export { useSafeAreaInsets, copyToClipboard
|
|
54
|
+
export { useSafeAreaInsets, copyToClipboard } from "@buoy-gg/shared-ui";
|
|
38
55
|
|
|
39
|
-
// ---- Manual chrome (advanced)
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
export { JsModal
|
|
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;
|
|
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;
|
|
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"}
|