@ateam-ai-mobile/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1870 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AdasChatScreen: () => AdasChatScreen,
34
+ ChatInput: () => ChatInput,
35
+ DaySeparator: () => DaySeparator,
36
+ EmbeddedPluginWebView: () => EmbeddedPluginWebView,
37
+ JobProgressBar: () => JobProgressBar,
38
+ Lightbox: () => Lightbox,
39
+ MessageBubble: () => MessageBubble,
40
+ Plugin: () => Plugin,
41
+ PluginSurface: () => PluginSurface,
42
+ TableCardBlock: () => TableCardBlock,
43
+ TypingIndicator: () => TypingIndicator,
44
+ UIActionsRow: () => UIActionsRow,
45
+ WidgetWebView: () => WidgetWebView,
46
+ buildPluginBridgeJS: () => buildPluginBridgeJS,
47
+ setClipboardProvider: () => setClipboardProvider,
48
+ setUIActionNavigator: () => setUIActionNavigator,
49
+ useLightboxStore: () => useLightboxStore,
50
+ usePluginBridge: () => usePluginBridge
51
+ });
52
+ module.exports = __toCommonJS(index_exports);
53
+
54
+ // src/AdasChatScreen.tsx
55
+ var import_react4 = require("react");
56
+ var import_react_native8 = require("react-native");
57
+ var import_core7 = require("@ateam-ai-mobile/core");
58
+
59
+ // src/chat/MessageBubble.tsx
60
+ var import_react2 = require("react");
61
+ var import_react_native4 = require("react-native");
62
+ var import_react_native_markdown_display = __toESM(require("react-native-markdown-display"));
63
+ var import_core4 = require("@ateam-ai-mobile/core");
64
+ var import_plugin_runtime2 = require("@ateam-ai-mobile/plugin-runtime");
65
+
66
+ // src/chat/JobProgressBar.tsx
67
+ var import_react = require("react");
68
+ var import_react_native = require("react-native");
69
+ var import_core = require("@ateam-ai-mobile/core");
70
+ var import_jsx_runtime = require("react/jsx-runtime");
71
+ var WARM_GREEN = "#5A8A5C";
72
+ var WARM_GREEN_SOFT = "rgba(90, 138, 92, 0.14)";
73
+ var WARM_RED = "#C15545";
74
+ var WARM_RED_SOFT = "rgba(193, 85, 69, 0.14)";
75
+ var WARM_AMBER = "#D49A4E";
76
+ var WARM_AMBER_SOFT = "rgba(212, 154, 78, 0.16)";
77
+ function JobProgressBar({ progress }) {
78
+ const pulseAnim = (0, import_react.useRef)(new import_react_native.Animated.Value(0.5)).current;
79
+ const [elapsed, setElapsed] = (0, import_react.useState)(0);
80
+ const isRunning = progress.status === "running";
81
+ const isDone = progress.status === "done";
82
+ const isError = progress.status === "error";
83
+ const isPaused = progress.status === "paused";
84
+ (0, import_react.useEffect)(() => {
85
+ if (!isRunning) {
86
+ pulseAnim.setValue(1);
87
+ return;
88
+ }
89
+ const anim = import_react_native.Animated.loop(
90
+ import_react_native.Animated.sequence([
91
+ import_react_native.Animated.timing(pulseAnim, { toValue: 1, duration: 800, useNativeDriver: true }),
92
+ import_react_native.Animated.timing(pulseAnim, { toValue: 0.5, duration: 800, useNativeDriver: true })
93
+ ])
94
+ );
95
+ anim.start();
96
+ return () => anim.stop();
97
+ }, [isRunning, pulseAnim]);
98
+ (0, import_react.useEffect)(() => {
99
+ if (!isRunning || !progress.chainStartedAt) {
100
+ setElapsed(0);
101
+ return;
102
+ }
103
+ const start = progress.chainStartedAt;
104
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
105
+ const id = setInterval(() => {
106
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
107
+ }, 1e3);
108
+ return () => clearInterval(id);
109
+ }, [isRunning, progress.chainStartedAt]);
110
+ const statusColor = isDone ? WARM_GREEN : isError ? WARM_RED : isPaused ? WARM_AMBER : import_core.ada.orange;
111
+ const statusBg = isDone ? WARM_GREEN_SOFT : isError ? WARM_RED_SOFT : isPaused ? WARM_AMBER_SOFT : import_core.ada.orangeSoft;
112
+ if (isDone || isError) {
113
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.row, children: [
114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.dot, { backgroundColor: statusColor }] }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [styles.label, { color: statusColor }], children: isDone ? "Completed" : "Failed" })
116
+ ] }) });
117
+ }
118
+ if (isPaused) {
119
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.row, children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.dot, { backgroundColor: statusColor }] }),
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [styles.label, { color: statusColor }], children: "Waiting for your reply" })
122
+ ] }) });
123
+ }
124
+ const elapsedStr = progress.chainStartedAt ? `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, "0")}` : null;
125
+ const metaParts = [
126
+ progress.iteration != null && progress.iteration > 0 ? `iter ${progress.iteration}` : null,
127
+ elapsedStr
128
+ ].filter(Boolean);
129
+ const lp = progress.liveProgress;
130
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.container, children: [
131
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.row, children: [
132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
133
+ import_react_native.Animated.View,
134
+ {
135
+ style: [styles.dot, { backgroundColor: import_core.ada.orange, opacity: pulseAnim }]
136
+ }
137
+ ),
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: styles.skill, numberOfLines: 1, children: progress.skillSlug || "Processing..." }),
139
+ !!progress.currentTool && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: styles.tool, numberOfLines: 1, children: ` \u2192 ${progress.currentTool}` }),
140
+ metaParts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: styles.meta, numberOfLines: 1, children: ` \xB7 ${metaParts.join(" \xB7 ")}` })
141
+ ] }),
142
+ !!lp?.message && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.liveProgressRow, children: [
143
+ lp.step != null && lp.total != null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.Text, { style: styles.liveProgressStep, children: [
144
+ "[",
145
+ lp.step,
146
+ "/",
147
+ lp.total,
148
+ "]"
149
+ ] }),
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: styles.liveProgressMsg, numberOfLines: 1, children: lp.message })
151
+ ] }),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.progressTrack, { backgroundColor: statusBg }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
153
+ import_react_native.Animated.View,
154
+ {
155
+ style: [styles.progressFill, { backgroundColor: import_core.ada.orange, opacity: pulseAnim }]
156
+ }
157
+ ) })
158
+ ] });
159
+ }
160
+ var styles = import_react_native.StyleSheet.create({
161
+ container: {
162
+ marginTop: 8,
163
+ paddingTop: 8,
164
+ borderTopWidth: 1,
165
+ borderTopColor: import_core.ada.border
166
+ },
167
+ row: {
168
+ flexDirection: "row",
169
+ alignItems: "center",
170
+ flexWrap: "nowrap",
171
+ overflow: "hidden"
172
+ },
173
+ dot: {
174
+ width: 7,
175
+ height: 7,
176
+ borderRadius: 4,
177
+ marginRight: 6,
178
+ flexShrink: 0
179
+ },
180
+ label: {
181
+ fontSize: import_core.fontSize.xs,
182
+ fontWeight: "600"
183
+ },
184
+ // Running state text segments
185
+ skill: {
186
+ fontSize: import_core.fontSize.xs,
187
+ fontWeight: "600",
188
+ color: import_core.ada.orange,
189
+ flexShrink: 1
190
+ },
191
+ tool: {
192
+ fontSize: import_core.fontSize.xs,
193
+ color: import_core.ada.inkMuted,
194
+ flexShrink: 2
195
+ },
196
+ meta: {
197
+ fontSize: import_core.fontSize.xs,
198
+ color: import_core.ada.inkMuted,
199
+ flexShrink: 0
200
+ },
201
+ liveProgressRow: {
202
+ flexDirection: "row",
203
+ alignItems: "center",
204
+ marginTop: 4,
205
+ gap: 4
206
+ },
207
+ liveProgressStep: {
208
+ fontSize: import_core.fontSize.xs,
209
+ fontWeight: "600",
210
+ color: import_core.ada.inkMuted
211
+ },
212
+ liveProgressMsg: {
213
+ fontSize: import_core.fontSize.xs,
214
+ color: import_core.ada.orange,
215
+ flex: 1
216
+ },
217
+ progressTrack: {
218
+ height: 3,
219
+ borderRadius: 2,
220
+ marginTop: 6,
221
+ overflow: "hidden"
222
+ },
223
+ progressFill: {
224
+ height: "100%",
225
+ width: "60%",
226
+ borderRadius: 2
227
+ }
228
+ });
229
+
230
+ // src/chat/UIActionsRow.tsx
231
+ var import_react_native2 = require("react-native");
232
+ var import_core2 = require("@ateam-ai-mobile/core");
233
+ var import_plugin_runtime = require("@ateam-ai-mobile/plugin-runtime");
234
+
235
+ // src/providers.ts
236
+ var _clipboard = null;
237
+ function setClipboardProvider(clipboard) {
238
+ _clipboard = clipboard;
239
+ }
240
+ function getClipboard() {
241
+ return _clipboard;
242
+ }
243
+ var _navigator = null;
244
+ function setUIActionNavigator(navigate) {
245
+ _navigator = navigate;
246
+ }
247
+ function getUIActionNavigator() {
248
+ return _navigator;
249
+ }
250
+
251
+ // src/chat/UIActionsRow.tsx
252
+ var import_jsx_runtime2 = require("react/jsx-runtime");
253
+ var fontFam = import_react_native2.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
254
+ var ICON_MAP = {
255
+ timer: "\u23F1",
256
+ memory: "\u{1F9E0}",
257
+ calendar: "\u{1F4C5}",
258
+ home: "\u{1F3E0}",
259
+ bell: "\u{1F514}",
260
+ star: "\u2B50",
261
+ bolt: "\u26A1"
262
+ };
263
+ function UIActionsRow({ actions }) {
264
+ if (!Array.isArray(actions) || actions.length === 0) return null;
265
+ const handlePress = (a) => {
266
+ (0, import_plugin_runtime.getHaptics)()?.selection();
267
+ const id = a.pluginId || a.target;
268
+ if (!id) return;
269
+ const focus = a.payload?.id || a.payload?.entity_id || a.payload?.focusId;
270
+ const surface = import_plugin_runtime.useSurfaceStore.getState().manifests[id];
271
+ if (surface) {
272
+ import_plugin_runtime.useSurfaceStore.getState().show(id, focus ? { focus } : void 0);
273
+ return;
274
+ }
275
+ const params = {};
276
+ if (focus) params.focus = String(focus);
277
+ if (a.intent === "suggested_action" && a.payload?.tool) {
278
+ params.tool = String(a.payload.tool);
279
+ if (a.payload?.args) {
280
+ try {
281
+ params.args = JSON.stringify(a.payload.args);
282
+ } catch {
283
+ }
284
+ }
285
+ }
286
+ if (a.intent === "quick_toggle" && a.payload?.verb) {
287
+ params.verb = String(a.payload.verb);
288
+ }
289
+ const navigate = getUIActionNavigator();
290
+ if (!navigate) {
291
+ console.warn("[UIActionsRow] no navigator registered \u2014 call setUIActionNavigator() in the host to handle plugin chips");
292
+ return;
293
+ }
294
+ navigate({ pluginId: id, params });
295
+ };
296
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.row, children: actions.map((a, i) => {
297
+ const icon = a.icon ? ICON_MAP[a.icon.toLowerCase()] : null;
298
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
299
+ import_react_native2.Pressable,
300
+ {
301
+ accessibilityRole: "button",
302
+ accessibilityLabel: a.description || a.label,
303
+ onPress: () => handlePress(a),
304
+ style: ({ pressed }) => [styles2.chip, pressed && styles2.chipPressed],
305
+ children: [
306
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles2.icon, children: icon }) : null,
307
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles2.label, numberOfLines: 1, children: a.label })
308
+ ]
309
+ },
310
+ `${a.target || "a"}-${i}`
311
+ );
312
+ }) });
313
+ }
314
+ var styles2 = import_react_native2.StyleSheet.create({
315
+ row: {
316
+ flexDirection: "row",
317
+ flexWrap: "wrap",
318
+ gap: 6,
319
+ marginTop: 10
320
+ },
321
+ chip: {
322
+ flexDirection: "row",
323
+ alignItems: "center",
324
+ gap: 6,
325
+ paddingHorizontal: 12,
326
+ paddingVertical: 6,
327
+ backgroundColor: import_core2.ada.orangeSoft,
328
+ borderColor: import_core2.ada.orange,
329
+ borderWidth: 1,
330
+ borderRadius: 14
331
+ },
332
+ chipPressed: {
333
+ backgroundColor: import_core2.ada.orange
334
+ },
335
+ icon: {
336
+ fontSize: 12,
337
+ lineHeight: 14
338
+ },
339
+ label: {
340
+ color: import_core2.ada.orangeDeep,
341
+ fontSize: import_core2.fontSize.xs,
342
+ fontWeight: "600",
343
+ fontFamily: fontFam
344
+ }
345
+ });
346
+
347
+ // src/chat/TableCardBlock.tsx
348
+ var import_react_native3 = require("react-native");
349
+ var import_core3 = require("@ateam-ai-mobile/core");
350
+ var import_jsx_runtime3 = require("react/jsx-runtime");
351
+ var fontFam2 = import_react_native3.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
352
+ function TableCardBlock({ columns, rows }) {
353
+ if (!columns?.length || !rows?.length) return null;
354
+ const [titleCol, ...detailCols] = columns;
355
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles3.wrap, children: rows.map((row, i) => {
356
+ const cells = Array.isArray(row) ? row : [row];
357
+ const title = cells[0];
358
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles3.card, children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles3.title, children: String(title ?? "") }),
360
+ detailCols.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles3.details, children: detailCols.map((col, ci) => {
361
+ const value = cells[ci + 1];
362
+ if (value == null || value === "") return null;
363
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles3.row, children: [
364
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles3.label, children: String(col) }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles3.value, children: String(value) })
366
+ ] }, ci);
367
+ }) })
368
+ ] }, i);
369
+ }) });
370
+ }
371
+ var styles3 = import_react_native3.StyleSheet.create({
372
+ wrap: {
373
+ marginVertical: 6,
374
+ gap: 8
375
+ },
376
+ card: {
377
+ backgroundColor: import_core3.ada.paper,
378
+ borderRadius: import_core3.borderRadius.md,
379
+ borderWidth: 1,
380
+ borderColor: import_core3.ada.border,
381
+ padding: import_core3.spacing.md
382
+ },
383
+ title: {
384
+ color: import_core3.ada.ink,
385
+ fontSize: import_core3.fontSize.md,
386
+ fontWeight: "600",
387
+ fontFamily: fontFam2,
388
+ letterSpacing: -0.2,
389
+ marginBottom: 6
390
+ },
391
+ details: {
392
+ gap: 4
393
+ },
394
+ row: {
395
+ flexDirection: "row",
396
+ alignItems: "flex-start",
397
+ gap: 8
398
+ },
399
+ label: {
400
+ color: import_core3.ada.inkMuted,
401
+ fontSize: import_core3.fontSize.xs,
402
+ fontFamily: fontFam2,
403
+ fontWeight: "500",
404
+ width: 84,
405
+ textTransform: "uppercase",
406
+ letterSpacing: 0.6,
407
+ paddingTop: 2
408
+ },
409
+ value: {
410
+ flex: 1,
411
+ color: import_core3.ada.inkSoft,
412
+ fontSize: import_core3.fontSize.sm,
413
+ fontFamily: fontFam2,
414
+ lineHeight: 18
415
+ }
416
+ });
417
+
418
+ // src/stores/lightboxStore.ts
419
+ var import_zustand = require("zustand");
420
+ var useLightboxStore = (0, import_zustand.create)((set) => ({
421
+ uri: null,
422
+ show: (uri) => set({ uri }),
423
+ hide: () => set({ uri: null })
424
+ }));
425
+
426
+ // src/chat/MessageBubble.tsx
427
+ var import_jsx_runtime4 = require("react/jsx-runtime");
428
+ var fontFam3 = import_react_native4.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
429
+ function formatTime(date) {
430
+ const h = date.getHours();
431
+ const m = date.getMinutes().toString().padStart(2, "0");
432
+ return `${h}:${m}`;
433
+ }
434
+ function DaySeparator({ date }) {
435
+ const today = /* @__PURE__ */ new Date();
436
+ const yesterday = new Date(today);
437
+ yesterday.setDate(yesterday.getDate() - 1);
438
+ let label;
439
+ if (isSameDay(date, today)) {
440
+ label = "Today";
441
+ } else if (isSameDay(date, yesterday)) {
442
+ label = "Yesterday";
443
+ } else {
444
+ label = date.toLocaleDateString(void 0, { weekday: "long", month: "short", day: "numeric" });
445
+ }
446
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: separatorStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: separatorStyles.pill, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: separatorStyles.text, children: label }) }) });
447
+ }
448
+ function isSameDay(a, b) {
449
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
450
+ }
451
+ var separatorStyles = import_react_native4.StyleSheet.create({
452
+ container: {
453
+ alignItems: "center",
454
+ marginVertical: 14
455
+ },
456
+ pill: {
457
+ backgroundColor: import_core4.ada.paperSoft,
458
+ paddingHorizontal: 14,
459
+ paddingVertical: 5,
460
+ borderRadius: 999
461
+ },
462
+ text: {
463
+ fontSize: 12,
464
+ fontWeight: "600",
465
+ color: import_core4.ada.inkMuted,
466
+ letterSpacing: 0.4,
467
+ fontFamily: fontFam3
468
+ }
469
+ });
470
+ function MessageBubble({ message }) {
471
+ const isUser = message.role === "user";
472
+ const isSystem = message.role === "system";
473
+ const isNotification = !isUser && message.kind === "notification";
474
+ const [copied, setCopied] = (0, import_react2.useState)("");
475
+ const showCopied = (0, import_react2.useCallback)((label) => {
476
+ setCopied(label);
477
+ (0, import_plugin_runtime2.getHaptics)()?.notification("success");
478
+ setTimeout(() => setCopied(""), 1500);
479
+ }, []);
480
+ const copyText = (0, import_react2.useCallback)(async (text) => {
481
+ const clipboard = getClipboard();
482
+ if (!clipboard) return;
483
+ await clipboard.setString(text);
484
+ showCopied("Copied!");
485
+ }, [showCopied]);
486
+ const handleLongPress = (0, import_react2.useCallback)(() => {
487
+ copyText(message.content);
488
+ }, [message.content, copyText]);
489
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles4.row, isUser && styles4.rowUser], children: [
490
+ !isUser && !isNotification && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.avatarOrb, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
491
+ import_plugin_runtime2.AdaOrb,
492
+ {
493
+ size: 28,
494
+ state: message.jobProgress?.status === "running" ? "thinking" : "idle"
495
+ }
496
+ ) }),
497
+ isNotification && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.notificationLeftStripe }),
498
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
499
+ import_react_native4.Pressable,
500
+ {
501
+ accessibilityRole: "text",
502
+ accessibilityLabel: `${isUser ? "You" : isNotification ? "Notification" : "Ada"}: ${message.content}`,
503
+ accessibilityHint: "Long-press to copy",
504
+ onLongPress: handleLongPress,
505
+ style: [
506
+ styles4.bubble,
507
+ isUser && styles4.bubbleUser,
508
+ isSystem && styles4.bubbleSystem,
509
+ isNotification && styles4.bubbleNotification
510
+ ],
511
+ children: [
512
+ isNotification && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles4.notificationHeader, children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.notificationHeaderIcon, children: "\u{1F514}" }),
514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.notificationHeaderText, children: "Notification" })
515
+ ] }),
516
+ message.attachments && message.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.attachments, children: message.attachments.map((att, i) => att.previewUri ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
517
+ import_react_native4.Pressable,
518
+ {
519
+ accessibilityRole: "imagebutton",
520
+ accessibilityLabel: `Photo${att.name ? `: ${att.name}` : ""}. Tap to enlarge.`,
521
+ onPress: () => useLightboxStore.getState().show(att.previewUri),
522
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
523
+ import_react_native4.Image,
524
+ {
525
+ source: {
526
+ uri: att.previewUri,
527
+ ...att.previewUri.startsWith("http") ? {
528
+ headers: { Authorization: `Bearer ${(0, import_core4.getConfig)().tokenProvider.getToken()}` }
529
+ } : {}
530
+ },
531
+ style: styles4.attachmentImage,
532
+ resizeMode: "cover",
533
+ onError: (e) => console.warn("[Image] Failed to load:", att.previewUri?.slice(0, 100), e.nativeEvent.error)
534
+ }
535
+ )
536
+ },
537
+ i
538
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.attachmentFile, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.attachmentFileName, numberOfLines: 1, children: att.name }) }, i)) }),
539
+ isUser ? (
540
+ // Hide the "See attachment" placeholder when the bubble already shows the photo.
541
+ message.content && !(message.content === "See attachment" && message.attachments?.length) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { selectable: true, style: styles4.userText, children: message.content }) : null
542
+ ) : renderAssistantContent(message.content, copyText),
543
+ !!copied && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.copiedBadge, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.copiedText, children: copied }) }),
544
+ message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolCallsBadge, { calls: message.toolCalls }),
545
+ message.jobProgress && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(JobProgressBar, { progress: message.jobProgress }),
546
+ !isUser && message.actions && message.actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UIActionsRow, { actions: message.actions }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles4.timeText, isUser && styles4.timeTextUser], children: formatTime(message.timestamp) })
548
+ ]
549
+ }
550
+ ),
551
+ isUser && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.avatarSpacer })
552
+ ] });
553
+ }
554
+ function renderAssistantContent(content, copyText) {
555
+ if (!content) return null;
556
+ if (!content.includes(import_core4.TABLE_BLOCK_OPEN)) {
557
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarkdownBubble, { content, copyText });
558
+ }
559
+ const segments = [];
560
+ let cursor = 0;
561
+ while (cursor < content.length) {
562
+ const open = content.indexOf(import_core4.TABLE_BLOCK_OPEN, cursor);
563
+ if (open === -1) {
564
+ segments.push({ kind: "md", content: content.slice(cursor) });
565
+ break;
566
+ }
567
+ if (open > cursor) {
568
+ segments.push({ kind: "md", content: content.slice(cursor, open) });
569
+ }
570
+ const close = content.indexOf(import_core4.TABLE_BLOCK_CLOSE, open + import_core4.TABLE_BLOCK_OPEN.length);
571
+ if (close === -1) {
572
+ segments.push({ kind: "md", content: content.slice(open) });
573
+ break;
574
+ }
575
+ const payload = content.slice(open + import_core4.TABLE_BLOCK_OPEN.length, close);
576
+ segments.push({ kind: "table", content: payload });
577
+ cursor = close + import_core4.TABLE_BLOCK_CLOSE.length;
578
+ }
579
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: segments.map((seg, i) => {
580
+ if (seg.kind === "md") {
581
+ const trimmed = seg.content.trim();
582
+ if (!trimmed) return null;
583
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarkdownBubble, { content: seg.content, copyText }, i);
584
+ }
585
+ try {
586
+ const data = JSON.parse(decodeURIComponent(seg.content));
587
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TableCardBlock, { columns: data.columns, rows: data.rows }, i);
588
+ } catch {
589
+ return null;
590
+ }
591
+ }) });
592
+ }
593
+ function MarkdownBubble({ content, copyText }) {
594
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
595
+ import_react_native_markdown_display.default,
596
+ {
597
+ style: markdownStyles,
598
+ rules: {
599
+ code_inline: (node) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
600
+ import_react_native4.Text,
601
+ {
602
+ style: markdownStyles.code_inline,
603
+ onPress: () => copyText(node.content),
604
+ children: node.content
605
+ },
606
+ node.key
607
+ ),
608
+ fence: (node) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
609
+ import_react_native4.Pressable,
610
+ {
611
+ onPress: () => copyText(node.content),
612
+ style: markdownStyles.fence,
613
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: markdownStyles.fenceText, children: node.content })
614
+ },
615
+ node.key
616
+ )
617
+ },
618
+ children: content
619
+ }
620
+ );
621
+ }
622
+ function ToolCallsBadge({ calls }) {
623
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
624
+ const label = calls.filter((c) => c.name === "send_message_to_skill").map((c) => c.args.skill_name || c.skill).filter(Boolean).join(", ") || "Tool call";
625
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles4.toolCalls, children: [
626
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Pressable, { style: styles4.toolToggle, onPress: () => setExpanded(!expanded), children: [
627
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.toolLabel, children: label }),
628
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.toolChevron, children: expanded ? "\u25B2" : "\u25BC" })
629
+ ] }),
630
+ expanded && calls.map((tc, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles4.toolItem, children: [
631
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.toolName, children: tc.name }),
632
+ tc.skill && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles4.toolSkill, children: tc.skill })
633
+ ] }, i))
634
+ ] });
635
+ }
636
+ var styles4 = import_react_native4.StyleSheet.create({
637
+ row: {
638
+ flexDirection: "row",
639
+ alignItems: "flex-end",
640
+ marginBottom: 12,
641
+ paddingHorizontal: 12,
642
+ gap: 8
643
+ },
644
+ rowUser: {
645
+ justifyContent: "flex-end"
646
+ },
647
+ avatarOrb: {
648
+ width: 28,
649
+ height: 28,
650
+ justifyContent: "center",
651
+ alignItems: "center"
652
+ },
653
+ avatarSpacer: {
654
+ width: 28,
655
+ height: 28
656
+ },
657
+ bubble: {
658
+ maxWidth: "78%",
659
+ backgroundColor: import_core4.ada.botBubble,
660
+ borderWidth: 1,
661
+ borderColor: import_core4.ada.botBubbleBorder,
662
+ borderRadius: import_core4.borderRadius.xl,
663
+ paddingHorizontal: 16,
664
+ paddingVertical: 12
665
+ },
666
+ bubbleUser: {
667
+ backgroundColor: import_core4.ada.userBubble,
668
+ borderColor: "transparent"
669
+ },
670
+ bubbleSystem: {
671
+ backgroundColor: import_core4.ada.orangeSoft,
672
+ borderColor: import_core4.ada.orange,
673
+ maxWidth: "90%"
674
+ },
675
+ bubbleNotification: {
676
+ backgroundColor: import_core4.ada.orangeSoft,
677
+ borderColor: "rgba(255, 122, 40, 0.35)",
678
+ borderRadius: import_core4.borderRadius.lg,
679
+ maxWidth: "90%"
680
+ },
681
+ notificationLeftStripe: {
682
+ width: 3,
683
+ alignSelf: "stretch",
684
+ backgroundColor: import_core4.ada.orange,
685
+ borderRadius: 2,
686
+ marginRight: 4
687
+ },
688
+ notificationHeader: {
689
+ flexDirection: "row",
690
+ alignItems: "center",
691
+ gap: 6,
692
+ marginBottom: 6
693
+ },
694
+ notificationHeaderIcon: {
695
+ fontSize: 12
696
+ },
697
+ notificationHeaderText: {
698
+ color: import_core4.ada.orangeDeep,
699
+ fontSize: 11,
700
+ fontWeight: "700",
701
+ letterSpacing: 0.6,
702
+ textTransform: "uppercase",
703
+ fontFamily: fontFam3
704
+ },
705
+ userText: {
706
+ color: import_core4.ada.userBubbleText,
707
+ fontSize: import_core4.fontSize.md,
708
+ lineHeight: 22,
709
+ fontFamily: fontFam3
710
+ },
711
+ timeText: {
712
+ fontSize: 11,
713
+ color: import_core4.ada.inkMuted,
714
+ alignSelf: "flex-end",
715
+ marginTop: 4,
716
+ fontFamily: fontFam3
717
+ },
718
+ timeTextUser: {
719
+ color: "rgba(250,246,238,0.55)"
720
+ },
721
+ attachments: {
722
+ flexDirection: "row",
723
+ flexWrap: "wrap",
724
+ gap: 6,
725
+ marginBottom: 8
726
+ },
727
+ attachmentImage: {
728
+ width: 120,
729
+ height: 90,
730
+ borderRadius: 8
731
+ },
732
+ attachmentFile: {
733
+ backgroundColor: "rgba(255,255,255,0.15)",
734
+ borderRadius: 8,
735
+ paddingHorizontal: 10,
736
+ paddingVertical: 6
737
+ },
738
+ attachmentFileName: {
739
+ color: "rgba(255,255,255,0.8)",
740
+ fontSize: 11,
741
+ maxWidth: 100
742
+ },
743
+ copiedBadge: {
744
+ position: "absolute",
745
+ top: 6,
746
+ right: 6,
747
+ backgroundColor: "#5A8A5C",
748
+ paddingHorizontal: 8,
749
+ paddingVertical: 2,
750
+ borderRadius: import_core4.borderRadius.sm
751
+ },
752
+ copiedText: {
753
+ color: "#fff",
754
+ fontSize: import_core4.fontSize.xs,
755
+ fontWeight: "600"
756
+ },
757
+ toolCalls: {
758
+ marginTop: 8,
759
+ borderTopWidth: 1,
760
+ borderTopColor: import_core4.ada.border,
761
+ paddingTop: 6
762
+ },
763
+ toolToggle: {
764
+ flexDirection: "row",
765
+ alignItems: "center",
766
+ gap: 6
767
+ },
768
+ toolLabel: {
769
+ color: import_core4.ada.inkSoft,
770
+ fontSize: import_core4.fontSize.xs,
771
+ fontWeight: "500"
772
+ },
773
+ toolChevron: {
774
+ color: import_core4.ada.inkMuted,
775
+ fontSize: 8
776
+ },
777
+ toolItem: {
778
+ marginTop: 4,
779
+ paddingLeft: 8
780
+ },
781
+ toolName: {
782
+ color: import_core4.ada.inkSoft,
783
+ fontSize: import_core4.fontSize.xs,
784
+ fontFamily: "monospace"
785
+ },
786
+ toolSkill: {
787
+ color: import_core4.ada.orangeDeep,
788
+ fontSize: import_core4.fontSize.xs
789
+ }
790
+ });
791
+ var markdownStyles = import_react_native4.StyleSheet.create({
792
+ body: {
793
+ color: import_core4.ada.botBubbleText,
794
+ fontSize: import_core4.fontSize.md,
795
+ lineHeight: 22,
796
+ fontFamily: fontFam3
797
+ },
798
+ paragraph: {
799
+ marginTop: 0,
800
+ marginBottom: 4
801
+ },
802
+ strong: {
803
+ fontWeight: "700",
804
+ color: import_core4.ada.botBubbleText
805
+ },
806
+ em: {
807
+ fontStyle: "italic"
808
+ },
809
+ code_inline: {
810
+ backgroundColor: import_core4.ada.paperDeep,
811
+ color: import_core4.ada.orangeDeep,
812
+ paddingHorizontal: 5,
813
+ paddingVertical: 1,
814
+ borderRadius: 4,
815
+ fontFamily: "monospace",
816
+ fontSize: import_core4.fontSize.sm
817
+ },
818
+ fence: {
819
+ backgroundColor: import_core4.ada.paperDeep,
820
+ padding: 10,
821
+ borderRadius: 10
822
+ },
823
+ fenceText: {
824
+ fontFamily: "monospace",
825
+ fontSize: import_core4.fontSize.sm,
826
+ color: import_core4.ada.ink
827
+ },
828
+ link: {
829
+ color: import_core4.ada.orangeDeep
830
+ },
831
+ list_item: {
832
+ color: import_core4.ada.botBubbleText
833
+ },
834
+ bullet_list: {
835
+ marginVertical: 4
836
+ }
837
+ });
838
+
839
+ // src/chat/TypingIndicator.tsx
840
+ var import_react_native5 = require("react-native");
841
+ var import_plugin_runtime3 = require("@ateam-ai-mobile/plugin-runtime");
842
+ var import_jsx_runtime5 = require("react/jsx-runtime");
843
+ function TypingIndicator() {
844
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: styles5.row, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_plugin_runtime3.AdaOrb, { size: 32, state: "thinking" }) });
845
+ }
846
+ var styles5 = import_react_native5.StyleSheet.create({
847
+ row: {
848
+ flexDirection: "row",
849
+ alignItems: "center",
850
+ marginBottom: 12,
851
+ paddingHorizontal: 16,
852
+ paddingVertical: 4
853
+ }
854
+ });
855
+
856
+ // src/chat/ChatInput.tsx
857
+ var import_react3 = require("react");
858
+ var import_react_native6 = require("react-native");
859
+ var import_react_native_svg = __toESM(require("react-native-svg"));
860
+ var import_core5 = require("@ateam-ai-mobile/core");
861
+ var import_plugin_runtime4 = require("@ateam-ai-mobile/plugin-runtime");
862
+ var import_jsx_runtime6 = require("react/jsx-runtime");
863
+ function ChatInput({ onSend, onCallStart, loading, prefill, placeholder }) {
864
+ const [text, setText] = (0, import_react3.useState)("");
865
+ const [height, setHeight] = (0, import_react3.useState)(40);
866
+ const inputRef = (0, import_react3.useRef)(null);
867
+ (0, import_react3.useEffect)(() => {
868
+ if (prefill) {
869
+ setText(prefill);
870
+ inputRef.current?.focus();
871
+ }
872
+ }, [prefill]);
873
+ const handleSend = (0, import_react3.useCallback)(() => {
874
+ const trimmed = text.trim();
875
+ if (!trimmed) return;
876
+ (0, import_plugin_runtime4.getHaptics)()?.impact("light");
877
+ onSend(trimmed);
878
+ setText("");
879
+ setHeight(40);
880
+ }, [text, loading, onSend]);
881
+ const canSend = text.trim().length > 0;
882
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native6.View, { style: styles6.dock, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native6.View, { style: styles6.inputRow, children: [
883
+ onCallStart && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native6.Pressable, { style: styles6.callBtn, onPress: onCallStart, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_svg.default, { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: import_core5.colors.light.accent, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_svg.Path, { d: "M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.63A2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z" }) }) }),
884
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
885
+ import_react_native6.TextInput,
886
+ {
887
+ ref: inputRef,
888
+ style: [styles6.input, { height: Math.min(Math.max(40, height), 120) }],
889
+ value: text,
890
+ onChangeText: setText,
891
+ onContentSizeChange: (e) => setHeight(e.nativeEvent.contentSize.height),
892
+ placeholder: placeholder || "Message A-Team...",
893
+ placeholderTextColor: import_core5.colors.light.textMuted,
894
+ multiline: true,
895
+ returnKeyType: "default",
896
+ blurOnSubmit: false,
897
+ onSubmitEditing: () => {
898
+ if (import_react_native6.Platform.OS === "web") handleSend();
899
+ }
900
+ }
901
+ ),
902
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
903
+ import_react_native6.Pressable,
904
+ {
905
+ style: [styles6.sendBtn, canSend && styles6.sendBtnActive],
906
+ onPress: handleSend,
907
+ disabled: !canSend,
908
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native_svg.default, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: canSend ? "#fff" : import_core5.colors.light.textMuted, strokeWidth: 2, children: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_svg.Line, { x1: "22", y1: "2", x2: "11", y2: "13" }),
910
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_svg.Polygon, { points: "22 2 15 22 11 13 2 9 22 2" })
911
+ ] })
912
+ }
913
+ )
914
+ ] }) });
915
+ }
916
+ var styles6 = import_react_native6.StyleSheet.create({
917
+ dock: {
918
+ paddingHorizontal: 12,
919
+ paddingVertical: 8,
920
+ backgroundColor: import_core5.colors.light.bgPrimary,
921
+ borderTopWidth: 0.5,
922
+ borderTopColor: import_core5.colors.light.border
923
+ },
924
+ inputRow: {
925
+ flexDirection: "row",
926
+ alignItems: "flex-end",
927
+ backgroundColor: import_core5.colors.light.bgInput,
928
+ borderRadius: import_core5.borderRadius.xl,
929
+ borderWidth: 1,
930
+ borderColor: import_core5.colors.light.border,
931
+ paddingHorizontal: 4,
932
+ paddingVertical: 4,
933
+ gap: 4
934
+ },
935
+ callBtn: {
936
+ width: 36,
937
+ height: 36,
938
+ borderRadius: 18,
939
+ justifyContent: "center",
940
+ alignItems: "center"
941
+ },
942
+ input: {
943
+ flex: 1,
944
+ fontSize: 16,
945
+ color: import_core5.colors.light.textPrimary,
946
+ paddingHorizontal: 8,
947
+ paddingVertical: 8,
948
+ maxHeight: 120
949
+ },
950
+ sendBtn: {
951
+ width: 36,
952
+ height: 36,
953
+ borderRadius: 18,
954
+ justifyContent: "center",
955
+ alignItems: "center",
956
+ backgroundColor: import_core5.colors.light.bgTertiary
957
+ },
958
+ sendBtnActive: {
959
+ backgroundColor: import_core5.colors.light.accent
960
+ }
961
+ });
962
+
963
+ // src/Lightbox.tsx
964
+ var import_react_native7 = require("react-native");
965
+ var import_core6 = require("@ateam-ai-mobile/core");
966
+ var import_jsx_runtime7 = require("react/jsx-runtime");
967
+ function Lightbox() {
968
+ const uri = useLightboxStore((s) => s.uri);
969
+ const hide = useLightboxStore((s) => s.hide);
970
+ if (!uri) return null;
971
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Modal, { visible: true, transparent: true, animationType: "fade", onRequestClose: hide, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.Pressable, { style: styles7.backdrop, onPress: hide, children: [
972
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
973
+ import_react_native7.Image,
974
+ {
975
+ source: {
976
+ uri,
977
+ ...uri.startsWith("http") ? {
978
+ headers: { Authorization: `Bearer ${(0, import_core6.getConfig)().tokenProvider.getToken()}` }
979
+ } : {}
980
+ },
981
+ style: styles7.image,
982
+ resizeMode: "contain"
983
+ }
984
+ ),
985
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.View, { style: styles7.closeHint, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: styles7.closeHintText, children: "Tap to close" }) })
986
+ ] }) });
987
+ }
988
+ var styles7 = import_react_native7.StyleSheet.create({
989
+ backdrop: {
990
+ flex: 1,
991
+ backgroundColor: "rgba(0,0,0,0.92)",
992
+ justifyContent: "center"
993
+ },
994
+ image: {
995
+ width: "100%",
996
+ height: "85%"
997
+ },
998
+ closeHint: {
999
+ position: "absolute",
1000
+ bottom: 40,
1001
+ alignSelf: "center"
1002
+ },
1003
+ closeHintText: {
1004
+ color: "rgba(255,255,255,0.6)",
1005
+ fontSize: 13
1006
+ }
1007
+ });
1008
+
1009
+ // src/AdasChatScreen.tsx
1010
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1011
+ function isSameDay2(a, b) {
1012
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1013
+ }
1014
+ function AdasChatScreen({
1015
+ onCallStart,
1016
+ placeholder,
1017
+ footerAccessory,
1018
+ withLightbox = true
1019
+ }) {
1020
+ const messages = (0, import_core7.useChatStore)((s) => s.messages);
1021
+ const loading = (0, import_core7.useChatStore)((s) => s.loading);
1022
+ const conversationLoaded = (0, import_core7.useChatStore)((s) => s.conversationLoaded);
1023
+ const loadConversation = (0, import_core7.useChatStore)((s) => s.loadConversation);
1024
+ const sendMessage = (0, import_core7.useChatStore)((s) => s.sendMessage);
1025
+ const listRef = (0, import_react4.useRef)(null);
1026
+ const [refreshing, setRefreshing] = (0, import_react4.useState)(false);
1027
+ const [prefill, setPrefill] = (0, import_react4.useState)("");
1028
+ (0, import_react4.useEffect)(() => {
1029
+ if (!conversationLoaded) loadConversation();
1030
+ }, [conversationLoaded, loadConversation]);
1031
+ (0, import_react4.useEffect)(() => {
1032
+ const unsub = import_core7.pluginCommandEmitter.on("prepare-chat-message", ({ text }) => {
1033
+ if (text) setPrefill(text);
1034
+ });
1035
+ return unsub;
1036
+ }, []);
1037
+ const useInverted = import_react_native8.Platform.OS !== "web";
1038
+ const listData = (0, import_react4.useMemo)(
1039
+ () => useInverted ? [...messages].reverse() : messages,
1040
+ [messages, useInverted]
1041
+ );
1042
+ const scrollToLatest = (0, import_react4.useCallback)((animated) => {
1043
+ if (useInverted) listRef.current?.scrollToOffset({ offset: 0, animated });
1044
+ else listRef.current?.scrollToEnd({ animated });
1045
+ }, [useInverted]);
1046
+ const handleSend = (0, import_react4.useCallback)((text) => {
1047
+ setPrefill("");
1048
+ sendMessage(text);
1049
+ scrollToLatest(true);
1050
+ }, [sendMessage, scrollToLatest]);
1051
+ const handleRefresh = (0, import_react4.useCallback)(async () => {
1052
+ setRefreshing(true);
1053
+ try {
1054
+ await loadConversation();
1055
+ } catch {
1056
+ }
1057
+ setRefreshing(false);
1058
+ }, [loadConversation]);
1059
+ const renderItem = (0, import_react4.useCallback)(({ item, index }) => {
1060
+ const older = useInverted ? listData[index + 1] : listData[index - 1];
1061
+ const showDaySep = !older || !isSameDay2(older.timestamp, item.timestamp);
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native8.View, { children: [
1063
+ showDaySep && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DaySeparator, { date: item.timestamp }),
1064
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageBubble, { message: item })
1065
+ ] });
1066
+ }, [listData, useInverted]);
1067
+ const typingIndicator = loading && !(useInverted ? listData[0] : listData[listData.length - 1])?.jobProgress ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TypingIndicator, {}) : null;
1068
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1069
+ import_react_native8.KeyboardAvoidingView,
1070
+ {
1071
+ style: styles8.root,
1072
+ behavior: import_react_native8.Platform.OS === "ios" ? "padding" : void 0,
1073
+ children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1075
+ import_react_native8.FlatList,
1076
+ {
1077
+ ref: listRef,
1078
+ style: styles8.list,
1079
+ data: listData,
1080
+ inverted: useInverted,
1081
+ keyExtractor: (m) => m.id,
1082
+ renderItem,
1083
+ ListHeaderComponent: useInverted ? typingIndicator : null,
1084
+ ListFooterComponent: useInverted ? null : typingIndicator,
1085
+ onContentSizeChange: useInverted ? void 0 : () => scrollToLatest(false),
1086
+ refreshControl: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native8.RefreshControl, { refreshing, onRefresh: handleRefresh, tintColor: import_core7.ada.orange }),
1087
+ keyboardShouldPersistTaps: "handled",
1088
+ contentContainerStyle: styles8.listContent
1089
+ }
1090
+ ),
1091
+ footerAccessory,
1092
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1093
+ ChatInput,
1094
+ {
1095
+ onSend: handleSend,
1096
+ onCallStart,
1097
+ loading,
1098
+ prefill,
1099
+ placeholder
1100
+ }
1101
+ ),
1102
+ withLightbox && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Lightbox, {})
1103
+ ]
1104
+ }
1105
+ );
1106
+ }
1107
+ var styles8 = import_react_native8.StyleSheet.create({
1108
+ root: {
1109
+ flex: 1,
1110
+ backgroundColor: import_core7.ada.paper
1111
+ },
1112
+ list: {
1113
+ flex: 1
1114
+ },
1115
+ listContent: {
1116
+ paddingVertical: 12
1117
+ }
1118
+ });
1119
+
1120
+ // src/plugin/Plugin.tsx
1121
+ var import_react7 = __toESM(require("react"));
1122
+ var import_react_native10 = require("react-native");
1123
+ var import_core10 = require("@ateam-ai-mobile/core");
1124
+ var import_plugin_runtime6 = require("@ateam-ai-mobile/plugin-runtime");
1125
+
1126
+ // src/plugin/EmbeddedPluginWebView.tsx
1127
+ var import_react6 = require("react");
1128
+ var import_react_native9 = require("react-native");
1129
+ var import_react_native_webview = require("react-native-webview");
1130
+ var import_core9 = require("@ateam-ai-mobile/core");
1131
+ var import_plugin_runtime5 = require("@ateam-ai-mobile/plugin-runtime");
1132
+
1133
+ // src/plugin/usePluginBridge.ts
1134
+ var import_react5 = require("react");
1135
+ var import_core8 = require("@ateam-ai-mobile/core");
1136
+ function usePluginBridge(pluginId, connectorId, commandBufferRef, wsRef) {
1137
+ const webViewRef = (0, import_react5.useRef)(null);
1138
+ const sendInit = (0, import_react5.useCallback)(() => {
1139
+ const msg = JSON.stringify({
1140
+ source: "adas-host",
1141
+ pluginId,
1142
+ message: {
1143
+ type: "init",
1144
+ payload: {
1145
+ tenant: (0, import_core8.getTenant)(),
1146
+ connectorId: connectorId || null,
1147
+ mcpEndpoint: null,
1148
+ slug: null,
1149
+ skillSlug: null
1150
+ }
1151
+ }
1152
+ });
1153
+ webViewRef.current?.postMessage(msg);
1154
+ }, [pluginId, connectorId]);
1155
+ const sendToPlugin = (0, import_react5.useCallback)(
1156
+ (type, payload) => {
1157
+ const msg = JSON.stringify({
1158
+ source: "adas-host",
1159
+ message: { type, payload }
1160
+ });
1161
+ webViewRef.current?.postMessage(msg);
1162
+ },
1163
+ []
1164
+ );
1165
+ const dispatchToWebView = (0, import_react5.useCallback)((cmd) => {
1166
+ if (cmd.command === "__focus") return;
1167
+ const msg = JSON.stringify({
1168
+ source: "adas-host",
1169
+ pluginId,
1170
+ message: {
1171
+ type: "plugin.command",
1172
+ payload: {
1173
+ command: cmd.command,
1174
+ args: cmd.args,
1175
+ correlationId: cmd.correlationId
1176
+ }
1177
+ }
1178
+ });
1179
+ webViewRef.current?.postMessage(msg);
1180
+ }, [pluginId]);
1181
+ (0, import_react5.useEffect)(() => {
1182
+ const unsub = import_core8.pluginCommandEmitter.on("plugin-command", (cmd) => {
1183
+ if (cmd.pluginId !== pluginId) return;
1184
+ dispatchToWebView(cmd);
1185
+ });
1186
+ return unsub;
1187
+ }, [pluginId, dispatchToWebView]);
1188
+ (0, import_react5.useEffect)(() => {
1189
+ if (!commandBufferRef) return;
1190
+ const timer = setTimeout(() => {
1191
+ const buf = commandBufferRef.current;
1192
+ const remaining = [];
1193
+ for (const cmd of buf) {
1194
+ if (cmd.pluginId === pluginId) {
1195
+ dispatchToWebView(cmd);
1196
+ } else {
1197
+ remaining.push(cmd);
1198
+ }
1199
+ }
1200
+ commandBufferRef.current = remaining;
1201
+ }, 1e3);
1202
+ return () => clearTimeout(timer);
1203
+ }, [pluginId, commandBufferRef, dispatchToWebView]);
1204
+ const handleMessage = (0, import_react5.useCallback)(
1205
+ async (event) => {
1206
+ let data;
1207
+ try {
1208
+ data = JSON.parse(event.nativeEvent.data);
1209
+ } catch {
1210
+ return;
1211
+ }
1212
+ if (data.source !== "adas-plugin") return;
1213
+ const action = data.message?.action || data.action;
1214
+ const payload = data.message?.payload || data.payload;
1215
+ const type = data.message?.type || data.type;
1216
+ if (action === "mcp-fetch" && payload?.requestId) {
1217
+ const { requestId, body } = payload;
1218
+ try {
1219
+ const result = await (0, import_core8.mcpFetch)(body);
1220
+ sendToPlugin("mcp-fetch-result", { requestId, result, error: null });
1221
+ } catch (err) {
1222
+ console.warn("[PluginBridge] mcp-fetch ERROR:", err.message);
1223
+ sendToPlugin("mcp-fetch-result", { requestId, result: null, error: err.message });
1224
+ }
1225
+ return;
1226
+ }
1227
+ if (action === "mcp-call" && payload?.requestId) {
1228
+ const { requestId, toolName, args: callArgs } = payload;
1229
+ if (toolName) {
1230
+ try {
1231
+ const rpcResponse = await (0, import_core8.mcpToolCall)(toolName, callArgs || {});
1232
+ const result = rpcResponse?.result ?? rpcResponse;
1233
+ sendToPlugin("mcp-result", { requestId, result, error: null });
1234
+ } catch (err) {
1235
+ sendToPlugin("mcp-result", { requestId, result: null, error: err.message });
1236
+ }
1237
+ return;
1238
+ }
1239
+ const cId = payload.connectorId || callArgs?.connectorId || callArgs?.params?.connectorId;
1240
+ const tool = payload.tool || callArgs?.tool || callArgs?.params?.tool;
1241
+ const toolArgs = payload.args || callArgs?.args || callArgs?.params?.args || {};
1242
+ if (!cId || !tool) {
1243
+ console.error(`[PluginBridge] mcp-call missing params \u2014 connectorId: ${cId}, tool: ${tool}`);
1244
+ sendToPlugin("mcp-result", { requestId, result: null, error: `Missing connectorId or tool in mcp-call. Got connectorId=${cId}, tool=${tool}` });
1245
+ return;
1246
+ }
1247
+ try {
1248
+ const data2 = await (0, import_core8.connectorCall)(cId, tool, toolArgs);
1249
+ if (data2.ok) {
1250
+ sendToPlugin("mcp-result", { requestId, result: data2.result, error: null });
1251
+ } else {
1252
+ sendToPlugin("mcp-result", { requestId, result: null, error: data2.error || "MCP call failed" });
1253
+ }
1254
+ } catch (err) {
1255
+ sendToPlugin("mcp-result", { requestId, result: null, error: err.message });
1256
+ }
1257
+ return;
1258
+ }
1259
+ if (action === "close") {
1260
+ import_core8.pluginCommandEmitter.emit("plugin-close", { pluginId });
1261
+ return;
1262
+ }
1263
+ if (type === "plugin.command.result" && payload?.correlationId) {
1264
+ const ws = wsRef?.current;
1265
+ if (ws && ws.readyState === WebSocket.OPEN) {
1266
+ try {
1267
+ ws.send(JSON.stringify({
1268
+ type: "plugin-command-result",
1269
+ correlationId: payload.correlationId,
1270
+ result: payload.result,
1271
+ error: payload.error
1272
+ }));
1273
+ } catch (err) {
1274
+ console.warn("[PluginBridge] WS send failed, falling back to POST:", err.message);
1275
+ await (0, import_core8.postCommandResult)(payload.correlationId, payload.result, payload.error).catch(() => {
1276
+ });
1277
+ }
1278
+ } else {
1279
+ try {
1280
+ await (0, import_core8.postCommandResult)(payload.correlationId, payload.result, payload.error);
1281
+ } catch (err) {
1282
+ console.warn("[PluginBridge] Failed to send command result:", err.message);
1283
+ }
1284
+ }
1285
+ return;
1286
+ }
1287
+ },
1288
+ [sendToPlugin]
1289
+ );
1290
+ return {
1291
+ webViewRef,
1292
+ sendInit,
1293
+ handleMessage
1294
+ };
1295
+ }
1296
+ function buildPluginBridgeJS(jwt, tenant) {
1297
+ return `
1298
+ (function() {
1299
+ function _dbg(msg) {
1300
+ console.log('[PluginBridge] ' + msg);
1301
+ }
1302
+
1303
+ // 1. Seed localStorage. JWT is required by web-shared plugin code
1304
+ // (cpFeApi.ts etc. read localStorage.adas.jwt directly). Origin lockdown
1305
+ // lives in EmbeddedPluginWebView (originWhitelist +
1306
+ // onShouldStartLoadWithRequest) \u2014 not here. Never log the token.
1307
+ try {
1308
+ localStorage.setItem('adas.jwt', ${JSON.stringify(jwt)});
1309
+ localStorage.setItem('adas.tenant', ${JSON.stringify(tenant)});
1310
+ _dbg('localStorage seeded: jwt=' + (${JSON.stringify(jwt)} ? '[present]' : '[absent]') + ' tenant=' + ${JSON.stringify(tenant)});
1311
+ } catch(e) { _dbg('localStorage ERROR: ' + e.message); }
1312
+
1313
+ // 2. Intercept ALL fetch calls \u2014 proxy /mcp through the RN bridge
1314
+ var _origFetch = window.fetch.bind(window);
1315
+ var _mcpReqId = 0;
1316
+ var _pendingMcp = {};
1317
+
1318
+ window.fetch = function(url, opts) {
1319
+ var urlStr = (typeof url === 'string') ? url : (url && url.url) || '';
1320
+ var method = (opts && opts.method) || 'GET';
1321
+
1322
+ _dbg('FETCH ' + method + ' ' + urlStr.substring(0, 80));
1323
+
1324
+ // Intercept /mcp calls
1325
+ if (urlStr === '/mcp' || urlStr.endsWith('/mcp')) {
1326
+ _dbg(' -> INTERCEPTED /mcp, proxying via RN bridge');
1327
+ return new Promise(function(resolve, reject) {
1328
+ var requestId = 'mcp-f-' + (++_mcpReqId) + '-' + Date.now();
1329
+ _pendingMcp[requestId] = { resolve: resolve, reject: reject };
1330
+ window.ReactNativeWebView.postMessage(JSON.stringify({
1331
+ source: 'adas-plugin',
1332
+ action: 'mcp-fetch',
1333
+ payload: { requestId: requestId, body: opts && opts.body ? opts.body : null }
1334
+ }));
1335
+ setTimeout(function() {
1336
+ if (_pendingMcp[requestId]) {
1337
+ delete _pendingMcp[requestId];
1338
+ _dbg('TIMEOUT mcp-fetch ' + requestId);
1339
+ reject(new Error('MCP fetch timeout (30s)'));
1340
+ }
1341
+ }, 30000);
1342
+ });
1343
+ }
1344
+
1345
+ // Inject Authorization header ONLY for same-origin requests. A relative
1346
+ // path resolves against window.location.origin; an absolute URL must
1347
+ // literally match. This stops a plugin from leaking the JWT to a foreign
1348
+ // host via fetch('https://attacker.com/x') or via <base href>.
1349
+ var sameOrigin = false;
1350
+ try {
1351
+ var resolved = new URL(urlStr, window.location.origin);
1352
+ sameOrigin = (resolved.origin === window.location.origin);
1353
+ } catch (_) { sameOrigin = false; }
1354
+ if (sameOrigin) {
1355
+ opts = Object.assign({}, opts || {});
1356
+ var h = opts.headers;
1357
+ if (h instanceof Headers) {
1358
+ if (!h.has('Authorization')) { var jwt = localStorage.getItem('adas.jwt'); if (jwt) h.set('Authorization', 'Bearer ' + jwt); }
1359
+ } else {
1360
+ h = Object.assign({}, h || {});
1361
+ if (!h['Authorization'] && !h['authorization']) { var jwt2 = localStorage.getItem('adas.jwt'); if (jwt2) h['Authorization'] = 'Bearer ' + jwt2; }
1362
+ opts.headers = h;
1363
+ }
1364
+ }
1365
+
1366
+ return _origFetch(url, opts).then(function(res) {
1367
+ _dbg(' <- ' + res.status + ' ' + urlStr.substring(0, 60));
1368
+ return res;
1369
+ }).catch(function(err) {
1370
+ _dbg(' <- FETCH ERR: ' + err.message + ' ' + urlStr.substring(0, 40));
1371
+ throw err;
1372
+ });
1373
+ };
1374
+
1375
+ // Handle results from RN host
1376
+ function _handleHostMessage(d) {
1377
+ if (!d || d.source !== 'adas-host') return;
1378
+ var msg = d.message || {};
1379
+
1380
+ // mcp-fetch-result: response to intercepted fetch("/mcp")
1381
+ if (msg.type === 'mcp-fetch-result' && msg.payload) {
1382
+ var p = msg.payload;
1383
+ var pending = _pendingMcp[p.requestId];
1384
+ if (pending) {
1385
+ delete _pendingMcp[p.requestId];
1386
+ if (p.error) {
1387
+ pending.reject(new Error(p.error));
1388
+ } else {
1389
+ pending.resolve(new Response(JSON.stringify(p.result), {
1390
+ status: 200,
1391
+ headers: { 'Content-Type': 'application/json' }
1392
+ }));
1393
+ }
1394
+ }
1395
+ }
1396
+ }
1397
+
1398
+ // 3. Override window.parent.postMessage (plugins use this in iframes)
1399
+ window.parent = window.parent || {};
1400
+ window.parent.postMessage = function(data, origin) {
1401
+ if (data && data.source === 'adas-plugin') {
1402
+ window.ReactNativeWebView.postMessage(JSON.stringify(data));
1403
+ }
1404
+ };
1405
+
1406
+ // 4. Also intercept direct window.postMessage
1407
+ var origPost = window.postMessage.bind(window);
1408
+ window.postMessage = function(data, origin) {
1409
+ if (data && data.source === 'adas-plugin') {
1410
+ window.ReactNativeWebView.postMessage(JSON.stringify(data));
1411
+ }
1412
+ origPost(data, origin);
1413
+ };
1414
+
1415
+ // 5. Forward messages from RN host -> plugin
1416
+ // iOS: window 'message' event, Android: document 'message' event
1417
+ window.addEventListener('message', function(e) {
1418
+ try {
1419
+ var d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
1420
+ if (d && d.source === 'adas-host') {
1421
+ _handleHostMessage(d);
1422
+ if (typeof e.data === 'string') {
1423
+ window.dispatchEvent(new MessageEvent('message', { data: d }));
1424
+ }
1425
+ }
1426
+ } catch(_) {}
1427
+ });
1428
+
1429
+ document.addEventListener('message', function(e) {
1430
+ try {
1431
+ var d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
1432
+ _handleHostMessage(d);
1433
+ window.dispatchEvent(new MessageEvent('message', { data: d }));
1434
+ } catch(_) {}
1435
+ });
1436
+
1437
+ _dbg('Bridge JS loaded. Waiting for plugin...');
1438
+ true;
1439
+ })();
1440
+ `;
1441
+ }
1442
+
1443
+ // src/plugin/EmbeddedPluginWebView.tsx
1444
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1445
+ function EmbeddedPluginWebView({ pluginId, manifest, commandBufferRef }) {
1446
+ const [error, setError] = (0, import_react6.useState)(null);
1447
+ const token = (0, import_core9.getToken)();
1448
+ const tenant = (0, import_core9.getTenant)();
1449
+ const { webViewRef, sendInit, handleMessage } = usePluginBridge(pluginId, manifest._connectorId, commandBufferRef);
1450
+ const url = (0, import_plugin_runtime5.resolvePluginUrl)(manifest, tenant || void 0);
1451
+ const bridgeJS = buildPluginBridgeJS(token || "", tenant || "");
1452
+ const allowedHosts = (0, import_react6.useMemo)(() => {
1453
+ const hosts = /* @__PURE__ */ new Set();
1454
+ const urls = [(0, import_core9.getCoreApiUrl)()];
1455
+ const voice = (0, import_core9.getConfig)().voiceUrl;
1456
+ if (voice) urls.push(voice);
1457
+ for (const u of urls) {
1458
+ try {
1459
+ hosts.add(new URL(u).hostname);
1460
+ } catch {
1461
+ }
1462
+ }
1463
+ return hosts;
1464
+ }, []);
1465
+ const isAllowedPluginUrl = (0, import_react6.useCallback)((rawUrl) => {
1466
+ if (!rawUrl) return false;
1467
+ let parsed;
1468
+ try {
1469
+ parsed = new URL(rawUrl);
1470
+ } catch {
1471
+ return false;
1472
+ }
1473
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return false;
1474
+ if (parsed.protocol === "http:") return false;
1475
+ return allowedHosts.has(parsed.hostname);
1476
+ }, [allowedHosts]);
1477
+ const originWhitelist = (0, import_react6.useMemo)(
1478
+ () => Array.from(allowedHosts).map((h) => `https://${h}`),
1479
+ [allowedHosts]
1480
+ );
1481
+ if (error) {
1482
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native9.View, { style: styles9.center, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native9.Text, { style: styles9.errorText, children: error }) });
1483
+ }
1484
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native9.View, { style: styles9.container, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1485
+ import_react_native_webview.WebView,
1486
+ {
1487
+ ref: webViewRef,
1488
+ source: { uri: url },
1489
+ style: styles9.webview,
1490
+ javaScriptEnabled: true,
1491
+ domStorageEnabled: true,
1492
+ allowsInlineMediaPlayback: true,
1493
+ mediaPlaybackRequiresUserAction: false,
1494
+ mixedContentMode: "never",
1495
+ originWhitelist,
1496
+ allowsBackForwardNavigationGestures: false,
1497
+ setSupportMultipleWindows: false,
1498
+ javaScriptCanOpenWindowsAutomatically: false,
1499
+ onShouldStartLoadWithRequest: (req) => {
1500
+ if (!isAllowedPluginUrl(req.url)) {
1501
+ console.warn(`[EmbeddedPluginWebView] BLOCKED nav to disallowed origin: ${req.url}`);
1502
+ return false;
1503
+ }
1504
+ return true;
1505
+ },
1506
+ injectedJavaScriptBeforeContentLoaded: bridgeJS,
1507
+ onMessage: handleMessage,
1508
+ onLoad: () => {
1509
+ setTimeout(sendInit, 100);
1510
+ setTimeout(sendInit, 500);
1511
+ setTimeout(sendInit, 1200);
1512
+ },
1513
+ onError: (syntheticEvent) => {
1514
+ const { nativeEvent } = syntheticEvent;
1515
+ console.warn("[EmbeddedPluginWebView] Error:", nativeEvent.description);
1516
+ setError(`WebView error: ${nativeEvent.description}`);
1517
+ },
1518
+ onHttpError: (syntheticEvent) => {
1519
+ const { nativeEvent } = syntheticEvent;
1520
+ console.warn("[EmbeddedPluginWebView] HTTP error:", nativeEvent.statusCode, nativeEvent.url);
1521
+ if (nativeEvent.statusCode >= 400) {
1522
+ setError(`HTTP ${nativeEvent.statusCode}: Failed to load plugin`);
1523
+ }
1524
+ },
1525
+ startInLoadingState: true,
1526
+ renderLoading: () => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native9.View, { style: styles9.webviewLoading, children: [
1527
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native9.ActivityIndicator, { size: "small", color: import_core9.ada.orange }),
1528
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native9.Text, { style: styles9.loadingText, children: "Loading plugin..." })
1529
+ ] }),
1530
+ ...import_react_native9.Platform.OS === "ios" ? { bounces: false } : {}
1531
+ }
1532
+ ) });
1533
+ }
1534
+ var styles9 = import_react_native9.StyleSheet.create({
1535
+ container: {
1536
+ flex: 1,
1537
+ backgroundColor: import_core9.ada.paper
1538
+ },
1539
+ center: {
1540
+ flex: 1,
1541
+ justifyContent: "center",
1542
+ alignItems: "center",
1543
+ backgroundColor: import_core9.ada.paper,
1544
+ paddingHorizontal: 32
1545
+ },
1546
+ errorText: {
1547
+ color: "#C15545",
1548
+ fontSize: 16,
1549
+ textAlign: "center"
1550
+ },
1551
+ webview: {
1552
+ flex: 1,
1553
+ backgroundColor: import_core9.ada.paper
1554
+ },
1555
+ webviewLoading: {
1556
+ position: "absolute",
1557
+ top: 0,
1558
+ left: 0,
1559
+ right: 0,
1560
+ bottom: 0,
1561
+ justifyContent: "center",
1562
+ alignItems: "center",
1563
+ backgroundColor: import_core9.ada.paper,
1564
+ gap: 12
1565
+ },
1566
+ loadingText: {
1567
+ color: import_core9.ada.inkSoft,
1568
+ fontSize: 14
1569
+ }
1570
+ });
1571
+
1572
+ // src/plugin/Plugin.tsx
1573
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1574
+ var PluginErrorBoundary = class extends import_react7.default.Component {
1575
+ constructor() {
1576
+ super(...arguments);
1577
+ this.state = { hasError: false, error: null };
1578
+ }
1579
+ static getDerivedStateFromError(error) {
1580
+ return { hasError: true, error };
1581
+ }
1582
+ componentDidCatch(error, info) {
1583
+ console.error(`[Plugin:${this.props.pluginId}] Crash:`, error, info.componentStack);
1584
+ }
1585
+ render() {
1586
+ if (this.state.hasError) {
1587
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles10.error, children: [
1588
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.Text, { style: styles10.errorTitle, children: "Plugin Error" }),
1589
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.Text, { style: styles10.errorMessage, children: this.state.error?.message || "An unexpected error occurred" }),
1590
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1591
+ import_react_native10.Pressable,
1592
+ {
1593
+ style: styles10.retryBtn,
1594
+ onPress: () => this.setState({ hasError: false, error: null }),
1595
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.Text, { style: styles10.retryText, children: "Retry" })
1596
+ }
1597
+ )
1598
+ ] });
1599
+ }
1600
+ return this.props.children;
1601
+ }
1602
+ };
1603
+ function Plugin({ id, manifest, commandBufferRef, props: engineProps }) {
1604
+ const { plugin, loading, error, bundleMissing } = (0, import_plugin_runtime6.usePlugin)(id, manifest);
1605
+ const connectorId = id.startsWith("mcp:") ? id.split(":")[1] : manifest._connectorId;
1606
+ const bridge = (0, import_plugin_runtime6.useNativeBridge)(id, connectorId, commandBufferRef);
1607
+ const native = (0, import_plugin_runtime6.getPluginNative)();
1608
+ const theme = (0, import_plugin_runtime6.usePluginTheme)();
1609
+ const user = (0, import_core10.getUser)();
1610
+ const context = (0, import_react7.useMemo)(() => ({
1611
+ tenant: (0, import_core10.getTenant)(),
1612
+ pluginId: id,
1613
+ connectorId,
1614
+ user: user ? { id: user.actorId || "", name: user.name || "", roles: [] } : void 0
1615
+ }), [id, connectorId, user?.actorId, user?.name]);
1616
+ const iframeUrl = manifest?.render?.iframeUrl;
1617
+ if (bundleMissing && iframeUrl) {
1618
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EmbeddedPluginWebView, { pluginId: id, manifest, commandBufferRef });
1619
+ }
1620
+ if (loading) {
1621
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles10.center, children: [
1622
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.ActivityIndicator, { size: "small", color: import_core10.colors.dark.accent }),
1623
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.Text, { style: styles10.loadingText, children: "Loading plugin..." })
1624
+ ] });
1625
+ }
1626
+ if (error || !plugin) {
1627
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.View, { style: styles10.center, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.Text, { style: styles10.errorMessage, children: error || "Plugin not found" }) });
1628
+ }
1629
+ const { Component } = plugin;
1630
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PluginErrorBoundary, { pluginId: id, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Component, { bridge, native, theme, context, props: engineProps }) });
1631
+ }
1632
+ var styles10 = import_react_native10.StyleSheet.create({
1633
+ center: {
1634
+ flex: 1,
1635
+ justifyContent: "center",
1636
+ alignItems: "center",
1637
+ backgroundColor: import_core10.colors.dark.bgPrimary,
1638
+ paddingHorizontal: 32,
1639
+ gap: 12
1640
+ },
1641
+ loadingText: {
1642
+ color: import_core10.colors.dark.textSecondary,
1643
+ fontSize: 14
1644
+ },
1645
+ error: {
1646
+ flex: 1,
1647
+ justifyContent: "center",
1648
+ alignItems: "center",
1649
+ backgroundColor: import_core10.colors.dark.bgPrimary,
1650
+ paddingHorizontal: 32,
1651
+ gap: 8
1652
+ },
1653
+ errorTitle: {
1654
+ color: import_core10.colors.dark.red,
1655
+ fontSize: 16,
1656
+ fontWeight: "600"
1657
+ },
1658
+ errorMessage: {
1659
+ color: import_core10.colors.dark.textSecondary,
1660
+ fontSize: 14,
1661
+ textAlign: "center"
1662
+ },
1663
+ retryBtn: {
1664
+ marginTop: 12,
1665
+ paddingHorizontal: 20,
1666
+ paddingVertical: 10,
1667
+ backgroundColor: import_core10.colors.dark.accentSoft,
1668
+ borderRadius: 8
1669
+ },
1670
+ retryText: {
1671
+ color: import_core10.colors.dark.accent,
1672
+ fontSize: 14,
1673
+ fontWeight: "600"
1674
+ }
1675
+ });
1676
+
1677
+ // src/plugin/PluginSurface.tsx
1678
+ var import_react8 = require("react");
1679
+ var import_react_native11 = require("react-native");
1680
+ var import_core11 = require("@ateam-ai-mobile/core");
1681
+ var import_plugin_runtime7 = require("@ateam-ai-mobile/plugin-runtime");
1682
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1683
+ function PluginSurface({ pluginId, surface, dismiss, props }) {
1684
+ const [manifest, setManifest] = (0, import_react8.useState)(null);
1685
+ const [error, setError] = (0, import_react8.useState)(null);
1686
+ (0, import_react8.useEffect)(() => {
1687
+ let cancelled = false;
1688
+ (0, import_plugin_runtime7.getPlugin)(pluginId).then((m) => {
1689
+ if (!cancelled) m ? setManifest(m) : setError("Plugin not found");
1690
+ }).catch((e) => {
1691
+ if (!cancelled) setError(e?.message || "Failed to load");
1692
+ });
1693
+ return () => {
1694
+ cancelled = true;
1695
+ };
1696
+ }, [pluginId]);
1697
+ const body = manifest ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Plugin, { id: pluginId, manifest, props }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native11.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: error ? null : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native11.ActivityIndicator, { color: import_core11.ada.orange }) });
1698
+ if (surface.type === "fullscreen") {
1699
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1700
+ import_plugin_runtime7.SurfaceFullscreen,
1701
+ {
1702
+ visible: true,
1703
+ onClose: dismiss,
1704
+ status: surface.title,
1705
+ showWordmark: true,
1706
+ closeIcon: "x",
1707
+ closeLabel: "Close",
1708
+ children: body
1709
+ }
1710
+ );
1711
+ }
1712
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1713
+ import_plugin_runtime7.SurfaceDrawer,
1714
+ {
1715
+ visible: true,
1716
+ onClose: dismiss,
1717
+ title: surface.title || pluginId,
1718
+ subtitle: surface.subtitle,
1719
+ privacy: !!surface.privacy,
1720
+ children: body
1721
+ }
1722
+ );
1723
+ }
1724
+
1725
+ // src/widgets/WidgetWebView.tsx
1726
+ var import_react9 = require("react");
1727
+ var import_react_native12 = require("react-native");
1728
+ var import_react_native_webview2 = require("react-native-webview");
1729
+ var import_core12 = require("@ateam-ai-mobile/core");
1730
+ var import_core13 = require("@ateam-ai-mobile/core");
1731
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1732
+ var fontFam4 = import_react_native12.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
1733
+ function WidgetWebView({ visible, title, bundleUrl, onClose }) {
1734
+ const [loading, setLoading] = (0, import_react9.useState)(true);
1735
+ const [error, setError] = (0, import_react9.useState)(null);
1736
+ const uri = (0, import_react9.useMemo)(() => {
1737
+ if (!bundleUrl) return null;
1738
+ if (/^https?:\/\//i.test(bundleUrl)) return bundleUrl;
1739
+ return `${(0, import_core13.getCoreApiUrl)()}${bundleUrl}`;
1740
+ }, [bundleUrl]);
1741
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1742
+ import_react_native12.Modal,
1743
+ {
1744
+ visible,
1745
+ animationType: "slide",
1746
+ presentationStyle: "pageSheet",
1747
+ onRequestClose: onClose,
1748
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native12.View, { style: styles11.root, children: [
1749
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native12.View, { style: styles11.bar, children: [
1750
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Pressable, { onPress: onClose, hitSlop: 12, style: styles11.closeBtn, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Text, { style: styles11.closeIcon, children: "\u2715" }) }),
1751
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Text, { style: styles11.title, numberOfLines: 1, children: title || "Widget" }),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.View, { style: styles11.closeBtn })
1753
+ ] }),
1754
+ !uri ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.View, { style: styles11.errorWrap, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Text, { style: styles11.errorText, children: "No bundle available for this widget." }) }) : error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native12.View, { style: styles11.errorWrap, children: [
1755
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Text, { style: styles11.errorText, children: "Couldn't load widget" }),
1756
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.Text, { style: styles11.errorSub, children: error })
1757
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1759
+ import_react_native_webview2.WebView,
1760
+ {
1761
+ source: { uri },
1762
+ style: styles11.web,
1763
+ onLoadStart: () => {
1764
+ setLoading(true);
1765
+ setError(null);
1766
+ },
1767
+ onLoadEnd: () => setLoading(false),
1768
+ onError: (e) => {
1769
+ setLoading(false);
1770
+ setError(e.nativeEvent.description || "Unknown error");
1771
+ },
1772
+ onHttpError: (e) => {
1773
+ setLoading(false);
1774
+ setError(`HTTP ${e.nativeEvent.statusCode}`);
1775
+ },
1776
+ originWhitelist: ["https://*"],
1777
+ setSupportMultipleWindows: false
1778
+ }
1779
+ ),
1780
+ loading && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.View, { pointerEvents: "none", style: styles11.spinner, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.ActivityIndicator, { color: import_core12.ada.orange }) })
1781
+ ] })
1782
+ ] })
1783
+ }
1784
+ );
1785
+ }
1786
+ var styles11 = import_react_native12.StyleSheet.create({
1787
+ root: {
1788
+ flex: 1,
1789
+ backgroundColor: import_core12.ada.paper
1790
+ },
1791
+ bar: {
1792
+ flexDirection: "row",
1793
+ alignItems: "center",
1794
+ paddingHorizontal: 14,
1795
+ paddingVertical: 12,
1796
+ borderBottomWidth: 1,
1797
+ borderBottomColor: import_core12.ada.border,
1798
+ backgroundColor: import_core12.ada.paperSoft
1799
+ },
1800
+ closeBtn: {
1801
+ width: 32,
1802
+ height: 32,
1803
+ alignItems: "center",
1804
+ justifyContent: "center"
1805
+ },
1806
+ closeIcon: {
1807
+ fontSize: 18,
1808
+ color: import_core12.ada.ink,
1809
+ fontWeight: "500"
1810
+ },
1811
+ title: {
1812
+ flex: 1,
1813
+ textAlign: "center",
1814
+ fontSize: 16,
1815
+ fontWeight: "600",
1816
+ color: import_core12.ada.ink,
1817
+ fontFamily: fontFam4,
1818
+ letterSpacing: -0.2
1819
+ },
1820
+ web: {
1821
+ flex: 1,
1822
+ backgroundColor: import_core12.ada.paper
1823
+ },
1824
+ spinner: {
1825
+ position: "absolute",
1826
+ top: 80,
1827
+ left: 0,
1828
+ right: 0,
1829
+ alignItems: "center"
1830
+ },
1831
+ errorWrap: {
1832
+ flex: 1,
1833
+ alignItems: "center",
1834
+ justifyContent: "center",
1835
+ paddingHorizontal: 24
1836
+ },
1837
+ errorText: {
1838
+ fontSize: 15,
1839
+ color: import_core12.ada.ink,
1840
+ fontWeight: "600",
1841
+ marginBottom: 6
1842
+ },
1843
+ errorSub: {
1844
+ fontSize: 13,
1845
+ color: import_core12.ada.inkMuted,
1846
+ textAlign: "center"
1847
+ }
1848
+ });
1849
+ // Annotate the CommonJS export names for ESM import in node:
1850
+ 0 && (module.exports = {
1851
+ AdasChatScreen,
1852
+ ChatInput,
1853
+ DaySeparator,
1854
+ EmbeddedPluginWebView,
1855
+ JobProgressBar,
1856
+ Lightbox,
1857
+ MessageBubble,
1858
+ Plugin,
1859
+ PluginSurface,
1860
+ TableCardBlock,
1861
+ TypingIndicator,
1862
+ UIActionsRow,
1863
+ WidgetWebView,
1864
+ buildPluginBridgeJS,
1865
+ setClipboardProvider,
1866
+ setUIActionNavigator,
1867
+ useLightboxStore,
1868
+ usePluginBridge
1869
+ });
1870
+ //# sourceMappingURL=index.js.map