@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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Badge = Badge;
|
|
7
|
+
exports.DiffViewer = DiffViewer;
|
|
8
|
+
exports.InfoRow = InfoRow;
|
|
9
|
+
exports.StatusDot = StatusDot;
|
|
10
|
+
exports.methodColor = methodColor;
|
|
11
|
+
exports.statusColor = statusColor;
|
|
12
|
+
exports.typeColor = typeColor;
|
|
13
|
+
var _react = _interopRequireDefault(require("react"));
|
|
14
|
+
var _reactNative = require("react-native");
|
|
15
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
16
|
+
var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
|
|
17
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
|
+
/**
|
|
20
|
+
* Facade primitives for the public custom-tools toolkit (see toolkit.ts).
|
|
21
|
+
*
|
|
22
|
+
* shared-ui grew several overlapping treatments of the same jobs (seven badge
|
|
23
|
+
* variants, four copy buttons, two diff viewers...). The public surface merges
|
|
24
|
+
* each job into ONE component with variants, implemented here on toolTheme —
|
|
25
|
+
* shared-ui's internals stay unchanged for first-party tools.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Semantic color helpers — pair with <Badge>/<StatusDot> instead of exporting
|
|
30
|
+
// a preset component per domain.
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
const STATUS_COLORS = {
|
|
33
|
+
success: _sharedUi.toolTheme.colors.success,
|
|
34
|
+
active: _sharedUi.toolTheme.colors.success,
|
|
35
|
+
connected: _sharedUi.toolTheme.colors.success,
|
|
36
|
+
error: _sharedUi.toolTheme.colors.error,
|
|
37
|
+
failed: _sharedUi.toolTheme.colors.error,
|
|
38
|
+
warning: _sharedUi.toolTheme.colors.warning,
|
|
39
|
+
stale: _sharedUi.toolTheme.colors.warning,
|
|
40
|
+
pending: _sharedUi.toolTheme.colors.info,
|
|
41
|
+
loading: _sharedUi.toolTheme.colors.info,
|
|
42
|
+
fetching: _sharedUi.toolTheme.colors.info,
|
|
43
|
+
info: _sharedUi.toolTheme.colors.info,
|
|
44
|
+
inactive: _sharedUi.toolTheme.colors.textMuted,
|
|
45
|
+
idle: _sharedUi.toolTheme.colors.textMuted
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Map a status word ("success", "pending", "stale"...) to a semantic color. */
|
|
49
|
+
function statusColor(status) {
|
|
50
|
+
return STATUS_COLORS[status.toLowerCase()] ?? _sharedUi.toolTheme.colors.textMuted;
|
|
51
|
+
}
|
|
52
|
+
const METHOD_COLORS = {
|
|
53
|
+
GET: _sharedUi.toolTheme.colors.info,
|
|
54
|
+
POST: _sharedUi.toolTheme.colors.success,
|
|
55
|
+
PUT: _sharedUi.toolTheme.colors.warning,
|
|
56
|
+
PATCH: _sharedUi.toolTheme.colors.warning,
|
|
57
|
+
DELETE: _sharedUi.toolTheme.colors.error
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Map an HTTP method to its conventional color. */
|
|
61
|
+
function methodColor(method) {
|
|
62
|
+
return METHOD_COLORS[method.toUpperCase()] ?? _sharedUi.toolTheme.colors.textSecondary;
|
|
63
|
+
}
|
|
64
|
+
const TYPE_COLORS = {
|
|
65
|
+
string: _sharedUi.toolTheme.colors.success,
|
|
66
|
+
number: _sharedUi.toolTheme.colors.info,
|
|
67
|
+
boolean: _sharedUi.toolTheme.colors.warning,
|
|
68
|
+
object: _sharedUi.toolTheme.colors.accent,
|
|
69
|
+
array: _sharedUi.toolTheme.colors.accent,
|
|
70
|
+
function: _sharedUi.toolTheme.colors.error,
|
|
71
|
+
null: _sharedUi.toolTheme.colors.textMuted,
|
|
72
|
+
undefined: _sharedUi.toolTheme.colors.textMuted
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** Map a JS value-type name to its display color. */
|
|
76
|
+
function typeColor(type) {
|
|
77
|
+
return TYPE_COLORS[type.toLowerCase()] ?? _sharedUi.toolTheme.colors.textSecondary;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Badge — the one chip. Color it with statusColor()/methodColor()/typeColor()
|
|
82
|
+
// (or any hex); shape/typography are props, not sibling components.
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
function Badge({
|
|
86
|
+
children,
|
|
87
|
+
color = _sharedUi.toolTheme.colors.textSecondary,
|
|
88
|
+
icon,
|
|
89
|
+
size = "small",
|
|
90
|
+
pill = false,
|
|
91
|
+
mono = false,
|
|
92
|
+
style
|
|
93
|
+
}) {
|
|
94
|
+
const isText = typeof children === "string" || typeof children === "number";
|
|
95
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
96
|
+
style: [badgeStyles.base, size === "medium" && badgeStyles.medium, pill && badgeStyles.pill, {
|
|
97
|
+
backgroundColor: color + "1A",
|
|
98
|
+
borderColor: color + "40"
|
|
99
|
+
}, style],
|
|
100
|
+
children: [icon, isText ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
101
|
+
style: [badgeStyles.text, size === "medium" && badgeStyles.textMedium, mono && badgeStyles.textMono, {
|
|
102
|
+
color
|
|
103
|
+
}],
|
|
104
|
+
numberOfLines: 1,
|
|
105
|
+
children: children
|
|
106
|
+
}) : children]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const badgeStyles = _reactNative.StyleSheet.create({
|
|
110
|
+
base: {
|
|
111
|
+
flexDirection: "row",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
alignSelf: "flex-start",
|
|
114
|
+
gap: 4,
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderRadius: _sharedUi.toolTheme.radius.sm,
|
|
117
|
+
paddingHorizontal: 6,
|
|
118
|
+
paddingVertical: 2
|
|
119
|
+
},
|
|
120
|
+
medium: {
|
|
121
|
+
paddingHorizontal: 8,
|
|
122
|
+
paddingVertical: 3
|
|
123
|
+
},
|
|
124
|
+
pill: {
|
|
125
|
+
borderRadius: _sharedUi.toolTheme.radius.pill,
|
|
126
|
+
minWidth: 20,
|
|
127
|
+
justifyContent: "center"
|
|
128
|
+
},
|
|
129
|
+
text: {
|
|
130
|
+
fontSize: 10,
|
|
131
|
+
fontWeight: "600",
|
|
132
|
+
letterSpacing: 0.3
|
|
133
|
+
},
|
|
134
|
+
textMedium: {
|
|
135
|
+
fontSize: 12
|
|
136
|
+
},
|
|
137
|
+
textMono: {
|
|
138
|
+
fontFamily: _sharedUi.toolTheme.text.mono.fontFamily
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// StatusDot — the bare status circle (list rows, connection indicators).
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
function StatusDot({
|
|
147
|
+
status,
|
|
148
|
+
color,
|
|
149
|
+
size = 8,
|
|
150
|
+
style
|
|
151
|
+
}) {
|
|
152
|
+
const dotColor = color ?? (status ? statusColor(status) : _sharedUi.toolTheme.colors.textMuted);
|
|
153
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
154
|
+
style: [{
|
|
155
|
+
width: size,
|
|
156
|
+
height: size,
|
|
157
|
+
borderRadius: size / 2,
|
|
158
|
+
backgroundColor: dotColor
|
|
159
|
+
}, style]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// InfoRow — the label/value line of every detail screen, with optional copy.
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
function InfoRow({
|
|
168
|
+
label,
|
|
169
|
+
value,
|
|
170
|
+
copyable = false,
|
|
171
|
+
style
|
|
172
|
+
}) {
|
|
173
|
+
const isText = typeof value === "string" || typeof value === "number";
|
|
174
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
175
|
+
style: [infoRowStyles.row, style],
|
|
176
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
177
|
+
style: infoRowStyles.label,
|
|
178
|
+
numberOfLines: 1,
|
|
179
|
+
children: label
|
|
180
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
181
|
+
style: infoRowStyles.value,
|
|
182
|
+
children: isText ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
183
|
+
style: infoRowStyles.valueText,
|
|
184
|
+
numberOfLines: 2,
|
|
185
|
+
children: String(value)
|
|
186
|
+
}) : value
|
|
187
|
+
}), copyable && value != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CopyButton, {
|
|
188
|
+
value: value,
|
|
189
|
+
size: 12
|
|
190
|
+
})]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const infoRowStyles = _reactNative.StyleSheet.create({
|
|
194
|
+
row: {
|
|
195
|
+
flexDirection: "row",
|
|
196
|
+
alignItems: "center",
|
|
197
|
+
gap: _sharedUi.toolTheme.spacing.sm,
|
|
198
|
+
paddingVertical: 3
|
|
199
|
+
},
|
|
200
|
+
label: {
|
|
201
|
+
..._sharedUi.toolTheme.text.label,
|
|
202
|
+
color: _sharedUi.toolTheme.colors.textMuted,
|
|
203
|
+
minWidth: 72
|
|
204
|
+
},
|
|
205
|
+
value: {
|
|
206
|
+
flex: 1,
|
|
207
|
+
flexDirection: "row",
|
|
208
|
+
alignItems: "center"
|
|
209
|
+
},
|
|
210
|
+
valueText: {
|
|
211
|
+
..._sharedUi.toolTheme.text.mono,
|
|
212
|
+
color: _sharedUi.toolTheme.colors.text
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// DiffViewer — one before/after component; the rendering strategy is a mode.
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
function DiffViewer({
|
|
221
|
+
oldValue,
|
|
222
|
+
newValue,
|
|
223
|
+
mode = "tree",
|
|
224
|
+
showUnchanged = true,
|
|
225
|
+
scrollEnabled = false,
|
|
226
|
+
height = 220
|
|
227
|
+
}) {
|
|
228
|
+
if (mode === "text") {
|
|
229
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.SplitDiffViewer, {
|
|
230
|
+
oldValue: oldValue,
|
|
231
|
+
newValue: newValue,
|
|
232
|
+
height: height
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.TreeDiffViewer, {
|
|
236
|
+
oldValue: oldValue,
|
|
237
|
+
newValue: newValue,
|
|
238
|
+
showUnchanged: showUnchanged,
|
|
239
|
+
scrollEnabled: scrollEnabled
|
|
240
|
+
});
|
|
241
|
+
}
|
|
@@ -4,7 +4,9 @@ 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";
|
|
9
|
+
import { ToolModalShell } from "./ToolHost.js";
|
|
8
10
|
import { useDevToolsSettings } from "./DevToolsSettingsModal";
|
|
9
11
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
12
|
const AppHostContext = /*#__PURE__*/createContext(null);
|
|
@@ -63,10 +65,11 @@ export const AppHostProvider = ({
|
|
|
63
65
|
}) => {
|
|
64
66
|
const appDef = installedAppsRef.current.find(app => app.id === appId);
|
|
65
67
|
if (appDef) {
|
|
66
|
-
const resolvedIcon =
|
|
68
|
+
const resolvedIcon = renderAppIcon(appDef.icon, {
|
|
67
69
|
slot: "dial",
|
|
68
|
-
size: 20
|
|
69
|
-
|
|
70
|
+
size: 20,
|
|
71
|
+
color: appDef.color
|
|
72
|
+
});
|
|
70
73
|
open({
|
|
71
74
|
id: appDef.id,
|
|
72
75
|
title: appDef.name,
|
|
@@ -76,6 +79,7 @@ export const AppHostProvider = ({
|
|
|
76
79
|
singleton: appDef.singleton,
|
|
77
80
|
icon: resolvedIcon,
|
|
78
81
|
color: appDef.color,
|
|
82
|
+
modal: appDef.modal,
|
|
79
83
|
minimized: minimized // Preserve minimized state
|
|
80
84
|
});
|
|
81
85
|
}
|
|
@@ -250,6 +254,22 @@ const AppRenderer = ({
|
|
|
250
254
|
enableSharedModalDimensions: true
|
|
251
255
|
} : {})
|
|
252
256
|
};
|
|
257
|
+
|
|
258
|
+
// Declarative chrome: the component is a plain body — wrap it in the shared
|
|
259
|
+
// modal shell (JsModal + managed header + search + toasts).
|
|
260
|
+
if (app.modal && (app.launchMode === "self-modal" || !app.launchMode)) {
|
|
261
|
+
return /*#__PURE__*/_jsx(ToolModalShell, {
|
|
262
|
+
app: app,
|
|
263
|
+
config: app.modal,
|
|
264
|
+
visible: isVisible,
|
|
265
|
+
zIndex: zIndex,
|
|
266
|
+
onClose: onClose,
|
|
267
|
+
onMinimize: onMinimize,
|
|
268
|
+
initialModalState: app.restoreState,
|
|
269
|
+
enableSharedModalDimensions: globalEnableSharedModalDimensions === true,
|
|
270
|
+
bodyProps: mergedProps
|
|
271
|
+
});
|
|
272
|
+
}
|
|
253
273
|
if (app.launchMode === "self-modal") {
|
|
254
274
|
return /*#__PURE__*/_jsx(Comp, {
|
|
255
275
|
...mergedProps,
|
|
@@ -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,
|
|
@@ -182,7 +184,8 @@ export const FloatingMenu = ({
|
|
|
182
184
|
launchMode: app.launchMode ?? "self-modal",
|
|
183
185
|
singleton: app.singleton ?? true,
|
|
184
186
|
icon: resolvedIcon,
|
|
185
|
-
color: app.color
|
|
187
|
+
color: app.color,
|
|
188
|
+
modal: app.modal
|
|
186
189
|
});
|
|
187
190
|
}
|
|
188
191
|
};
|
|
@@ -280,7 +283,11 @@ export const FloatingMenu = ({
|
|
|
280
283
|
actions: mergedActions
|
|
281
284
|
});
|
|
282
285
|
}
|
|
283
|
-
return app.icon
|
|
286
|
+
return renderAppIcon(app.icon, {
|
|
287
|
+
slot: "row",
|
|
288
|
+
size: 16,
|
|
289
|
+
color: app.color
|
|
290
|
+
});
|
|
284
291
|
})()
|
|
285
292
|
}, `row-${app.id}`))]
|
|
286
293
|
})
|