@ateam-ai-mobile/plugin-runtime 0.1.0
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/dist/index.d.mts +1129 -0
- package/dist/index.d.ts +1129 -0
- package/dist/index.js +2388 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2369 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +31 -0
- package/src/api/plugins.ts +257 -0
- package/src/bridge/native.ts +266 -0
- package/src/bridge/types.ts +268 -0
- package/src/bundles/bundleManager.ts +396 -0
- package/src/bundles/storage.ts +68 -0
- package/src/hooks/usePlugin.ts +185 -0
- package/src/hooks/useRemotePlugins.ts +97 -0
- package/src/host.ts +117 -0
- package/src/index.ts +79 -0
- package/src/registry/SurfaceDispatcher.tsx +66 -0
- package/src/registry/store.ts +131 -0
- package/src/registry/types.ts +60 -0
- package/src/sdk/components.ts +370 -0
- package/src/sdk/index.ts +76 -0
- package/src/sdk/primitives.ts +22 -0
- package/src/sdk/registration.ts +52 -0
- package/src/sdk/types.ts +315 -0
- package/src/sdk/useApi.ts +78 -0
- package/src/sdk/usePluginTheme.ts +86 -0
- package/src/surfaces/AdaOrb.tsx +318 -0
- package/src/surfaces/SurfaceCard.tsx +137 -0
- package/src/surfaces/SurfaceDrawer.tsx +216 -0
- package/src/surfaces/SurfaceFullscreen.tsx +207 -0
- package/src/utils/sha256.ts +85 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2369 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/sdk/index.ts
|
|
8
|
+
var sdk_exports = {};
|
|
9
|
+
__export(sdk_exports, {
|
|
10
|
+
ActionButton: () => ActionButton,
|
|
11
|
+
ActivityIndicator: () => ActivityIndicator,
|
|
12
|
+
Animated: () => Animated,
|
|
13
|
+
Badge: () => Badge,
|
|
14
|
+
Card: () => Card,
|
|
15
|
+
EmptyState: () => EmptyState,
|
|
16
|
+
ErrorState: () => ErrorState,
|
|
17
|
+
FlatList: () => FlatList,
|
|
18
|
+
Image: () => Image,
|
|
19
|
+
ListItem: () => ListItem,
|
|
20
|
+
LoadingState: () => LoadingState,
|
|
21
|
+
Modal: () => Modal,
|
|
22
|
+
Platform: () => Platform,
|
|
23
|
+
PluginSDK: () => PluginSDK,
|
|
24
|
+
Pressable: () => Pressable,
|
|
25
|
+
RefreshControl: () => RefreshControl,
|
|
26
|
+
ScrollView: () => ScrollView,
|
|
27
|
+
SearchBar: () => SearchBar,
|
|
28
|
+
SectionList: () => SectionList,
|
|
29
|
+
StyleSheet: () => StyleSheet,
|
|
30
|
+
Switch: () => Switch,
|
|
31
|
+
TabBar: () => TabBar,
|
|
32
|
+
Text: () => Text,
|
|
33
|
+
TextInput: () => TextInput,
|
|
34
|
+
TouchableOpacity: () => TouchableOpacity,
|
|
35
|
+
View: () => View,
|
|
36
|
+
register: () => register,
|
|
37
|
+
registerService: () => registerService,
|
|
38
|
+
setPluginThemeColors: () => setPluginThemeColors,
|
|
39
|
+
useApi: () => useApi,
|
|
40
|
+
usePluginTheme: () => usePluginTheme
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/sdk/registration.ts
|
|
44
|
+
function register(id, config) {
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
type: config.type || "ui",
|
|
48
|
+
capabilities: config.capabilities,
|
|
49
|
+
Component: config.Component
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function registerService(id, config) {
|
|
53
|
+
return {
|
|
54
|
+
id,
|
|
55
|
+
type: "service",
|
|
56
|
+
capabilities: config.capabilities,
|
|
57
|
+
hooks: config.hooks,
|
|
58
|
+
onStart: config.onStart,
|
|
59
|
+
onStop: config.onStop
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/sdk/primitives.ts
|
|
64
|
+
import {
|
|
65
|
+
View,
|
|
66
|
+
Text,
|
|
67
|
+
ScrollView,
|
|
68
|
+
FlatList,
|
|
69
|
+
SectionList,
|
|
70
|
+
Pressable,
|
|
71
|
+
TouchableOpacity,
|
|
72
|
+
TextInput,
|
|
73
|
+
Image,
|
|
74
|
+
ActivityIndicator,
|
|
75
|
+
Modal,
|
|
76
|
+
Switch,
|
|
77
|
+
StyleSheet,
|
|
78
|
+
Animated,
|
|
79
|
+
Platform,
|
|
80
|
+
RefreshControl
|
|
81
|
+
} from "react-native";
|
|
82
|
+
|
|
83
|
+
// src/sdk/components.ts
|
|
84
|
+
import React from "react";
|
|
85
|
+
import {
|
|
86
|
+
View as View2,
|
|
87
|
+
Text as Text2,
|
|
88
|
+
Pressable as Pressable2,
|
|
89
|
+
TextInput as TextInput2,
|
|
90
|
+
ActivityIndicator as ActivityIndicator2,
|
|
91
|
+
StyleSheet as StyleSheet2
|
|
92
|
+
} from "react-native";
|
|
93
|
+
function Card({ theme, children, style, onPress }) {
|
|
94
|
+
const cardStyle = [
|
|
95
|
+
styles.card,
|
|
96
|
+
{
|
|
97
|
+
backgroundColor: theme.colors.surface,
|
|
98
|
+
borderColor: theme.colors.border
|
|
99
|
+
},
|
|
100
|
+
style
|
|
101
|
+
];
|
|
102
|
+
if (onPress) {
|
|
103
|
+
return React.createElement(Pressable2, { style: cardStyle, onPress }, children);
|
|
104
|
+
}
|
|
105
|
+
return React.createElement(View2, { style: cardStyle }, children);
|
|
106
|
+
}
|
|
107
|
+
var BADGE_COLORS = {
|
|
108
|
+
accent: { bg: "accentSoft", text: "accent" },
|
|
109
|
+
success: { bg: "successSoft", text: "success" },
|
|
110
|
+
warning: { bg: "warningSoft", text: "warning" },
|
|
111
|
+
error: { bg: "errorSoft", text: "error" },
|
|
112
|
+
purple: { bg: "purpleSoft", text: "purple" }
|
|
113
|
+
};
|
|
114
|
+
function Badge({ theme, label, color = "accent", style }) {
|
|
115
|
+
const mapping = BADGE_COLORS[color];
|
|
116
|
+
return React.createElement(
|
|
117
|
+
View2,
|
|
118
|
+
{
|
|
119
|
+
style: [
|
|
120
|
+
styles.badge,
|
|
121
|
+
{
|
|
122
|
+
backgroundColor: theme.colors[mapping.bg],
|
|
123
|
+
borderRadius: theme.borderRadius.sm
|
|
124
|
+
},
|
|
125
|
+
style
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
React.createElement(Text2, {
|
|
129
|
+
style: [styles.badgeText, { color: theme.colors[mapping.text] }]
|
|
130
|
+
}, label)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
function TabBar({ theme, tabs, activeTab, onTabChange, style }) {
|
|
134
|
+
return React.createElement(
|
|
135
|
+
View2,
|
|
136
|
+
{
|
|
137
|
+
style: [
|
|
138
|
+
styles.tabBar,
|
|
139
|
+
{ borderBottomColor: theme.colors.border },
|
|
140
|
+
style
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
tabs.map(
|
|
144
|
+
(tab) => React.createElement(
|
|
145
|
+
Pressable2,
|
|
146
|
+
{
|
|
147
|
+
key: tab,
|
|
148
|
+
style: [
|
|
149
|
+
styles.tab,
|
|
150
|
+
{
|
|
151
|
+
borderBottomColor: tab === activeTab ? theme.colors.accent : "transparent"
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
onPress: () => onTabChange(tab)
|
|
155
|
+
},
|
|
156
|
+
React.createElement(Text2, {
|
|
157
|
+
style: {
|
|
158
|
+
color: tab === activeTab ? theme.colors.accent : theme.colors.textSecondary,
|
|
159
|
+
fontSize: theme.fontSize.sm,
|
|
160
|
+
fontWeight: tab === activeTab ? "600" : "400"
|
|
161
|
+
}
|
|
162
|
+
}, tab)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function ListItem({ theme, title, subtitle, right, onPress, style }) {
|
|
168
|
+
const content = [
|
|
169
|
+
React.createElement(
|
|
170
|
+
View2,
|
|
171
|
+
{ key: "content", style: styles.listItemContent },
|
|
172
|
+
React.createElement(Text2, {
|
|
173
|
+
style: { color: theme.colors.text, fontSize: theme.fontSize.md },
|
|
174
|
+
numberOfLines: 1
|
|
175
|
+
}, title),
|
|
176
|
+
subtitle ? React.createElement(Text2, {
|
|
177
|
+
style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.xs, marginTop: 2 },
|
|
178
|
+
numberOfLines: 1
|
|
179
|
+
}, subtitle) : null
|
|
180
|
+
),
|
|
181
|
+
right ? React.createElement(View2, { key: "right" }, right) : null
|
|
182
|
+
];
|
|
183
|
+
if (onPress) {
|
|
184
|
+
return React.createElement(
|
|
185
|
+
Pressable2,
|
|
186
|
+
{ style: [styles.listItem, { borderBottomColor: theme.colors.border }, style], onPress },
|
|
187
|
+
...content
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return React.createElement(
|
|
191
|
+
View2,
|
|
192
|
+
{ style: [styles.listItem, { borderBottomColor: theme.colors.border }, style] },
|
|
193
|
+
...content
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
function SearchBar({ theme, value, onChangeText, placeholder = "Search...", style }) {
|
|
197
|
+
return React.createElement(TextInput2, {
|
|
198
|
+
style: [
|
|
199
|
+
styles.searchBar,
|
|
200
|
+
{
|
|
201
|
+
backgroundColor: theme.colors.input,
|
|
202
|
+
color: theme.colors.text,
|
|
203
|
+
borderColor: theme.colors.border,
|
|
204
|
+
borderRadius: theme.borderRadius.sm,
|
|
205
|
+
fontSize: theme.fontSize.sm
|
|
206
|
+
},
|
|
207
|
+
style
|
|
208
|
+
],
|
|
209
|
+
value,
|
|
210
|
+
onChangeText,
|
|
211
|
+
placeholder,
|
|
212
|
+
placeholderTextColor: theme.colors.textMuted,
|
|
213
|
+
autoCapitalize: "none",
|
|
214
|
+
autoCorrect: false
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function EmptyState({ theme, title, subtitle, style }) {
|
|
218
|
+
return React.createElement(
|
|
219
|
+
View2,
|
|
220
|
+
{ style: [styles.emptyState, style] },
|
|
221
|
+
React.createElement(Text2, {
|
|
222
|
+
style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.md, textAlign: "center" }
|
|
223
|
+
}, title),
|
|
224
|
+
subtitle ? React.createElement(Text2, {
|
|
225
|
+
style: { color: theme.colors.textMuted, fontSize: theme.fontSize.sm, textAlign: "center", marginTop: 4 }
|
|
226
|
+
}, subtitle) : null
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
function ErrorState({ theme, message, onRetry, style }) {
|
|
230
|
+
return React.createElement(
|
|
231
|
+
View2,
|
|
232
|
+
{ style: [styles.emptyState, style] },
|
|
233
|
+
React.createElement(Text2, {
|
|
234
|
+
style: { color: theme.colors.error, fontSize: theme.fontSize.md, textAlign: "center" }
|
|
235
|
+
}, message),
|
|
236
|
+
onRetry ? React.createElement(
|
|
237
|
+
Pressable2,
|
|
238
|
+
{
|
|
239
|
+
style: [styles.retryButton, { backgroundColor: theme.colors.accentSoft, borderRadius: theme.borderRadius.sm }],
|
|
240
|
+
onPress: onRetry
|
|
241
|
+
},
|
|
242
|
+
React.createElement(Text2, {
|
|
243
|
+
style: { color: theme.colors.accent, fontSize: theme.fontSize.sm, fontWeight: "600" }
|
|
244
|
+
}, "Retry")
|
|
245
|
+
) : null
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
function LoadingState({ theme, message = "Loading...", style }) {
|
|
249
|
+
return React.createElement(
|
|
250
|
+
View2,
|
|
251
|
+
{ style: [styles.emptyState, style] },
|
|
252
|
+
React.createElement(ActivityIndicator2, { size: "small", color: theme.colors.accent }),
|
|
253
|
+
React.createElement(Text2, {
|
|
254
|
+
style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.sm, marginTop: 8 }
|
|
255
|
+
}, message)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
function ActionButton({ theme, label, icon, onPress, style }) {
|
|
259
|
+
return React.createElement(
|
|
260
|
+
Pressable2,
|
|
261
|
+
{
|
|
262
|
+
style: [
|
|
263
|
+
styles.fab,
|
|
264
|
+
{ backgroundColor: theme.colors.accent, borderRadius: theme.borderRadius.full },
|
|
265
|
+
style
|
|
266
|
+
],
|
|
267
|
+
onPress
|
|
268
|
+
},
|
|
269
|
+
icon || React.createElement(Text2, { style: styles.fabText }, label || "+")
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
var styles = StyleSheet2.create({
|
|
273
|
+
card: {
|
|
274
|
+
padding: 12,
|
|
275
|
+
borderRadius: 12,
|
|
276
|
+
borderWidth: 1,
|
|
277
|
+
marginBottom: 8
|
|
278
|
+
},
|
|
279
|
+
badge: {
|
|
280
|
+
paddingHorizontal: 8,
|
|
281
|
+
paddingVertical: 3
|
|
282
|
+
},
|
|
283
|
+
badgeText: {
|
|
284
|
+
fontSize: 11,
|
|
285
|
+
fontWeight: "600"
|
|
286
|
+
},
|
|
287
|
+
tabBar: {
|
|
288
|
+
flexDirection: "row",
|
|
289
|
+
borderBottomWidth: 1
|
|
290
|
+
},
|
|
291
|
+
tab: {
|
|
292
|
+
flex: 1,
|
|
293
|
+
padding: 12,
|
|
294
|
+
alignItems: "center",
|
|
295
|
+
borderBottomWidth: 2
|
|
296
|
+
},
|
|
297
|
+
listItem: {
|
|
298
|
+
flexDirection: "row",
|
|
299
|
+
alignItems: "center",
|
|
300
|
+
paddingVertical: 12,
|
|
301
|
+
paddingHorizontal: 16,
|
|
302
|
+
borderBottomWidth: StyleSheet2.hairlineWidth
|
|
303
|
+
},
|
|
304
|
+
listItemContent: {
|
|
305
|
+
flex: 1
|
|
306
|
+
},
|
|
307
|
+
searchBar: {
|
|
308
|
+
height: 40,
|
|
309
|
+
paddingHorizontal: 12,
|
|
310
|
+
borderWidth: 1
|
|
311
|
+
},
|
|
312
|
+
emptyState: {
|
|
313
|
+
flex: 1,
|
|
314
|
+
justifyContent: "center",
|
|
315
|
+
alignItems: "center",
|
|
316
|
+
padding: 32
|
|
317
|
+
},
|
|
318
|
+
retryButton: {
|
|
319
|
+
marginTop: 12,
|
|
320
|
+
paddingHorizontal: 16,
|
|
321
|
+
paddingVertical: 8
|
|
322
|
+
},
|
|
323
|
+
fab: {
|
|
324
|
+
position: "absolute",
|
|
325
|
+
bottom: 24,
|
|
326
|
+
right: 24,
|
|
327
|
+
width: 56,
|
|
328
|
+
height: 56,
|
|
329
|
+
justifyContent: "center",
|
|
330
|
+
alignItems: "center",
|
|
331
|
+
shadowColor: "#000",
|
|
332
|
+
shadowOffset: { width: 0, height: 2 },
|
|
333
|
+
shadowOpacity: 0.25,
|
|
334
|
+
shadowRadius: 4,
|
|
335
|
+
elevation: 5
|
|
336
|
+
},
|
|
337
|
+
fabText: {
|
|
338
|
+
color: "#fff",
|
|
339
|
+
fontSize: 24,
|
|
340
|
+
fontWeight: "600"
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// src/sdk/usePluginTheme.ts
|
|
345
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
346
|
+
import { ada, spacing, fontSize, borderRadius } from "@ateam-ai-mobile/core";
|
|
347
|
+
var adaColors = {
|
|
348
|
+
bg: ada.paper,
|
|
349
|
+
surface: ada.paperSoft,
|
|
350
|
+
surfaceAlt: ada.paperDeep,
|
|
351
|
+
input: ada.paper,
|
|
352
|
+
border: ada.border,
|
|
353
|
+
text: ada.ink,
|
|
354
|
+
textSecondary: ada.inkSoft,
|
|
355
|
+
textMuted: ada.inkMuted,
|
|
356
|
+
accent: ada.orange,
|
|
357
|
+
accentSoft: ada.orangeSoft,
|
|
358
|
+
success: "#5A8A5C",
|
|
359
|
+
successSoft: "rgba(90,138,92,0.14)",
|
|
360
|
+
warning: "#D49A4E",
|
|
361
|
+
warningSoft: "rgba(212,154,78,0.16)",
|
|
362
|
+
error: "#C15545",
|
|
363
|
+
errorSoft: "rgba(193,85,69,0.14)",
|
|
364
|
+
purple: ada.orangeDeep,
|
|
365
|
+
purpleSoft: ada.orangeSoft
|
|
366
|
+
};
|
|
367
|
+
var _colors = adaColors;
|
|
368
|
+
var _isDark = false;
|
|
369
|
+
var _subscribers = /* @__PURE__ */ new Set();
|
|
370
|
+
function setPluginThemeColors(colors, opts) {
|
|
371
|
+
_colors = { ...adaColors, ...colors };
|
|
372
|
+
if (opts?.isDark !== void 0) _isDark = opts.isDark;
|
|
373
|
+
_subscribers.forEach((fn) => fn());
|
|
374
|
+
}
|
|
375
|
+
function subscribe(fn) {
|
|
376
|
+
_subscribers.add(fn);
|
|
377
|
+
return () => {
|
|
378
|
+
_subscribers.delete(fn);
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function usePluginTheme() {
|
|
382
|
+
const colors = useSyncExternalStore(subscribe, () => _colors);
|
|
383
|
+
const isDark = useSyncExternalStore(subscribe, () => _isDark);
|
|
384
|
+
return useMemo(() => ({
|
|
385
|
+
colors,
|
|
386
|
+
spacing: {
|
|
387
|
+
xs: spacing.xs,
|
|
388
|
+
sm: spacing.sm,
|
|
389
|
+
md: spacing.md,
|
|
390
|
+
lg: spacing.lg,
|
|
391
|
+
xl: spacing.xl,
|
|
392
|
+
xxl: spacing.xxl
|
|
393
|
+
},
|
|
394
|
+
fontSize: {
|
|
395
|
+
xs: fontSize.xs,
|
|
396
|
+
sm: fontSize.sm,
|
|
397
|
+
md: fontSize.md,
|
|
398
|
+
lg: fontSize.lg,
|
|
399
|
+
xl: fontSize.xl
|
|
400
|
+
},
|
|
401
|
+
borderRadius: {
|
|
402
|
+
sm: borderRadius.sm,
|
|
403
|
+
md: borderRadius.md,
|
|
404
|
+
lg: borderRadius.lg,
|
|
405
|
+
full: borderRadius.full
|
|
406
|
+
},
|
|
407
|
+
isDark
|
|
408
|
+
}), [colors, isDark]);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/sdk/useApi.ts
|
|
412
|
+
import { useMemo as useMemo2 } from "react";
|
|
413
|
+
function useApi(bridge) {
|
|
414
|
+
return useMemo2(() => {
|
|
415
|
+
const apiFn = async (connectorId, tool, args = {}) => {
|
|
416
|
+
if (!bridge) {
|
|
417
|
+
throw new Error("useApi: bridge is required. Pass it from plugin props.");
|
|
418
|
+
}
|
|
419
|
+
return bridge.mcpCall(tool, args, connectorId);
|
|
420
|
+
};
|
|
421
|
+
const callMethod = async (first, second, third) => {
|
|
422
|
+
if (!bridge) {
|
|
423
|
+
throw new Error("useApi: bridge is required. Pass it from plugin props.");
|
|
424
|
+
}
|
|
425
|
+
if (typeof second === "string") {
|
|
426
|
+
return bridge.mcpCall(second, third || {}, first);
|
|
427
|
+
}
|
|
428
|
+
if (typeof third === "string") {
|
|
429
|
+
return bridge.mcpCall(first, second || {}, third);
|
|
430
|
+
}
|
|
431
|
+
return bridge.mcpCall(first, second || {});
|
|
432
|
+
};
|
|
433
|
+
Object.defineProperty(apiFn, "call", {
|
|
434
|
+
value: callMethod,
|
|
435
|
+
writable: false,
|
|
436
|
+
configurable: false
|
|
437
|
+
});
|
|
438
|
+
return apiFn;
|
|
439
|
+
}, [bridge]);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/sdk/index.ts
|
|
443
|
+
var PluginSDK = { register, registerService };
|
|
444
|
+
|
|
445
|
+
// src/host.ts
|
|
446
|
+
var _haptics = null;
|
|
447
|
+
function setHapticsProvider(haptics) {
|
|
448
|
+
_haptics = haptics;
|
|
449
|
+
}
|
|
450
|
+
function getHaptics() {
|
|
451
|
+
return _haptics;
|
|
452
|
+
}
|
|
453
|
+
function unavailable(api) {
|
|
454
|
+
return new Proxy({}, {
|
|
455
|
+
get: (_t, method) => () => Promise.reject(new Error(`[adas] native.${api}.${String(method)} unavailable \u2014 host did not call setPluginNativeProvider()`))
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
var noopNative = {
|
|
459
|
+
camera: unavailable("camera"),
|
|
460
|
+
location: unavailable("location"),
|
|
461
|
+
storage: unavailable("storage"),
|
|
462
|
+
biometrics: unavailable("biometrics"),
|
|
463
|
+
biometricsV2: unavailable("biometricsV2"),
|
|
464
|
+
notifications: unavailable("notifications"),
|
|
465
|
+
haptics: { impact: () => {
|
|
466
|
+
}, notification: () => {
|
|
467
|
+
}, selection: () => {
|
|
468
|
+
} },
|
|
469
|
+
connectivity: { isOnline: () => true, onChange: () => () => {
|
|
470
|
+
} },
|
|
471
|
+
fileSystem: unavailable("fileSystem"),
|
|
472
|
+
cookies: unavailable("cookies"),
|
|
473
|
+
sharing: unavailable("sharing"),
|
|
474
|
+
scanner: unavailable("scanner"),
|
|
475
|
+
nfc: unavailable("nfc")
|
|
476
|
+
};
|
|
477
|
+
var _pluginNative = noopNative;
|
|
478
|
+
function setPluginNativeProvider(native) {
|
|
479
|
+
_pluginNative = native;
|
|
480
|
+
}
|
|
481
|
+
function getPluginNative() {
|
|
482
|
+
return _pluginNative;
|
|
483
|
+
}
|
|
484
|
+
var _nativeRegistry = /* @__PURE__ */ new Map();
|
|
485
|
+
function registerNativePlugin(id, plugin) {
|
|
486
|
+
_nativeRegistry.set(id, plugin);
|
|
487
|
+
}
|
|
488
|
+
function unregisterNativePlugin(id) {
|
|
489
|
+
_nativeRegistry.delete(id);
|
|
490
|
+
}
|
|
491
|
+
var nativePluginRegistry = {
|
|
492
|
+
has(id) {
|
|
493
|
+
return _nativeRegistry.has(id);
|
|
494
|
+
},
|
|
495
|
+
async get(id) {
|
|
496
|
+
const entry = _nativeRegistry.get(id);
|
|
497
|
+
if (!entry) return null;
|
|
498
|
+
if (typeof entry === "function") {
|
|
499
|
+
try {
|
|
500
|
+
return await entry();
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.warn(`[adas-plugins] lazy native plugin '${id}' failed to load:`, err);
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return entry;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
var _bundleModules = /* @__PURE__ */ new Map();
|
|
510
|
+
function registerBundleModules(modules) {
|
|
511
|
+
for (const [name, mod] of Object.entries(modules)) {
|
|
512
|
+
_bundleModules.set(name, mod);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function resolveBundleModule(name) {
|
|
516
|
+
return _bundleModules.get(name);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/bundles/bundleManager.ts
|
|
520
|
+
import React2 from "react";
|
|
521
|
+
import {
|
|
522
|
+
useState,
|
|
523
|
+
useEffect,
|
|
524
|
+
useCallback,
|
|
525
|
+
useMemo as useMemo3,
|
|
526
|
+
useRef,
|
|
527
|
+
useContext,
|
|
528
|
+
useReducer,
|
|
529
|
+
useLayoutEffect,
|
|
530
|
+
useImperativeHandle,
|
|
531
|
+
useDebugValue
|
|
532
|
+
} from "react";
|
|
533
|
+
import * as ReactNative from "react-native";
|
|
534
|
+
import { apiFetchRaw } from "@ateam-ai-mobile/core";
|
|
535
|
+
|
|
536
|
+
// src/utils/sha256.ts
|
|
537
|
+
var K = new Uint32Array([
|
|
538
|
+
1116352408,
|
|
539
|
+
1899447441,
|
|
540
|
+
3049323471,
|
|
541
|
+
3921009573,
|
|
542
|
+
961987163,
|
|
543
|
+
1508970993,
|
|
544
|
+
2453635748,
|
|
545
|
+
2870763221,
|
|
546
|
+
3624381080,
|
|
547
|
+
310598401,
|
|
548
|
+
607225278,
|
|
549
|
+
1426881987,
|
|
550
|
+
1925078388,
|
|
551
|
+
2162078206,
|
|
552
|
+
2614888103,
|
|
553
|
+
3248222580,
|
|
554
|
+
3835390401,
|
|
555
|
+
4022224774,
|
|
556
|
+
264347078,
|
|
557
|
+
604807628,
|
|
558
|
+
770255983,
|
|
559
|
+
1249150122,
|
|
560
|
+
1555081692,
|
|
561
|
+
1996064986,
|
|
562
|
+
2554220882,
|
|
563
|
+
2821834349,
|
|
564
|
+
2952996808,
|
|
565
|
+
3210313671,
|
|
566
|
+
3336571891,
|
|
567
|
+
3584528711,
|
|
568
|
+
113926993,
|
|
569
|
+
338241895,
|
|
570
|
+
666307205,
|
|
571
|
+
773529912,
|
|
572
|
+
1294757372,
|
|
573
|
+
1396182291,
|
|
574
|
+
1695183700,
|
|
575
|
+
1986661051,
|
|
576
|
+
2177026350,
|
|
577
|
+
2456956037,
|
|
578
|
+
2730485921,
|
|
579
|
+
2820302411,
|
|
580
|
+
3259730800,
|
|
581
|
+
3345764771,
|
|
582
|
+
3516065817,
|
|
583
|
+
3600352804,
|
|
584
|
+
4094571909,
|
|
585
|
+
275423344,
|
|
586
|
+
430227734,
|
|
587
|
+
506948616,
|
|
588
|
+
659060556,
|
|
589
|
+
883997877,
|
|
590
|
+
958139571,
|
|
591
|
+
1322822218,
|
|
592
|
+
1537002063,
|
|
593
|
+
1747873779,
|
|
594
|
+
1955562222,
|
|
595
|
+
2024104815,
|
|
596
|
+
2227730452,
|
|
597
|
+
2361852424,
|
|
598
|
+
2428436474,
|
|
599
|
+
2756734187,
|
|
600
|
+
3204031479,
|
|
601
|
+
3329325298
|
|
602
|
+
]);
|
|
603
|
+
var rotr = (n, x) => x >>> n | x << 32 - n;
|
|
604
|
+
function sha256Hex(message) {
|
|
605
|
+
const utf8 = unescape(encodeURIComponent(message));
|
|
606
|
+
const bytes = new Uint8Array(utf8.length);
|
|
607
|
+
for (let i = 0; i < utf8.length; i++) bytes[i] = utf8.charCodeAt(i);
|
|
608
|
+
const bitLen = bytes.length * 8;
|
|
609
|
+
const padLen = (56 - (bytes.length + 1) % 64 + 64) % 64;
|
|
610
|
+
const buf = new Uint8Array(bytes.length + 1 + padLen + 8);
|
|
611
|
+
buf.set(bytes);
|
|
612
|
+
buf[bytes.length] = 128;
|
|
613
|
+
const len = buf.length;
|
|
614
|
+
buf[len - 4] = bitLen >>> 24 & 255;
|
|
615
|
+
buf[len - 3] = bitLen >>> 16 & 255;
|
|
616
|
+
buf[len - 2] = bitLen >>> 8 & 255;
|
|
617
|
+
buf[len - 1] = bitLen & 255;
|
|
618
|
+
const h = new Uint32Array([
|
|
619
|
+
1779033703,
|
|
620
|
+
3144134277,
|
|
621
|
+
1013904242,
|
|
622
|
+
2773480762,
|
|
623
|
+
1359893119,
|
|
624
|
+
2600822924,
|
|
625
|
+
528734635,
|
|
626
|
+
1541459225
|
|
627
|
+
]);
|
|
628
|
+
const w = new Uint32Array(64);
|
|
629
|
+
for (let i = 0; i < buf.length; i += 64) {
|
|
630
|
+
for (let t = 0; t < 16; t++) {
|
|
631
|
+
const j = i + t * 4;
|
|
632
|
+
w[t] = buf[j] << 24 | buf[j + 1] << 16 | buf[j + 2] << 8 | buf[j + 3];
|
|
633
|
+
}
|
|
634
|
+
for (let t = 16; t < 64; t++) {
|
|
635
|
+
const s0 = rotr(7, w[t - 15]) ^ rotr(18, w[t - 15]) ^ w[t - 15] >>> 3;
|
|
636
|
+
const s1 = rotr(17, w[t - 2]) ^ rotr(19, w[t - 2]) ^ w[t - 2] >>> 10;
|
|
637
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 | 0;
|
|
638
|
+
}
|
|
639
|
+
let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
|
|
640
|
+
for (let t = 0; t < 64; t++) {
|
|
641
|
+
const S1 = rotr(6, e) ^ rotr(11, e) ^ rotr(25, e);
|
|
642
|
+
const ch = e & f ^ ~e & g;
|
|
643
|
+
const temp1 = hh + S1 + ch + K[t] + w[t] | 0;
|
|
644
|
+
const S0 = rotr(2, a) ^ rotr(13, a) ^ rotr(22, a);
|
|
645
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
646
|
+
const temp2 = S0 + maj | 0;
|
|
647
|
+
hh = g;
|
|
648
|
+
g = f;
|
|
649
|
+
f = e;
|
|
650
|
+
e = d + temp1 | 0;
|
|
651
|
+
d = c;
|
|
652
|
+
c = b;
|
|
653
|
+
b = a;
|
|
654
|
+
a = temp1 + temp2 | 0;
|
|
655
|
+
}
|
|
656
|
+
h[0] = h[0] + a | 0;
|
|
657
|
+
h[1] = h[1] + b | 0;
|
|
658
|
+
h[2] = h[2] + c | 0;
|
|
659
|
+
h[3] = h[3] + d | 0;
|
|
660
|
+
h[4] = h[4] + e | 0;
|
|
661
|
+
h[5] = h[5] + f | 0;
|
|
662
|
+
h[6] = h[6] + g | 0;
|
|
663
|
+
h[7] = h[7] + hh | 0;
|
|
664
|
+
}
|
|
665
|
+
let out = "";
|
|
666
|
+
for (let i = 0; i < 8; i++) {
|
|
667
|
+
out += h[i].toString(16).padStart(8, "0");
|
|
668
|
+
}
|
|
669
|
+
return out;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/bundles/storage.ts
|
|
673
|
+
var MemoryBundleStorage = class {
|
|
674
|
+
constructor() {
|
|
675
|
+
this.index = {};
|
|
676
|
+
this.files = /* @__PURE__ */ new Map();
|
|
677
|
+
}
|
|
678
|
+
async loadIndex() {
|
|
679
|
+
return this.index;
|
|
680
|
+
}
|
|
681
|
+
async saveIndex(index) {
|
|
682
|
+
this.index = index;
|
|
683
|
+
}
|
|
684
|
+
async readBundle(localPath) {
|
|
685
|
+
return this.files.get(localPath) ?? null;
|
|
686
|
+
}
|
|
687
|
+
async writeBundle(key, content) {
|
|
688
|
+
const localPath = `memory://${key}`;
|
|
689
|
+
this.files.set(localPath, content);
|
|
690
|
+
return localPath;
|
|
691
|
+
}
|
|
692
|
+
async deleteBundle(localPath) {
|
|
693
|
+
this.files.delete(localPath);
|
|
694
|
+
}
|
|
695
|
+
async clearAll() {
|
|
696
|
+
this.index = {};
|
|
697
|
+
this.files.clear();
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
var _storage = new MemoryBundleStorage();
|
|
701
|
+
function setBundleStorage(storage) {
|
|
702
|
+
_storage = storage;
|
|
703
|
+
}
|
|
704
|
+
function getBundleStorage() {
|
|
705
|
+
return _storage;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/bundles/bundleManager.ts
|
|
709
|
+
var ReactShim = {
|
|
710
|
+
__esModule: true,
|
|
711
|
+
default: React2,
|
|
712
|
+
...React2,
|
|
713
|
+
// Explicitly include hooks (React 19 may not have them as own enumerable props)
|
|
714
|
+
useState,
|
|
715
|
+
useEffect,
|
|
716
|
+
useCallback,
|
|
717
|
+
useMemo: useMemo3,
|
|
718
|
+
useRef,
|
|
719
|
+
useContext,
|
|
720
|
+
useReducer,
|
|
721
|
+
useLayoutEffect,
|
|
722
|
+
useImperativeHandle,
|
|
723
|
+
useDebugValue,
|
|
724
|
+
createElement: React2.createElement,
|
|
725
|
+
Fragment: React2.Fragment,
|
|
726
|
+
Component: React2.Component,
|
|
727
|
+
PureComponent: React2.PureComponent,
|
|
728
|
+
createContext: React2.createContext,
|
|
729
|
+
forwardRef: React2.forwardRef,
|
|
730
|
+
memo: React2.memo,
|
|
731
|
+
Children: React2.Children,
|
|
732
|
+
cloneElement: React2.cloneElement,
|
|
733
|
+
isValidElement: React2.isValidElement,
|
|
734
|
+
createRef: React2.createRef,
|
|
735
|
+
lazy: React2.lazy,
|
|
736
|
+
Suspense: React2.Suspense
|
|
737
|
+
};
|
|
738
|
+
var PluginSDKShim = Object.freeze({
|
|
739
|
+
...sdk_exports,
|
|
740
|
+
PluginSDK: Object.freeze({
|
|
741
|
+
...PluginSDK,
|
|
742
|
+
register: PluginSDK.register
|
|
743
|
+
}),
|
|
744
|
+
register
|
|
745
|
+
});
|
|
746
|
+
var requireShim = (name) => {
|
|
747
|
+
if (name === "react") return ReactShim;
|
|
748
|
+
if (name === "react-native") return ReactNative;
|
|
749
|
+
if (name === "@adas/plugin-sdk") return PluginSDKShim;
|
|
750
|
+
const hostModule = resolveBundleModule(name);
|
|
751
|
+
if (hostModule !== void 0) return hostModule;
|
|
752
|
+
console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
|
|
753
|
+
return {};
|
|
754
|
+
};
|
|
755
|
+
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
756
|
+
var BundleManager = class {
|
|
757
|
+
constructor() {
|
|
758
|
+
/** In-memory module cache: bundleUrl → evaluated module.exports */
|
|
759
|
+
this.modules = /* @__PURE__ */ new Map();
|
|
760
|
+
/** In-flight download deduplication: bundleUrl → Promise */
|
|
761
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
762
|
+
}
|
|
763
|
+
// ── Public API ──────────────────────────────────────────────
|
|
764
|
+
/**
|
|
765
|
+
* Load a plugin component. Returns the RegisteredUIPlugin object.
|
|
766
|
+
* Handles: in-memory cache → disk cache → download → evaluate → extract.
|
|
767
|
+
*
|
|
768
|
+
* `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be
|
|
769
|
+
* provided for any bundle the user has not previously verified — without
|
|
770
|
+
* it, a tampered bundle is `eval`'d as JS with full RN runtime + every
|
|
771
|
+
* native capability the manifest declares.
|
|
772
|
+
*/
|
|
773
|
+
async load(pluginId, bundleUrl, version, expectedHash) {
|
|
774
|
+
const cached = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
775
|
+
if (cached) return cached;
|
|
776
|
+
let modulePromise = this.inflight.get(bundleUrl);
|
|
777
|
+
if (!modulePromise) {
|
|
778
|
+
modulePromise = this.loadModule(bundleUrl, version, expectedHash);
|
|
779
|
+
this.inflight.set(bundleUrl, modulePromise);
|
|
780
|
+
modulePromise.catch(() => {
|
|
781
|
+
}).finally(() => this.inflight.delete(bundleUrl));
|
|
782
|
+
}
|
|
783
|
+
await modulePromise;
|
|
784
|
+
const component = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
785
|
+
if (!component) {
|
|
786
|
+
throw new Error(
|
|
787
|
+
`Bundle loaded from ${bundleUrl} but no component found for "${pluginId}". Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
return component;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Prewarm: load every plugin bundle in the background after app boot, so a
|
|
794
|
+
* later open is instant (in-memory hit) instead of round-tripping the
|
|
795
|
+
* network. Errors are swallowed per-plugin — one bad bundle shouldn't
|
|
796
|
+
* block others. Concurrency capped at 3.
|
|
797
|
+
*/
|
|
798
|
+
async prewarmAll(items) {
|
|
799
|
+
const queue = [...items];
|
|
800
|
+
const concurrency = Math.min(3, queue.length);
|
|
801
|
+
const start = Date.now();
|
|
802
|
+
let okCount = 0;
|
|
803
|
+
let failCount = 0;
|
|
804
|
+
const worker = async () => {
|
|
805
|
+
while (queue.length > 0) {
|
|
806
|
+
const it = queue.shift();
|
|
807
|
+
if (!it) return;
|
|
808
|
+
try {
|
|
809
|
+
await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash);
|
|
810
|
+
okCount++;
|
|
811
|
+
} catch (err) {
|
|
812
|
+
failCount++;
|
|
813
|
+
console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
await Promise.all(Array.from({ length: concurrency }, worker));
|
|
818
|
+
console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Clear all caches (in-memory + storage).
|
|
822
|
+
*/
|
|
823
|
+
async clearAll() {
|
|
824
|
+
this.modules.clear();
|
|
825
|
+
try {
|
|
826
|
+
await getBundleStorage().clearAll();
|
|
827
|
+
} catch (err) {
|
|
828
|
+
console.warn("[BundleManager] clearAll error:", err);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Clear cache for a specific plugin/bundle.
|
|
833
|
+
*/
|
|
834
|
+
async clearPlugin(pluginId) {
|
|
835
|
+
const storage = getBundleStorage();
|
|
836
|
+
const index = await storage.loadIndex();
|
|
837
|
+
const entry = index[pluginId];
|
|
838
|
+
if (entry) {
|
|
839
|
+
this.modules.delete(entry.bundleUrl);
|
|
840
|
+
delete index[pluginId];
|
|
841
|
+
await storage.saveIndex(index);
|
|
842
|
+
try {
|
|
843
|
+
await storage.deleteBundle(entry.localPath);
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
// ── Private: module loading pipeline ────────────────────────
|
|
849
|
+
async loadModule(bundleUrl, version, expectedHash) {
|
|
850
|
+
if (this.modules.has(bundleUrl)) return;
|
|
851
|
+
const storage = getBundleStorage();
|
|
852
|
+
const index = await storage.loadIndex();
|
|
853
|
+
const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;
|
|
854
|
+
let content = null;
|
|
855
|
+
for (const entry of Object.values(index)) {
|
|
856
|
+
if (entry.bundleUrl === bundleUrl && entry.version === version) {
|
|
857
|
+
const age = Date.now() - entry.timestamp;
|
|
858
|
+
const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;
|
|
859
|
+
if (age < CACHE_TTL && hashOk) {
|
|
860
|
+
content = await storage.readBundle(entry.localPath);
|
|
861
|
+
if (content) break;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (!content) {
|
|
866
|
+
const downloaded = await this.download(bundleUrl, expectedLc);
|
|
867
|
+
content = downloaded.content;
|
|
868
|
+
const safeName = bundleUrl.replace(/[^a-zA-Z0-9]/g, "_").slice(-80);
|
|
869
|
+
try {
|
|
870
|
+
const localPath = await storage.writeBundle(`${safeName}@${version}.js`, content);
|
|
871
|
+
index[bundleUrl] = {
|
|
872
|
+
localPath,
|
|
873
|
+
version,
|
|
874
|
+
timestamp: Date.now(),
|
|
875
|
+
bundleUrl,
|
|
876
|
+
bundleHash: downloaded.sha256
|
|
877
|
+
};
|
|
878
|
+
await storage.saveIndex(index);
|
|
879
|
+
} catch (err) {
|
|
880
|
+
console.warn("[BundleManager] Cache write error:", err);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
const mod = this.evaluate(content, bundleUrl);
|
|
884
|
+
this.modules.set(bundleUrl, mod);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Download + integrity-verify a bundle. Refuses to return when:
|
|
888
|
+
* - manifest hash and computed hash disagree
|
|
889
|
+
* - server's `X-Bundle-SHA256` disagrees with manifest hash
|
|
890
|
+
* - no expected hash AND no server hash header (cannot prove the bundle
|
|
891
|
+
* wasn't tampered with — fail closed rather than trust the wire).
|
|
892
|
+
*/
|
|
893
|
+
async download(bundleUrl, expectedHash) {
|
|
894
|
+
const cacheBust = `${bundleUrl}${bundleUrl.includes("?") ? "&" : "?"}t=${Date.now()}`;
|
|
895
|
+
const response = await apiFetchRaw(cacheBust);
|
|
896
|
+
if (!response.ok) {
|
|
897
|
+
const body = await response.text().catch(() => "");
|
|
898
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText} \u2014 ${body.substring(0, 200)}`);
|
|
899
|
+
}
|
|
900
|
+
const content = await response.text();
|
|
901
|
+
if (content.length < 50 || content.trimStart().startsWith("<!")) {
|
|
902
|
+
throw new Error("Bundle content is empty or HTML \u2014 server may have returned an error page");
|
|
903
|
+
}
|
|
904
|
+
const headerHash = (response.headers.get("x-bundle-sha256") || "").toLowerCase();
|
|
905
|
+
const computed = sha256Hex(content);
|
|
906
|
+
if (expectedHash) {
|
|
907
|
+
if (computed !== expectedHash) {
|
|
908
|
+
throw new Error(
|
|
909
|
+
`Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
if (headerHash && headerHash !== expectedHash) {
|
|
913
|
+
throw new Error(
|
|
914
|
+
`Bundle hash mismatch: manifest=${expectedHash.slice(0, 12)}... server=${headerHash.slice(0, 12)}.... Refusing to evaluate.`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
} else if (headerHash) {
|
|
918
|
+
if (computed !== headerHash) {
|
|
919
|
+
throw new Error(
|
|
920
|
+
`Bundle body sha256 (${computed.slice(0, 12)}...) does not match server's X-Bundle-SHA256 (${headerHash.slice(0, 12)}...).`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
console.warn(`[BundleManager] No manifest bundleHash \u2014 verified via X-Bundle-SHA256 header only`);
|
|
924
|
+
} else {
|
|
925
|
+
throw new Error(
|
|
926
|
+
`No bundleHash in manifest and no X-Bundle-SHA256 header from server. Refusing to evaluate untrusted bundle.`
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
return { content, sha256: computed };
|
|
930
|
+
}
|
|
931
|
+
evaluate(content, bundleUrl) {
|
|
932
|
+
let cleaned = content.replace(
|
|
933
|
+
/__toESM\(require\(("[^"]+")\)(?:,\s*\d+)?\)/g,
|
|
934
|
+
"require($1)"
|
|
935
|
+
);
|
|
936
|
+
cleaned = cleaned.replace(
|
|
937
|
+
/module\.exports\s*=\s*__toCommonJS\((\w+)\)/g,
|
|
938
|
+
"module.exports = $1"
|
|
939
|
+
);
|
|
940
|
+
const mod = { exports: {} };
|
|
941
|
+
try {
|
|
942
|
+
const factory = new Function(
|
|
943
|
+
"module",
|
|
944
|
+
"exports",
|
|
945
|
+
"require",
|
|
946
|
+
"__filename",
|
|
947
|
+
"__dirname",
|
|
948
|
+
cleaned
|
|
949
|
+
);
|
|
950
|
+
factory(mod, mod.exports, requireShim, bundleUrl, "adas-plugin-bundles");
|
|
951
|
+
} catch (err) {
|
|
952
|
+
throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
953
|
+
}
|
|
954
|
+
const raw = mod.exports;
|
|
955
|
+
const materialized = {};
|
|
956
|
+
for (const key of Object.getOwnPropertyNames(raw)) {
|
|
957
|
+
try {
|
|
958
|
+
materialized[key] = raw[key];
|
|
959
|
+
} catch {
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return materialized;
|
|
963
|
+
}
|
|
964
|
+
// ── Private: component extraction ───────────────────────────
|
|
965
|
+
/**
|
|
966
|
+
* Find a plugin component in a loaded module.
|
|
967
|
+
* Handles: default export, direct key match, multi-plugin .id match.
|
|
968
|
+
*/
|
|
969
|
+
getComponentFromMemory(pluginId, bundleUrl) {
|
|
970
|
+
const mod = this.modules.get(bundleUrl);
|
|
971
|
+
if (!mod) return null;
|
|
972
|
+
const shortName = pluginId.includes(":") ? pluginId.split(":").pop() : pluginId;
|
|
973
|
+
if (mod[pluginId] && typeof mod[pluginId].Component === "function") {
|
|
974
|
+
return mod[pluginId];
|
|
975
|
+
}
|
|
976
|
+
const pascal = shortName.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
977
|
+
if (mod[pascal] && typeof mod[pascal] === "object" && typeof mod[pascal].Component === "function") {
|
|
978
|
+
return mod[pascal];
|
|
979
|
+
}
|
|
980
|
+
if (mod.default && typeof mod.default === "object" && typeof mod.default.Component === "function") {
|
|
981
|
+
return mod.default;
|
|
982
|
+
}
|
|
983
|
+
for (const key of Object.keys(mod)) {
|
|
984
|
+
const val = mod[key];
|
|
985
|
+
if (val && typeof val === "object" && val.id === shortName && typeof val.Component === "function") {
|
|
986
|
+
return val;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
describeModule(mod) {
|
|
992
|
+
if (!mod) return "(empty)";
|
|
993
|
+
return Object.keys(mod).map((k) => {
|
|
994
|
+
const v = mod[k];
|
|
995
|
+
if (v && typeof v === "object" && v.id) return `${k}(id="${v.id}")`;
|
|
996
|
+
return k;
|
|
997
|
+
}).join(", ");
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
var bundleManager = new BundleManager();
|
|
1001
|
+
|
|
1002
|
+
// src/bridge/native.ts
|
|
1003
|
+
import { useCallback as useCallback2, useMemo as useMemo4, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
1004
|
+
import {
|
|
1005
|
+
connectorCall,
|
|
1006
|
+
mcpFetch as mcpFetchApi,
|
|
1007
|
+
unwrapMcpResult,
|
|
1008
|
+
postCommandResult,
|
|
1009
|
+
getTenant,
|
|
1010
|
+
getUser,
|
|
1011
|
+
pluginCommandEmitter,
|
|
1012
|
+
useChatStore
|
|
1013
|
+
} from "@ateam-ai-mobile/core";
|
|
1014
|
+
function buildContext(pluginId, connectorId) {
|
|
1015
|
+
const user = getUser();
|
|
1016
|
+
return {
|
|
1017
|
+
tenant: getTenant(),
|
|
1018
|
+
pluginId,
|
|
1019
|
+
connectorId,
|
|
1020
|
+
user: user ? { id: user.actorId || "", name: user.name || "", roles: [] } : void 0
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function useNativeBridge(pluginId, connectorId, commandBufferRef) {
|
|
1024
|
+
const initCallbackRef = useRef2(null);
|
|
1025
|
+
const commandListenersRef = useRef2(/* @__PURE__ */ new Set());
|
|
1026
|
+
const initializedRef = useRef2(false);
|
|
1027
|
+
const fireInit = useCallback2(() => {
|
|
1028
|
+
if (initializedRef.current || !initCallbackRef.current) return;
|
|
1029
|
+
initCallbackRef.current(buildContext(pluginId, connectorId));
|
|
1030
|
+
initializedRef.current = true;
|
|
1031
|
+
}, [pluginId, connectorId]);
|
|
1032
|
+
const onInit = useCallback2((cb) => {
|
|
1033
|
+
initCallbackRef.current = cb;
|
|
1034
|
+
setTimeout(fireInit, 0);
|
|
1035
|
+
}, [fireInit]);
|
|
1036
|
+
const mcpCall = useCallback2(async (toolOrConnector, argsOrTool, cIdOrArgs) => {
|
|
1037
|
+
let tool;
|
|
1038
|
+
let args;
|
|
1039
|
+
let cId;
|
|
1040
|
+
if (typeof argsOrTool === "string") {
|
|
1041
|
+
cId = toolOrConnector;
|
|
1042
|
+
tool = argsOrTool;
|
|
1043
|
+
args = cIdOrArgs || {};
|
|
1044
|
+
} else {
|
|
1045
|
+
tool = toolOrConnector;
|
|
1046
|
+
args = argsOrTool;
|
|
1047
|
+
cId = typeof cIdOrArgs === "string" ? cIdOrArgs : void 0;
|
|
1048
|
+
}
|
|
1049
|
+
const targetConnector = cId || connectorId;
|
|
1050
|
+
if (!targetConnector) {
|
|
1051
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2014 no connectorId. tool=${tool}`);
|
|
1052
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1053
|
+
}
|
|
1054
|
+
try {
|
|
1055
|
+
const data = await connectorCall(targetConnector, tool, args);
|
|
1056
|
+
if (!data.ok && data.error) {
|
|
1057
|
+
throw new Error(data.error);
|
|
1058
|
+
}
|
|
1059
|
+
return unwrapMcpResult(data.result);
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2192 connector=${targetConnector} tool=${tool}:`, err?.message || err);
|
|
1062
|
+
throw err;
|
|
1063
|
+
}
|
|
1064
|
+
}, [pluginId, connectorId]);
|
|
1065
|
+
const mcpFetch = useCallback2(async (body) => {
|
|
1066
|
+
return mcpFetchApi(body);
|
|
1067
|
+
}, []);
|
|
1068
|
+
const sendCommand = useCallback2((type, payload) => {
|
|
1069
|
+
if (type === "plugin.command.result" && payload?.correlationId) {
|
|
1070
|
+
postCommandResult(payload.correlationId, payload.result, payload.error);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
}, [pluginId]);
|
|
1074
|
+
const onCommand = useCallback2((cb) => {
|
|
1075
|
+
commandListenersRef.current.add(cb);
|
|
1076
|
+
if (commandBufferRef) {
|
|
1077
|
+
const buf = commandBufferRef.current;
|
|
1078
|
+
const remaining = [];
|
|
1079
|
+
for (const cmd of buf) {
|
|
1080
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") {
|
|
1081
|
+
try {
|
|
1082
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
} else {
|
|
1086
|
+
remaining.push(cmd);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
commandBufferRef.current = remaining;
|
|
1090
|
+
}
|
|
1091
|
+
for (const cmd of pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
1092
|
+
try {
|
|
1093
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1094
|
+
} catch {
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return () => {
|
|
1098
|
+
commandListenersRef.current.delete(cb);
|
|
1099
|
+
};
|
|
1100
|
+
}, [pluginId, commandBufferRef]);
|
|
1101
|
+
useEffect2(() => {
|
|
1102
|
+
const unsub = pluginCommandEmitter.on("plugin-command", (cmd) => {
|
|
1103
|
+
if (cmd.pluginId !== pluginId) return;
|
|
1104
|
+
if (cmd.command === "__focus") return;
|
|
1105
|
+
commandListenersRef.current.forEach((fn) => {
|
|
1106
|
+
try {
|
|
1107
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1108
|
+
} catch {
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
});
|
|
1112
|
+
return unsub;
|
|
1113
|
+
}, [pluginId]);
|
|
1114
|
+
useEffect2(() => {
|
|
1115
|
+
const timer = setTimeout(() => {
|
|
1116
|
+
const dispatch = (cmd) => {
|
|
1117
|
+
commandListenersRef.current.forEach((fn) => {
|
|
1118
|
+
try {
|
|
1119
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1120
|
+
} catch {
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1124
|
+
if (commandBufferRef) {
|
|
1125
|
+
const remaining = [];
|
|
1126
|
+
for (const cmd of commandBufferRef.current) {
|
|
1127
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") dispatch(cmd);
|
|
1128
|
+
else remaining.push(cmd);
|
|
1129
|
+
}
|
|
1130
|
+
commandBufferRef.current = remaining;
|
|
1131
|
+
}
|
|
1132
|
+
for (const cmd of pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
1133
|
+
dispatch(cmd);
|
|
1134
|
+
}
|
|
1135
|
+
}, 500);
|
|
1136
|
+
return () => clearTimeout(timer);
|
|
1137
|
+
}, [pluginId, commandBufferRef]);
|
|
1138
|
+
useEffect2(() => {
|
|
1139
|
+
return () => {
|
|
1140
|
+
commandListenersRef.current.clear();
|
|
1141
|
+
initializedRef.current = false;
|
|
1142
|
+
};
|
|
1143
|
+
}, []);
|
|
1144
|
+
const close = useCallback2(() => {
|
|
1145
|
+
pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
1146
|
+
}, [pluginId]);
|
|
1147
|
+
const sendMessage = useCallback2((text) => {
|
|
1148
|
+
const t = (text || "").trim();
|
|
1149
|
+
if (!t) return;
|
|
1150
|
+
try {
|
|
1151
|
+
useChatStore.getState().sendMessage(t);
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
1154
|
+
}
|
|
1155
|
+
}, []);
|
|
1156
|
+
const prepareMessage = useCallback2((text) => {
|
|
1157
|
+
const t = (text || "").trim();
|
|
1158
|
+
if (!t) return;
|
|
1159
|
+
pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
1160
|
+
}, []);
|
|
1161
|
+
return useMemo4(
|
|
1162
|
+
() => ({ onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage }),
|
|
1163
|
+
[onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage]
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
function createNativeBridge(pluginId, connectorId) {
|
|
1167
|
+
const commandListeners = /* @__PURE__ */ new Set();
|
|
1168
|
+
return {
|
|
1169
|
+
onInit(cb) {
|
|
1170
|
+
setTimeout(() => {
|
|
1171
|
+
cb(buildContext(pluginId, connectorId));
|
|
1172
|
+
}, 0);
|
|
1173
|
+
},
|
|
1174
|
+
async mcpCall(tool, args, cId) {
|
|
1175
|
+
const targetConnector = cId || connectorId;
|
|
1176
|
+
if (!targetConnector) {
|
|
1177
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1178
|
+
}
|
|
1179
|
+
const data = await connectorCall(targetConnector, tool, args);
|
|
1180
|
+
if (!data.ok && data.error) throw new Error(data.error);
|
|
1181
|
+
return unwrapMcpResult(data.result);
|
|
1182
|
+
},
|
|
1183
|
+
async mcpFetch(body) {
|
|
1184
|
+
return mcpFetchApi(body);
|
|
1185
|
+
},
|
|
1186
|
+
sendCommand(_type, _payload) {
|
|
1187
|
+
},
|
|
1188
|
+
onCommand(cb) {
|
|
1189
|
+
commandListeners.add(cb);
|
|
1190
|
+
return () => {
|
|
1191
|
+
commandListeners.delete(cb);
|
|
1192
|
+
};
|
|
1193
|
+
},
|
|
1194
|
+
close() {
|
|
1195
|
+
pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
1196
|
+
},
|
|
1197
|
+
sendMessage(text) {
|
|
1198
|
+
const t = (text || "").trim();
|
|
1199
|
+
if (!t) return;
|
|
1200
|
+
try {
|
|
1201
|
+
useChatStore.getState().sendMessage(t);
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
prepareMessage(text) {
|
|
1207
|
+
const t = (text || "").trim();
|
|
1208
|
+
if (!t) return;
|
|
1209
|
+
pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/hooks/usePlugin.ts
|
|
1215
|
+
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
1216
|
+
function extractBundleUrl(pluginId, manifest) {
|
|
1217
|
+
const render = manifest.render;
|
|
1218
|
+
if (!render || typeof render !== "object") return { url: null, source: "none" };
|
|
1219
|
+
const injected = manifest.bundleUrl || render.bundleUrl || render.reactNative?.bundleUrl || null;
|
|
1220
|
+
const hasNativeComponent = !!(render.reactNative?.component || render.component);
|
|
1221
|
+
if (hasNativeComponent) {
|
|
1222
|
+
return {
|
|
1223
|
+
url: `/api/ui-plugins/${encodeURIComponent(pluginId)}/bundle.js`,
|
|
1224
|
+
source: injected ? "ignored-explicit" : "derived-default",
|
|
1225
|
+
...injected && { ignored: injected }
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
return { url: null, source: "none" };
|
|
1229
|
+
}
|
|
1230
|
+
function extractBundleHash(manifest) {
|
|
1231
|
+
if (manifest.bundleHash) return manifest.bundleHash;
|
|
1232
|
+
const render = manifest.render;
|
|
1233
|
+
if (!render || typeof render !== "object") return void 0;
|
|
1234
|
+
if (render.bundleHash) return render.bundleHash;
|
|
1235
|
+
if (render.reactNative?.bundleHash) return render.reactNative.bundleHash;
|
|
1236
|
+
return void 0;
|
|
1237
|
+
}
|
|
1238
|
+
function extractComponentId(manifest) {
|
|
1239
|
+
const render = manifest.render;
|
|
1240
|
+
if (!render || typeof render !== "object") return null;
|
|
1241
|
+
return render.component || render.reactNative?.component || null;
|
|
1242
|
+
}
|
|
1243
|
+
function usePlugin(pluginId, manifest) {
|
|
1244
|
+
const [plugin, setPlugin] = useState2(null);
|
|
1245
|
+
const [loading, setLoading] = useState2(true);
|
|
1246
|
+
const [error, setError] = useState2(null);
|
|
1247
|
+
const [bundleMissing, setBundleMissing] = useState2(false);
|
|
1248
|
+
const version = manifest.version || "1.0.0";
|
|
1249
|
+
const { url: bundleUrl, ignored: ignoredUrl } = extractBundleUrl(pluginId, manifest);
|
|
1250
|
+
const bundleHash = extractBundleHash(manifest);
|
|
1251
|
+
const componentId = extractComponentId(manifest);
|
|
1252
|
+
const lookupId = componentId || pluginId;
|
|
1253
|
+
if (ignoredUrl) {
|
|
1254
|
+
console.warn(`[usePlugin] ${pluginId}: IGNORED connector-injected bundleUrl="${ignoredUrl}". The host derives bundle URLs from pluginId via the canonical route.`);
|
|
1255
|
+
}
|
|
1256
|
+
useEffect3(() => {
|
|
1257
|
+
let cancelled = false;
|
|
1258
|
+
(async () => {
|
|
1259
|
+
try {
|
|
1260
|
+
if (nativePluginRegistry.has(lookupId)) {
|
|
1261
|
+
const registered = await nativePluginRegistry.get(lookupId);
|
|
1262
|
+
if (cancelled) return;
|
|
1263
|
+
if (!registered) {
|
|
1264
|
+
throw new Error(`Local plugin not found: ${lookupId}`);
|
|
1265
|
+
}
|
|
1266
|
+
if (registered.type === "service") {
|
|
1267
|
+
throw new Error(`Plugin ${lookupId} is a service plugin and cannot be rendered`);
|
|
1268
|
+
}
|
|
1269
|
+
setPlugin(registered);
|
|
1270
|
+
setLoading(false);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
if (bundleUrl) {
|
|
1274
|
+
try {
|
|
1275
|
+
const component = await bundleManager.load(pluginId, bundleUrl, version, bundleHash);
|
|
1276
|
+
if (cancelled) return;
|
|
1277
|
+
setPlugin(component);
|
|
1278
|
+
setLoading(false);
|
|
1279
|
+
return;
|
|
1280
|
+
} catch (bundleErr) {
|
|
1281
|
+
const msg = bundleErr instanceof Error ? bundleErr.message : String(bundleErr);
|
|
1282
|
+
console.warn(`[usePlugin] ${pluginId}: Bundle load failed, falling back to WebView:`, msg);
|
|
1283
|
+
if (!cancelled) {
|
|
1284
|
+
setBundleMissing(true);
|
|
1285
|
+
setLoading(false);
|
|
1286
|
+
}
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const iframeUrl = manifest?.render?.iframeUrl || manifest?.render?.mode === "adaptive" && manifest?.render?.iframe?.iframeUrl;
|
|
1291
|
+
if (iframeUrl) {
|
|
1292
|
+
if (!cancelled) {
|
|
1293
|
+
setBundleMissing(true);
|
|
1294
|
+
setLoading(false);
|
|
1295
|
+
}
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
throw new Error(
|
|
1299
|
+
`Plugin "${manifest.name || pluginId}" has no bundle URL and is not in the local registry. Check that the solution definition includes a valid render.reactNative.bundleUrl.`
|
|
1300
|
+
);
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
if (cancelled) return;
|
|
1303
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1304
|
+
console.error(`[usePlugin] Failed to load ${pluginId}:`, msg);
|
|
1305
|
+
setError(msg);
|
|
1306
|
+
setLoading(false);
|
|
1307
|
+
}
|
|
1308
|
+
})();
|
|
1309
|
+
return () => {
|
|
1310
|
+
cancelled = true;
|
|
1311
|
+
};
|
|
1312
|
+
}, [pluginId, lookupId, bundleUrl, bundleHash, version]);
|
|
1313
|
+
return { plugin, loading, error, bundleMissing };
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/hooks/useRemotePlugins.ts
|
|
1317
|
+
import { useEffect as useEffect4, useState as useState3, useCallback as useCallback3 } from "react";
|
|
1318
|
+
import { apiFetch } from "@ateam-ai-mobile/core";
|
|
1319
|
+
function useRemotePlugins(solutionId) {
|
|
1320
|
+
const [plugins, setPlugins] = useState3([]);
|
|
1321
|
+
const [loading, setLoading] = useState3(true);
|
|
1322
|
+
const [refreshing, setRefreshing] = useState3(false);
|
|
1323
|
+
const [error, setError] = useState3(null);
|
|
1324
|
+
const fetchRemotePlugins = useCallback3(async () => {
|
|
1325
|
+
try {
|
|
1326
|
+
setError(null);
|
|
1327
|
+
const targetSolutionId = solutionId || "current";
|
|
1328
|
+
let data;
|
|
1329
|
+
try {
|
|
1330
|
+
data = await apiFetch(`/api/solutions/${targetSolutionId}/ui-plugins`);
|
|
1331
|
+
} catch (err) {
|
|
1332
|
+
if (err?.message?.includes("404")) {
|
|
1333
|
+
console.warn(`[useRemotePlugins] Solution ${targetSolutionId} not found or has no plugins`);
|
|
1334
|
+
setPlugins([]);
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
throw err;
|
|
1338
|
+
}
|
|
1339
|
+
const uiPlugins = (data.plugins || []).map((plugin) => ({
|
|
1340
|
+
id: plugin.id,
|
|
1341
|
+
name: plugin.name,
|
|
1342
|
+
version: plugin.version || "1.0.0",
|
|
1343
|
+
source: "remote",
|
|
1344
|
+
manifest: {
|
|
1345
|
+
id: plugin.id,
|
|
1346
|
+
name: plugin.name,
|
|
1347
|
+
version: plugin.version || "1.0.0",
|
|
1348
|
+
type: "ui",
|
|
1349
|
+
description: plugin.description || "",
|
|
1350
|
+
capabilities: plugin.capabilities || {},
|
|
1351
|
+
channels: plugin.channels || ["default"],
|
|
1352
|
+
render: plugin.render && typeof plugin.render === "object" ? plugin.render : { mode: "react-native", component: plugin.name || plugin.id },
|
|
1353
|
+
entry_point: plugin.entry_point || plugin.name,
|
|
1354
|
+
sandbox: plugin.sandbox !== false,
|
|
1355
|
+
requires_auth: plugin.requires_auth !== false
|
|
1356
|
+
}
|
|
1357
|
+
}));
|
|
1358
|
+
setPlugins(uiPlugins);
|
|
1359
|
+
} catch (err) {
|
|
1360
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1361
|
+
console.error("[useRemotePlugins] Failed to fetch plugins:", message);
|
|
1362
|
+
setError(message);
|
|
1363
|
+
setPlugins([]);
|
|
1364
|
+
} finally {
|
|
1365
|
+
setLoading(false);
|
|
1366
|
+
setRefreshing(false);
|
|
1367
|
+
}
|
|
1368
|
+
}, [solutionId]);
|
|
1369
|
+
const refresh = useCallback3(async () => {
|
|
1370
|
+
setRefreshing(true);
|
|
1371
|
+
await fetchRemotePlugins();
|
|
1372
|
+
}, [fetchRemotePlugins]);
|
|
1373
|
+
useEffect4(() => {
|
|
1374
|
+
setLoading(true);
|
|
1375
|
+
fetchRemotePlugins();
|
|
1376
|
+
}, [fetchRemotePlugins]);
|
|
1377
|
+
return {
|
|
1378
|
+
plugins,
|
|
1379
|
+
loading,
|
|
1380
|
+
error,
|
|
1381
|
+
refreshing,
|
|
1382
|
+
refresh
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// src/api/plugins.ts
|
|
1387
|
+
import { apiFetch as apiFetch2 } from "@ateam-ai-mobile/core";
|
|
1388
|
+
import { getCoreApiUrl } from "@ateam-ai-mobile/core";
|
|
1389
|
+
function supportsNative(manifest) {
|
|
1390
|
+
const mode = manifest.render?.mode;
|
|
1391
|
+
return mode === "react-native" || mode === "adaptive";
|
|
1392
|
+
}
|
|
1393
|
+
function getNativeComponentId(manifest) {
|
|
1394
|
+
if (manifest.render?.mode === "react-native") return manifest.render.component;
|
|
1395
|
+
if (manifest.render?.mode === "adaptive") return manifest.render.reactNative?.component;
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
function getNativeBundleUrl(manifest) {
|
|
1399
|
+
const direct = manifest.bundleUrl;
|
|
1400
|
+
if (direct) return direct;
|
|
1401
|
+
const render = manifest.render;
|
|
1402
|
+
if (render.mode === "react-native") return render.bundleUrl || null;
|
|
1403
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
function getNativeBundleHash(manifest) {
|
|
1407
|
+
const direct = manifest.bundleHash;
|
|
1408
|
+
if (direct) return direct;
|
|
1409
|
+
const render = manifest.render;
|
|
1410
|
+
if (render.mode === "react-native") return render.bundleHash || null;
|
|
1411
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleHash || null;
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function supportsWebView(manifest) {
|
|
1415
|
+
return manifest.render?.mode === "iframe" || manifest.render?.mode === "adaptive";
|
|
1416
|
+
}
|
|
1417
|
+
async function listPlugins() {
|
|
1418
|
+
const res = await apiFetch2("/mcp", {
|
|
1419
|
+
method: "POST",
|
|
1420
|
+
body: JSON.stringify({
|
|
1421
|
+
jsonrpc: "2.0",
|
|
1422
|
+
id: Date.now(),
|
|
1423
|
+
method: "tools/call",
|
|
1424
|
+
params: {
|
|
1425
|
+
name: "cp.listContextPlugins",
|
|
1426
|
+
arguments: {}
|
|
1427
|
+
}
|
|
1428
|
+
})
|
|
1429
|
+
});
|
|
1430
|
+
const structured = res.result?.structuredContent?.plugins;
|
|
1431
|
+
if (structured) return structured;
|
|
1432
|
+
const text = res.result?.content?.[0]?.text;
|
|
1433
|
+
if (!text) return [];
|
|
1434
|
+
try {
|
|
1435
|
+
const parsed = JSON.parse(text);
|
|
1436
|
+
return parsed.plugins || parsed || [];
|
|
1437
|
+
} catch {
|
|
1438
|
+
return [];
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
async function getPlugin(pluginId) {
|
|
1442
|
+
const res = await apiFetch2("/mcp", {
|
|
1443
|
+
method: "POST",
|
|
1444
|
+
body: JSON.stringify({
|
|
1445
|
+
jsonrpc: "2.0",
|
|
1446
|
+
id: Date.now(),
|
|
1447
|
+
method: "tools/call",
|
|
1448
|
+
params: {
|
|
1449
|
+
name: "cp.getContextPlugin",
|
|
1450
|
+
arguments: { id: pluginId }
|
|
1451
|
+
// Backend expects { id }, NOT { pluginId }
|
|
1452
|
+
}
|
|
1453
|
+
})
|
|
1454
|
+
});
|
|
1455
|
+
if (res.result?.structuredContent) return res.result.structuredContent;
|
|
1456
|
+
const text = res.result?.content?.[0]?.text;
|
|
1457
|
+
if (!text) return null;
|
|
1458
|
+
try {
|
|
1459
|
+
const parsed = JSON.parse(text);
|
|
1460
|
+
if (parsed.error) {
|
|
1461
|
+
console.warn("[getPlugin] Backend error:", parsed.error);
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
return parsed;
|
|
1465
|
+
} catch {
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
function resolvePluginUrl(manifest, tenant) {
|
|
1470
|
+
const render = manifest.render;
|
|
1471
|
+
let url;
|
|
1472
|
+
if (render?.mode === "iframe") {
|
|
1473
|
+
url = render.iframeUrl;
|
|
1474
|
+
} else if (render?.mode === "adaptive") {
|
|
1475
|
+
url = render.iframeUrl || render.iframe?.iframeUrl;
|
|
1476
|
+
}
|
|
1477
|
+
if (!url) return "";
|
|
1478
|
+
if (url.startsWith("http")) {
|
|
1479
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
1480
|
+
return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;
|
|
1481
|
+
}
|
|
1482
|
+
if (tenant && url.startsWith("/mcp-ui/")) {
|
|
1483
|
+
const tenantUrl = url.replace("/mcp-ui/", `/mcp-ui/${encodeURIComponent(tenant)}/`);
|
|
1484
|
+
return `${getCoreApiUrl()}${tenantUrl}`;
|
|
1485
|
+
}
|
|
1486
|
+
return `${getCoreApiUrl()}${url}`;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// src/surfaces/SurfaceDrawer.tsx
|
|
1490
|
+
import { useEffect as useEffect6, useRef as useRef3 } from "react";
|
|
1491
|
+
import {
|
|
1492
|
+
View as View4,
|
|
1493
|
+
Text as Text3,
|
|
1494
|
+
Pressable as Pressable3,
|
|
1495
|
+
StyleSheet as StyleSheet4,
|
|
1496
|
+
Animated as Animated3,
|
|
1497
|
+
Dimensions,
|
|
1498
|
+
Platform as Platform2
|
|
1499
|
+
} from "react-native";
|
|
1500
|
+
import Svg2, { Path } from "react-native-svg";
|
|
1501
|
+
import { ada as ada2 } from "@ateam-ai-mobile/core";
|
|
1502
|
+
|
|
1503
|
+
// src/surfaces/AdaOrb.tsx
|
|
1504
|
+
import { useEffect as useEffect5 } from "react";
|
|
1505
|
+
import { View as View3, StyleSheet as StyleSheet3 } from "react-native";
|
|
1506
|
+
import Animated2, {
|
|
1507
|
+
useSharedValue,
|
|
1508
|
+
useAnimatedProps,
|
|
1509
|
+
withRepeat,
|
|
1510
|
+
withTiming,
|
|
1511
|
+
withSequence,
|
|
1512
|
+
Easing,
|
|
1513
|
+
cancelAnimation
|
|
1514
|
+
} from "react-native-reanimated";
|
|
1515
|
+
import Svg, { Circle, Defs, RadialGradient, Stop, Text as SvgText } from "react-native-svg";
|
|
1516
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1517
|
+
var AnimatedCircle = Animated2.createAnimatedComponent(Circle);
|
|
1518
|
+
function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
1519
|
+
const showInscription = inscription && size >= 60;
|
|
1520
|
+
const center = size / 2;
|
|
1521
|
+
const coreR = size * 0.32;
|
|
1522
|
+
const innerHaloR = size * 0.42;
|
|
1523
|
+
const outerHaloR = size * 0.5;
|
|
1524
|
+
const coreScale = useSharedValue(1);
|
|
1525
|
+
const innerScale = useSharedValue(1);
|
|
1526
|
+
const outerScale = useSharedValue(1);
|
|
1527
|
+
const outerOpacity = useSharedValue(0.55);
|
|
1528
|
+
const rippleCycle = useSharedValue(0);
|
|
1529
|
+
const orbit1 = useSharedValue(0);
|
|
1530
|
+
const orbit2 = useSharedValue(0);
|
|
1531
|
+
const orbit3 = useSharedValue(0);
|
|
1532
|
+
const beat = useSharedValue(1);
|
|
1533
|
+
useEffect5(() => {
|
|
1534
|
+
cancelAnimation(coreScale);
|
|
1535
|
+
cancelAnimation(innerScale);
|
|
1536
|
+
cancelAnimation(outerScale);
|
|
1537
|
+
cancelAnimation(outerOpacity);
|
|
1538
|
+
cancelAnimation(rippleCycle);
|
|
1539
|
+
cancelAnimation(orbit1);
|
|
1540
|
+
cancelAnimation(orbit2);
|
|
1541
|
+
cancelAnimation(orbit3);
|
|
1542
|
+
cancelAnimation(beat);
|
|
1543
|
+
rippleCycle.value = 0;
|
|
1544
|
+
orbit1.value = 0;
|
|
1545
|
+
orbit2.value = 0;
|
|
1546
|
+
orbit3.value = 0;
|
|
1547
|
+
beat.value = 1;
|
|
1548
|
+
let breathMs = 3600;
|
|
1549
|
+
let breathAmount = 0.025;
|
|
1550
|
+
if (state === "listening") {
|
|
1551
|
+
breathMs = 2e3;
|
|
1552
|
+
breathAmount = 0.05;
|
|
1553
|
+
rippleCycle.value = withRepeat(
|
|
1554
|
+
withTiming(1, { duration: 2e3, easing: Easing.out(Easing.quad) }),
|
|
1555
|
+
-1,
|
|
1556
|
+
false
|
|
1557
|
+
);
|
|
1558
|
+
} else if (state === "thinking") {
|
|
1559
|
+
breathMs = 2400;
|
|
1560
|
+
breathAmount = 0.04;
|
|
1561
|
+
orbit1.value = withRepeat(
|
|
1562
|
+
withTiming(1, { duration: 1100, easing: Easing.linear }),
|
|
1563
|
+
-1,
|
|
1564
|
+
false
|
|
1565
|
+
);
|
|
1566
|
+
orbit2.value = withRepeat(
|
|
1567
|
+
withTiming(1, { duration: 1900, easing: Easing.linear }),
|
|
1568
|
+
-1,
|
|
1569
|
+
false
|
|
1570
|
+
);
|
|
1571
|
+
orbit3.value = withRepeat(
|
|
1572
|
+
withTiming(-1, { duration: 2700, easing: Easing.linear }),
|
|
1573
|
+
-1,
|
|
1574
|
+
false
|
|
1575
|
+
);
|
|
1576
|
+
} else if (state === "speaking") {
|
|
1577
|
+
breathMs = 700;
|
|
1578
|
+
breathAmount = 0.13;
|
|
1579
|
+
beat.value = withRepeat(
|
|
1580
|
+
withSequence(
|
|
1581
|
+
withTiming(1.08, { duration: 90, easing: Easing.out(Easing.quad) }),
|
|
1582
|
+
withTiming(1, { duration: 130, easing: Easing.in(Easing.quad) }),
|
|
1583
|
+
withTiming(1.05, { duration: 80, easing: Easing.out(Easing.quad) }),
|
|
1584
|
+
withTiming(1, { duration: 200, easing: Easing.in(Easing.quad) }),
|
|
1585
|
+
withTiming(1, { duration: 280 })
|
|
1586
|
+
),
|
|
1587
|
+
-1,
|
|
1588
|
+
false
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
const breathEasing = state === "speaking" ? Easing.out(Easing.cubic) : Easing.inOut(Easing.sin);
|
|
1592
|
+
coreScale.value = withRepeat(
|
|
1593
|
+
withSequence(
|
|
1594
|
+
withTiming(1 + breathAmount, { duration: breathMs, easing: breathEasing }),
|
|
1595
|
+
withTiming(1, { duration: breathMs, easing: breathEasing })
|
|
1596
|
+
),
|
|
1597
|
+
-1,
|
|
1598
|
+
false
|
|
1599
|
+
);
|
|
1600
|
+
innerScale.value = withRepeat(
|
|
1601
|
+
withSequence(
|
|
1602
|
+
withTiming(1 + breathAmount * 1.4, { duration: breathMs * 1.15, easing: breathEasing }),
|
|
1603
|
+
withTiming(1, { duration: breathMs * 1.15, easing: breathEasing })
|
|
1604
|
+
),
|
|
1605
|
+
-1,
|
|
1606
|
+
false
|
|
1607
|
+
);
|
|
1608
|
+
outerScale.value = withRepeat(
|
|
1609
|
+
withSequence(
|
|
1610
|
+
withTiming(1 + breathAmount * 1.8, { duration: breathMs * 1.3, easing: breathEasing }),
|
|
1611
|
+
withTiming(1, { duration: breathMs * 1.3, easing: breathEasing })
|
|
1612
|
+
),
|
|
1613
|
+
-1,
|
|
1614
|
+
false
|
|
1615
|
+
);
|
|
1616
|
+
outerOpacity.value = withRepeat(
|
|
1617
|
+
withSequence(
|
|
1618
|
+
withTiming(state === "idle" ? 0.55 : 0.8, { duration: breathMs * 1.3, easing: breathEasing }),
|
|
1619
|
+
withTiming(0.35, { duration: breathMs * 1.3, easing: breathEasing })
|
|
1620
|
+
),
|
|
1621
|
+
-1,
|
|
1622
|
+
false
|
|
1623
|
+
);
|
|
1624
|
+
}, [state]);
|
|
1625
|
+
const coreProps = useAnimatedProps(() => ({
|
|
1626
|
+
r: coreR * coreScale.value * beat.value
|
|
1627
|
+
}));
|
|
1628
|
+
const innerProps = useAnimatedProps(() => ({
|
|
1629
|
+
r: innerHaloR * innerScale.value * beat.value
|
|
1630
|
+
}));
|
|
1631
|
+
const outerProps = useAnimatedProps(() => ({
|
|
1632
|
+
r: outerHaloR * outerScale.value,
|
|
1633
|
+
opacity: outerOpacity.value
|
|
1634
|
+
}));
|
|
1635
|
+
const ripple1Props = useAnimatedProps(() => {
|
|
1636
|
+
const t = rippleCycle.value;
|
|
1637
|
+
return {
|
|
1638
|
+
r: outerHaloR * (1 + t * 0.55),
|
|
1639
|
+
opacity: state === "listening" ? 0.55 * (1 - t) : 0
|
|
1640
|
+
};
|
|
1641
|
+
});
|
|
1642
|
+
const ripple2Props = useAnimatedProps(() => {
|
|
1643
|
+
const t = (rippleCycle.value + 0.34) % 1;
|
|
1644
|
+
return {
|
|
1645
|
+
r: outerHaloR * (1 + t * 0.55),
|
|
1646
|
+
opacity: state === "listening" ? 0.45 * (1 - t) : 0
|
|
1647
|
+
};
|
|
1648
|
+
});
|
|
1649
|
+
const ripple3Props = useAnimatedProps(() => {
|
|
1650
|
+
const t = (rippleCycle.value + 0.67) % 1;
|
|
1651
|
+
return {
|
|
1652
|
+
r: outerHaloR * (1 + t * 0.55),
|
|
1653
|
+
opacity: state === "listening" ? 0.35 * (1 - t) : 0
|
|
1654
|
+
};
|
|
1655
|
+
});
|
|
1656
|
+
const spark1Props = useAnimatedProps(() => {
|
|
1657
|
+
const angle = orbit1.value * Math.PI * 2 - Math.PI / 2;
|
|
1658
|
+
const r = coreR * 0.55;
|
|
1659
|
+
return {
|
|
1660
|
+
cx: center + Math.cos(angle) * r,
|
|
1661
|
+
cy: center + Math.sin(angle) * r,
|
|
1662
|
+
opacity: state === "thinking" ? 1 : 0
|
|
1663
|
+
};
|
|
1664
|
+
});
|
|
1665
|
+
const spark2Props = useAnimatedProps(() => {
|
|
1666
|
+
const angle = orbit2.value * Math.PI * 2 + Math.PI * 0.6;
|
|
1667
|
+
const r = coreR * 0.85;
|
|
1668
|
+
return {
|
|
1669
|
+
cx: center + Math.cos(angle) * r,
|
|
1670
|
+
cy: center + Math.sin(angle) * r,
|
|
1671
|
+
opacity: state === "thinking" ? 0.9 : 0
|
|
1672
|
+
};
|
|
1673
|
+
});
|
|
1674
|
+
const spark3Props = useAnimatedProps(() => {
|
|
1675
|
+
const angle = orbit3.value * Math.PI * 2 + Math.PI * 1.3;
|
|
1676
|
+
const r = coreR * 1.05;
|
|
1677
|
+
return {
|
|
1678
|
+
cx: center + Math.cos(angle) * r,
|
|
1679
|
+
cy: center + Math.sin(angle) * r,
|
|
1680
|
+
opacity: state === "thinking" ? 0.7 : 0
|
|
1681
|
+
};
|
|
1682
|
+
});
|
|
1683
|
+
return /* @__PURE__ */ jsx(View3, { style: [styles2.wrap, { width: size, height: size }], children: /* @__PURE__ */ jsxs(Svg, { width: size, height: size, children: [
|
|
1684
|
+
/* @__PURE__ */ jsxs(Defs, { children: [
|
|
1685
|
+
/* @__PURE__ */ jsxs(RadialGradient, { id: "adaCore", cx: "36%", cy: "30%", r: "70%", children: [
|
|
1686
|
+
/* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FFE0B0", stopOpacity: "1" }),
|
|
1687
|
+
/* @__PURE__ */ jsx(Stop, { offset: "28%", stopColor: "#FFA85A", stopOpacity: "1" }),
|
|
1688
|
+
/* @__PURE__ */ jsx(Stop, { offset: "65%", stopColor: "#FF7A28", stopOpacity: "1" }),
|
|
1689
|
+
/* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#B8410A", stopOpacity: "1" })
|
|
1690
|
+
] }),
|
|
1691
|
+
/* @__PURE__ */ jsxs(RadialGradient, { id: "adaInner", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1692
|
+
/* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FF8A3C", stopOpacity: "0.55" }),
|
|
1693
|
+
/* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FF7028", stopOpacity: "0" })
|
|
1694
|
+
] }),
|
|
1695
|
+
/* @__PURE__ */ jsxs(RadialGradient, { id: "adaOuter", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1696
|
+
/* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FF7A28", stopOpacity: "0.4" }),
|
|
1697
|
+
/* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FF6018", stopOpacity: "0" })
|
|
1698
|
+
] }),
|
|
1699
|
+
/* @__PURE__ */ jsxs(RadialGradient, { id: "adaSpark", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1700
|
+
/* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FFFAEB", stopOpacity: "1" }),
|
|
1701
|
+
/* @__PURE__ */ jsx(Stop, { offset: "60%", stopColor: "#FFD095", stopOpacity: "0.9" }),
|
|
1702
|
+
/* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FFA85A", stopOpacity: "0" })
|
|
1703
|
+
] })
|
|
1704
|
+
] }),
|
|
1705
|
+
/* @__PURE__ */ jsx(
|
|
1706
|
+
AnimatedCircle,
|
|
1707
|
+
{
|
|
1708
|
+
cx: center,
|
|
1709
|
+
cy: center,
|
|
1710
|
+
stroke: "#FF7A28",
|
|
1711
|
+
strokeWidth: 1.5,
|
|
1712
|
+
fill: "none",
|
|
1713
|
+
animatedProps: ripple1Props
|
|
1714
|
+
}
|
|
1715
|
+
),
|
|
1716
|
+
/* @__PURE__ */ jsx(
|
|
1717
|
+
AnimatedCircle,
|
|
1718
|
+
{
|
|
1719
|
+
cx: center,
|
|
1720
|
+
cy: center,
|
|
1721
|
+
stroke: "#FF7A28",
|
|
1722
|
+
strokeWidth: 1.5,
|
|
1723
|
+
fill: "none",
|
|
1724
|
+
animatedProps: ripple2Props
|
|
1725
|
+
}
|
|
1726
|
+
),
|
|
1727
|
+
/* @__PURE__ */ jsx(
|
|
1728
|
+
AnimatedCircle,
|
|
1729
|
+
{
|
|
1730
|
+
cx: center,
|
|
1731
|
+
cy: center,
|
|
1732
|
+
stroke: "#FF7A28",
|
|
1733
|
+
strokeWidth: 1.5,
|
|
1734
|
+
fill: "none",
|
|
1735
|
+
animatedProps: ripple3Props
|
|
1736
|
+
}
|
|
1737
|
+
),
|
|
1738
|
+
/* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaOuter)", animatedProps: outerProps }),
|
|
1739
|
+
/* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaInner)", animatedProps: innerProps }),
|
|
1740
|
+
/* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaCore)", animatedProps: coreProps }),
|
|
1741
|
+
/* @__PURE__ */ jsx(
|
|
1742
|
+
AnimatedCircle,
|
|
1743
|
+
{
|
|
1744
|
+
r: Math.max(3, size * 0.05),
|
|
1745
|
+
fill: "url(#adaSpark)",
|
|
1746
|
+
animatedProps: spark1Props
|
|
1747
|
+
}
|
|
1748
|
+
),
|
|
1749
|
+
/* @__PURE__ */ jsx(
|
|
1750
|
+
AnimatedCircle,
|
|
1751
|
+
{
|
|
1752
|
+
r: Math.max(2.5, size * 0.04),
|
|
1753
|
+
fill: "url(#adaSpark)",
|
|
1754
|
+
animatedProps: spark2Props
|
|
1755
|
+
}
|
|
1756
|
+
),
|
|
1757
|
+
/* @__PURE__ */ jsx(
|
|
1758
|
+
AnimatedCircle,
|
|
1759
|
+
{
|
|
1760
|
+
r: Math.max(2, size * 0.032),
|
|
1761
|
+
fill: "url(#adaSpark)",
|
|
1762
|
+
animatedProps: spark3Props
|
|
1763
|
+
}
|
|
1764
|
+
),
|
|
1765
|
+
showInscription && /* @__PURE__ */ jsx(
|
|
1766
|
+
SvgText,
|
|
1767
|
+
{
|
|
1768
|
+
x: center,
|
|
1769
|
+
y: center + size * 0.11,
|
|
1770
|
+
fontSize: size * 0.34,
|
|
1771
|
+
fontWeight: "600",
|
|
1772
|
+
fill: "#FFF5E0",
|
|
1773
|
+
textAnchor: "middle",
|
|
1774
|
+
letterSpacing: -size * 5e-3,
|
|
1775
|
+
children: "iA"
|
|
1776
|
+
}
|
|
1777
|
+
)
|
|
1778
|
+
] }) });
|
|
1779
|
+
}
|
|
1780
|
+
var styles2 = StyleSheet3.create({
|
|
1781
|
+
wrap: {
|
|
1782
|
+
alignItems: "center",
|
|
1783
|
+
justifyContent: "center"
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
|
|
1787
|
+
// src/surfaces/SurfaceDrawer.tsx
|
|
1788
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1789
|
+
var SCREEN_H = Dimensions.get("window").height;
|
|
1790
|
+
var fontFam = Platform2.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1791
|
+
function SurfaceDrawer({
|
|
1792
|
+
visible,
|
|
1793
|
+
onClose,
|
|
1794
|
+
title,
|
|
1795
|
+
subtitle,
|
|
1796
|
+
privacy,
|
|
1797
|
+
showOrb = true,
|
|
1798
|
+
heightRatio = 0.85,
|
|
1799
|
+
children
|
|
1800
|
+
}) {
|
|
1801
|
+
const sheetH = Math.round(SCREEN_H * heightRatio);
|
|
1802
|
+
const fadeAnim = useRef3(new Animated3.Value(0)).current;
|
|
1803
|
+
const slideAnim = useRef3(new Animated3.Value(sheetH)).current;
|
|
1804
|
+
useEffect6(() => {
|
|
1805
|
+
if (visible) {
|
|
1806
|
+
Animated3.parallel([
|
|
1807
|
+
Animated3.timing(fadeAnim, { toValue: 1, duration: 220, useNativeDriver: true }),
|
|
1808
|
+
Animated3.spring(slideAnim, { toValue: 0, useNativeDriver: true, tension: 80, friction: 12 })
|
|
1809
|
+
]).start();
|
|
1810
|
+
} else {
|
|
1811
|
+
fadeAnim.setValue(0);
|
|
1812
|
+
slideAnim.setValue(sheetH);
|
|
1813
|
+
}
|
|
1814
|
+
}, [visible]);
|
|
1815
|
+
const close = () => {
|
|
1816
|
+
Animated3.parallel([
|
|
1817
|
+
Animated3.timing(fadeAnim, { toValue: 0, duration: 160, useNativeDriver: true }),
|
|
1818
|
+
Animated3.timing(slideAnim, { toValue: sheetH, duration: 200, useNativeDriver: true })
|
|
1819
|
+
]).start(() => onClose());
|
|
1820
|
+
};
|
|
1821
|
+
if (!visible) return null;
|
|
1822
|
+
return /* @__PURE__ */ jsxs2(Animated3.View, { style: [styles3.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
1823
|
+
/* @__PURE__ */ jsx2(Pressable3, { style: StyleSheet4.absoluteFill, onPress: close }),
|
|
1824
|
+
/* @__PURE__ */ jsxs2(
|
|
1825
|
+
Animated3.View,
|
|
1826
|
+
{
|
|
1827
|
+
style: [
|
|
1828
|
+
styles3.sheet,
|
|
1829
|
+
{ height: sheetH, transform: [{ translateY: slideAnim }] }
|
|
1830
|
+
],
|
|
1831
|
+
children: [
|
|
1832
|
+
/* @__PURE__ */ jsx2(View4, { style: styles3.handle }),
|
|
1833
|
+
/* @__PURE__ */ jsxs2(View4, { style: styles3.header, children: [
|
|
1834
|
+
showOrb ? /* @__PURE__ */ jsx2(View4, { style: styles3.headerOrb, children: /* @__PURE__ */ jsx2(AdaOrb, { size: 36, state: "idle" }) }) : null,
|
|
1835
|
+
/* @__PURE__ */ jsxs2(View4, { style: { flex: 1 }, children: [
|
|
1836
|
+
/* @__PURE__ */ jsx2(Text3, { style: styles3.title, children: title }),
|
|
1837
|
+
subtitle ? /* @__PURE__ */ jsx2(Text3, { style: styles3.subtitle, children: subtitle }) : null,
|
|
1838
|
+
privacy ? /* @__PURE__ */ jsxs2(View4, { style: styles3.privacyPill, children: [
|
|
1839
|
+
/* @__PURE__ */ jsxs2(Svg2, { width: 11, height: 11, viewBox: "0 0 24 24", fill: "none", stroke: ada2.inkSoft, strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1840
|
+
/* @__PURE__ */ jsx2(Path, { d: "M19 11H5a2 2 0 00-2 2v7a2 2 0 002 2h14a2 2 0 002-2v-7a2 2 0 00-2-2z" }),
|
|
1841
|
+
/* @__PURE__ */ jsx2(Path, { d: "M7 11V7a5 5 0 0110 0v4" })
|
|
1842
|
+
] }),
|
|
1843
|
+
/* @__PURE__ */ jsx2(Text3, { style: styles3.privacyText, children: "Only you can see this" })
|
|
1844
|
+
] }) : null
|
|
1845
|
+
] }),
|
|
1846
|
+
/* @__PURE__ */ jsx2(
|
|
1847
|
+
Pressable3,
|
|
1848
|
+
{
|
|
1849
|
+
accessibilityRole: "button",
|
|
1850
|
+
accessibilityLabel: "Close",
|
|
1851
|
+
style: styles3.closeBtn,
|
|
1852
|
+
onPress: close,
|
|
1853
|
+
hitSlop: 12,
|
|
1854
|
+
children: /* @__PURE__ */ jsx2(Svg2, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: ada2.inkSoft, strokeWidth: 2.2, strokeLinecap: "round", children: /* @__PURE__ */ jsx2(Path, { d: "M18 6L6 18M6 6l12 12" }) })
|
|
1855
|
+
}
|
|
1856
|
+
)
|
|
1857
|
+
] }),
|
|
1858
|
+
/* @__PURE__ */ jsx2(View4, { style: styles3.body, children })
|
|
1859
|
+
]
|
|
1860
|
+
}
|
|
1861
|
+
)
|
|
1862
|
+
] });
|
|
1863
|
+
}
|
|
1864
|
+
var styles3 = StyleSheet4.create({
|
|
1865
|
+
overlay: {
|
|
1866
|
+
position: "absolute",
|
|
1867
|
+
top: 0,
|
|
1868
|
+
left: 0,
|
|
1869
|
+
right: 0,
|
|
1870
|
+
bottom: 0,
|
|
1871
|
+
backgroundColor: "rgba(26, 20, 16, 0.35)",
|
|
1872
|
+
justifyContent: "flex-end",
|
|
1873
|
+
zIndex: 999,
|
|
1874
|
+
elevation: 999
|
|
1875
|
+
},
|
|
1876
|
+
sheet: {
|
|
1877
|
+
backgroundColor: ada2.paper,
|
|
1878
|
+
borderTopLeftRadius: 28,
|
|
1879
|
+
borderTopRightRadius: 28,
|
|
1880
|
+
overflow: "hidden"
|
|
1881
|
+
},
|
|
1882
|
+
handle: {
|
|
1883
|
+
width: 40,
|
|
1884
|
+
height: 4,
|
|
1885
|
+
borderRadius: 2,
|
|
1886
|
+
backgroundColor: ada2.inkMuted,
|
|
1887
|
+
opacity: 0.3,
|
|
1888
|
+
alignSelf: "center",
|
|
1889
|
+
marginTop: 10,
|
|
1890
|
+
marginBottom: 6
|
|
1891
|
+
},
|
|
1892
|
+
header: {
|
|
1893
|
+
flexDirection: "row",
|
|
1894
|
+
alignItems: "center",
|
|
1895
|
+
paddingHorizontal: 22,
|
|
1896
|
+
paddingVertical: 16,
|
|
1897
|
+
gap: 12,
|
|
1898
|
+
borderBottomWidth: 1,
|
|
1899
|
+
borderBottomColor: ada2.border
|
|
1900
|
+
},
|
|
1901
|
+
headerOrb: {
|
|
1902
|
+
width: 36,
|
|
1903
|
+
height: 36,
|
|
1904
|
+
alignItems: "center",
|
|
1905
|
+
justifyContent: "center"
|
|
1906
|
+
},
|
|
1907
|
+
title: {
|
|
1908
|
+
fontSize: 17,
|
|
1909
|
+
fontWeight: "600",
|
|
1910
|
+
color: ada2.ink,
|
|
1911
|
+
letterSpacing: -0.2,
|
|
1912
|
+
fontFamily: fontFam
|
|
1913
|
+
},
|
|
1914
|
+
subtitle: {
|
|
1915
|
+
fontSize: 13,
|
|
1916
|
+
fontWeight: "400",
|
|
1917
|
+
color: ada2.inkSoft,
|
|
1918
|
+
marginTop: 2,
|
|
1919
|
+
letterSpacing: -0.1,
|
|
1920
|
+
fontFamily: fontFam
|
|
1921
|
+
},
|
|
1922
|
+
privacyPill: {
|
|
1923
|
+
flexDirection: "row",
|
|
1924
|
+
alignItems: "center",
|
|
1925
|
+
gap: 5,
|
|
1926
|
+
marginTop: 4
|
|
1927
|
+
},
|
|
1928
|
+
privacyText: {
|
|
1929
|
+
fontSize: 11,
|
|
1930
|
+
fontWeight: "500",
|
|
1931
|
+
color: ada2.inkSoft,
|
|
1932
|
+
letterSpacing: 0.2,
|
|
1933
|
+
fontFamily: fontFam
|
|
1934
|
+
},
|
|
1935
|
+
closeBtn: {
|
|
1936
|
+
width: 32,
|
|
1937
|
+
height: 32,
|
|
1938
|
+
borderRadius: 16,
|
|
1939
|
+
backgroundColor: ada2.paperSoft,
|
|
1940
|
+
justifyContent: "center",
|
|
1941
|
+
alignItems: "center"
|
|
1942
|
+
},
|
|
1943
|
+
body: {
|
|
1944
|
+
flex: 1
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
// src/surfaces/SurfaceCard.tsx
|
|
1949
|
+
import {
|
|
1950
|
+
View as View5,
|
|
1951
|
+
Text as Text4,
|
|
1952
|
+
Pressable as Pressable4,
|
|
1953
|
+
StyleSheet as StyleSheet5,
|
|
1954
|
+
Platform as Platform3
|
|
1955
|
+
} from "react-native";
|
|
1956
|
+
import { ada as ada3, borderRadius as borderRadius2 } from "@ateam-ai-mobile/core";
|
|
1957
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1958
|
+
var fontFam2 = Platform3.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1959
|
+
function SurfaceCard({
|
|
1960
|
+
icon,
|
|
1961
|
+
title,
|
|
1962
|
+
subtitle,
|
|
1963
|
+
onPress,
|
|
1964
|
+
onLongPress,
|
|
1965
|
+
showArrow,
|
|
1966
|
+
children
|
|
1967
|
+
}) {
|
|
1968
|
+
const arrow = showArrow ?? !!onPress;
|
|
1969
|
+
const handlePress = () => {
|
|
1970
|
+
if (!onPress) return;
|
|
1971
|
+
getHaptics()?.impact("light");
|
|
1972
|
+
onPress();
|
|
1973
|
+
};
|
|
1974
|
+
const handleLongPress = () => {
|
|
1975
|
+
if (!onLongPress) return;
|
|
1976
|
+
getHaptics()?.impact("medium");
|
|
1977
|
+
onLongPress();
|
|
1978
|
+
};
|
|
1979
|
+
return /* @__PURE__ */ jsxs3(
|
|
1980
|
+
Pressable4,
|
|
1981
|
+
{
|
|
1982
|
+
accessibilityRole: "button",
|
|
1983
|
+
accessibilityLabel: [icon, title, subtitle].filter(Boolean).join(", "),
|
|
1984
|
+
accessibilityHint: onLongPress ? "Long-press for more options" : void 0,
|
|
1985
|
+
style: styles4.card,
|
|
1986
|
+
onPress: onPress ? handlePress : void 0,
|
|
1987
|
+
onLongPress: onLongPress ? handleLongPress : void 0,
|
|
1988
|
+
disabled: !onPress && !onLongPress,
|
|
1989
|
+
children: [
|
|
1990
|
+
/* @__PURE__ */ jsxs3(View5, { style: styles4.row, children: [
|
|
1991
|
+
icon ? /* @__PURE__ */ jsx3(Text4, { style: styles4.icon, children: icon }) : null,
|
|
1992
|
+
/* @__PURE__ */ jsxs3(View5, { style: { flex: 1 }, children: [
|
|
1993
|
+
/* @__PURE__ */ jsx3(Text4, { style: styles4.title, children: title }),
|
|
1994
|
+
subtitle ? /* @__PURE__ */ jsx3(Text4, { style: styles4.subtitle, children: subtitle }) : null
|
|
1995
|
+
] }),
|
|
1996
|
+
arrow ? /* @__PURE__ */ jsx3(Text4, { style: styles4.arrow, children: "\u2192" }) : null
|
|
1997
|
+
] }),
|
|
1998
|
+
children ? /* @__PURE__ */ jsx3(View5, { style: styles4.body, children }) : null
|
|
1999
|
+
]
|
|
2000
|
+
}
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
var styles4 = StyleSheet5.create({
|
|
2004
|
+
card: {
|
|
2005
|
+
backgroundColor: ada3.paperSoft,
|
|
2006
|
+
borderWidth: 1,
|
|
2007
|
+
borderColor: ada3.border,
|
|
2008
|
+
borderRadius: borderRadius2.xl,
|
|
2009
|
+
paddingHorizontal: 16,
|
|
2010
|
+
paddingVertical: 14
|
|
2011
|
+
},
|
|
2012
|
+
row: {
|
|
2013
|
+
flexDirection: "row",
|
|
2014
|
+
alignItems: "center",
|
|
2015
|
+
gap: 14
|
|
2016
|
+
},
|
|
2017
|
+
icon: {
|
|
2018
|
+
fontSize: 22
|
|
2019
|
+
},
|
|
2020
|
+
title: {
|
|
2021
|
+
fontSize: 15,
|
|
2022
|
+
fontWeight: "600",
|
|
2023
|
+
color: ada3.ink,
|
|
2024
|
+
letterSpacing: -0.2,
|
|
2025
|
+
fontFamily: fontFam2
|
|
2026
|
+
},
|
|
2027
|
+
subtitle: {
|
|
2028
|
+
fontSize: 13,
|
|
2029
|
+
fontWeight: "400",
|
|
2030
|
+
color: ada3.inkMuted,
|
|
2031
|
+
marginTop: 2,
|
|
2032
|
+
letterSpacing: -0.1,
|
|
2033
|
+
fontFamily: fontFam2
|
|
2034
|
+
},
|
|
2035
|
+
arrow: {
|
|
2036
|
+
fontSize: 18,
|
|
2037
|
+
color: ada3.inkMuted,
|
|
2038
|
+
fontWeight: "400"
|
|
2039
|
+
},
|
|
2040
|
+
body: {
|
|
2041
|
+
marginTop: 10,
|
|
2042
|
+
paddingTop: 10,
|
|
2043
|
+
borderTopWidth: 1,
|
|
2044
|
+
borderTopColor: ada3.border
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// src/surfaces/SurfaceFullscreen.tsx
|
|
2049
|
+
import { useEffect as useEffect7, useRef as useRef4 } from "react";
|
|
2050
|
+
import {
|
|
2051
|
+
View as View6,
|
|
2052
|
+
Text as Text5,
|
|
2053
|
+
Pressable as Pressable5,
|
|
2054
|
+
StyleSheet as StyleSheet6,
|
|
2055
|
+
Animated as Animated4,
|
|
2056
|
+
Platform as Platform4
|
|
2057
|
+
} from "react-native";
|
|
2058
|
+
import Svg3, { Path as Path2 } from "react-native-svg";
|
|
2059
|
+
import { ada as ada4 } from "@ateam-ai-mobile/core";
|
|
2060
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2061
|
+
var fontFam3 = Platform4.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
2062
|
+
function SurfaceFullscreen({
|
|
2063
|
+
visible,
|
|
2064
|
+
onClose,
|
|
2065
|
+
status,
|
|
2066
|
+
hero,
|
|
2067
|
+
children,
|
|
2068
|
+
showWordmark = true,
|
|
2069
|
+
closeIcon = "x",
|
|
2070
|
+
closeLabel = "Close"
|
|
2071
|
+
}) {
|
|
2072
|
+
const fadeAnim = useRef4(new Animated4.Value(0)).current;
|
|
2073
|
+
const heroScale = useRef4(new Animated4.Value(0.6)).current;
|
|
2074
|
+
useEffect7(() => {
|
|
2075
|
+
if (visible) {
|
|
2076
|
+
Animated4.parallel([
|
|
2077
|
+
Animated4.timing(fadeAnim, { toValue: 1, duration: 280, useNativeDriver: true }),
|
|
2078
|
+
Animated4.spring(heroScale, { toValue: 1, useNativeDriver: true, tension: 60, friction: 11 })
|
|
2079
|
+
]).start();
|
|
2080
|
+
} else {
|
|
2081
|
+
fadeAnim.setValue(0);
|
|
2082
|
+
heroScale.setValue(0.6);
|
|
2083
|
+
}
|
|
2084
|
+
}, [visible]);
|
|
2085
|
+
const close = () => {
|
|
2086
|
+
Animated4.parallel([
|
|
2087
|
+
Animated4.timing(fadeAnim, { toValue: 0, duration: 180, useNativeDriver: true }),
|
|
2088
|
+
Animated4.timing(heroScale, { toValue: 0.6, duration: 180, useNativeDriver: true })
|
|
2089
|
+
]).start(() => onClose());
|
|
2090
|
+
};
|
|
2091
|
+
if (!visible) return null;
|
|
2092
|
+
const renderCloseIcon = () => {
|
|
2093
|
+
if (closeIcon === "hangup") {
|
|
2094
|
+
return /* @__PURE__ */ jsx4(Svg3, { width: 22, height: 22, viewBox: "0 0 24 24", fill: ada4.paper, children: /* @__PURE__ */ jsx4(Path2, { d: "M3.68 16.07l3.92-3.11c.28-.23.68-.28 1.01-.14l2.77 1.15c.36.15.61.5.61.89v2.82c4.34-.76 7.93-4.35 8.69-8.69h-2.82c-.39 0-.74-.25-.89-.61L16.12 5.6c-.13-.33-.09-.73.14-1.01l3.11-3.92c.29-.37.81-.41 1.15-.08l3.24 3.24c.2.2.31.47.31.76C24 14.44 14.44 24 4.59 24c-.29 0-.56-.11-.76-.31L.59 20.45c-.33-.34-.29-.86.09-1.15z" }) });
|
|
2095
|
+
}
|
|
2096
|
+
if (closeIcon === "x") {
|
|
2097
|
+
return /* @__PURE__ */ jsx4(Svg3, { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: ada4.paper, strokeWidth: 2.5, strokeLinecap: "round", children: /* @__PURE__ */ jsx4(Path2, { d: "M18 6L6 18M6 6l12 12" }) });
|
|
2098
|
+
}
|
|
2099
|
+
return closeIcon;
|
|
2100
|
+
};
|
|
2101
|
+
return /* @__PURE__ */ jsxs4(Animated4.View, { style: [styles5.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
2102
|
+
/* @__PURE__ */ jsxs4(View6, { style: styles5.topBar, children: [
|
|
2103
|
+
showWordmark ? /* @__PURE__ */ jsx4(Text5, { style: styles5.wordmark, children: "ada" }) : null,
|
|
2104
|
+
status ? /* @__PURE__ */ jsx4(Text5, { style: styles5.status, children: status }) : null
|
|
2105
|
+
] }),
|
|
2106
|
+
hero ? /* @__PURE__ */ jsx4(View6, { style: styles5.heroWrap, children: /* @__PURE__ */ jsx4(Animated4.View, { style: { transform: [{ scale: heroScale }] }, children: hero }) }) : null,
|
|
2107
|
+
/* @__PURE__ */ jsx4(View6, { style: styles5.body, children }),
|
|
2108
|
+
/* @__PURE__ */ jsxs4(View6, { style: styles5.bottomBar, children: [
|
|
2109
|
+
/* @__PURE__ */ jsx4(
|
|
2110
|
+
Pressable5,
|
|
2111
|
+
{
|
|
2112
|
+
accessibilityRole: "button",
|
|
2113
|
+
accessibilityLabel: closeLabel,
|
|
2114
|
+
style: styles5.closeBtn,
|
|
2115
|
+
onPress: close,
|
|
2116
|
+
hitSlop: 12,
|
|
2117
|
+
children: renderCloseIcon()
|
|
2118
|
+
}
|
|
2119
|
+
),
|
|
2120
|
+
closeLabel ? /* @__PURE__ */ jsx4(Text5, { style: styles5.closeLabel, children: closeLabel }) : null
|
|
2121
|
+
] })
|
|
2122
|
+
] });
|
|
2123
|
+
}
|
|
2124
|
+
var styles5 = StyleSheet6.create({
|
|
2125
|
+
overlay: {
|
|
2126
|
+
position: "absolute",
|
|
2127
|
+
top: 0,
|
|
2128
|
+
left: 0,
|
|
2129
|
+
right: 0,
|
|
2130
|
+
bottom: 0,
|
|
2131
|
+
backgroundColor: ada4.paper,
|
|
2132
|
+
zIndex: 1e3,
|
|
2133
|
+
elevation: 1e3
|
|
2134
|
+
},
|
|
2135
|
+
topBar: {
|
|
2136
|
+
paddingTop: Platform4.OS === "ios" ? 64 : 36,
|
|
2137
|
+
paddingHorizontal: 24,
|
|
2138
|
+
alignItems: "center",
|
|
2139
|
+
gap: 6
|
|
2140
|
+
},
|
|
2141
|
+
wordmark: {
|
|
2142
|
+
fontSize: 14,
|
|
2143
|
+
fontFamily: fontFam3,
|
|
2144
|
+
fontWeight: "500",
|
|
2145
|
+
color: ada4.inkMuted,
|
|
2146
|
+
letterSpacing: 4
|
|
2147
|
+
},
|
|
2148
|
+
status: {
|
|
2149
|
+
fontSize: 17,
|
|
2150
|
+
fontFamily: fontFam3,
|
|
2151
|
+
fontWeight: "500",
|
|
2152
|
+
color: ada4.ink,
|
|
2153
|
+
letterSpacing: -0.2
|
|
2154
|
+
},
|
|
2155
|
+
heroWrap: {
|
|
2156
|
+
paddingTop: 24,
|
|
2157
|
+
paddingBottom: 12,
|
|
2158
|
+
alignItems: "center",
|
|
2159
|
+
justifyContent: "center"
|
|
2160
|
+
},
|
|
2161
|
+
body: {
|
|
2162
|
+
flex: 1
|
|
2163
|
+
},
|
|
2164
|
+
bottomBar: {
|
|
2165
|
+
paddingBottom: Platform4.OS === "ios" ? 48 : 32,
|
|
2166
|
+
paddingTop: 12,
|
|
2167
|
+
alignItems: "center",
|
|
2168
|
+
gap: 8
|
|
2169
|
+
},
|
|
2170
|
+
closeBtn: {
|
|
2171
|
+
width: 64,
|
|
2172
|
+
height: 64,
|
|
2173
|
+
borderRadius: 32,
|
|
2174
|
+
backgroundColor: ada4.ink,
|
|
2175
|
+
justifyContent: "center",
|
|
2176
|
+
alignItems: "center",
|
|
2177
|
+
shadowColor: ada4.ink,
|
|
2178
|
+
shadowOffset: { width: 0, height: 8 },
|
|
2179
|
+
shadowOpacity: 0.18,
|
|
2180
|
+
shadowRadius: 14,
|
|
2181
|
+
elevation: 8
|
|
2182
|
+
},
|
|
2183
|
+
closeLabel: {
|
|
2184
|
+
fontSize: 13,
|
|
2185
|
+
fontFamily: fontFam3,
|
|
2186
|
+
fontWeight: "500",
|
|
2187
|
+
color: ada4.inkMuted,
|
|
2188
|
+
letterSpacing: 0.3
|
|
2189
|
+
}
|
|
2190
|
+
});
|
|
2191
|
+
|
|
2192
|
+
// src/registry/store.ts
|
|
2193
|
+
import { create } from "zustand";
|
|
2194
|
+
var useSurfaceStore = create((set, get) => ({
|
|
2195
|
+
manifests: {},
|
|
2196
|
+
activeDrawer: null,
|
|
2197
|
+
activeFullscreen: null,
|
|
2198
|
+
activeProps: {},
|
|
2199
|
+
mountKey: 0,
|
|
2200
|
+
register: (manifest) => set((s) => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),
|
|
2201
|
+
unregister: (id) => set((s) => {
|
|
2202
|
+
const next = { ...s.manifests };
|
|
2203
|
+
delete next[id];
|
|
2204
|
+
const nextProps = { ...s.activeProps };
|
|
2205
|
+
delete nextProps[id];
|
|
2206
|
+
return { manifests: next, activeProps: nextProps };
|
|
2207
|
+
}),
|
|
2208
|
+
show: (id, props) => {
|
|
2209
|
+
const m = get().manifests[id];
|
|
2210
|
+
if (!m) {
|
|
2211
|
+
console.warn("[SurfaceStore] show: unknown surface id", id);
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
set((s) => {
|
|
2215
|
+
const nextProps = props !== void 0 ? { ...s.activeProps, [id]: props } : s.activeProps;
|
|
2216
|
+
const mountKey = s.mountKey + 1;
|
|
2217
|
+
if (m.type === "drawer") return { activeDrawer: id, activeProps: nextProps, mountKey };
|
|
2218
|
+
if (m.type === "fullscreen") return { activeFullscreen: id, activeProps: nextProps, mountKey };
|
|
2219
|
+
return { mountKey };
|
|
2220
|
+
});
|
|
2221
|
+
},
|
|
2222
|
+
hide: (id) => {
|
|
2223
|
+
const s = get();
|
|
2224
|
+
if (!id) {
|
|
2225
|
+
const cleanup2 = { ...s.activeProps };
|
|
2226
|
+
if (s.activeDrawer) delete cleanup2[s.activeDrawer];
|
|
2227
|
+
if (s.activeFullscreen) delete cleanup2[s.activeFullscreen];
|
|
2228
|
+
set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup2 });
|
|
2229
|
+
return;
|
|
2230
|
+
}
|
|
2231
|
+
const cleanup = { ...s.activeProps };
|
|
2232
|
+
delete cleanup[id];
|
|
2233
|
+
const patch = { activeProps: cleanup };
|
|
2234
|
+
if (s.activeDrawer === id) patch.activeDrawer = null;
|
|
2235
|
+
if (s.activeFullscreen === id) patch.activeFullscreen = null;
|
|
2236
|
+
set(patch);
|
|
2237
|
+
},
|
|
2238
|
+
list: (filter) => {
|
|
2239
|
+
const all = Object.values(get().manifests);
|
|
2240
|
+
return all.filter((m) => {
|
|
2241
|
+
if (filter?.type && m.type !== filter.type) return false;
|
|
2242
|
+
if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;
|
|
2243
|
+
if (filter?.placement) {
|
|
2244
|
+
if (filter.placement === "unset") {
|
|
2245
|
+
if (m.placement) return false;
|
|
2246
|
+
} else if (m.placement !== filter.placement) {
|
|
2247
|
+
return false;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return true;
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
2253
|
+
}));
|
|
2254
|
+
function isMountSurface(m) {
|
|
2255
|
+
return m.type === "drawer" || m.type === "fullscreen";
|
|
2256
|
+
}
|
|
2257
|
+
function isInlineSurface(m) {
|
|
2258
|
+
return m.type === "card" || m.type === "header" || m.type === "ambient" || m.type === "nudge";
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// src/registry/SurfaceDispatcher.tsx
|
|
2262
|
+
import { useEffect as useEffect8 } from "react";
|
|
2263
|
+
import { pluginCommandEmitter as pluginCommandEmitter2 } from "@ateam-ai-mobile/core";
|
|
2264
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2265
|
+
function SurfaceDispatcher() {
|
|
2266
|
+
const manifests = useSurfaceStore((s) => s.manifests);
|
|
2267
|
+
const activeDrawer = useSurfaceStore((s) => s.activeDrawer);
|
|
2268
|
+
const activeFullscreen = useSurfaceStore((s) => s.activeFullscreen);
|
|
2269
|
+
const activeProps = useSurfaceStore((s) => s.activeProps);
|
|
2270
|
+
const mountKey = useSurfaceStore((s) => s.mountKey);
|
|
2271
|
+
const hide = useSurfaceStore((s) => s.hide);
|
|
2272
|
+
useEffect8(() => {
|
|
2273
|
+
const unsub = pluginCommandEmitter2.on("plugin-close", ({ pluginId } = {}) => {
|
|
2274
|
+
if (!pluginId) return;
|
|
2275
|
+
const s = useSurfaceStore.getState();
|
|
2276
|
+
if (s.activeDrawer === pluginId || s.activeFullscreen === pluginId) {
|
|
2277
|
+
s.hide(pluginId);
|
|
2278
|
+
}
|
|
2279
|
+
});
|
|
2280
|
+
return unsub;
|
|
2281
|
+
}, []);
|
|
2282
|
+
const drawer = activeDrawer ? manifests[activeDrawer] : null;
|
|
2283
|
+
const fullscreen = activeFullscreen ? manifests[activeFullscreen] : null;
|
|
2284
|
+
const drawerProps = activeDrawer ? activeProps[activeDrawer] : void 0;
|
|
2285
|
+
const fullscreenProps = activeFullscreen ? activeProps[activeFullscreen] : void 0;
|
|
2286
|
+
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
2287
|
+
drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ jsx5(
|
|
2288
|
+
drawer.Component,
|
|
2289
|
+
{
|
|
2290
|
+
dismiss: () => hide(drawer.id),
|
|
2291
|
+
props: drawerProps
|
|
2292
|
+
},
|
|
2293
|
+
`drawer-${drawer.id}-${mountKey}`
|
|
2294
|
+
) : null,
|
|
2295
|
+
fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ jsx5(
|
|
2296
|
+
fullscreen.Component,
|
|
2297
|
+
{
|
|
2298
|
+
dismiss: () => hide(fullscreen.id),
|
|
2299
|
+
props: fullscreenProps
|
|
2300
|
+
},
|
|
2301
|
+
`fullscreen-${fullscreen.id}-${mountKey}`
|
|
2302
|
+
) : null
|
|
2303
|
+
] });
|
|
2304
|
+
}
|
|
2305
|
+
export {
|
|
2306
|
+
ActionButton,
|
|
2307
|
+
ActivityIndicator,
|
|
2308
|
+
AdaOrb,
|
|
2309
|
+
Animated,
|
|
2310
|
+
Badge,
|
|
2311
|
+
Card,
|
|
2312
|
+
EmptyState,
|
|
2313
|
+
ErrorState,
|
|
2314
|
+
FlatList,
|
|
2315
|
+
Image,
|
|
2316
|
+
ListItem,
|
|
2317
|
+
LoadingState,
|
|
2318
|
+
MemoryBundleStorage,
|
|
2319
|
+
Modal,
|
|
2320
|
+
Platform,
|
|
2321
|
+
PluginSDK,
|
|
2322
|
+
Pressable,
|
|
2323
|
+
RefreshControl,
|
|
2324
|
+
ScrollView,
|
|
2325
|
+
SearchBar,
|
|
2326
|
+
SectionList,
|
|
2327
|
+
StyleSheet,
|
|
2328
|
+
SurfaceCard,
|
|
2329
|
+
SurfaceDispatcher,
|
|
2330
|
+
SurfaceDrawer,
|
|
2331
|
+
SurfaceFullscreen,
|
|
2332
|
+
Switch,
|
|
2333
|
+
TabBar,
|
|
2334
|
+
Text,
|
|
2335
|
+
TextInput,
|
|
2336
|
+
TouchableOpacity,
|
|
2337
|
+
View,
|
|
2338
|
+
bundleManager,
|
|
2339
|
+
createNativeBridge,
|
|
2340
|
+
getHaptics,
|
|
2341
|
+
getNativeBundleHash,
|
|
2342
|
+
getNativeBundleUrl,
|
|
2343
|
+
getNativeComponentId,
|
|
2344
|
+
getPlugin,
|
|
2345
|
+
getPluginNative,
|
|
2346
|
+
isInlineSurface,
|
|
2347
|
+
isMountSurface,
|
|
2348
|
+
listPlugins,
|
|
2349
|
+
nativePluginRegistry,
|
|
2350
|
+
register,
|
|
2351
|
+
registerBundleModules,
|
|
2352
|
+
registerNativePlugin,
|
|
2353
|
+
registerService,
|
|
2354
|
+
resolvePluginUrl,
|
|
2355
|
+
setBundleStorage,
|
|
2356
|
+
setHapticsProvider,
|
|
2357
|
+
setPluginNativeProvider,
|
|
2358
|
+
setPluginThemeColors,
|
|
2359
|
+
supportsNative,
|
|
2360
|
+
supportsWebView,
|
|
2361
|
+
unregisterNativePlugin,
|
|
2362
|
+
useApi,
|
|
2363
|
+
useNativeBridge,
|
|
2364
|
+
usePlugin,
|
|
2365
|
+
usePluginTheme,
|
|
2366
|
+
useRemotePlugins,
|
|
2367
|
+
useSurfaceStore
|
|
2368
|
+
};
|
|
2369
|
+
//# sourceMappingURL=index.mjs.map
|