@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.
- package/lib/commonjs/floatingMenu/AppHost.js +23 -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 +14 -7
- package/lib/commonjs/floatingMenu/ToolHost.js +274 -0
- package/lib/commonjs/floatingMenu/allToolsRegistry.js +7 -0
- package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +16 -0
- package/lib/commonjs/floatingMenu/autoExternalSync.js +21 -2
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +15 -7
- package/lib/commonjs/index.js +27 -0
- package/lib/commonjs/toolkit.js +584 -0
- package/lib/commonjs/toolkitComponents.js +241 -0
- package/lib/module/floatingMenu/AppHost.js +23 -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 +14 -7
- package/lib/module/floatingMenu/ToolHost.js +266 -0
- package/lib/module/floatingMenu/allToolsRegistry.js +8 -1
- package/lib/module/floatingMenu/autoDiscoverPresets.js +16 -0
- package/lib/module/floatingMenu/autoExternalSync.js +21 -2
- package/lib/module/floatingMenu/dial/DialDevTools.js +15 -7
- package/lib/module/index.js +6 -0
- package/lib/module/toolkit.js +62 -0
- 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/AppHostLogic.d.ts +3 -0
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.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 +77 -0
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.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/defaultConfig.d.ts +1 -1
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.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 +62 -2
- package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/toolkit.d.ts +31 -0
- package/lib/typescript/commonjs/toolkit.d.ts.map +1 -0
- 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/AppHostLogic.d.ts +3 -0
- package/lib/typescript/module/floatingMenu/AppHostLogic.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 +77 -0
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.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/defaultConfig.d.ts +1 -1
- package/lib/typescript/module/floatingMenu/defaultConfig.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 +62 -2
- package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/toolkit.d.ts +31 -0
- package/lib/typescript/module/toolkit.d.ts.map +1 -0
- package/lib/typescript/module/toolkitComponents.d.ts +61 -0
- package/lib/typescript/module/toolkitComponents.d.ts.map +1 -0
- package/package.json +19 -5
|
@@ -8,7 +8,9 @@ var _sharedUi = require("@buoy-gg/shared-ui");
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _reactNative = require("react-native");
|
|
10
10
|
var _AppHostLogic = require("./AppHostLogic.js");
|
|
11
|
+
var _AppIcon = require("./AppIcon.js");
|
|
11
12
|
var _MinimizedToolsContext = require("./MinimizedToolsContext.js");
|
|
13
|
+
var _ToolHost = require("./ToolHost.js");
|
|
12
14
|
var _DevToolsSettingsModal = require("./DevToolsSettingsModal");
|
|
13
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -68,10 +70,11 @@ const AppHostProvider = ({
|
|
|
68
70
|
}) => {
|
|
69
71
|
const appDef = installedAppsRef.current.find(app => app.id === appId);
|
|
70
72
|
if (appDef) {
|
|
71
|
-
const resolvedIcon =
|
|
73
|
+
const resolvedIcon = (0, _AppIcon.renderAppIcon)(appDef.icon, {
|
|
72
74
|
slot: "dial",
|
|
73
|
-
size: 20
|
|
74
|
-
|
|
75
|
+
size: 20,
|
|
76
|
+
color: appDef.color
|
|
77
|
+
});
|
|
75
78
|
open({
|
|
76
79
|
id: appDef.id,
|
|
77
80
|
title: appDef.name,
|
|
@@ -81,6 +84,7 @@ const AppHostProvider = ({
|
|
|
81
84
|
singleton: appDef.singleton,
|
|
82
85
|
icon: resolvedIcon,
|
|
83
86
|
color: appDef.color,
|
|
87
|
+
modal: appDef.modal,
|
|
84
88
|
minimized: minimized // Preserve minimized state
|
|
85
89
|
});
|
|
86
90
|
}
|
|
@@ -257,6 +261,22 @@ const AppRenderer = ({
|
|
|
257
261
|
enableSharedModalDimensions: true
|
|
258
262
|
} : {})
|
|
259
263
|
};
|
|
264
|
+
|
|
265
|
+
// Declarative chrome: the component is a plain body — wrap it in the shared
|
|
266
|
+
// modal shell (JsModal + managed header + search + toasts).
|
|
267
|
+
if (app.modal && (app.launchMode === "self-modal" || !app.launchMode)) {
|
|
268
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolHost.ToolModalShell, {
|
|
269
|
+
app: app,
|
|
270
|
+
config: app.modal,
|
|
271
|
+
visible: isVisible,
|
|
272
|
+
zIndex: zIndex,
|
|
273
|
+
onClose: onClose,
|
|
274
|
+
onMinimize: onMinimize,
|
|
275
|
+
initialModalState: app.restoreState,
|
|
276
|
+
enableSharedModalDimensions: globalEnableSharedModalDimensions === true,
|
|
277
|
+
bodyProps: mergedProps
|
|
278
|
+
});
|
|
279
|
+
}
|
|
260
280
|
if (app.launchMode === "self-modal") {
|
|
261
281
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Comp, {
|
|
262
282
|
...mergedProps,
|
|
@@ -7,6 +7,7 @@ exports.useAppHost = exports.AppOverlay = exports.AppHostProvider = void 0;
|
|
|
7
7
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _AppHostLogic = require("./AppHostLogic.js");
|
|
10
|
+
var _AppIcon = require("./AppIcon.js");
|
|
10
11
|
var _floatingToolsWeb = require("./floatingTools.web.js");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
/**
|
|
@@ -86,7 +87,11 @@ const AppHostProvider = ({
|
|
|
86
87
|
}) => {
|
|
87
88
|
const appDef = installedAppsRef.current.find(app => app.id === appId);
|
|
88
89
|
if (appDef && appDef.component) {
|
|
89
|
-
const resolvedIcon =
|
|
90
|
+
const resolvedIcon = (0, _AppIcon.renderAppIcon)(appDef.icon, {
|
|
91
|
+
slot: "dial",
|
|
92
|
+
size: 20,
|
|
93
|
+
color: appDef.color
|
|
94
|
+
});
|
|
90
95
|
open({
|
|
91
96
|
id: appDef.id,
|
|
92
97
|
title: appDef.name,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.AppTextIcon = AppTextIcon;
|
|
7
|
+
exports.renderAppIcon = renderAppIcon;
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
/**
|
|
14
|
+
* Icon resolution for `InstalledApp.icon`.
|
|
15
|
+
*
|
|
16
|
+
* Custom tools may provide an icon three ways:
|
|
17
|
+
* - a component `({ slot, size }) => node` (dynamic, hook-capable)
|
|
18
|
+
* - a pre-built ReactNode
|
|
19
|
+
* - a PLAIN STRING — "AUTH", "KIT", "🗑️" — rendered as an auto-fitting text
|
|
20
|
+
* icon so simple tools never need an icon library.
|
|
21
|
+
*
|
|
22
|
+
* Every menu surface (dial, row, minimize stack, restore path) resolves icons
|
|
23
|
+
* through `renderAppIcon` so all three forms behave identically.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Visible glyphs, not UTF-16 units — "🗑️" is one glyph, not three. */
|
|
27
|
+
function glyphCount(label) {
|
|
28
|
+
// Strip variation selectors + zero-width joiners before counting.
|
|
29
|
+
const stripped = label.replace(/[\uFE00-\uFE0F\u200D]/g, "");
|
|
30
|
+
return Math.max(Array.from(stripped).length, 1);
|
|
31
|
+
}
|
|
32
|
+
function AppTextIcon({
|
|
33
|
+
label,
|
|
34
|
+
size,
|
|
35
|
+
color = _sharedUi.toolTheme.colors.text
|
|
36
|
+
}) {
|
|
37
|
+
const chars = glyphCount(label);
|
|
38
|
+
// Short labels stay square; longer ones ("AUTH") get a wider box — the
|
|
39
|
+
// dial/row layouts have breathing room and the label reads better than a
|
|
40
|
+
// squashed square would. The computed font size is what fits the box on
|
|
41
|
+
// web; adjustsFontSizeToFit covers native font-metric drift.
|
|
42
|
+
const maxWidth = chars <= 2 ? size : size * 1.6;
|
|
43
|
+
const fontSize = Math.max(7, Math.min(size * (chars === 1 ? 0.7 : 0.62), maxWidth / (0.62 * chars)));
|
|
44
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
45
|
+
style: {
|
|
46
|
+
minWidth: size,
|
|
47
|
+
maxWidth,
|
|
48
|
+
height: size,
|
|
49
|
+
alignItems: "center",
|
|
50
|
+
justifyContent: "center"
|
|
51
|
+
},
|
|
52
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
53
|
+
numberOfLines: 1,
|
|
54
|
+
adjustsFontSizeToFit: true,
|
|
55
|
+
minimumFontScale: 0.5,
|
|
56
|
+
style: {
|
|
57
|
+
color,
|
|
58
|
+
fontSize,
|
|
59
|
+
fontWeight: "800",
|
|
60
|
+
letterSpacing: 0.5,
|
|
61
|
+
textAlign: "center"
|
|
62
|
+
},
|
|
63
|
+
children: label
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve any `InstalledApp.icon` form to a renderable node. Function icons
|
|
70
|
+
* become elements (createElement — hooks keep working); strings become
|
|
71
|
+
* auto-fitting text icons; nodes pass through.
|
|
72
|
+
*/
|
|
73
|
+
function renderAppIcon(icon, ctx) {
|
|
74
|
+
if (icon == null) return icon;
|
|
75
|
+
if (typeof icon === "function") {
|
|
76
|
+
return /*#__PURE__*/_react.default.createElement(icon, {
|
|
77
|
+
slot: ctx.slot,
|
|
78
|
+
size: ctx.size,
|
|
79
|
+
state: ctx.state,
|
|
80
|
+
actions: ctx.actions
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (typeof icon === "string" || typeof icon === "number") {
|
|
84
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(AppTextIcon, {
|
|
85
|
+
label: String(icon),
|
|
86
|
+
size: ctx.size,
|
|
87
|
+
color: ctx.color
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return icon;
|
|
91
|
+
}
|
|
@@ -9,6 +9,7 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
var _AppHost = require("./AppHost");
|
|
10
10
|
var _FloatingMenu = require("./FloatingMenu.js");
|
|
11
11
|
var _autoDiscoverPresets = require("./autoDiscoverPresets.js");
|
|
12
|
+
var _AppIcon = require("./AppIcon.js");
|
|
12
13
|
var _DevToolsVisibilityContext = require("./DevToolsVisibilityContext.js");
|
|
13
14
|
var _optionalRequire = require("./optionalRequire.js");
|
|
14
15
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
@@ -230,6 +231,11 @@ const FloatingDevTools = ({
|
|
|
230
231
|
// identity (and re-ran discovery) on every parent render.
|
|
231
232
|
const finalApps = (0, _react.useMemo)(() => userApps ? (0, _autoDiscoverPresets.autoDiscoverPresetsWithCustom)(userApps) : (0, _autoDiscoverPresets.autoDiscoverPresets)(), [userApps]);
|
|
232
233
|
|
|
234
|
+
// Custom tools that declared a sync adapter — registered with the broker as
|
|
235
|
+
// `custom:<id>` by AutoExternalSync. Memoized so the sync tools map (keyed
|
|
236
|
+
// by identity) doesn't rebuild every render.
|
|
237
|
+
const syncApps = (0, _react.useMemo)(() => finalApps.filter(app => app.sync), [finalApps]);
|
|
238
|
+
|
|
233
239
|
// Check if debug-borders is installed and auto-render the overlay
|
|
234
240
|
const DebugBordersOverlay = (0, _react.useMemo)(() => {
|
|
235
241
|
try {
|
|
@@ -366,13 +372,11 @@ const FloatingDevTools = ({
|
|
|
366
372
|
const getToolIcon = (0, _react.useCallback)(id => {
|
|
367
373
|
const tool = finalApps.find(app => app.id === id);
|
|
368
374
|
if (!tool) return null;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
return tool.icon;
|
|
375
|
+
return (0, _AppIcon.renderAppIcon)(tool.icon, {
|
|
376
|
+
slot: "dial",
|
|
377
|
+
size: 20,
|
|
378
|
+
color: tool.color
|
|
379
|
+
});
|
|
376
380
|
}, [finalApps]);
|
|
377
381
|
|
|
378
382
|
// Gate: Free tier only works in development mode
|
|
@@ -394,7 +398,8 @@ const FloatingDevTools = ({
|
|
|
394
398
|
options: typeof externalSync === "object" ? externalSync : undefined,
|
|
395
399
|
requiredEnvVars: requiredEnvVars,
|
|
396
400
|
isPro: isLicensed,
|
|
397
|
-
licenseKey: licenseKey
|
|
401
|
+
licenseKey: licenseKey,
|
|
402
|
+
customApps: syncApps
|
|
398
403
|
})]
|
|
399
404
|
});
|
|
400
405
|
}
|
|
@@ -434,7 +439,8 @@ const FloatingDevTools = ({
|
|
|
434
439
|
options: typeof externalSync === "object" ? externalSync : undefined,
|
|
435
440
|
requiredEnvVars: requiredEnvVars,
|
|
436
441
|
isPro: isLicensed,
|
|
437
|
-
licenseKey: licenseKey
|
|
442
|
+
licenseKey: licenseKey,
|
|
443
|
+
customApps: syncApps
|
|
438
444
|
})]
|
|
439
445
|
})
|
|
440
446
|
})
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
10
|
var _floatingTools = require("./floatingTools");
|
|
11
|
+
var _AppIcon = require("./AppIcon.js");
|
|
11
12
|
var _DialDevTools = require("./dial/DialDevTools");
|
|
12
13
|
var _DevToolsSettingsModal = require("./DevToolsSettingsModal");
|
|
13
14
|
var _AppHost = require("./AppHost");
|
|
@@ -172,12 +173,13 @@ const FloatingMenu = ({
|
|
|
172
173
|
|
|
173
174
|
// Only open modal if not a toggle-only tool
|
|
174
175
|
if (app.launchMode !== "toggle-only") {
|
|
175
|
-
// Resolve the icon for minimize stack display
|
|
176
|
-
//
|
|
177
|
-
const resolvedIcon =
|
|
176
|
+
// Resolve the icon for minimize stack display (createElement keeps
|
|
177
|
+
// function-icon hooks working; strings become text icons).
|
|
178
|
+
const resolvedIcon = (0, _AppIcon.renderAppIcon)(app.icon, {
|
|
178
179
|
slot: "dial",
|
|
179
|
-
size: 20
|
|
180
|
-
|
|
180
|
+
size: 20,
|
|
181
|
+
color: app.color
|
|
182
|
+
});
|
|
181
183
|
open({
|
|
182
184
|
id: app.id,
|
|
183
185
|
title: app.name,
|
|
@@ -186,7 +188,8 @@ const FloatingMenu = ({
|
|
|
186
188
|
launchMode: app.launchMode ?? "self-modal",
|
|
187
189
|
singleton: app.singleton ?? true,
|
|
188
190
|
icon: resolvedIcon,
|
|
189
|
-
color: app.color
|
|
191
|
+
color: app.color,
|
|
192
|
+
modal: app.modal
|
|
190
193
|
});
|
|
191
194
|
}
|
|
192
195
|
};
|
|
@@ -284,7 +287,11 @@ const FloatingMenu = ({
|
|
|
284
287
|
actions: mergedActions
|
|
285
288
|
});
|
|
286
289
|
}
|
|
287
|
-
return app.icon
|
|
290
|
+
return (0, _AppIcon.renderAppIcon)(app.icon, {
|
|
291
|
+
slot: "row",
|
|
292
|
+
size: 16,
|
|
293
|
+
color: app.color
|
|
294
|
+
});
|
|
288
295
|
})()
|
|
289
296
|
}, `row-${app.id}`))]
|
|
290
297
|
})
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ToolModalShell = ToolModalShell;
|
|
7
|
+
exports.useToolHeader = useToolHeader;
|
|
8
|
+
exports.useToolHost = useToolHost;
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
+
var _reactNative = require("react-native");
|
|
11
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
|
+
/**
|
|
15
|
+
* ToolHost — the declarative chrome layer for custom tools.
|
|
16
|
+
*
|
|
17
|
+
* When an `InstalledApp` declares `modal: {...}`, the AppHost renders its
|
|
18
|
+
* component inside `ToolModalShell` instead of raw: the shared JsModal
|
|
19
|
+
* (drag/resize, floating⇄bottom-sheet, minimize, dimension persistence) plus
|
|
20
|
+
* a managed header (title/subtitle, back navigation, search, actions) and a
|
|
21
|
+
* lightweight toast layer. The tool body talks back to the shell through
|
|
22
|
+
* `useToolHost()` / `useToolHeader()`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Per-screen header overrides a tool body can apply (see `useToolHeader`). */
|
|
26
|
+
|
|
27
|
+
const noop = () => {};
|
|
28
|
+
const FALLBACK_CONTEXT = {
|
|
29
|
+
search: "",
|
|
30
|
+
isSearchActive: false,
|
|
31
|
+
clearSearch: noop,
|
|
32
|
+
close: noop,
|
|
33
|
+
showToast: noop,
|
|
34
|
+
setHeaderOverride: noop
|
|
35
|
+
};
|
|
36
|
+
const ToolHostContext = /*#__PURE__*/(0, _react.createContext)(null);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Access the hosting shell from a custom tool body: search query, toasts,
|
|
40
|
+
* close. Safe to call outside the shell (returns inert no-ops) so tool
|
|
41
|
+
* components can also be mounted standalone.
|
|
42
|
+
*/
|
|
43
|
+
function useToolHost() {
|
|
44
|
+
return (0, _react.useContext)(ToolHostContext) ?? FALLBACK_CONTEXT;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Declare per-screen header state (title/subtitle/actions/back) from inside a
|
|
49
|
+
* tool body. Applies while the calling component is mounted and reverts to
|
|
50
|
+
* the preset's static config on unmount — so a detail screen simply mounts
|
|
51
|
+
* with its own header and the list header returns when it unmounts.
|
|
52
|
+
*
|
|
53
|
+
* ```tsx
|
|
54
|
+
* useToolHeader(
|
|
55
|
+
* { title: flag.key, onBack: () => setScreen("list"), actions: <CopyButton value={flag} /> },
|
|
56
|
+
* [flag.key]
|
|
57
|
+
* );
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
function useToolHeader(override, deps) {
|
|
61
|
+
const {
|
|
62
|
+
setHeaderOverride
|
|
63
|
+
} = useToolHost();
|
|
64
|
+
(0, _react.useEffect)(() => {
|
|
65
|
+
setHeaderOverride(override);
|
|
66
|
+
return () => setHeaderOverride(null);
|
|
67
|
+
// Caller-provided deps intentionally control when the override refreshes.
|
|
68
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
|
+
}, deps);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Toast layer
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
const TOAST_DURATION_MS = 2200;
|
|
77
|
+
const TOAST_COLORS = {
|
|
78
|
+
success: _sharedUi.gameUIColors.success,
|
|
79
|
+
error: _sharedUi.gameUIColors.error,
|
|
80
|
+
info: _sharedUi.gameUIColors.info
|
|
81
|
+
};
|
|
82
|
+
function ToastView({
|
|
83
|
+
message,
|
|
84
|
+
type,
|
|
85
|
+
nonce
|
|
86
|
+
}) {
|
|
87
|
+
const opacity = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
88
|
+
(0, _react.useEffect)(() => {
|
|
89
|
+
opacity.setValue(0);
|
|
90
|
+
_reactNative.Animated.sequence([_reactNative.Animated.timing(opacity, {
|
|
91
|
+
toValue: 1,
|
|
92
|
+
duration: 150,
|
|
93
|
+
useNativeDriver: true
|
|
94
|
+
}), _reactNative.Animated.delay(TOAST_DURATION_MS - 450), _reactNative.Animated.timing(opacity, {
|
|
95
|
+
toValue: 0,
|
|
96
|
+
duration: 300,
|
|
97
|
+
useNativeDriver: true
|
|
98
|
+
})]).start();
|
|
99
|
+
}, [nonce, opacity]);
|
|
100
|
+
const color = TOAST_COLORS[type];
|
|
101
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
|
|
102
|
+
pointerEvents: "none",
|
|
103
|
+
style: [styles.toast, {
|
|
104
|
+
opacity,
|
|
105
|
+
borderColor: color + "60"
|
|
106
|
+
}],
|
|
107
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
108
|
+
style: [styles.toastDot, {
|
|
109
|
+
backgroundColor: color
|
|
110
|
+
}]
|
|
111
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
112
|
+
style: styles.toastText,
|
|
113
|
+
numberOfLines: 2,
|
|
114
|
+
children: message
|
|
115
|
+
})]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Shell
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
function ToolModalShell({
|
|
124
|
+
app,
|
|
125
|
+
config,
|
|
126
|
+
visible,
|
|
127
|
+
zIndex,
|
|
128
|
+
onClose,
|
|
129
|
+
onMinimize,
|
|
130
|
+
initialModalState,
|
|
131
|
+
enableSharedModalDimensions,
|
|
132
|
+
bodyProps
|
|
133
|
+
}) {
|
|
134
|
+
const Body = app.component;
|
|
135
|
+
const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
|
|
136
|
+
const [search, setSearch] = (0, _react.useState)("");
|
|
137
|
+
const [headerOverride, setHeaderOverride] = (0, _react.useState)(null);
|
|
138
|
+
const [toast, setToast] = (0, _react.useState)(null);
|
|
139
|
+
const toastTimeoutRef = (0, _react.useRef)(null);
|
|
140
|
+
const clearSearch = (0, _react.useCallback)(() => {
|
|
141
|
+
setIsSearchActive(false);
|
|
142
|
+
setSearch("");
|
|
143
|
+
}, []);
|
|
144
|
+
const showToast = (0, _react.useCallback)((message, type = "info") => {
|
|
145
|
+
if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current);
|
|
146
|
+
setToast(prev => ({
|
|
147
|
+
message,
|
|
148
|
+
type,
|
|
149
|
+
nonce: (prev?.nonce ?? 0) + 1
|
|
150
|
+
}));
|
|
151
|
+
toastTimeoutRef.current = setTimeout(() => setToast(null), TOAST_DURATION_MS);
|
|
152
|
+
}, []);
|
|
153
|
+
(0, _react.useEffect)(() => {
|
|
154
|
+
return () => {
|
|
155
|
+
if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current);
|
|
156
|
+
};
|
|
157
|
+
}, []);
|
|
158
|
+
const contextValue = (0, _react.useMemo)(() => ({
|
|
159
|
+
search: isSearchActive ? search : "",
|
|
160
|
+
isSearchActive,
|
|
161
|
+
clearSearch,
|
|
162
|
+
close: onClose,
|
|
163
|
+
showToast,
|
|
164
|
+
setHeaderOverride,
|
|
165
|
+
instanceId: app.instanceId
|
|
166
|
+
}), [search, isSearchActive, clearSearch, onClose, showToast, app.instanceId]);
|
|
167
|
+
const title = headerOverride?.title ?? config.title ?? app.title ?? app.id;
|
|
168
|
+
const subtitle = headerOverride?.subtitle ?? config.subtitle;
|
|
169
|
+
const onBack = headerOverride?.onBack;
|
|
170
|
+
const actions = headerOverride?.actions ?? config.headerActions;
|
|
171
|
+
const searchable = headerOverride?.searchable ?? config.searchable;
|
|
172
|
+
|
|
173
|
+
// A screen that hides search while it's open would strand the input with
|
|
174
|
+
// no way to dismiss it — close it on entry instead.
|
|
175
|
+
(0, _react.useEffect)(() => {
|
|
176
|
+
if (!searchable && isSearchActive) clearSearch();
|
|
177
|
+
}, [searchable, isSearchActive, clearSearch]);
|
|
178
|
+
|
|
179
|
+
// Tool-scoped persistence via the custom namespace unless the user has the
|
|
180
|
+
// shared-dimensions global setting on (all tools share one window size).
|
|
181
|
+
const persistenceKey = enableSharedModalDimensions ? _sharedUi.devToolsStorageKeys.modal.root() : _sharedUi.devToolsStorageKeys.custom(app.id).modal();
|
|
182
|
+
const header = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
183
|
+
children: [onBack && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
184
|
+
onBack: onBack
|
|
185
|
+
}), isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Search, {
|
|
186
|
+
value: search,
|
|
187
|
+
onChangeText: setSearch,
|
|
188
|
+
onCancel: clearSearch,
|
|
189
|
+
placeholder: config.searchPlaceholder
|
|
190
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
191
|
+
title: title,
|
|
192
|
+
subtitle: subtitle
|
|
193
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
|
|
194
|
+
children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HeaderSearchButton, {
|
|
195
|
+
testID: `tool-search-${app.id}`,
|
|
196
|
+
onPress: () => setIsSearchActive(true)
|
|
197
|
+
})]
|
|
198
|
+
})]
|
|
199
|
+
});
|
|
200
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToolHostContext.Provider, {
|
|
201
|
+
value: contextValue,
|
|
202
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.JsModal, {
|
|
203
|
+
visible: visible,
|
|
204
|
+
onClose: onClose,
|
|
205
|
+
onBack: onBack,
|
|
206
|
+
onMinimize: onMinimize,
|
|
207
|
+
initialModalState: initialModalState,
|
|
208
|
+
zIndex: zIndex,
|
|
209
|
+
persistenceKey: persistenceKey,
|
|
210
|
+
enablePersistence: true,
|
|
211
|
+
initialMode: config.initialMode ?? "bottomSheet",
|
|
212
|
+
header: {
|
|
213
|
+
customContent: header,
|
|
214
|
+
showToggleButton: true
|
|
215
|
+
}
|
|
216
|
+
// Tool bodies own their scrolling (ScrollView/FlatList) — the shell
|
|
217
|
+
// must not add a competing wrapper.
|
|
218
|
+
,
|
|
219
|
+
disableScrollWrapper: true,
|
|
220
|
+
styles: {},
|
|
221
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
222
|
+
style: styles.bodyRoot,
|
|
223
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TickProvider, {
|
|
224
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Body, {
|
|
225
|
+
...bodyProps
|
|
226
|
+
})
|
|
227
|
+
}), toast && /*#__PURE__*/(0, _jsxRuntime.jsx)(ToastView, {
|
|
228
|
+
message: toast.message,
|
|
229
|
+
type: toast.type,
|
|
230
|
+
nonce: toast.nonce
|
|
231
|
+
})]
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const styles = _reactNative.StyleSheet.create({
|
|
237
|
+
bodyRoot: {
|
|
238
|
+
flex: 1,
|
|
239
|
+
minHeight: 0,
|
|
240
|
+
backgroundColor: _sharedUi.buoyColors.base
|
|
241
|
+
},
|
|
242
|
+
toast: {
|
|
243
|
+
position: "absolute",
|
|
244
|
+
bottom: 16,
|
|
245
|
+
alignSelf: "center",
|
|
246
|
+
flexDirection: "row",
|
|
247
|
+
alignItems: "center",
|
|
248
|
+
gap: 8,
|
|
249
|
+
maxWidth: "86%",
|
|
250
|
+
backgroundColor: _sharedUi.buoyColors.card,
|
|
251
|
+
borderWidth: 1,
|
|
252
|
+
borderRadius: 8,
|
|
253
|
+
paddingHorizontal: 12,
|
|
254
|
+
paddingVertical: 8,
|
|
255
|
+
shadowColor: "#000",
|
|
256
|
+
shadowOpacity: 0.4,
|
|
257
|
+
shadowRadius: 8,
|
|
258
|
+
shadowOffset: {
|
|
259
|
+
width: 0,
|
|
260
|
+
height: 2
|
|
261
|
+
},
|
|
262
|
+
elevation: 6
|
|
263
|
+
},
|
|
264
|
+
toastDot: {
|
|
265
|
+
width: 6,
|
|
266
|
+
height: 6,
|
|
267
|
+
borderRadius: 3
|
|
268
|
+
},
|
|
269
|
+
toastText: {
|
|
270
|
+
flex: 1,
|
|
271
|
+
fontSize: 12,
|
|
272
|
+
color: _sharedUi.buoyColors.text
|
|
273
|
+
}
|
|
274
|
+
});
|
|
@@ -113,6 +113,13 @@ const ALL_TOOLS_REGISTRY = exports.ALL_TOOLS_REGISTRY = [{
|
|
|
113
113
|
renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.EventsIcon, {
|
|
114
114
|
size: size
|
|
115
115
|
})
|
|
116
|
+
}, {
|
|
117
|
+
id: "time-machine",
|
|
118
|
+
name: "TIME MACHINE",
|
|
119
|
+
color: _floatingToolsCore.TIME_MACHINE_ICON_COLOR,
|
|
120
|
+
renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.TimeMachineIcon, {
|
|
121
|
+
size: size
|
|
122
|
+
})
|
|
116
123
|
}, {
|
|
117
124
|
id: "jotai",
|
|
118
125
|
name: "JOTAI",
|
|
@@ -189,6 +189,22 @@ function autoDiscoverPresets() {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
},
|
|
192
|
+
// Time Machine
|
|
193
|
+
{
|
|
194
|
+
name: "@buoy-gg/time-machine",
|
|
195
|
+
loader: () => {
|
|
196
|
+
try {
|
|
197
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
198
|
+
const {
|
|
199
|
+
timeMachineToolPreset
|
|
200
|
+
} = require("@buoy-gg/time-machine");
|
|
201
|
+
return timeMachineToolPreset;
|
|
202
|
+
} catch (e) {
|
|
203
|
+
(0, _optionalRequire.reportOptionalRequireError)("@buoy-gg/time-machine", e);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
192
208
|
// Redux DevTools
|
|
193
209
|
{
|
|
194
210
|
name: "@buoy-gg/redux",
|
|
@@ -44,6 +44,7 @@ let zustandModule = null;
|
|
|
44
44
|
let jotaiModule = null;
|
|
45
45
|
let eventsModule = null;
|
|
46
46
|
let consoleModule = null;
|
|
47
|
+
let timeMachineModule = null;
|
|
47
48
|
let envModule = null;
|
|
48
49
|
let highlightUpdatesModule = null;
|
|
49
50
|
let debugBordersModule = null;
|
|
@@ -107,6 +108,11 @@ try {
|
|
|
107
108
|
} catch (e) {
|
|
108
109
|
(0, _optionalRequire.reportOptionalRequireError)("@buoy-gg/console", e);
|
|
109
110
|
}
|
|
111
|
+
try {
|
|
112
|
+
timeMachineModule = require("@buoy-gg/time-machine");
|
|
113
|
+
} catch (e) {
|
|
114
|
+
(0, _optionalRequire.reportOptionalRequireError)("@buoy-gg/time-machine", e);
|
|
115
|
+
}
|
|
110
116
|
try {
|
|
111
117
|
envModule = require("@buoy-gg/env");
|
|
112
118
|
} catch (e) {
|
|
@@ -174,7 +180,8 @@ function AutoExternalSync({
|
|
|
174
180
|
options,
|
|
175
181
|
requiredEnvVars,
|
|
176
182
|
isPro,
|
|
177
|
-
licenseKey
|
|
183
|
+
licenseKey,
|
|
184
|
+
customApps
|
|
178
185
|
}) {
|
|
179
186
|
const {
|
|
180
187
|
useExternalSyncSocket,
|
|
@@ -260,6 +267,9 @@ function AutoExternalSync({
|
|
|
260
267
|
if (consoleModule?.consoleSyncAdapter) {
|
|
261
268
|
map.console = consoleModule.consoleSyncAdapter;
|
|
262
269
|
}
|
|
270
|
+
if (timeMachineModule?.timeMachineSyncAdapter) {
|
|
271
|
+
map["time-machine"] = timeMachineModule.timeMachineSyncAdapter;
|
|
272
|
+
}
|
|
263
273
|
if (envModule?.createEnvSyncAdapter) {
|
|
264
274
|
map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
|
|
265
275
|
}
|
|
@@ -284,8 +294,17 @@ function AutoExternalSync({
|
|
|
284
294
|
if (assetsModule?.assetsSyncAdapter) {
|
|
285
295
|
map.assets = assetsModule.assetsSyncAdapter;
|
|
286
296
|
}
|
|
297
|
+
|
|
298
|
+
// Third-party custom tools that declared InstalledApp.sync. Namespaced so
|
|
299
|
+
// a custom tool named "network" can't shadow the first-party adapter on
|
|
300
|
+
// the broker, in MCP call_action, or in desktop watch state.
|
|
301
|
+
for (const app of customApps ?? []) {
|
|
302
|
+
if (app?.sync) {
|
|
303
|
+
map[`custom:${app.id}`] = app.sync;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
287
306
|
return map;
|
|
288
|
-
}, [queryAdapter, requiredEnvVars]);
|
|
307
|
+
}, [queryAdapter, requiredEnvVars, customApps]);
|
|
289
308
|
useExternalSync({
|
|
290
309
|
tools,
|
|
291
310
|
socket,
|