@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.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