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