@buoy-gg/core 7.0.16 → 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.
Files changed (77) hide show
  1. package/lib/commonjs/floatingMenu/AppHost.js +23 -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 +14 -7
  6. package/lib/commonjs/floatingMenu/ToolHost.js +274 -0
  7. package/lib/commonjs/floatingMenu/allToolsRegistry.js +7 -0
  8. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +16 -0
  9. package/lib/commonjs/floatingMenu/autoExternalSync.js +21 -2
  10. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +15 -7
  11. package/lib/commonjs/index.js +27 -0
  12. package/lib/commonjs/toolkit.js +584 -0
  13. package/lib/commonjs/toolkitComponents.js +241 -0
  14. package/lib/module/floatingMenu/AppHost.js +23 -3
  15. package/lib/module/floatingMenu/AppHost.web.js +6 -1
  16. package/lib/module/floatingMenu/AppIcon.js +84 -0
  17. package/lib/module/floatingMenu/FloatingDevTools.js +15 -9
  18. package/lib/module/floatingMenu/FloatingMenu.js +14 -7
  19. package/lib/module/floatingMenu/ToolHost.js +266 -0
  20. package/lib/module/floatingMenu/allToolsRegistry.js +8 -1
  21. package/lib/module/floatingMenu/autoDiscoverPresets.js +16 -0
  22. package/lib/module/floatingMenu/autoExternalSync.js +21 -2
  23. package/lib/module/floatingMenu/dial/DialDevTools.js +15 -7
  24. package/lib/module/index.js +6 -0
  25. package/lib/module/toolkit.js +62 -0
  26. package/lib/module/toolkitComponents.js +230 -0
  27. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +3 -0
  30. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts +30 -0
  32. package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts +77 -0
  36. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -0
  37. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +8 -1
  40. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  42. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/floatingMenu/types.d.ts +62 -2
  45. package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/index.d.ts +2 -0
  47. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/toolkit.d.ts +31 -0
  49. package/lib/typescript/commonjs/toolkit.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/toolkitComponents.d.ts +61 -0
  51. package/lib/typescript/commonjs/toolkitComponents.d.ts.map +1 -0
  52. package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -1
  53. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +1 -1
  54. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +3 -0
  55. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -1
  56. package/lib/typescript/module/floatingMenu/AppIcon.d.ts +30 -0
  57. package/lib/typescript/module/floatingMenu/AppIcon.d.ts.map +1 -0
  58. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  59. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
  60. package/lib/typescript/module/floatingMenu/ToolHost.d.ts +77 -0
  61. package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -0
  62. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts.map +1 -1
  63. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  64. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +8 -1
  65. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  66. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  67. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
  68. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  69. package/lib/typescript/module/floatingMenu/types.d.ts +62 -2
  70. package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -1
  71. package/lib/typescript/module/index.d.ts +2 -0
  72. package/lib/typescript/module/index.d.ts.map +1 -1
  73. package/lib/typescript/module/toolkit.d.ts +31 -0
  74. package/lib/typescript/module/toolkit.d.ts.map +1 -0
  75. package/lib/typescript/module/toolkitComponents.d.ts +61 -0
  76. package/lib/typescript/module/toolkitComponents.d.ts.map +1 -0
  77. package/package.json +19 -5
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ToolHost — the declarative chrome layer for custom tools.
5
+ *
6
+ * When an `InstalledApp` declares `modal: {...}`, the AppHost renders its
7
+ * component inside `ToolModalShell` instead of raw: the shared JsModal
8
+ * (drag/resize, floating⇄bottom-sheet, minimize, dimension persistence) plus
9
+ * a managed header (title/subtitle, back navigation, search, actions) and a
10
+ * lightweight toast layer. The tool body talks back to the shell through
11
+ * `useToolHost()` / `useToolHeader()`.
12
+ */
13
+ import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
14
+ import { Animated, StyleSheet, Text, View } from "react-native";
15
+ import { JsModal, ModalHeader, HeaderSearchButton, TickProvider, devToolsStorageKeys, buoyColors, gameUIColors } from "@buoy-gg/shared-ui";
16
+
17
+ /** Per-screen header overrides a tool body can apply (see `useToolHeader`). */
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ const noop = () => {};
20
+ const FALLBACK_CONTEXT = {
21
+ search: "",
22
+ isSearchActive: false,
23
+ clearSearch: noop,
24
+ close: noop,
25
+ showToast: noop,
26
+ setHeaderOverride: noop
27
+ };
28
+ const ToolHostContext = /*#__PURE__*/createContext(null);
29
+
30
+ /**
31
+ * Access the hosting shell from a custom tool body: search query, toasts,
32
+ * close. Safe to call outside the shell (returns inert no-ops) so tool
33
+ * components can also be mounted standalone.
34
+ */
35
+ export function useToolHost() {
36
+ return useContext(ToolHostContext) ?? FALLBACK_CONTEXT;
37
+ }
38
+
39
+ /**
40
+ * Declare per-screen header state (title/subtitle/actions/back) from inside a
41
+ * tool body. Applies while the calling component is mounted and reverts to
42
+ * the preset's static config on unmount — so a detail screen simply mounts
43
+ * with its own header and the list header returns when it unmounts.
44
+ *
45
+ * ```tsx
46
+ * useToolHeader(
47
+ * { title: flag.key, onBack: () => setScreen("list"), actions: <CopyButton value={flag} /> },
48
+ * [flag.key]
49
+ * );
50
+ * ```
51
+ */
52
+ export function useToolHeader(override, deps) {
53
+ const {
54
+ setHeaderOverride
55
+ } = useToolHost();
56
+ useEffect(() => {
57
+ setHeaderOverride(override);
58
+ return () => setHeaderOverride(null);
59
+ // Caller-provided deps intentionally control when the override refreshes.
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, deps);
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Toast layer
66
+ // ---------------------------------------------------------------------------
67
+
68
+ const TOAST_DURATION_MS = 2200;
69
+ const TOAST_COLORS = {
70
+ success: gameUIColors.success,
71
+ error: gameUIColors.error,
72
+ info: gameUIColors.info
73
+ };
74
+ function ToastView({
75
+ message,
76
+ type,
77
+ nonce
78
+ }) {
79
+ const opacity = useRef(new Animated.Value(0)).current;
80
+ useEffect(() => {
81
+ opacity.setValue(0);
82
+ Animated.sequence([Animated.timing(opacity, {
83
+ toValue: 1,
84
+ duration: 150,
85
+ useNativeDriver: true
86
+ }), Animated.delay(TOAST_DURATION_MS - 450), Animated.timing(opacity, {
87
+ toValue: 0,
88
+ duration: 300,
89
+ useNativeDriver: true
90
+ })]).start();
91
+ }, [nonce, opacity]);
92
+ const color = TOAST_COLORS[type];
93
+ return /*#__PURE__*/_jsxs(Animated.View, {
94
+ pointerEvents: "none",
95
+ style: [styles.toast, {
96
+ opacity,
97
+ borderColor: color + "60"
98
+ }],
99
+ children: [/*#__PURE__*/_jsx(View, {
100
+ style: [styles.toastDot, {
101
+ backgroundColor: color
102
+ }]
103
+ }), /*#__PURE__*/_jsx(Text, {
104
+ style: styles.toastText,
105
+ numberOfLines: 2,
106
+ children: message
107
+ })]
108
+ });
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Shell
113
+ // ---------------------------------------------------------------------------
114
+
115
+ export function ToolModalShell({
116
+ app,
117
+ config,
118
+ visible,
119
+ zIndex,
120
+ onClose,
121
+ onMinimize,
122
+ initialModalState,
123
+ enableSharedModalDimensions,
124
+ bodyProps
125
+ }) {
126
+ const Body = app.component;
127
+ const [isSearchActive, setIsSearchActive] = useState(false);
128
+ const [search, setSearch] = useState("");
129
+ const [headerOverride, setHeaderOverride] = useState(null);
130
+ const [toast, setToast] = useState(null);
131
+ const toastTimeoutRef = useRef(null);
132
+ const clearSearch = useCallback(() => {
133
+ setIsSearchActive(false);
134
+ setSearch("");
135
+ }, []);
136
+ const showToast = useCallback((message, type = "info") => {
137
+ if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current);
138
+ setToast(prev => ({
139
+ message,
140
+ type,
141
+ nonce: (prev?.nonce ?? 0) + 1
142
+ }));
143
+ toastTimeoutRef.current = setTimeout(() => setToast(null), TOAST_DURATION_MS);
144
+ }, []);
145
+ useEffect(() => {
146
+ return () => {
147
+ if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current);
148
+ };
149
+ }, []);
150
+ const contextValue = useMemo(() => ({
151
+ search: isSearchActive ? search : "",
152
+ isSearchActive,
153
+ clearSearch,
154
+ close: onClose,
155
+ showToast,
156
+ setHeaderOverride,
157
+ instanceId: app.instanceId
158
+ }), [search, isSearchActive, clearSearch, onClose, showToast, app.instanceId]);
159
+ const title = headerOverride?.title ?? config.title ?? app.title ?? app.id;
160
+ const subtitle = headerOverride?.subtitle ?? config.subtitle;
161
+ const onBack = headerOverride?.onBack;
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]);
170
+
171
+ // Tool-scoped persistence via the custom namespace unless the user has the
172
+ // shared-dimensions global setting on (all tools share one window size).
173
+ const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.custom(app.id).modal();
174
+ const header = /*#__PURE__*/_jsxs(ModalHeader, {
175
+ children: [onBack && !isSearchActive && /*#__PURE__*/_jsx(ModalHeader.Navigation, {
176
+ onBack: onBack
177
+ }), isSearchActive ? /*#__PURE__*/_jsx(ModalHeader.Search, {
178
+ value: search,
179
+ onChangeText: setSearch,
180
+ onCancel: clearSearch,
181
+ placeholder: config.searchPlaceholder
182
+ }) : /*#__PURE__*/_jsx(ModalHeader.Content, {
183
+ title: title,
184
+ subtitle: subtitle
185
+ }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
186
+ children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
187
+ testID: `tool-search-${app.id}`,
188
+ onPress: () => setIsSearchActive(true)
189
+ })]
190
+ })]
191
+ });
192
+ return /*#__PURE__*/_jsx(ToolHostContext.Provider, {
193
+ value: contextValue,
194
+ children: /*#__PURE__*/_jsx(JsModal, {
195
+ visible: visible,
196
+ onClose: onClose,
197
+ onBack: onBack,
198
+ onMinimize: onMinimize,
199
+ initialModalState: initialModalState,
200
+ zIndex: zIndex,
201
+ persistenceKey: persistenceKey,
202
+ enablePersistence: true,
203
+ initialMode: config.initialMode ?? "bottomSheet",
204
+ header: {
205
+ customContent: header,
206
+ showToggleButton: true
207
+ }
208
+ // Tool bodies own their scrolling (ScrollView/FlatList) — the shell
209
+ // must not add a competing wrapper.
210
+ ,
211
+ disableScrollWrapper: true,
212
+ styles: {},
213
+ children: /*#__PURE__*/_jsxs(View, {
214
+ style: styles.bodyRoot,
215
+ children: [/*#__PURE__*/_jsx(TickProvider, {
216
+ children: /*#__PURE__*/_jsx(Body, {
217
+ ...bodyProps
218
+ })
219
+ }), toast && /*#__PURE__*/_jsx(ToastView, {
220
+ message: toast.message,
221
+ type: toast.type,
222
+ nonce: toast.nonce
223
+ })]
224
+ })
225
+ })
226
+ });
227
+ }
228
+ const styles = StyleSheet.create({
229
+ bodyRoot: {
230
+ flex: 1,
231
+ minHeight: 0,
232
+ backgroundColor: buoyColors.base
233
+ },
234
+ toast: {
235
+ position: "absolute",
236
+ bottom: 16,
237
+ alignSelf: "center",
238
+ flexDirection: "row",
239
+ alignItems: "center",
240
+ gap: 8,
241
+ maxWidth: "86%",
242
+ backgroundColor: buoyColors.card,
243
+ borderWidth: 1,
244
+ borderRadius: 8,
245
+ paddingHorizontal: 12,
246
+ paddingVertical: 8,
247
+ shadowColor: "#000",
248
+ shadowOpacity: 0.4,
249
+ shadowRadius: 8,
250
+ shadowOffset: {
251
+ width: 0,
252
+ height: 2
253
+ },
254
+ elevation: 6
255
+ },
256
+ toastDot: {
257
+ width: 6,
258
+ height: 6,
259
+ borderRadius: 3
260
+ },
261
+ toastText: {
262
+ flex: 1,
263
+ fontSize: 12,
264
+ color: buoyColors.text
265
+ }
266
+ });
@@ -27,7 +27,7 @@ import { ImageOverlayIcon } from "@buoy-gg/shared-ui";
27
27
  // in shared/icons/*.json and generated into @buoy-gg/floating-tools-core, which
28
28
  // IS a required dependency. Unavailable items therefore render the ACTUAL tool
29
29
  // icon (just dimmed by DialIcon), and it can no longer drift from the real one.
30
- import { ZustandIcon, ZUSTAND_ICON_COLOR, JotaiIcon, JOTAI_ICON_COLOR, IMAGE_OVERLAY_ICON_COLOR } from "@buoy-gg/floating-tools-core";
30
+ import { ZustandIcon, ZUSTAND_ICON_COLOR, JotaiIcon, JOTAI_ICON_COLOR, IMAGE_OVERLAY_ICON_COLOR, TimeMachineIcon, TIME_MACHINE_ICON_COLOR } from "@buoy-gg/floating-tools-core";
31
31
 
32
32
  // ─── Registry entry type ──────────────────────────────────────────────────────
33
33
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -109,6 +109,13 @@ export const ALL_TOOLS_REGISTRY = [{
109
109
  renderIcon: size => /*#__PURE__*/_jsx(EventsIcon, {
110
110
  size: size
111
111
  })
112
+ }, {
113
+ id: "time-machine",
114
+ name: "TIME MACHINE",
115
+ color: TIME_MACHINE_ICON_COLOR,
116
+ renderIcon: size => /*#__PURE__*/_jsx(TimeMachineIcon, {
117
+ size: size
118
+ })
112
119
  }, {
113
120
  id: "jotai",
114
121
  name: "JOTAI",
@@ -185,6 +185,22 @@ export function autoDiscoverPresets() {
185
185
  }
186
186
  }
187
187
  },
188
+ // Time Machine
189
+ {
190
+ name: "@buoy-gg/time-machine",
191
+ loader: () => {
192
+ try {
193
+ // @ts-ignore - Dynamic import that may not exist
194
+ const {
195
+ timeMachineToolPreset
196
+ } = require("@buoy-gg/time-machine");
197
+ return timeMachineToolPreset;
198
+ } catch (e) {
199
+ reportOptionalRequireError("@buoy-gg/time-machine", e);
200
+ return null;
201
+ }
202
+ }
203
+ },
188
204
  // Redux DevTools
189
205
  {
190
206
  name: "@buoy-gg/redux",
@@ -39,6 +39,7 @@ let zustandModule = null;
39
39
  let jotaiModule = null;
40
40
  let eventsModule = null;
41
41
  let consoleModule = null;
42
+ let timeMachineModule = null;
42
43
  let envModule = null;
43
44
  let highlightUpdatesModule = null;
44
45
  let debugBordersModule = null;
@@ -102,6 +103,11 @@ try {
102
103
  } catch (e) {
103
104
  reportOptionalRequireError("@buoy-gg/console", e);
104
105
  }
106
+ try {
107
+ timeMachineModule = require("@buoy-gg/time-machine");
108
+ } catch (e) {
109
+ reportOptionalRequireError("@buoy-gg/time-machine", e);
110
+ }
105
111
  try {
106
112
  envModule = require("@buoy-gg/env");
107
113
  } catch (e) {
@@ -169,7 +175,8 @@ export function AutoExternalSync({
169
175
  options,
170
176
  requiredEnvVars,
171
177
  isPro,
172
- licenseKey
178
+ licenseKey,
179
+ customApps
173
180
  }) {
174
181
  const {
175
182
  useExternalSyncSocket,
@@ -255,6 +262,9 @@ export function AutoExternalSync({
255
262
  if (consoleModule?.consoleSyncAdapter) {
256
263
  map.console = consoleModule.consoleSyncAdapter;
257
264
  }
265
+ if (timeMachineModule?.timeMachineSyncAdapter) {
266
+ map["time-machine"] = timeMachineModule.timeMachineSyncAdapter;
267
+ }
258
268
  if (envModule?.createEnvSyncAdapter) {
259
269
  map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
260
270
  }
@@ -279,8 +289,17 @@ export function AutoExternalSync({
279
289
  if (assetsModule?.assetsSyncAdapter) {
280
290
  map.assets = assetsModule.assetsSyncAdapter;
281
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
+ }
282
301
  return map;
283
- }, [queryAdapter, requiredEnvVars]);
302
+ }, [queryAdapter, requiredEnvVars, customApps]);
284
303
  useExternalSync({
285
304
  tools,
286
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,
@@ -200,7 +207,8 @@ export const DialDevTools = ({
200
207
  launchMode: app.launchMode ?? "self-modal",
201
208
  singleton: app.singleton ?? true,
202
209
  icon: resolvedIcon,
203
- color: app.color
210
+ color: app.color,
211
+ modal: app.modal
204
212
  });
205
213
  }
206
214
 
@@ -20,6 +20,12 @@ export { DevToolsSettingsModal, useDevToolsSettings } from "./floatingMenu/DevTo
20
20
  // Export AppHost components for advanced use cases
21
21
  export { AppHostProvider, AppOverlay, useAppHost } from "./floatingMenu/AppHost";
22
22
 
23
+ // Declarative chrome for custom tools (pairs with InstalledApp.modal)
24
+ export { useToolHost, useToolHeader } from "./floatingMenu/ToolHost.js";
25
+
26
+ // The custom-tools toolkit: blessed shared-ui components/hooks/tokens
27
+ export * from "./toolkit.js";
28
+
23
29
  // Export DevTools visibility tracking
24
30
  export { DevToolsVisibilityProvider, useDevToolsVisibility } from "./floatingMenu/DevToolsVisibilityContext.js";
25
31
 
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * The public custom-tools toolkit — the blessed subset of Buoy's shared UI
5
+ * re-exported from `@buoy-gg/core`, so third-party tool authors need exactly
6
+ * one dependency and never touch `@buoy-gg/shared-ui` (which stays internal
7
+ * and free to churn).
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
+ *
14
+ * Everything here is considered API: additions are minor bumps, removals are
15
+ * major. If a component you want is missing, that's deliberate — request it
16
+ * rather than deep-importing.
17
+ */
18
+
19
+ // ---- Theme tokens ---------------------------------------------------------
20
+ export { toolTheme } from "@buoy-gg/shared-ui";
21
+
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";
25
+
26
+ // ---- Buttons --------------------------------------------------------------
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";
32
+
33
+ // ---- Structure ------------------------------------------------------------
34
+ export { SectionHeader, CollapsibleSection, TabSelector, EmptyState, SearchBar, EventStepperFooter } from "@buoy-gg/shared-ui";
35
+
36
+ // ---- Data -----------------------------------------------------------------
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";
42
+
43
+ // ---- Time -----------------------------------------------------------------
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";
49
+
50
+ // ---- Persistence ----------------------------------------------------------
51
+ export { usePersistedState, persistentStorage, devToolsStorageKeys } from "@buoy-gg/shared-ui";
52
+
53
+ // ---- Hooks / utils --------------------------------------------------------
54
+ export { useSafeAreaInsets, copyToClipboard } from "@buoy-gg/shared-ui";
55
+
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";
60
+
61
+ // ---- Icons (curated lucide set) ------------------------------------------
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";